Guard public API surface of TypeChecker against synthesized nodes

This commit is contained in:
Ron Buckton 2017-02-07 17:09:33 -08:00
parent 75fa22c682
commit db23ca7c8b
2 changed files with 129 additions and 42 deletions

View file

@ -64,6 +64,11 @@ namespace ts {
undefinedSymbol.declarations = []; undefinedSymbol.declarations = [];
const argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments"); const argumentsSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "arguments");
// for public members that accept a Node or one of its subtypes, we must guard against
// synthetic nodes created during transformations by calling `getParseTreeNode`.
// for most of these, we perform the guard only on `checker` to avoid any possible
// extra cost of calling `getParseTreeNode` when calling these functions from inside the
// checker.
const checker: TypeChecker = { const checker: TypeChecker = {
getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"),
getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"),
@ -74,8 +79,15 @@ namespace ts {
isUnknownSymbol: symbol => symbol === unknownSymbol, isUnknownSymbol: symbol => symbol === unknownSymbol,
getDiagnostics, getDiagnostics,
getGlobalDiagnostics, getGlobalDiagnostics,
getTypeOfSymbolAtLocation, getTypeOfSymbolAtLocation: (symbol, location) => {
getSymbolsOfParameterPropertyDeclaration, location = getParseTreeNode(location);
return location ? getTypeOfSymbolAtLocation(symbol, location) : unknownType;
},
getSymbolsOfParameterPropertyDeclaration: (parameter, parameterName) => {
parameter = getParseTreeNode(parameter, isParameter);
Debug.assert(parameter !== undefined, "Cannot get symbols of a synthetic parameter that cannot be resolved to a parse-tree node.");
return getSymbolsOfParameterPropertyDeclaration(parameter, parameterName);
},
getDeclaredTypeOfSymbol, getDeclaredTypeOfSymbol,
getPropertiesOfType, getPropertiesOfType,
getPropertyOfType, getPropertyOfType,
@ -83,37 +95,88 @@ namespace ts {
getSignaturesOfType, getSignaturesOfType,
getIndexTypeOfType, getIndexTypeOfType,
getBaseTypes, getBaseTypes,
getTypeFromTypeNode, getTypeFromTypeNode: node => {
node = getParseTreeNode(node, isTypeNode);
return node ? getTypeFromTypeNode(node) : unknownType;
},
getParameterType: getTypeAtPosition, getParameterType: getTypeAtPosition,
getReturnTypeOfSignature, getReturnTypeOfSignature,
getNonNullableType, getNonNullableType,
getSymbolsInScope, getSymbolsInScope: (location, meaning) => {
getSymbolAtLocation, location = getParseTreeNode(location);
getShorthandAssignmentValueSymbol, return location ? getSymbolsInScope(location, meaning) : [];
getExportSpecifierLocalTargetSymbol, },
getTypeAtLocation: getTypeOfNode, getSymbolAtLocation: node => {
getPropertySymbolOfDestructuringAssignment, node = getParseTreeNode(node);
signatureToString, return node ? getSymbolAtLocation(node) : undefined;
typeToString, },
getShorthandAssignmentValueSymbol: node => {
node = getParseTreeNode(node);
return node ? getShorthandAssignmentValueSymbol(node) : undefined;
},
getExportSpecifierLocalTargetSymbol: node => {
node = getParseTreeNode(node, isExportSpecifier);
return node ? getExportSpecifierLocalTargetSymbol(node) : undefined;
},
getTypeAtLocation: node => {
node = getParseTreeNode(node);
return node ? getTypeOfNode(node) : unknownType;
},
getPropertySymbolOfDestructuringAssignment: location => {
location = getParseTreeNode(location, isIdentifier);
return location ? getPropertySymbolOfDestructuringAssignment(location) : undefined;
},
signatureToString: (signature, enclosingDeclaration?, flags?, kind?) => {
return signatureToString(signature, getParseTreeNode(enclosingDeclaration), flags, kind);
},
typeToString: (type, enclosingDeclaration?, flags?) => {
return typeToString(type, getParseTreeNode(enclosingDeclaration), flags);
},
getSymbolDisplayBuilder, getSymbolDisplayBuilder,
symbolToString, symbolToString: (symbol, enclosingDeclaration?, meaning?) => {
return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning);
},
getAugmentedPropertiesOfType, getAugmentedPropertiesOfType,
getRootSymbols, getRootSymbols,
getContextualType, getContextualType: node => {
node = getParseTreeNode(node, isExpression)
return node ? getContextualType(node) : undefined;
},
getFullyQualifiedName, getFullyQualifiedName,
getResolvedSignature, getResolvedSignature: (node, candidatesOutArray?) => {
getConstantValue, node = getParseTreeNode(node, isCallLikeExpression);
isValidPropertyAccess, return node ? getResolvedSignature(node, candidatesOutArray) : undefined;
getSignatureFromDeclaration, },
isImplementationOfOverload, getConstantValue: node => {
node = getParseTreeNode(node, canHaveConstantValue);
return node ? getConstantValue(node) : undefined;
},
isValidPropertyAccess: (node, propertyName) => {
node = getParseTreeNode(node, isPropertyAccessOrQualifiedName);
return node ? isValidPropertyAccess(node, propertyName) : false;
},
getSignatureFromDeclaration: declaration => {
declaration = getParseTreeNode(declaration, isFunctionLike);
return declaration ? getSignatureFromDeclaration(declaration) : undefined;
},
isImplementationOfOverload: node => {
node = getParseTreeNode(node, isFunctionLike);
return node ? isImplementationOfOverload(node) : undefined;
},
getAliasedSymbol: resolveAlias, getAliasedSymbol: resolveAlias,
getEmitResolver, getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray, getExportsOfModule: getExportsOfModuleAsArray,
getExportsAndPropertiesOfModule, getExportsAndPropertiesOfModule,
getAmbientModules, getAmbientModules,
getJsxElementAttributesType, getJsxElementAttributesType: node => {
node = getParseTreeNode(node, isJsxOpeningLikeElement);
return node ? getJsxElementAttributesType(node) : undefined;
},
getJsxIntrinsicTagNames, getJsxIntrinsicTagNames,
isOptionalParameter, isOptionalParameter: node => {
node = getParseTreeNode(node, isParameter);
return node ? isOptionalParameter(node) : false;
},
tryGetMemberInModuleExports, tryGetMemberInModuleExports,
tryFindAmbientModuleWithoutAugmentations: moduleName => { tryFindAmbientModuleWithoutAugmentations: moduleName => {
// we deliberately exclude augmentations // we deliberately exclude augmentations
@ -19819,14 +19882,14 @@ namespace ts {
} }
function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] {
const symbols = createMap<Symbol>();
let memberFlags: ModifierFlags = ModifierFlags.None;
if (isInsideWithStatementBody(location)) { if (isInsideWithStatementBody(location)) {
// We cannot answer semantic questions within a with block, do not proceed any further // We cannot answer semantic questions within a with block, do not proceed any further
return []; return [];
} }
const symbols = createMap<Symbol>();
let memberFlags: ModifierFlags = ModifierFlags.None;
populateSymbols(); populateSymbols();
return symbolsToArray(symbols); return symbolsToArray(symbols);
@ -20086,6 +20149,7 @@ namespace ts {
if (node.kind === SyntaxKind.SourceFile) { if (node.kind === SyntaxKind.SourceFile) {
return isExternalModule(<SourceFile>node) ? getMergedSymbol(node.symbol) : undefined; return isExternalModule(<SourceFile>node) ? getMergedSymbol(node.symbol) : undefined;
} }
if (isInsideWithStatementBody(node)) { if (isInsideWithStatementBody(node)) {
// We cannot answer semantic questions within a with block, do not proceed any further // We cannot answer semantic questions within a with block, do not proceed any further
return undefined; return undefined;
@ -20536,12 +20600,6 @@ namespace ts {
} }
function isValueAliasDeclaration(node: Node): boolean { function isValueAliasDeclaration(node: Node): boolean {
node = getParseTreeNode(node);
if (node === undefined) {
// A synthesized node comes from an emit transformation and is always a value.
return true;
}
switch (node.kind) { switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ImportClause: case SyntaxKind.ImportClause:
@ -20588,12 +20646,6 @@ namespace ts {
} }
function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean { function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean {
node = getParseTreeNode(node);
// Purely synthesized nodes are always emitted.
if (node === undefined) {
return true;
}
if (isAliasSymbolDeclaration(node)) { if (isAliasSymbolDeclaration(node)) {
const symbol = getSymbolOfNode(node); const symbol = getSymbolOfNode(node);
if (symbol && getSymbolLinks(symbol).referenced) { if (symbol && getSymbolLinks(symbol).referenced) {
@ -20629,8 +20681,7 @@ namespace ts {
} }
function getNodeCheckFlags(node: Node): NodeCheckFlags { function getNodeCheckFlags(node: Node): NodeCheckFlags {
node = getParseTreeNode(node); return getNodeLinks(node).flags;
return node ? getNodeLinks(node).flags : undefined;
} }
function getEnumMemberValue(node: EnumMember): number { function getEnumMemberValue(node: EnumMember): number {
@ -20638,6 +20689,16 @@ namespace ts {
return getNodeLinks(node).enumMemberValue; return getNodeLinks(node).enumMemberValue;
} }
function canHaveConstantValue(node: Node): node is EnumMember | PropertyAccessExpression | ElementAccessExpression {
switch (node.kind) {
case SyntaxKind.EnumMember:
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
return true;
}
return false;
}
function getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number { function getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number {
if (node.kind === SyntaxKind.EnumMember) { if (node.kind === SyntaxKind.EnumMember) {
return getEnumMemberValue(<EnumMember>node); return getEnumMemberValue(<EnumMember>node);
@ -20814,10 +20875,21 @@ namespace ts {
getReferencedImportDeclaration, getReferencedImportDeclaration,
getReferencedDeclarationWithCollidingName, getReferencedDeclarationWithCollidingName,
isDeclarationWithCollidingName, isDeclarationWithCollidingName,
isValueAliasDeclaration, isValueAliasDeclaration: node => {
node = getParseTreeNode(node);
// Synthesized nodes are always treated like values.
return node ? isValueAliasDeclaration(node) : true;
},
hasGlobalName, hasGlobalName,
isReferencedAliasDeclaration, isReferencedAliasDeclaration: (node, checkChildren?) => {
getNodeCheckFlags, node = getParseTreeNode(node);
// Synthesized nodes are always treated as referenced.
return node ? isReferencedAliasDeclaration(node, checkChildren) : true;
},
getNodeCheckFlags: node => {
node = getParseTreeNode(node);
return node ? getNodeCheckFlags(node) : undefined;
},
isTopLevelValueImportEqualsWithEntityName, isTopLevelValueImportEqualsWithEntityName,
isDeclarationVisible, isDeclarationVisible,
isImplementationOfOverload, isImplementationOfOverload,
@ -20827,7 +20899,10 @@ namespace ts {
writeBaseConstructorTypeOfClass, writeBaseConstructorTypeOfClass,
isSymbolAccessible, isSymbolAccessible,
isEntityNameVisible, isEntityNameVisible,
getConstantValue, getConstantValue: node => {
node = getParseTreeNode(node, canHaveConstantValue);
return node ? getConstantValue(node) : undefined;
},
collectLinkedAliases, collectLinkedAliases,
getReferencedValueDeclaration, getReferencedValueDeclaration,
getTypeReferenceSerializationKind, getTypeReferenceSerializationKind,

View file

@ -3731,6 +3731,12 @@ namespace ts {
return node.kind === SyntaxKind.PropertyAccessExpression; return node.kind === SyntaxKind.PropertyAccessExpression;
} }
export function isPropertyAccessOrQualifiedName(node: Node): node is PropertyAccessExpression | QualifiedName {
const kind = node.kind;
return kind === SyntaxKind.PropertyAccessExpression
|| kind === SyntaxKind.QualifiedName;
}
export function isElementAccessExpression(node: Node): node is ElementAccessExpression { export function isElementAccessExpression(node: Node): node is ElementAccessExpression {
return node.kind === SyntaxKind.ElementAccessExpression; return node.kind === SyntaxKind.ElementAccessExpression;
} }
@ -4081,6 +4087,12 @@ namespace ts {
|| kind === SyntaxKind.JsxExpression; || kind === SyntaxKind.JsxExpression;
} }
export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement {
const kind = node.kind;
return kind === SyntaxKind.JsxOpeningElement
|| kind === SyntaxKind.JsxSelfClosingElement;
}
// Clauses // Clauses
export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause {
@ -4531,7 +4543,7 @@ namespace ts {
*/ */
export function getParseTreeNode<T extends Node>(node: Node, nodeTest?: (node: Node) => node is T): T; export function getParseTreeNode<T extends Node>(node: Node, nodeTest?: (node: Node) => node is T): T;
export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => boolean): Node { export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => boolean): Node {
if (isParseTreeNode(node)) { if (node == undefined || isParseTreeNode(node)) {
return node; return node;
} }