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