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}