From 053b91506103cf185d852d816f4010e162e3729e Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 20 Jul 2016 14:58:46 -0700 Subject: [PATCH 01/10] Rebase SymbolWalker change onto master From PR #9847. --- Jakefile.js | 1 + src/compiler/checker.ts | 2 + src/compiler/symbolWalker.ts | 163 ++++++++++++++++++++++++++ src/compiler/tsconfig.json | 1 + src/compiler/types.ts | 8 ++ src/harness/tsconfig.json | 2 + src/harness/unittests/symbolWalker.ts | 53 +++++++++ src/services/tsconfig.json | 1 + 8 files changed, 231 insertions(+) create mode 100644 src/compiler/symbolWalker.ts create mode 100644 src/harness/unittests/symbolWalker.ts diff --git a/Jakefile.js b/Jakefile.js index 15fe40141f653..46573e701ff21 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -140,6 +140,7 @@ var harnessSources = harnessCoreSources.concat([ "transform.ts", "customTransforms.ts", "programMissingFiles.ts", + "symbolWalker.ts", ].map(function (f) { return path.join(unittestsDirectory, f); })).concat([ diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d2d3697f175c6..d69c6cdebdadc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1,5 +1,6 @@ /// /// +/// /* @internal */ namespace ts { @@ -204,6 +205,7 @@ namespace ts { getEmitResolver, getExportsOfModule: getExportsOfModuleAsArray, getExportsAndPropertiesOfModule, + getSymbolWalker: createGetSymbolWalker(getRestTypeOfSignature, getReturnTypeOfSignature, getBaseTypes, resolveStructuredTypeMembers, getTypeOfSymbol, getResolvedSymbol, getIndexTypeOfStructuredType), getAmbientModules, getAllAttributesTypeFromJsxOpeningLikeElement: node => { node = getParseTreeNode(node, isJsxOpeningLikeElement); diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts new file mode 100644 index 0000000000000..c85661df29a1a --- /dev/null +++ b/src/compiler/symbolWalker.ts @@ -0,0 +1,163 @@ +namespace ts { + export function createGetSymbolWalker( + getRestTypeOfSignature: (sig: Signature) => Type, + getReturnTypeOfSignature: (sig: Signature) => Type, + getBaseTypes: (type: Type) => Type[], + resolveStructuredTypeMembers: (type: ObjectType) => ResolvedType, + getTypeOfSymbol: (sym: Symbol) => Type, + getResolvedSymbol: (node: Node) => Symbol, + getIndexTypeOfStructuredType: (type: Type, kind: IndexKind) => Type) { + + return getSymbolWalker; + + function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker { + let visited: Type[] = []; + let visitedSymbols: Symbol[] = []; + + return { + visitType, + visitSymbol, + reset: (newCallback: (symbol: Symbol) => boolean = () => true) => { + accept = newCallback; + visited = []; + visitedSymbols = []; + } + }; + + function visitType(type: Type): void { + if (!type) { + return; + } + if (contains(visited, type)) { + return; + } + visited.push(type); + + // Reuse visitSymbol to visit the type's symbol, + // but be sure to bail on recuring into the type if accept declines the symbol. + const shouldBail = visitSymbol(type.symbol); + if (shouldBail) return; + + // Visit the type's related types, if any + if (type.flags & TypeFlags.Object) { + const objectType = type as ObjectType; + const objectFlags = objectType.objectFlags; + if (objectFlags & ObjectFlags.Reference) { + visitTypeReference(type as TypeReference); + } + if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) { + visitInterfaceType(type as InterfaceType); + } + if (objectFlags & (ObjectFlags.Tuple | ObjectFlags.Anonymous)) { + visitObjectType(objectType); + } + } + if (type.flags & TypeFlags.TypeParameter) { + visitTypeParameter(type as TypeParameter); + } + if (type.flags & TypeFlags.UnionOrIntersection) { + visitUnionOrIntersectionType(type as UnionOrIntersectionType); + } + } + + function visitTypeList(types: Type[]): void { + if (!types) { + return; + } + for (let i = 0; i < types.length; i++) { + visitType(types[i]); + } + } + + function visitTypeReference(type: TypeReference): void { + visitType(type.target); + visitTypeList(type.typeArguments); + } + + function visitTypeParameter(type: TypeParameter): void { + visitType(type.constraint); + } + + function visitUnionOrIntersectionType(type: UnionOrIntersectionType): void { + visitTypeList(type.types); + } + + function visitSignature(signature: Signature): void { + if (signature.typePredicate) { + visitType(signature.typePredicate.type); + } + visitTypeList(signature.typeParameters); + + for (const parameter of signature.parameters){ + visitSymbol(parameter); + } + visitType(getRestTypeOfSignature(signature)); + visitType(getReturnTypeOfSignature(signature)); + } + + function visitInterfaceType(interfaceT: InterfaceType): void { + visitObjectType(interfaceT); + visitTypeList(interfaceT.typeParameters); + visitTypeList(getBaseTypes(interfaceT)); + visitType(interfaceT.thisType); + } + + function visitObjectType(type: ObjectType): void { + const stringIndexType = getIndexTypeOfStructuredType(type, IndexKind.String); + visitType(stringIndexType); + const numberIndexType = getIndexTypeOfStructuredType(type, IndexKind.String); + visitType(numberIndexType); + + // The two checks above *should* have already resolved the type (if needed), so this should be cached + const resolved = resolveStructuredTypeMembers(type); + for (const signature of resolved.callSignatures) { + visitSignature(signature); + } + for (const signature of resolved.constructSignatures) { + visitSignature(signature); + } + for (const p of resolved.properties) { + visitSymbol(p); + } + } + + function visitSymbol(symbol: Symbol): boolean { + if (!symbol) { + return; + } + if (contains(visitedSymbols, symbol)) { + return; + } + visitedSymbols.push(symbol); + if (!accept(symbol)) { + return true; + } + const t = getTypeOfSymbol(symbol); + visitType(t); // Should handle members on classes and such + if (symbol.flags & SymbolFlags.HasExports) { + symbol.exports.forEach(visitSymbol); + } + forEach(symbol.declarations, d => { + // Type queries are too far resolved when we just visit the symbol's type + // (their type resolved directly to the member deeply referenced) + // So to get the intervening symbols, we need to check if there's a type + // query node on any of the symbol's declarations and get symbols there + if ((d as any).type && (d as any).type.kind === SyntaxKind.TypeQuery) { + const query = (d as any).type as TypeQueryNode; + const entity = leftmostSymbol(query.exprName); + visitSymbol(entity); + } + }); + } + } + + function leftmostSymbol(expr: QualifiedName | Identifier): Symbol { + if (expr.kind === SyntaxKind.Identifier) { + return getResolvedSymbol(expr as Identifier); + } + else { + return leftmostSymbol((expr as QualifiedName).left); + } + } + } +} \ No newline at end of file diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 3709d65b7fd23..c048359fcb7a4 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -14,6 +14,7 @@ "parser.ts", "utilities.ts", "binder.ts", + "symbolWalker.ts", "checker.ts", "factory.ts", "visitor.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 608bc779042df..7bce522d5bac7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2625,6 +2625,8 @@ namespace ts { /* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined; + getSymbolWalker(accept?: (symbol: Symbol) => boolean): SymbolWalker; + // Should not be called directly. Should only be accessed through the Program instance. /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; /* @internal */ getGlobalDiagnostics(): Diagnostic[]; @@ -2669,6 +2671,12 @@ namespace ts { InTypeAlias = 1 << 23, // Writing type in type alias declaration } + export interface SymbolWalker { + visitType(type: Type): void; + visitSymbol(symbol: Symbol): void; + reset(accept?: (symbol: Symbol) => boolean): void; + } + export interface SymbolDisplayBuilder { buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void; diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 66ca2fc3f4837..9165e59cb0a1a 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -21,6 +21,7 @@ "../compiler/parser.ts", "../compiler/utilities.ts", "../compiler/binder.ts", + "../compiler/symbolWalker.ts", "../compiler/checker.ts", "../compiler/factory.ts", "../compiler/visitor.ts", @@ -103,6 +104,7 @@ "./unittests/services/preProcessFile.ts", "./unittests/services/patternMatcher.ts", "./unittests/session.ts", + "./unittests/symbolWalker.ts", "./unittests/versionCache.ts", "./unittests/convertToBase64.ts", "./unittests/transpile.ts", diff --git a/src/harness/unittests/symbolWalker.ts b/src/harness/unittests/symbolWalker.ts new file mode 100644 index 0000000000000..275c37d41f673 --- /dev/null +++ b/src/harness/unittests/symbolWalker.ts @@ -0,0 +1,53 @@ +/// + +namespace ts { + describe("Symbol Walker", () => { + function test(description: string, source: string, verifier: (file: SourceFile, checker: TypeChecker, walker: SymbolWalker) => void) { + it(description, () => { + let {result} = Harness.Compiler.compileFiles([{ + unitName: "main.ts", + content: source + }], [], {}, {}, "/"); + let file = result.program.getSourceFile("main.ts"); + let checker = result.program.getTypeChecker(); + let walker = checker.getSymbolWalker(); + verifier(file, checker, walker); + + result = undefined; + file = undefined; + checker = undefined; + walker = undefined; + }); + } + + test("can be created", ` +interface Bar { + x: number; + y: number; + history: Bar[]; +} +export default function foo(a: number, b: Bar): void {}`, (file, checker, walker) => { + let foundCount = 0; + let stdLibRefSymbols = 0; + const expectedSymbols = ["default", "a", "b", "Bar", "x", "y", "history"]; + walker.reset(symbol => { + const isStdLibSymbol = forEach(symbol.declarations, d => { + return getSourceFileOfNode(d).hasNoDefaultLib; + }); + if (isStdLibSymbol) { + stdLibRefSymbols++; + return false; // Don't traverse into the stdlib. That's unnecessary for this test. + } + assert.equal(symbol.name, expectedSymbols[foundCount]); + foundCount++; + return true; + }); + const symbols = checker.getExportsOfModule(file.symbol); + for (const symbol of symbols) { + walker.visitSymbol(symbol); + } + assert.equal(foundCount, expectedSymbols.length); + assert.equal(stdLibRefSymbols, 1); // Expect 1 stdlib entry symbol - the implicit Array referenced by Bar.history + }); + }); +} \ No newline at end of file diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index f4ca2a7f130f5..d73014a93a24a 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -14,6 +14,7 @@ "../compiler/parser.ts", "../compiler/utilities.ts", "../compiler/binder.ts", + "../compiler/symbolWalker.ts", "../compiler/checker.ts", "../compiler/factory.ts", "../compiler/visitor.ts", From 2c8a5c40b835d01c0edb2b5c4ae419f2763ff65f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Jul 2016 10:35:49 -0700 Subject: [PATCH 02/10] Make SymbolWalker internal ...until required by an external consumer. --- src/compiler/symbolWalker.ts | 2 ++ src/compiler/types.ts | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index c85661df29a1a..0ae1e32ea9a15 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -1,4 +1,6 @@ +/** @internal */ namespace ts { + /** @internal */ export function createGetSymbolWalker( getRestTypeOfSignature: (sig: Signature) => Type, getReturnTypeOfSignature: (sig: Signature) => Type, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7bce522d5bac7..76922ab98e6ef 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2625,7 +2625,7 @@ namespace ts { /* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined; - getSymbolWalker(accept?: (symbol: Symbol) => boolean): SymbolWalker; + /* @internal */ getSymbolWalker(accept?: (symbol: Symbol) => boolean): SymbolWalker; // Should not be called directly. Should only be accessed through the Program instance. /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; @@ -2671,6 +2671,7 @@ namespace ts { InTypeAlias = 1 << 23, // Writing type in type alias declaration } + /* @internal */ export interface SymbolWalker { visitType(type: Type): void; visitSymbol(symbol: Symbol): void; From 801c1f70a2cc6aecac3f5bdbf872adf469726243 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 15 Aug 2017 13:09:24 -0700 Subject: [PATCH 03/10] Reshape SymbolWalker API 1. Expose visited types and symbols 2. Automatically reset before each walk --- src/compiler/symbolWalker.ts | 25 ++++++++++++++++--------- src/compiler/types.ts | 5 ++--- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index 0ae1e32ea9a15..784ee84036308 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -13,27 +13,34 @@ namespace ts { return getSymbolWalker; function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker { - let visited: Type[] = []; + let visitedTypes: Type[] = []; let visitedSymbols: Symbol[] = []; return { - visitType, - visitSymbol, - reset: (newCallback: (symbol: Symbol) => boolean = () => true) => { - accept = newCallback; - visited = []; + walkType: type => + { + visitedTypes = []; visitedSymbols = []; - } + visitType(type); + return { visitedTypes, visitedSymbols }; + }, + walkSymbol: symbol => + { + visitedTypes = []; + visitedSymbols = []; + visitSymbol(symbol); + return { visitedTypes, visitedSymbols }; + }, }; function visitType(type: Type): void { if (!type) { return; } - if (contains(visited, type)) { + if (contains(visitedTypes, type)) { return; } - visited.push(type); + visitedTypes.push(type); // Reuse visitSymbol to visit the type's symbol, // but be sure to bail on recuring into the type if accept declines the symbol. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 76922ab98e6ef..34ae124d636d3 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2673,9 +2673,8 @@ namespace ts { /* @internal */ export interface SymbolWalker { - visitType(type: Type): void; - visitSymbol(symbol: Symbol): void; - reset(accept?: (symbol: Symbol) => boolean): void; + walkType(root: Type) : { visitedTypes: Type[], visitedSymbols: Symbol[] }; + walkSymbol(root: Symbol) : { visitedTypes: Type[], visitedSymbols: Symbol[] }; } export interface SymbolDisplayBuilder { From f2eacc6395c1847da2425e761d03a852a79dc85b Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 15 Aug 2017 14:45:36 -0700 Subject: [PATCH 04/10] Use Maps to store visited types and symbols --- src/compiler/symbolWalker.ts | 27 +++++++++++++++------------ src/compiler/types.ts | 6 ++++-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index 784ee84036308..fda1febfb0467 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -13,23 +13,23 @@ namespace ts { return getSymbolWalker; function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker { - let visitedTypes: Type[] = []; - let visitedSymbols: Symbol[] = []; + let visitedTypes = createMap(); // Key is id as string + let visitedSymbols = createMap(); // Key is id as string return { walkType: type => { - visitedTypes = []; - visitedSymbols = []; + visitedTypes.clear(); + visitedSymbols.clear(); visitType(type); - return { visitedTypes, visitedSymbols }; + return { visitedTypes: arrayFrom(visitedTypes.values()), visitedSymbols: arrayFrom(visitedSymbols.values()) }; }, walkSymbol: symbol => { - visitedTypes = []; - visitedSymbols = []; + visitedTypes.clear(); + visitedSymbols.clear(); visitSymbol(symbol); - return { visitedTypes, visitedSymbols }; + return { visitedTypes: arrayFrom(visitedTypes.values()), visitedSymbols: arrayFrom(visitedSymbols.values()) }; }, }; @@ -37,10 +37,12 @@ namespace ts { if (!type) { return; } - if (contains(visitedTypes, type)) { + + const typeIdString = type.id.toString(); + if (visitedTypes.has(typeIdString)) { return; } - visitedTypes.push(type); + visitedTypes.set(typeIdString, type); // Reuse visitSymbol to visit the type's symbol, // but be sure to bail on recuring into the type if accept declines the symbol. @@ -134,10 +136,11 @@ namespace ts { if (!symbol) { return; } - if (contains(visitedSymbols, symbol)) { + const symbolIdString = getSymbolId(symbol).toString(); + if (visitedSymbols.has(symbolIdString)) { return; } - visitedSymbols.push(symbol); + visitedSymbols.set(symbolIdString, symbol); if (!accept(symbol)) { return true; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 34ae124d636d3..2182f02afe0cd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2673,8 +2673,10 @@ namespace ts { /* @internal */ export interface SymbolWalker { - walkType(root: Type) : { visitedTypes: Type[], visitedSymbols: Symbol[] }; - walkSymbol(root: Symbol) : { visitedTypes: Type[], visitedSymbols: Symbol[] }; + /** Note: Return values are not ordered. */ + walkType(root: Type) : { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; + /** Note: Return values are not ordered. */ + walkSymbol(root: Symbol) : { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; } export interface SymbolDisplayBuilder { From 129ace5047d787ab55396680fe23746e78074eaa Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 15 Aug 2017 16:06:54 -0700 Subject: [PATCH 05/10] Update SymbolWalker tests ...to consume revised API. --- src/harness/unittests/symbolWalker.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/harness/unittests/symbolWalker.ts b/src/harness/unittests/symbolWalker.ts index 275c37d41f673..8857fb66e0e96 100644 --- a/src/harness/unittests/symbolWalker.ts +++ b/src/harness/unittests/symbolWalker.ts @@ -2,7 +2,7 @@ namespace ts { describe("Symbol Walker", () => { - function test(description: string, source: string, verifier: (file: SourceFile, checker: TypeChecker, walker: SymbolWalker) => void) { + function test(description: string, source: string, verifier: (file: SourceFile, checker: TypeChecker) => void) { it(description, () => { let {result} = Harness.Compiler.compileFiles([{ unitName: "main.ts", @@ -10,13 +10,11 @@ namespace ts { }], [], {}, {}, "/"); let file = result.program.getSourceFile("main.ts"); let checker = result.program.getTypeChecker(); - let walker = checker.getSymbolWalker(); - verifier(file, checker, walker); + verifier(file, checker); result = undefined; file = undefined; checker = undefined; - walker = undefined; }); } @@ -26,11 +24,11 @@ interface Bar { y: number; history: Bar[]; } -export default function foo(a: number, b: Bar): void {}`, (file, checker, walker) => { +export default function foo(a: number, b: Bar): void {}`, (file, checker) => { let foundCount = 0; let stdLibRefSymbols = 0; const expectedSymbols = ["default", "a", "b", "Bar", "x", "y", "history"]; - walker.reset(symbol => { + let walker = checker.getSymbolWalker(symbol => { const isStdLibSymbol = forEach(symbol.declarations, d => { return getSourceFileOfNode(d).hasNoDefaultLib; }); @@ -44,7 +42,7 @@ export default function foo(a: number, b: Bar): void {}`, (file, checker, walker }); const symbols = checker.getExportsOfModule(file.symbol); for (const symbol of symbols) { - walker.visitSymbol(symbol); + walker.walkSymbol(symbol); } assert.equal(foundCount, expectedSymbols.length); assert.equal(stdLibRefSymbols, 1); // Expect 1 stdlib entry symbol - the implicit Array referenced by Bar.history From 8cbf42cff534682a4a5f5ec2182152eafb338f76 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 16 Aug 2017 14:54:59 -0700 Subject: [PATCH 06/10] Fix lint errors --- src/compiler/symbolWalker.ts | 10 ++++------ src/compiler/types.ts | 4 ++-- src/harness/unittests/symbolWalker.ts | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index fda1febfb0467..ade9e12d03950 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -13,19 +13,17 @@ namespace ts { return getSymbolWalker; function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker { - let visitedTypes = createMap(); // Key is id as string - let visitedSymbols = createMap(); // Key is id as string + const visitedTypes = createMap(); // Key is id as string + const visitedSymbols = createMap(); // Key is id as string return { - walkType: type => - { + walkType: type => { visitedTypes.clear(); visitedSymbols.clear(); visitType(type); return { visitedTypes: arrayFrom(visitedTypes.values()), visitedSymbols: arrayFrom(visitedSymbols.values()) }; }, - walkSymbol: symbol => - { + walkSymbol: symbol => { visitedTypes.clear(); visitedSymbols.clear(); visitSymbol(symbol); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2182f02afe0cd..37e12cc9d294c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2674,9 +2674,9 @@ namespace ts { /* @internal */ export interface SymbolWalker { /** Note: Return values are not ordered. */ - walkType(root: Type) : { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; + walkType(root: Type): { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; /** Note: Return values are not ordered. */ - walkSymbol(root: Symbol) : { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; + walkSymbol(root: Symbol): { visitedTypes: ReadonlyArray, visitedSymbols: ReadonlyArray }; } export interface SymbolDisplayBuilder { diff --git a/src/harness/unittests/symbolWalker.ts b/src/harness/unittests/symbolWalker.ts index 8857fb66e0e96..6d38fbb5198c9 100644 --- a/src/harness/unittests/symbolWalker.ts +++ b/src/harness/unittests/symbolWalker.ts @@ -28,7 +28,7 @@ export default function foo(a: number, b: Bar): void {}`, (file, checker) => { let foundCount = 0; let stdLibRefSymbols = 0; const expectedSymbols = ["default", "a", "b", "Bar", "x", "y", "history"]; - let walker = checker.getSymbolWalker(symbol => { + const walker = checker.getSymbolWalker(symbol => { const isStdLibSymbol = forEach(symbol.declarations, d => { return getSourceFileOfNode(d).hasNoDefaultLib; }); From d7ace2086fc9a2e46d5381444c421e861ff105b3 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 17 Aug 2017 13:17:51 -0700 Subject: [PATCH 07/10] Fix copy-paste error --- src/compiler/symbolWalker.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index ade9e12d03950..39f7f131c37bc 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -1,6 +1,5 @@ /** @internal */ namespace ts { - /** @internal */ export function createGetSymbolWalker( getRestTypeOfSignature: (sig: Signature) => Type, getReturnTypeOfSignature: (sig: Signature) => Type, @@ -114,7 +113,7 @@ namespace ts { function visitObjectType(type: ObjectType): void { const stringIndexType = getIndexTypeOfStructuredType(type, IndexKind.String); visitType(stringIndexType); - const numberIndexType = getIndexTypeOfStructuredType(type, IndexKind.String); + const numberIndexType = getIndexTypeOfStructuredType(type, IndexKind.Number); visitType(numberIndexType); // The two checks above *should* have already resolved the type (if needed), so this should be cached From 1a20b6a7c337d62a42d22e9e050bf64dc78f6679 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 17 Aug 2017 13:23:11 -0700 Subject: [PATCH 08/10] Add support for walking IndexTypes, IndexedAccessTypes, and MappedTypes. --- src/compiler/symbolWalker.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index 39f7f131c37bc..7f1745b8742e9 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -53,6 +53,9 @@ namespace ts { if (objectFlags & ObjectFlags.Reference) { visitTypeReference(type as TypeReference); } + if (objectFlags & ObjectFlags.Mapped) { + visitMappedType(type as MappedType); + } if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) { visitInterfaceType(type as InterfaceType); } @@ -66,6 +69,12 @@ namespace ts { if (type.flags & TypeFlags.UnionOrIntersection) { visitUnionOrIntersectionType(type as UnionOrIntersectionType); } + if (type.flags & TypeFlags.Index) { + visitIndexType(type as IndexType); + } + if (type.flags & TypeFlags.IndexedAccess) { + visitIndexedAccessType(type as IndexedAccessType); + } } function visitTypeList(types: Type[]): void { @@ -90,6 +99,23 @@ namespace ts { visitTypeList(type.types); } + function visitIndexType(type: IndexType): void { + visitType(type.type); + } + + function visitIndexedAccessType(type: IndexedAccessType): void { + visitType(type.objectType); + visitType(type.indexType); + visitType(type.constraint); + } + + function visitMappedType(type: MappedType): void { + visitType(type.typeParameter); + visitType(type.constraintType); + visitType(type.templateType); + visitType(type.modifiersType); + } + function visitSignature(signature: Signature): void { if (signature.typePredicate) { visitType(signature.typePredicate.type); From e02da343db817b148d2b533f6ec392b69f297a1b Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 17 Aug 2017 15:44:06 -0700 Subject: [PATCH 09/10] Retrieve type parameter constraint using getConstraintFromTypeParameter --- src/compiler/checker.ts | 2 +- src/compiler/symbolWalker.ts | 5 +++-- src/compiler/types.ts | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d69c6cdebdadc..4781f4bc18386 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -205,7 +205,7 @@ namespace ts { getEmitResolver, getExportsOfModule: getExportsOfModuleAsArray, getExportsAndPropertiesOfModule, - getSymbolWalker: createGetSymbolWalker(getRestTypeOfSignature, getReturnTypeOfSignature, getBaseTypes, resolveStructuredTypeMembers, getTypeOfSymbol, getResolvedSymbol, getIndexTypeOfStructuredType), + getSymbolWalker: createGetSymbolWalker(getRestTypeOfSignature, getReturnTypeOfSignature, getBaseTypes, resolveStructuredTypeMembers, getTypeOfSymbol, getResolvedSymbol, getIndexTypeOfStructuredType, getConstraintFromTypeParameter), getAmbientModules, getAllAttributesTypeFromJsxOpeningLikeElement: node => { node = getParseTreeNode(node, isJsxOpeningLikeElement); diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index 7f1745b8742e9..6567870c961d4 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -7,7 +7,8 @@ namespace ts { resolveStructuredTypeMembers: (type: ObjectType) => ResolvedType, getTypeOfSymbol: (sym: Symbol) => Type, getResolvedSymbol: (node: Node) => Symbol, - getIndexTypeOfStructuredType: (type: Type, kind: IndexKind) => Type) { + getIndexTypeOfStructuredType: (type: Type, kind: IndexKind) => Type, + getConstraintFromTypeParameter: (typeParameter: TypeParameter) => Type) { return getSymbolWalker; @@ -92,7 +93,7 @@ namespace ts { } function visitTypeParameter(type: TypeParameter): void { - visitType(type.constraint); + visitType(getConstraintFromTypeParameter(type)); } function visitUnionOrIntersectionType(type: UnionOrIntersectionType): void { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 37e12cc9d294c..26bf5e1a91f85 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3377,6 +3377,7 @@ namespace ts { // Type parameters (TypeFlags.TypeParameter) export interface TypeParameter extends TypeVariable { + /** Retrieve using getConstraintFromTypeParameter */ constraint: Type; // Constraint default?: Type; /* @internal */ From 89447748d500e66846f9f4068ada12424da0ce74 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 22 Aug 2017 10:47:37 -0700 Subject: [PATCH 10/10] Reuse exiting getFirstIdentifier function --- src/compiler/checker.ts | 2 +- src/compiler/symbolWalker.ts | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4781f4bc18386..cff540c3f066e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -205,7 +205,7 @@ namespace ts { getEmitResolver, getExportsOfModule: getExportsOfModuleAsArray, getExportsAndPropertiesOfModule, - getSymbolWalker: createGetSymbolWalker(getRestTypeOfSignature, getReturnTypeOfSignature, getBaseTypes, resolveStructuredTypeMembers, getTypeOfSymbol, getResolvedSymbol, getIndexTypeOfStructuredType, getConstraintFromTypeParameter), + getSymbolWalker: createGetSymbolWalker(getRestTypeOfSignature, getReturnTypeOfSignature, getBaseTypes, resolveStructuredTypeMembers, getTypeOfSymbol, getResolvedSymbol, getIndexTypeOfStructuredType, getConstraintFromTypeParameter, getFirstIdentifier), getAmbientModules, getAllAttributesTypeFromJsxOpeningLikeElement: node => { node = getParseTreeNode(node, isJsxOpeningLikeElement); diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index 6567870c961d4..e20ef9f9d9877 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -8,7 +8,8 @@ namespace ts { getTypeOfSymbol: (sym: Symbol) => Type, getResolvedSymbol: (node: Node) => Symbol, getIndexTypeOfStructuredType: (type: Type, kind: IndexKind) => Type, - getConstraintFromTypeParameter: (typeParameter: TypeParameter) => Type) { + getConstraintFromTypeParameter: (typeParameter: TypeParameter) => Type, + getFirstIdentifier: (node: EntityNameOrEntityNameExpression) => Identifier) { return getSymbolWalker; @@ -180,20 +181,11 @@ namespace ts { // query node on any of the symbol's declarations and get symbols there if ((d as any).type && (d as any).type.kind === SyntaxKind.TypeQuery) { const query = (d as any).type as TypeQueryNode; - const entity = leftmostSymbol(query.exprName); + const entity = getResolvedSymbol(getFirstIdentifier(query.exprName)); visitSymbol(entity); } }); } } - - function leftmostSymbol(expr: QualifiedName | Identifier): Symbol { - if (expr.kind === SyntaxKind.Identifier) { - return getResolvedSymbol(expr as Identifier); - } - else { - return leftmostSymbol((expr as QualifiedName).left); - } - } } } \ No newline at end of file