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}