Guard public API surface of TypeChecker against synthesized nodes
This commit is contained in:
parent
75fa22c682
commit
db23ca7c8b
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue