Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose public API for transformation. #13940

Merged
merged 19 commits into from
Feb 17, 2017
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Guard public API surface of TypeChecker against synthesized nodes
  • Loading branch information
rbuckton committed Feb 8, 2017
commit db23ca7c8bd8c3c7ee6bc4ec5adece781ce494fa
157 changes: 116 additions & 41 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
@@ -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,46 +79,104 @@ 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,
getIndexInfoOfType,
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<Symbol>();
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<Symbol>();
let memberFlags: ModifierFlags = ModifierFlags.None;

populateSymbols();

return symbolsToArray(symbols);
@@ -20086,6 +20149,7 @@ namespace ts {
if (node.kind === SyntaxKind.SourceFile) {
return isExternalModule(<SourceFile>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,15 +20681,24 @@ 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 {
computeEnumMemberValues(<EnumDeclaration>node.parent);
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(<EnumMember>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,
14 changes: 13 additions & 1 deletion src/compiler/utilities.ts
Original file line number Diff line number Diff line change
@@ -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<T extends Node>(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;
}