• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/** @internal */
2namespace ts {
3    export function createGetSymbolWalker(
4        getRestTypeOfSignature: (sig: Signature) => Type,
5        getTypePredicateOfSignature: (sig: Signature) => TypePredicate | undefined,
6        getReturnTypeOfSignature: (sig: Signature) => Type,
7        getBaseTypes: (type: Type) => Type[],
8        resolveStructuredTypeMembers: (type: ObjectType) => ResolvedType,
9        getTypeOfSymbol: (sym: Symbol) => Type,
10        getResolvedSymbol: (node: Node) => Symbol,
11        getIndexTypeOfStructuredType: (type: Type, kind: IndexKind) => Type | undefined,
12        getConstraintOfTypeParameter: (typeParameter: TypeParameter) => Type | undefined,
13        getFirstIdentifier: (node: EntityNameOrEntityNameExpression) => Identifier,
14        getTypeArguments: (type: TypeReference) => readonly Type[]) {
15
16        return getSymbolWalker;
17
18        function getSymbolWalker(accept: (symbol: Symbol) => boolean = () => true): SymbolWalker {
19            const visitedTypes: Type[] = []; // Sparse array from id to type
20            const visitedSymbols: Symbol[] = []; // Sparse array from id to symbol
21
22            return {
23                walkType: type => {
24                    try {
25                        visitType(type);
26                        return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
27                    }
28                    finally {
29                        clear(visitedTypes);
30                        clear(visitedSymbols);
31                    }
32                },
33                walkSymbol: symbol => {
34                    try {
35                        visitSymbol(symbol);
36                        return { visitedTypes: getOwnValues(visitedTypes), visitedSymbols: getOwnValues(visitedSymbols) };
37                    }
38                    finally {
39                        clear(visitedTypes);
40                        clear(visitedSymbols);
41                    }
42                },
43            };
44
45            function visitType(type: Type | undefined): void {
46                if (!type) {
47                    return;
48                }
49
50                if (visitedTypes[type.id]) {
51                    return;
52                }
53                visitedTypes[type.id] = type;
54
55                // Reuse visitSymbol to visit the type's symbol,
56                //  but be sure to bail on recuring into the type if accept declines the symbol.
57                const shouldBail = visitSymbol(type.symbol);
58                if (shouldBail) return;
59
60                // Visit the type's related types, if any
61                if (type.flags & TypeFlags.Object) {
62                    const objectType = type as ObjectType;
63                    const objectFlags = objectType.objectFlags;
64                    if (objectFlags & ObjectFlags.Reference) {
65                        visitTypeReference(type as TypeReference);
66                    }
67                    if (objectFlags & ObjectFlags.Mapped) {
68                        visitMappedType(type as MappedType);
69                    }
70                    if (objectFlags & (ObjectFlags.Class | ObjectFlags.Interface)) {
71                        visitInterfaceType(type as InterfaceType);
72                    }
73                    if (objectFlags & (ObjectFlags.Tuple | ObjectFlags.Anonymous)) {
74                        visitObjectType(objectType);
75                    }
76                }
77                if (type.flags & TypeFlags.TypeParameter) {
78                    visitTypeParameter(type as TypeParameter);
79                }
80                if (type.flags & TypeFlags.UnionOrIntersection) {
81                    visitUnionOrIntersectionType(type as UnionOrIntersectionType);
82                }
83                if (type.flags & TypeFlags.Index) {
84                    visitIndexType(type as IndexType);
85                }
86                if (type.flags & TypeFlags.IndexedAccess) {
87                    visitIndexedAccessType(type as IndexedAccessType);
88                }
89            }
90
91            function visitTypeReference(type: TypeReference): void {
92                visitType(type.target);
93                forEach(getTypeArguments(type), visitType);
94            }
95
96            function visitTypeParameter(type: TypeParameter): void {
97                visitType(getConstraintOfTypeParameter(type));
98            }
99
100            function visitUnionOrIntersectionType(type: UnionOrIntersectionType): void {
101                forEach(type.types, visitType);
102            }
103
104            function visitIndexType(type: IndexType): void {
105                visitType(type.type);
106            }
107
108            function visitIndexedAccessType(type: IndexedAccessType): void {
109                visitType(type.objectType);
110                visitType(type.indexType);
111                visitType(type.constraint);
112            }
113
114            function visitMappedType(type: MappedType): void {
115                visitType(type.typeParameter);
116                visitType(type.constraintType);
117                visitType(type.templateType);
118                visitType(type.modifiersType);
119            }
120
121            function visitSignature(signature: Signature): void {
122                const typePredicate = getTypePredicateOfSignature(signature);
123                if (typePredicate) {
124                    visitType(typePredicate.type);
125                }
126                forEach(signature.typeParameters, visitType);
127
128                for (const parameter of signature.parameters) {
129                    visitSymbol(parameter);
130                }
131                visitType(getRestTypeOfSignature(signature));
132                visitType(getReturnTypeOfSignature(signature));
133            }
134
135            function visitInterfaceType(interfaceT: InterfaceType): void {
136                visitObjectType(interfaceT);
137                forEach(interfaceT.typeParameters, visitType);
138                forEach(getBaseTypes(interfaceT), visitType);
139                visitType(interfaceT.thisType);
140            }
141
142            function visitObjectType(type: ObjectType): void {
143                const stringIndexType = getIndexTypeOfStructuredType(type, IndexKind.String);
144                visitType(stringIndexType);
145                const numberIndexType = getIndexTypeOfStructuredType(type, IndexKind.Number);
146                visitType(numberIndexType);
147
148                // The two checks above *should* have already resolved the type (if needed), so this should be cached
149                const resolved = resolveStructuredTypeMembers(type);
150                for (const signature of resolved.callSignatures) {
151                    visitSignature(signature);
152                }
153                for (const signature of resolved.constructSignatures) {
154                    visitSignature(signature);
155                }
156                for (const p of resolved.properties) {
157                    visitSymbol(p);
158                }
159            }
160
161            function visitSymbol(symbol: Symbol | undefined): boolean {
162                if (!symbol) {
163                    return false;
164                }
165                const symbolId = getSymbolId(symbol);
166                if (visitedSymbols[symbolId]) {
167                    return false;
168                }
169                visitedSymbols[symbolId] = symbol;
170                if (!accept(symbol)) {
171                    return true;
172                }
173                const t = getTypeOfSymbol(symbol);
174                visitType(t); // Should handle members on classes and such
175                if (symbol.exports) {
176                    symbol.exports.forEach(visitSymbol);
177                }
178                forEach(symbol.declarations, d => {
179                    // Type queries are too far resolved when we just visit the symbol's type
180                    //  (their type resolved directly to the member deeply referenced)
181                    // So to get the intervening symbols, we need to check if there's a type
182                    // query node on any of the symbol's declarations and get symbols there
183                    if ((d as any).type && (d as any).type.kind === SyntaxKind.TypeQuery) {
184                        const query = (d as any).type as TypeQueryNode;
185                        const entity = getResolvedSymbol(getFirstIdentifier(query.exprName));
186                        visitSymbol(entity);
187                    }
188                });
189                return false;
190            }
191        }
192    }
193}