From 053b91506103cf185d852d816f4010e162e3729e Mon Sep 17 00:00:00 2001
From: Wesley Wigham <t-weswig@microsoft.com>
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 @@
 /// <reference path="moduleNameResolver.ts"/>
 /// <reference path="binder.ts"/>
+/// <reference path="symbolWalker.ts" />
 
 /* @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 @@
+/// <reference path="..\harness.ts" />
+
+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 <t-weswig@microsoft.com>
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 <andrew.casey@microsoft.com>
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 <andrew.casey@microsoft.com>
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<Type>(); // Key is id as string
+            let visitedSymbols = createMap<Symbol>(); // 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<Type>, visitedSymbols: ReadonlyArray<Symbol> };
+        /** Note: Return values are not ordered. */
+        walkSymbol(root: Symbol) : { visitedTypes: ReadonlyArray<Type>, visitedSymbols: ReadonlyArray<Symbol> };
     }
 
     export interface SymbolDisplayBuilder {

From 129ace5047d787ab55396680fe23746e78074eaa Mon Sep 17 00:00:00 2001
From: Andrew Casey <andrew.casey@microsoft.com>
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 <andrew.casey@microsoft.com>
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<Type>(); // Key is id as string
-            let visitedSymbols = createMap<Symbol>(); // Key is id as string
+            const visitedTypes = createMap<Type>(); // Key is id as string
+            const visitedSymbols = createMap<Symbol>(); // 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<Type>, visitedSymbols: ReadonlyArray<Symbol> };
+        walkType(root: Type): { visitedTypes: ReadonlyArray<Type>, visitedSymbols: ReadonlyArray<Symbol> };
         /** Note: Return values are not ordered. */
-        walkSymbol(root: Symbol) : { visitedTypes: ReadonlyArray<Type>, visitedSymbols: ReadonlyArray<Symbol> };
+        walkSymbol(root: Symbol): { visitedTypes: ReadonlyArray<Type>, visitedSymbols: ReadonlyArray<Symbol> };
     }
 
     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 <andrew.casey@microsoft.com>
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 <andrew.casey@microsoft.com>
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 <andrew.casey@microsoft.com>
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 <andrew.casey@microsoft.com>
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