1+ namespace ts {
2+ export function createGetSymbolWalker (
3+ getRestTypeOfSignature : ( sig : Signature ) => Type ,
4+ getReturnTypeOfSignature : ( sig : Signature ) => Type ,
5+ getBaseTypes : ( type : Type ) => Type [ ] ,
6+ resolveStructuredTypeMembers : ( type : ObjectType ) => ResolvedType ,
7+ getTypeOfSymbol : ( sym : Symbol ) => Type ,
8+ getResolvedSymbol : ( node : Node ) => Symbol ,
9+ getIndexTypeOfStructuredType : ( type : Type , kind : IndexKind ) => Type ) {
10+
11+ return getSymbolWalker ;
12+
13+ function getSymbolWalker ( accept : ( symbol : Symbol ) => boolean = ( ) => true ) : SymbolWalker {
14+ let visited : Type [ ] = [ ] ;
15+ let visitedSymbols : Symbol [ ] = [ ] ;
16+
17+ return {
18+ visitType,
19+ visitSymbol,
20+ reset : ( newCallback : ( symbol : Symbol ) => boolean = ( ) => true ) => {
21+ accept = newCallback ;
22+ visited = [ ] ;
23+ visitedSymbols = [ ] ;
24+ }
25+ } ;
26+
27+ function visitType ( type : Type ) : void {
28+ if ( ! type ) {
29+ return ;
30+ }
31+ if ( contains ( visited , type ) ) {
32+ return ;
33+ }
34+ visited . push ( type ) ;
35+
36+ // Reuse visitSymbol to visit the type's symbol,
37+ // but be sure to bail on recuring into the type if accept declines the symbol.
38+ const shouldBail = visitSymbol ( type . symbol ) ;
39+ if ( shouldBail ) return ;
40+
41+ // Visit the type's related types, if any
42+ if ( type . flags & TypeFlags . Object ) {
43+ const objectType = type as ObjectType ;
44+ const objectFlags = objectType . objectFlags ;
45+ if ( objectFlags & ObjectFlags . Reference ) {
46+ visitTypeReference ( type as TypeReference ) ;
47+ }
48+ if ( objectFlags & ( ObjectFlags . Class | ObjectFlags . Interface ) ) {
49+ visitInterfaceType ( type as InterfaceType ) ;
50+ }
51+ if ( objectFlags & ( ObjectFlags . Tuple | ObjectFlags . Anonymous ) ) {
52+ visitObjectType ( objectType ) ;
53+ }
54+ }
55+ if ( type . flags & TypeFlags . TypeParameter ) {
56+ visitTypeParameter ( type as TypeParameter ) ;
57+ }
58+ if ( type . flags & TypeFlags . UnionOrIntersection ) {
59+ visitUnionOrIntersectionType ( type as UnionOrIntersectionType ) ;
60+ }
61+ }
62+
63+ function visitTypeList ( types : Type [ ] ) : void {
64+ if ( ! types ) {
65+ return ;
66+ }
67+ for ( let i = 0 ; i < types . length ; i ++ ) {
68+ visitType ( types [ i ] ) ;
69+ }
70+ }
71+
72+ function visitTypeReference ( type : TypeReference ) : void {
73+ visitType ( type . target ) ;
74+ visitTypeList ( type . typeArguments ) ;
75+ }
76+
77+ function visitTypeParameter ( type : TypeParameter ) : void {
78+ visitType ( type . constraint ) ;
79+ }
80+
81+ function visitUnionOrIntersectionType ( type : UnionOrIntersectionType ) : void {
82+ visitTypeList ( type . types ) ;
83+ }
84+
85+ function visitSignature ( signature : Signature ) : void {
86+ if ( signature . typePredicate ) {
87+ visitType ( signature . typePredicate . type ) ;
88+ }
89+ visitTypeList ( signature . typeParameters ) ;
90+
91+ for ( const parameter of signature . parameters ) {
92+ visitSymbol ( parameter ) ;
93+ }
94+ visitType ( getRestTypeOfSignature ( signature ) ) ;
95+ visitType ( getReturnTypeOfSignature ( signature ) ) ;
96+ }
97+
98+ function visitInterfaceType ( interfaceT : InterfaceType ) : void {
99+ visitObjectType ( interfaceT ) ;
100+ visitTypeList ( interfaceT . typeParameters ) ;
101+ visitTypeList ( getBaseTypes ( interfaceT ) ) ;
102+ visitType ( interfaceT . thisType ) ;
103+ }
104+
105+ function visitObjectType ( type : ObjectType ) : void {
106+ const stringIndexType = getIndexTypeOfStructuredType ( type , IndexKind . String ) ;
107+ visitType ( stringIndexType ) ;
108+ const numberIndexType = getIndexTypeOfStructuredType ( type , IndexKind . String ) ;
109+ visitType ( numberIndexType ) ;
110+
111+ // The two checks above *should* have already resolved the type (if needed), so this should be cached
112+ const resolved = resolveStructuredTypeMembers ( type ) ;
113+ for ( const signature of resolved . callSignatures ) {
114+ visitSignature ( signature ) ;
115+ }
116+ for ( const signature of resolved . constructSignatures ) {
117+ visitSignature ( signature ) ;
118+ }
119+ for ( const p of resolved . properties ) {
120+ visitSymbol ( p ) ;
121+ }
122+ }
123+
124+ function visitSymbol ( symbol : Symbol ) : boolean {
125+ if ( ! symbol ) {
126+ return ;
127+ }
128+ if ( contains ( visitedSymbols , symbol ) ) {
129+ return ;
130+ }
131+ visitedSymbols . push ( symbol ) ;
132+ if ( ! accept ( symbol ) ) {
133+ return true ;
134+ }
135+ const t = getTypeOfSymbol ( symbol ) ;
136+ visitType ( t ) ; // Should handle members on classes and such
137+ if ( symbol . flags & SymbolFlags . HasExports ) {
138+ symbol . exports . forEach ( visitSymbol ) ;
139+ }
140+ forEach ( symbol . declarations , d => {
141+ // Type queries are too far resolved when we just visit the symbol's type
142+ // (their type resolved directly to the member deeply referenced)
143+ // So to get the intervening symbols, we need to check if there's a type
144+ // query node on any of the symbol's declarations and get symbols there
145+ if ( ( d as any ) . type && ( d as any ) . type . kind === SyntaxKind . TypeQuery ) {
146+ const query = ( d as any ) . type as TypeQueryNode ;
147+ const entity = leftmostSymbol ( query . exprName ) ;
148+ visitSymbol ( entity ) ;
149+ }
150+ } ) ;
151+ }
152+ }
153+
154+ function leftmostSymbol ( expr : QualifiedName | Identifier ) : Symbol {
155+ if ( expr . kind === SyntaxKind . Identifier ) {
156+ return getResolvedSymbol ( expr as Identifier ) ;
157+ }
158+ else {
159+ return leftmostSymbol ( ( expr as QualifiedName ) . left ) ;
160+ }
161+ }
162+ }
163+ }
0 commit comments