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