From db23ca7c8bd8c3c7ee6bc4ec5adece781ce494fa Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 7 Feb 2017 17:09:33 -0800 Subject: [PATCH] Guard public API surface of TypeChecker against synthesized nodes --- src/compiler/checker.ts | 157 ++++++++++++++++++++++++++++---------- src/compiler/utilities.ts | 14 +++- 2 files changed, 129 insertions(+), 42 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4423b574e3..7ec623e277 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -64,6 +64,11 @@ namespace ts { undefinedSymbol.declarations = []; 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 = { getNodeCount: () => sum(host.getSourceFiles(), "nodeCount"), getIdentifierCount: () => sum(host.getSourceFiles(), "identifierCount"), @@ -74,8 +79,15 @@ namespace ts { isUnknownSymbol: symbol => symbol === unknownSymbol, getDiagnostics, getGlobalDiagnostics, - getTypeOfSymbolAtLocation, - getSymbolsOfParameterPropertyDeclaration, + getTypeOfSymbolAtLocation: (symbol, location) => { + 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, getPropertiesOfType, getPropertyOfType, @@ -83,37 +95,88 @@ namespace ts { getSignaturesOfType, getIndexTypeOfType, getBaseTypes, - getTypeFromTypeNode, + getTypeFromTypeNode: node => { + node = getParseTreeNode(node, isTypeNode); + return node ? getTypeFromTypeNode(node) : unknownType; + }, getParameterType: getTypeAtPosition, getReturnTypeOfSignature, getNonNullableType, - getSymbolsInScope, - getSymbolAtLocation, - getShorthandAssignmentValueSymbol, - getExportSpecifierLocalTargetSymbol, - getTypeAtLocation: getTypeOfNode, - getPropertySymbolOfDestructuringAssignment, - signatureToString, - typeToString, + getSymbolsInScope: (location, meaning) => { + location = getParseTreeNode(location); + return location ? getSymbolsInScope(location, meaning) : []; + }, + getSymbolAtLocation: node => { + node = getParseTreeNode(node); + return node ? getSymbolAtLocation(node) : undefined; + }, + 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, - symbolToString, + symbolToString: (symbol, enclosingDeclaration?, meaning?) => { + return symbolToString(symbol, getParseTreeNode(enclosingDeclaration), meaning); + }, getAugmentedPropertiesOfType, getRootSymbols, - getContextualType, + getContextualType: node => { + node = getParseTreeNode(node, isExpression) + return node ? getContextualType(node) : undefined; + }, getFullyQualifiedName, - getResolvedSignature, - getConstantValue, - isValidPropertyAccess, - getSignatureFromDeclaration, - isImplementationOfOverload, + getResolvedSignature: (node, candidatesOutArray?) => { + node = getParseTreeNode(node, isCallLikeExpression); + return node ? getResolvedSignature(node, candidatesOutArray) : undefined; + }, + 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, getEmitResolver, getExportsOfModule: getExportsOfModuleAsArray, getExportsAndPropertiesOfModule, getAmbientModules, - getJsxElementAttributesType, + getJsxElementAttributesType: node => { + node = getParseTreeNode(node, isJsxOpeningLikeElement); + return node ? getJsxElementAttributesType(node) : undefined; + }, getJsxIntrinsicTagNames, - isOptionalParameter, + isOptionalParameter: node => { + node = getParseTreeNode(node, isParameter); + return node ? isOptionalParameter(node) : false; + }, tryGetMemberInModuleExports, tryFindAmbientModuleWithoutAugmentations: moduleName => { // we deliberately exclude augmentations @@ -19819,14 +19882,14 @@ namespace ts { } function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { - const symbols = createMap(); - let memberFlags: ModifierFlags = ModifierFlags.None; - if (isInsideWithStatementBody(location)) { // We cannot answer semantic questions within a with block, do not proceed any further return []; } + const symbols = createMap(); + let memberFlags: ModifierFlags = ModifierFlags.None; + populateSymbols(); return symbolsToArray(symbols); @@ -20086,6 +20149,7 @@ namespace ts { if (node.kind === SyntaxKind.SourceFile) { return isExternalModule(node) ? getMergedSymbol(node.symbol) : undefined; } + if (isInsideWithStatementBody(node)) { // We cannot answer semantic questions within a with block, do not proceed any further return undefined; @@ -20536,12 +20600,6 @@ namespace ts { } 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) { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ImportClause: @@ -20588,12 +20646,6 @@ namespace ts { } function isReferencedAliasDeclaration(node: Node, checkChildren?: boolean): boolean { - node = getParseTreeNode(node); - // Purely synthesized nodes are always emitted. - if (node === undefined) { - return true; - } - if (isAliasSymbolDeclaration(node)) { const symbol = getSymbolOfNode(node); if (symbol && getSymbolLinks(symbol).referenced) { @@ -20629,8 +20681,7 @@ namespace ts { } function getNodeCheckFlags(node: Node): NodeCheckFlags { - node = getParseTreeNode(node); - return node ? getNodeLinks(node).flags : undefined; + return getNodeLinks(node).flags; } function getEnumMemberValue(node: EnumMember): number { @@ -20638,6 +20689,16 @@ namespace ts { 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 { if (node.kind === SyntaxKind.EnumMember) { return getEnumMemberValue(node); @@ -20814,10 +20875,21 @@ namespace ts { getReferencedImportDeclaration, getReferencedDeclarationWithCollidingName, isDeclarationWithCollidingName, - isValueAliasDeclaration, + isValueAliasDeclaration: node => { + node = getParseTreeNode(node); + // Synthesized nodes are always treated like values. + return node ? isValueAliasDeclaration(node) : true; + }, hasGlobalName, - isReferencedAliasDeclaration, - getNodeCheckFlags, + isReferencedAliasDeclaration: (node, checkChildren?) => { + 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, isDeclarationVisible, isImplementationOfOverload, @@ -20827,7 +20899,10 @@ namespace ts { writeBaseConstructorTypeOfClass, isSymbolAccessible, isEntityNameVisible, - getConstantValue, + getConstantValue: node => { + node = getParseTreeNode(node, canHaveConstantValue); + return node ? getConstantValue(node) : undefined; + }, collectLinkedAliases, getReferencedValueDeclaration, getTypeReferenceSerializationKind, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 91cd6a89b8..a38578a7a8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3731,6 +3731,12 @@ namespace ts { 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 { return node.kind === SyntaxKind.ElementAccessExpression; } @@ -4081,6 +4087,12 @@ namespace ts { || kind === SyntaxKind.JsxExpression; } + export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement { + const kind = node.kind; + return kind === SyntaxKind.JsxOpeningElement + || kind === SyntaxKind.JsxSelfClosingElement; + } + // Clauses export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { @@ -4531,7 +4543,7 @@ namespace ts { */ export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => node is T): T; export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => boolean): Node { - if (isParseTreeNode(node)) { + if (node == undefined || isParseTreeNode(node)) { return node; }