@@ -71,7 +71,7 @@ func NewModule(dir string) (*Module, error) {
7171
7272 packageName := getPackageNameFromModPath (mf .Module .Mod .Path )
7373 fmt .Printf ("Package Name: %s\n " , packageName )
74- m := Module {Name : filepath .Base (dir ), PackageName : packageName , packages : map [string ]* Pkg {}}
74+ m := & Module {Name : filepath .Base (dir ), PackageName : packageName , packages : map [string ]* Pkg {}}
7575
7676 baseImportPath := path .Dir (mf .Module .Mod .Path ) + "/"
7777 if baseImportPath == "./" {
@@ -114,94 +114,114 @@ func NewModule(dir string) (*Module, error) {
114114 // the definition from azcore/internal/shared into the APIView for azcore, making the type's
115115 // fields visible there.
116116 externalPackages := map [string ]* Pkg {}
117+
118+ // tracks which packages have had their type aliases resolved.
119+ // this prevents resolving dependent packages multiple times.
120+ processedPackages := map [string ]struct {}{}
121+
117122 for _ , p := range m .packages {
118- for alias , qn := range p .typeAliases {
119- // qn is a type name qualified with import path like
120- // "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared.TokenRequestOptions"
121- impPath := qn [:strings .LastIndex (qn , "." )]
122- typeName := qn [len (impPath )+ 1 :]
123- var source * Pkg
124- var ok bool
125- if source , ok = m .packages [impPath ]; ! ok {
126- // must be a package external to this module
127- if source , ok = externalPackages [impPath ]; ! ok && sdkRoot != "" {
128- // figure out a path to the package, index it
129- if _ , after , found := strings .Cut (impPath , "azure-sdk-for-go/sdk/" ); found {
130- p := filepath .Join (sdkRoot , strings .TrimSuffix (versionReg .ReplaceAllString (after , "/" ), "/" ))
131- pkg , err := NewPkg (p , "github.com/Azure/azure-sdk-for-go/sdk/" + after )
132- if err == nil {
133- pkg .Index ()
134- externalPackages [impPath ] = pkg
135- source = pkg
136- } else {
137- // types from this module will appear in the review without their definitions
138- fmt .Printf ("couldn't parse %s: %v\n " , impPath , err )
139- }
123+ recursiveResolveTypeAliases (m , p , externalPackages , sdkRoot , processedPackages )
124+ }
125+ return m , nil
126+ }
127+
128+ func recursiveResolveTypeAliases (m * Module , p * Pkg , externalPackages map [string ]* Pkg , sdkRoot string , processedPackages map [string ]struct {}) {
129+ if _ , ok := processedPackages [p .relName ]; ok {
130+ // already processed this package
131+ return
132+ }
133+
134+ for alias , qn := range p .typeAliases {
135+ // qn is a type name qualified with import path like
136+ // "github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared.TokenRequestOptions"
137+ impPath := qn [:strings .LastIndex (qn , "." )]
138+ typeName := qn [len (impPath )+ 1 :]
139+ var source * Pkg
140+ var ok bool
141+ if source , ok = m .packages [impPath ]; ! ok {
142+ // must be a package external to this module
143+ if source , ok = externalPackages [impPath ]; ! ok && sdkRoot != "" {
144+ // figure out a path to the package, index it
145+ if _ , after , found := strings .Cut (impPath , "azure-sdk-for-go/sdk/" ); found {
146+ p := filepath .Join (sdkRoot , strings .TrimSuffix (versionReg .ReplaceAllString (after , "/" ), "/" ))
147+ pkg , err := NewPkg (p , "github.com/Azure/azure-sdk-for-go/sdk/" + after )
148+ if err == nil {
149+ pkg .Index ()
150+ externalPackages [impPath ] = pkg
151+ source = pkg
152+ } else {
153+ // types from this module will appear in the review without their definitions
154+ fmt .Printf ("couldn't parse %s: %v\n " , impPath , err )
140155 }
141156 }
142157 }
158+ } else if len (source .typeAliases ) > 0 {
159+ // if the source has type aliases we need to resolve them first.
160+ // this is to handle recursive type aliases.
161+ recursiveResolveTypeAliases (m , source , externalPackages , sdkRoot , processedPackages )
162+ }
143163
144- level := DiagnosticLevelInfo
145- originalName := qn
146- if _ , after , found := strings .Cut (qn , m .Name ); found {
147- originalName = strings .TrimPrefix (after , "/" )
148- } else {
149- // this type is defined in another module
150- level = DiagnosticLevelWarning
151- }
164+ level := DiagnosticLevelInfo
165+ originalName := qn
166+ if _ , after , found := strings .Cut (qn , m .Name ); found {
167+ originalName = strings .TrimPrefix (after , "/" )
168+ } else {
169+ // this type is defined in another module
170+ level = DiagnosticLevelWarning
171+ }
152172
153- var t TokenMaker
154- if source == nil {
155- t = p .c .addSimpleType (* p , alias , p .Name (), originalName , nil )
156- } else if def , ok := recursiveFindTypeDef (typeName , source , m .packages ); ok {
157- switch n := def .n .Type .(type ) {
158- case * ast.InterfaceType :
159- t = p .c .addInterface (* def .p , alias , p .Name (), n , nil )
160- case * ast.StructType :
161- t = p .c .addStruct (* def .p , alias , p .Name (), def .n , nil )
162- hoistMethodsForType (source , alias , p )
163- // ensure that all struct field types that are structs are also aliased from this package
164- for _ , field := range n .Fields .List {
165- fieldTypeName := unwrapStructFieldTypeName (field )
166- if fieldTypeName == "" {
167- // we can ignore this field
168- continue
169- }
170-
171- // ensure that our package exports this type
172- if _ , ok := p .typeAliases [fieldTypeName ]; ok {
173- // found an alias
174- continue
175- }
176-
177- // no alias, add a diagnostic
178- p .diagnostics = append (p .diagnostics , Diagnostic {
179- Level : DiagnosticLevelError ,
180- TargetID : t .ID (),
181- Text : missingAliasFor + fieldTypeName ,
182- })
173+ var t TokenMaker
174+ if source == nil {
175+ t = p .c .addSimpleType (* p , alias , p .Name (), originalName , nil )
176+ } else if def , ok := recursiveFindTypeDef (typeName , source , m .packages ); ok {
177+ switch n := def .n .Type .(type ) {
178+ case * ast.InterfaceType :
179+ t = p .c .addInterface (* def .p , alias , p .Name (), n , nil )
180+ case * ast.StructType :
181+ t = p .c .addStruct (* def .p , alias , p .Name (), def .n , nil )
182+ hoistMethodsForType (source , alias , p )
183+ // ensure that all struct field types that are structs are also aliased from this package
184+ for _ , field := range n .Fields .List {
185+ fieldTypeName := unwrapStructFieldTypeName (field )
186+ if fieldTypeName == "" {
187+ // we can ignore this field
188+ continue
183189 }
184- case * ast.Ident :
185- t = p .c .addSimpleType (* p , alias , p .Name (), def .n .Type .(* ast.Ident ).Name , nil )
186- hoistMethodsForType (source , alias , p )
187- default :
188- fmt .Printf ("unexpected node type %T\n " , def .n .Type )
189- t = p .c .addSimpleType (* p , alias , p .Name (), originalName , nil )
190+
191+ // ensure that our package exports this type
192+ if _ , ok := p .typeAliases [fieldTypeName ]; ok {
193+ // found an alias
194+ continue
195+ }
196+
197+ // no alias, add a diagnostic
198+ p .diagnostics = append (p .diagnostics , Diagnostic {
199+ Level : DiagnosticLevelError ,
200+ TargetID : t .ID (),
201+ Text : missingAliasFor + fieldTypeName ,
202+ })
190203 }
191- } else {
192- fmt .Println ("found no definition for " + qn )
204+ case * ast.Ident :
205+ t = p .c .addSimpleType (* p , alias , p .Name (), def .n .Type .(* ast.Ident ).Name , nil )
206+ hoistMethodsForType (source , alias , p )
207+ default :
208+ fmt .Printf ("unexpected node type %T\n " , def .n .Type )
209+ t = p .c .addSimpleType (* p , alias , p .Name (), originalName , nil )
193210 }
211+ } else {
212+ fmt .Println ("found no definition for " + qn )
213+ }
194214
195- if t != nil {
196- p .diagnostics = append (p .diagnostics , Diagnostic {
197- Level : level ,
198- TargetID : t .ID (),
199- Text : aliasFor + originalName ,
200- })
201- }
215+ if t != nil {
216+ p .diagnostics = append (p .diagnostics , Diagnostic {
217+ Level : level ,
218+ TargetID : t .ID (),
219+ Text : aliasFor + originalName ,
220+ })
202221 }
203222 }
204- return & m , nil
223+
224+ processedPackages [p .relName ] = struct {}{}
205225}
206226
207227// returns the type name for the specified struct field.
0 commit comments