goscript 0.0.49 → 0.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -86,6 +86,37 @@ type NodeInfo struct {
86
86
  IdentifierMapping string // replacement name for this identifier (e.g., receiver -> "receiver")
87
87
  }
88
88
 
89
+ // PackageMetadataKey represents a key for looking up package metadata
90
+ type PackageMetadataKey struct {
91
+ PackagePath string // Full package path (e.g., "github.com/aperturerobotics/util/csync")
92
+ TypeName string // Type name (e.g., "Mutex")
93
+ MethodName string // Method name (e.g., "Lock")
94
+ }
95
+
96
+ // MethodMetadata represents metadata about a method
97
+ type MethodMetadata struct {
98
+ IsAsync bool // Whether the method is async
99
+ }
100
+
101
+ // GsMetadata represents the structure of a meta.json file in gs/ packages
102
+ type GsMetadata struct {
103
+ Dependencies []string `json:"dependencies,omitempty"`
104
+ AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
105
+ }
106
+
107
+ // InterfaceMethodKey uniquely identifies an interface method
108
+ type InterfaceMethodKey struct {
109
+ InterfaceType string // The string representation of the interface type
110
+ MethodName string // The method name
111
+ }
112
+
113
+ // ImplementationInfo tracks information about a struct that implements an interface method
114
+ type ImplementationInfo struct {
115
+ StructType *types.Named // The struct type that implements the interface
116
+ Method *types.Func // The method object
117
+ IsAsyncByFlow bool // Whether this implementation is async based on control flow analysis
118
+ }
119
+
89
120
  // Analysis holds information gathered during the analysis phase of the Go code compilation.
90
121
  // This data is used to make decisions about how to generate TypeScript code.
91
122
  // Analysis is read-only after being built and should not be modified during code generation.
@@ -116,12 +147,24 @@ type Analysis struct {
116
147
  // FunctionAssignments tracks which function literals are assigned to which variables
117
148
  FunctionAssignments map[types.Object]ast.Node
118
149
 
119
- // PackageMetadata holds package-level metadata
120
- PackageMetadata map[string]interface{}
150
+ // PackageMetadata holds package-level metadata with structured keys
151
+ PackageMetadata map[PackageMetadataKey]MethodMetadata
152
+
153
+ // NamedBasicTypes tracks types that should be implemented as type aliases with standalone functions
154
+ // This includes named types with basic underlying types (like uint32, string) that have methods
155
+ NamedBasicTypes map[types.Type]bool
121
156
 
122
- // WrapperTypes tracks types that should be implemented as wrapper classes
123
- // This includes both local types with methods and imported types that are known wrapper types
124
- WrapperTypes map[types.Type]bool
157
+ // AllPackages stores all loaded packages for dependency analysis
158
+ // Key: package path, Value: package data
159
+ AllPackages map[string]*packages.Package
160
+
161
+ // InterfaceImplementations tracks which struct types implement which interface methods
162
+ // This is used to determine interface method async status based on implementations
163
+ InterfaceImplementations map[InterfaceMethodKey][]ImplementationInfo
164
+
165
+ // InterfaceMethodAsyncStatus caches the async status determination for interface methods
166
+ // This is computed once during analysis and reused during code generation
167
+ InterfaceMethodAsyncStatus map[InterfaceMethodKey]bool
125
168
  }
126
169
 
127
170
  // PackageAnalysis holds cross-file analysis data for a package
@@ -143,24 +186,25 @@ type PackageAnalysis struct {
143
186
  TypeCalls map[string]map[string][]string
144
187
  }
145
188
 
146
- // GsMetadata represents the structure of a meta.json file in gs/ packages
147
- type GsMetadata struct {
148
- Dependencies []string `json:"dependencies,omitempty"`
149
- AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
150
- }
151
-
152
189
  // NewAnalysis creates a new Analysis instance.
153
- func NewAnalysis() *Analysis {
190
+ func NewAnalysis(allPackages map[string]*packages.Package) *Analysis {
191
+ if allPackages == nil {
192
+ allPackages = make(map[string]*packages.Package)
193
+ }
194
+
154
195
  return &Analysis{
155
- VariableUsage: make(map[types.Object]*VariableUsageInfo),
156
- Imports: make(map[string]*fileImport),
157
- FunctionData: make(map[types.Object]*FunctionInfo),
158
- NodeData: make(map[ast.Node]*NodeInfo),
159
- FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
160
- ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
161
- FunctionAssignments: make(map[types.Object]ast.Node),
162
- PackageMetadata: make(map[string]interface{}),
163
- WrapperTypes: make(map[types.Type]bool),
196
+ VariableUsage: make(map[types.Object]*VariableUsageInfo),
197
+ Imports: make(map[string]*fileImport),
198
+ FunctionData: make(map[types.Object]*FunctionInfo),
199
+ NodeData: make(map[ast.Node]*NodeInfo),
200
+ FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
201
+ ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
202
+ FunctionAssignments: make(map[types.Object]ast.Node),
203
+ PackageMetadata: make(map[PackageMetadataKey]MethodMetadata),
204
+ NamedBasicTypes: make(map[types.Type]bool),
205
+ AllPackages: allPackages,
206
+ InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
207
+ InterfaceMethodAsyncStatus: make(map[InterfaceMethodKey]bool),
164
208
  }
165
209
  }
166
210
 
@@ -174,6 +218,28 @@ func NewPackageAnalysis() *PackageAnalysis {
174
218
  }
175
219
  }
176
220
 
221
+ // ensureNodeData ensures that NodeData exists for a given node and returns it
222
+ func (a *Analysis) ensureNodeData(node ast.Node) *NodeInfo {
223
+ if node == nil {
224
+ return nil
225
+ }
226
+ if a.NodeData[node] == nil {
227
+ a.NodeData[node] = &NodeInfo{}
228
+ }
229
+ return a.NodeData[node]
230
+ }
231
+
232
+ // ensureFunctionData ensures that FunctionData exists for a given object and returns it
233
+ func (a *Analysis) ensureFunctionData(obj types.Object) *FunctionInfo {
234
+ if obj == nil {
235
+ return nil
236
+ }
237
+ if a.FunctionData[obj] == nil {
238
+ a.FunctionData[obj] = &FunctionInfo{}
239
+ }
240
+ return a.FunctionData[obj]
241
+ }
242
+
177
243
  // NeedsDefer returns whether the given node needs defer handling.
178
244
  func (a *Analysis) NeedsDefer(node ast.Node) bool {
179
245
  if node == nil {
@@ -387,10 +453,8 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
387
453
  }
388
454
 
389
455
  // Initialize and store async state for the current node
390
- if v.analysis.NodeData[node] == nil {
391
- v.analysis.NodeData[node] = &NodeInfo{}
392
- }
393
- v.analysis.NodeData[node].InAsyncContext = v.inAsyncFunction
456
+ nodeInfo := v.analysis.ensureNodeData(node)
457
+ nodeInfo.InAsyncContext = v.inAsyncFunction
394
458
 
395
459
  switch n := node.(type) {
396
460
  case *ast.GenDecl:
@@ -463,441 +527,365 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
463
527
  }
464
528
  }
465
529
  }
466
- // Continue traversal AFTER processing the declaration itself
467
- // to handle expressions within initial values if needed.
468
- // However, the core usage tracking is done above.
469
- // Let standard traversal handle children.
470
530
  return v
471
531
 
472
532
  case *ast.FuncDecl:
473
- // Save original states to restore after visiting
474
- originalInAsync := v.inAsyncFunction
475
- originalFuncObj := v.currentFuncObj
476
- originalFuncDecl := v.currentFuncDecl
477
- originalFuncLit := v.currentFuncLit
478
- originalReceiver := v.currentReceiver
479
-
480
- // Reset for current function
481
- v.currentFuncName = n.Name.Name
482
- v.currentFuncDecl = n
483
- v.currentFuncLit = nil
484
- v.currentReceiver = nil
485
-
486
- // Determine if this function declaration is async based on its body
487
- isAsync := false
488
- if n.Body != nil {
489
- containsAsyncOps := v.containsAsyncOperations(n.Body)
490
- if containsAsyncOps {
491
- // Get the object for this function declaration
492
- if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
493
- v.analysis.FunctionData[obj] = &FunctionInfo{
494
- IsAsync: true,
495
- NamedReturns: v.getNamedReturns(n),
496
- }
497
- isAsync = true
498
- }
533
+ return v.visitFuncDecl(n)
534
+
535
+ case *ast.FuncLit:
536
+ return v.visitFuncLit(n)
537
+
538
+ case *ast.BlockStmt:
539
+ return v.visitBlockStmt(n)
540
+
541
+ case *ast.UnaryExpr:
542
+ // We handle address-of (&) within AssignStmt where it's actually used.
543
+ return v
544
+
545
+ case *ast.CallExpr:
546
+ return v.visitCallExpr(n)
547
+
548
+ case *ast.SelectorExpr:
549
+ return v.visitSelectorExpr(n)
550
+
551
+ case *ast.AssignStmt:
552
+ return v.visitAssignStmt(n)
553
+
554
+ case *ast.ReturnStmt:
555
+ return v.visitReturnStmt(n)
556
+
557
+ case *ast.DeclStmt:
558
+ return v.visitDeclStmt(n)
559
+
560
+ case *ast.IfStmt:
561
+ return v.visitIfStmt(n)
562
+
563
+ case *ast.TypeAssertExpr:
564
+ return v.visitTypeAssertExpr(n)
565
+ }
566
+
567
+ // For all other nodes, continue traversal
568
+ return v
569
+ }
570
+
571
+ // visitFuncDecl handles function declaration analysis
572
+ func (v *analysisVisitor) visitFuncDecl(n *ast.FuncDecl) ast.Visitor {
573
+ // Save original states to restore after visiting
574
+ originalInAsync := v.inAsyncFunction
575
+ originalFuncObj := v.currentFuncObj
576
+ originalFuncDecl := v.currentFuncDecl
577
+ originalFuncLit := v.currentFuncLit
578
+ originalReceiver := v.currentReceiver
579
+
580
+ // Reset for current function
581
+ v.currentFuncName = n.Name.Name
582
+ v.currentFuncDecl = n
583
+ v.currentFuncLit = nil
584
+ v.currentReceiver = nil
585
+
586
+ // Determine if this function declaration is async based on its body
587
+ isAsync := false
588
+ if n.Body != nil {
589
+ containsAsyncOps := v.containsAsyncOperations(n.Body)
590
+ if containsAsyncOps {
591
+ // Get the object for this function declaration
592
+ if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
593
+ funcInfo := v.analysis.ensureFunctionData(obj)
594
+ funcInfo.IsAsync = true
595
+ funcInfo.NamedReturns = v.getNamedReturns(n)
596
+ isAsync = true
499
597
  }
500
598
  }
501
- if v.analysis.NodeData[n] == nil {
502
- v.analysis.NodeData[n] = &NodeInfo{}
503
- }
504
- v.analysis.NodeData[n].InAsyncContext = isAsync
505
-
506
- // Set current receiver if this is a method
507
- if n.Recv != nil && len(n.Recv.List) > 0 {
508
- // Assuming a single receiver for simplicity for now
509
- if len(n.Recv.List[0].Names) > 0 {
510
- if ident := n.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
511
- if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
512
- if vr, ok := def.(*types.Var); ok {
513
- v.currentReceiver = vr
514
- // Add the receiver variable to the VariableUsage map
515
- // to ensure it is properly analyzed for varRefing
516
- v.getOrCreateUsageInfo(v.currentReceiver)
517
-
518
- // Check if receiver is used in method body
519
- receiverUsed := false
520
- if n.Body != nil {
521
- if v.isInterfaceMethod(n) {
522
- receiverUsed = true
523
- } else {
524
- receiverUsed = v.containsReceiverUsage(n.Body, vr)
525
- }
599
+ }
600
+ nodeInfo := v.analysis.ensureNodeData(n)
601
+ nodeInfo.InAsyncContext = isAsync
602
+
603
+ // Set current receiver if this is a method
604
+ if n.Recv != nil && len(n.Recv.List) > 0 {
605
+ // Assuming a single receiver for simplicity for now
606
+ if len(n.Recv.List[0].Names) > 0 {
607
+ if ident := n.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
608
+ if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
609
+ if vr, ok := def.(*types.Var); ok {
610
+ v.currentReceiver = vr
611
+ // Add the receiver variable to the VariableUsage map
612
+ v.getOrCreateUsageInfo(v.currentReceiver)
613
+
614
+ // Check if receiver is used in method body
615
+ receiverUsed := false
616
+ if n.Body != nil {
617
+ if v.isInterfaceMethod(n) {
618
+ receiverUsed = true
619
+ } else {
620
+ receiverUsed = v.containsReceiverUsage(n.Body, vr)
526
621
  }
622
+ }
527
623
 
528
- // Update function data with receiver usage info
529
- if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
530
- if v.analysis.FunctionData[obj] == nil {
531
- v.analysis.FunctionData[obj] = &FunctionInfo{}
532
- }
533
- v.analysis.FunctionData[obj].ReceiverUsed = receiverUsed
534
- }
624
+ // Update function data with receiver usage info
625
+ if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
626
+ funcInfo := v.analysis.ensureFunctionData(obj)
627
+ funcInfo.ReceiverUsed = receiverUsed
535
628
  }
536
629
  }
537
630
  }
538
631
  }
539
632
  }
633
+ }
540
634
 
541
- // Store named return variables (sanitized for TypeScript)
542
- if n.Type != nil && n.Type.Results != nil {
543
- var namedReturns []string
544
- for _, field := range n.Type.Results.List {
545
- for _, name := range field.Names {
546
- namedReturns = append(namedReturns, sanitizeIdentifier(name.Name))
547
- }
548
- }
549
- if len(namedReturns) > 0 {
550
- if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
551
- if v.analysis.FunctionData[obj] == nil {
552
- v.analysis.FunctionData[obj] = &FunctionInfo{}
553
- }
554
- v.analysis.FunctionData[obj].NamedReturns = namedReturns
555
- }
635
+ // Store named return variables (sanitized for TypeScript)
636
+ if n.Type != nil && n.Type.Results != nil {
637
+ var namedReturns []string
638
+ for _, field := range n.Type.Results.List {
639
+ for _, name := range field.Names {
640
+ namedReturns = append(namedReturns, sanitizeIdentifier(name.Name))
556
641
  }
557
642
  }
558
-
559
- // Update visitor state for this function
560
- v.inAsyncFunction = isAsync
561
- v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
562
- v.analysis.NodeData[n].InAsyncContext = isAsync // Ensure FuncDecl node itself is marked
563
-
564
- if n.Body != nil {
565
- // Check if the body contains any defer statements
566
- if v.containsDefer(n.Body) {
567
- if v.analysis.NodeData[n] == nil {
568
- v.analysis.NodeData[n] = &NodeInfo{}
569
- }
570
- v.analysis.NodeData[n].NeedsDefer = true
571
- }
572
-
573
- // Visit the body with updated state
574
- ast.Walk(v, n.Body)
575
- }
576
-
577
- // Restore states after visiting
578
- defer func() {
579
- v.currentFuncName = ""
580
- v.inAsyncFunction = originalInAsync
581
- v.currentReceiver = originalReceiver
582
- v.currentFuncObj = originalFuncObj
583
- v.currentFuncDecl = originalFuncDecl
584
- v.currentFuncLit = originalFuncLit
585
- }()
586
- return nil // Stop traversal here, ast.Walk handled the body
587
-
588
- case *ast.FuncLit:
589
- // Save original inAsyncFunction state to restore after visiting
590
- originalInAsync := v.inAsyncFunction
591
- originalFuncDecl := v.currentFuncDecl
592
- originalFuncLit := v.currentFuncLit
593
-
594
- // Set current function literal
595
- v.currentFuncDecl = nil
596
- v.currentFuncLit = n
597
-
598
- // Determine if this function literal is async based on its body
599
- isAsync := v.containsAsyncOperations(n.Body)
600
- if v.analysis.NodeData[n] == nil {
601
- v.analysis.NodeData[n] = &NodeInfo{}
602
- }
603
- v.analysis.NodeData[n].InAsyncContext = isAsync
604
-
605
- // Store named return variables for function literal
606
- if n.Type != nil && n.Type.Results != nil {
607
- var namedReturns []string
608
- for _, field := range n.Type.Results.List {
609
- for _, name := range field.Names {
610
- namedReturns = append(namedReturns, name.Name)
611
- }
612
- }
613
- if len(namedReturns) > 0 {
614
- v.analysis.FuncLitData[n] = &FunctionInfo{
615
- IsAsync: isAsync,
616
- NamedReturns: namedReturns,
617
- }
643
+ if len(namedReturns) > 0 {
644
+ if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
645
+ funcInfo := v.analysis.ensureFunctionData(obj)
646
+ funcInfo.NamedReturns = namedReturns
618
647
  }
619
648
  }
649
+ }
620
650
 
621
- v.inAsyncFunction = isAsync
651
+ // Update visitor state for this function
652
+ v.inAsyncFunction = isAsync
653
+ v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
622
654
 
655
+ if n.Body != nil {
623
656
  // Check if the body contains any defer statements
624
- if n.Body != nil && v.containsDefer(n.Body) {
625
- if v.analysis.NodeData[n] == nil {
626
- v.analysis.NodeData[n] = &NodeInfo{}
627
- }
628
- v.analysis.NodeData[n].NeedsDefer = true
657
+ if v.containsDefer(n.Body) {
658
+ nodeInfo.NeedsDefer = true
629
659
  }
630
660
 
631
661
  // Visit the body with updated state
632
662
  ast.Walk(v, n.Body)
663
+ }
633
664
 
634
- // Restore inAsyncFunction state after visiting
665
+ // Restore states after visiting
666
+ defer func() {
667
+ v.currentFuncName = ""
635
668
  v.inAsyncFunction = originalInAsync
669
+ v.currentReceiver = originalReceiver
670
+ v.currentFuncObj = originalFuncObj
636
671
  v.currentFuncDecl = originalFuncDecl
637
672
  v.currentFuncLit = originalFuncLit
638
- return nil // Stop traversal here, ast.Walk handled the body
673
+ }()
674
+ return nil // Stop traversal here, ast.Walk handled the body
675
+ }
639
676
 
640
- case *ast.BlockStmt:
641
- if n == nil || len(n.List) == 0 {
642
- break
677
+ // visitFuncLit handles function literal analysis
678
+ func (v *analysisVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor {
679
+ // Save original inAsyncFunction state to restore after visiting
680
+ originalInAsync := v.inAsyncFunction
681
+ originalFuncDecl := v.currentFuncDecl
682
+ originalFuncLit := v.currentFuncLit
683
+
684
+ // Set current function literal
685
+ v.currentFuncDecl = nil
686
+ v.currentFuncLit = n
687
+
688
+ // Determine if this function literal is async based on its body
689
+ isAsync := v.containsAsyncOperations(n.Body)
690
+ nodeInfo := v.analysis.ensureNodeData(n)
691
+ nodeInfo.InAsyncContext = isAsync
692
+
693
+ // Store named return variables for function literal
694
+ if n.Type != nil && n.Type.Results != nil {
695
+ var namedReturns []string
696
+ for _, field := range n.Type.Results.List {
697
+ for _, name := range field.Names {
698
+ namedReturns = append(namedReturns, name.Name)
699
+ }
643
700
  }
644
-
645
- // Initialize NodeData for this block
646
- if v.analysis.NodeData[n] == nil {
647
- v.analysis.NodeData[n] = &NodeInfo{}
701
+ if len(namedReturns) > 0 {
702
+ v.analysis.FuncLitData[n] = &FunctionInfo{
703
+ IsAsync: isAsync,
704
+ NamedReturns: namedReturns,
705
+ }
648
706
  }
707
+ }
649
708
 
650
- // Check for defer statements in this block
651
- if v.containsDefer(n) {
652
- v.analysis.NodeData[n].NeedsDefer = true
653
- }
709
+ v.inAsyncFunction = isAsync
654
710
 
655
- // Store async state for this block
656
- v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
711
+ // Check if the body contains any defer statements
712
+ if n.Body != nil && v.containsDefer(n.Body) {
713
+ nodeInfo.NeedsDefer = true
714
+ }
657
715
 
658
- return v
716
+ // Visit the body with updated state
717
+ ast.Walk(v, n.Body)
659
718
 
660
- case *ast.UnaryExpr:
661
- // We handle address-of (&) within AssignStmt where it's actually used.
662
- // Standalone &x doesn't directly assign, but its usage in assignments
663
- // or function calls determines varRefing. Assignments are handled below.
664
- // Function calls like foo(&x) would require different tracking if needed.
665
- // TODO: for now, we focus on assignments
719
+ // Restore inAsyncFunction state after visiting
720
+ v.inAsyncFunction = originalInAsync
721
+ v.currentFuncDecl = originalFuncDecl
722
+ v.currentFuncLit = originalFuncLit
723
+ return nil // Stop traversal here, ast.Walk handled the body
724
+ }
725
+
726
+ // visitBlockStmt handles block statement analysis
727
+ func (v *analysisVisitor) visitBlockStmt(n *ast.BlockStmt) ast.Visitor {
728
+ if n == nil || len(n.List) == 0 {
666
729
  return v
730
+ }
667
731
 
668
- case *ast.CallExpr:
669
- // Check if this is a function call that might be async
670
- if funcIdent, ok := n.Fun.(*ast.Ident); ok {
671
- // Get the object for this function call
672
- if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
673
- // We're calling an async function, so mark current function as async if we're in one
674
- if v.currentFuncObj != nil {
675
- v.analysis.FunctionData[v.currentFuncObj] = &FunctionInfo{
676
- IsAsync: true,
677
- NamedReturns: v.getNamedReturns(v.currentFuncDecl),
678
- }
679
- v.inAsyncFunction = true // Update visitor state
680
- // Mark the FuncDecl node itself if possible (might need to store the node too)
681
- for nodeAst := range v.analysis.NodeData { // Find the node to update
682
- if fd, ok := nodeAst.(*ast.FuncDecl); ok && v.pkg.TypesInfo.ObjectOf(fd.Name) == v.currentFuncObj {
683
- if v.analysis.NodeData[nodeAst] == nil {
684
- v.analysis.NodeData[nodeAst] = &NodeInfo{}
685
- }
686
- v.analysis.NodeData[nodeAst].InAsyncContext = true
687
- }
732
+ // Initialize NodeData for this block
733
+ nodeInfo := v.analysis.ensureNodeData(n)
734
+
735
+ // Check for defer statements in this block
736
+ if v.containsDefer(n) {
737
+ nodeInfo.NeedsDefer = true
738
+ }
739
+
740
+ return v
741
+ }
742
+
743
+ // visitCallExpr handles call expression analysis
744
+ func (v *analysisVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor {
745
+ // Check if this is a function call that might be async
746
+ if funcIdent, ok := n.Fun.(*ast.Ident); ok {
747
+ // Get the object for this function call
748
+ if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
749
+ // We're calling an async function, so mark current function as async if we're in one
750
+ if v.currentFuncObj != nil {
751
+ funcInfo := v.analysis.ensureFunctionData(v.currentFuncObj)
752
+ funcInfo.IsAsync = true
753
+ funcInfo.NamedReturns = v.getNamedReturns(v.currentFuncDecl)
754
+ v.inAsyncFunction = true // Update visitor state
755
+ // Mark the FuncDecl node itself if possible
756
+ for nodeAst := range v.analysis.NodeData {
757
+ if fd, ok := nodeAst.(*ast.FuncDecl); ok && v.pkg.TypesInfo.ObjectOf(fd.Name) == v.currentFuncObj {
758
+ nodeInfo := v.analysis.ensureNodeData(nodeAst)
759
+ nodeInfo.InAsyncContext = true
688
760
  }
689
761
  }
690
762
  }
691
763
  }
764
+ }
692
765
 
693
- // Check for reflect function calls that operate on functions
694
- v.checkReflectUsage(n)
766
+ // Check for reflect function calls that operate on functions
767
+ v.checkReflectUsage(n)
695
768
 
696
- // Store async state for this call expression
697
- if v.analysis.NodeData[n] == nil {
698
- v.analysis.NodeData[n] = &NodeInfo{}
769
+ return v
770
+ }
771
+
772
+ // visitSelectorExpr handles selector expression analysis
773
+ func (v *analysisVisitor) visitSelectorExpr(n *ast.SelectorExpr) ast.Visitor {
774
+ // Check if this is a method value (method being used as a value, not called immediately)
775
+ if selection := v.pkg.TypesInfo.Selections[n]; selection != nil {
776
+ if selection.Kind() == types.MethodVal {
777
+ // This is a method value - mark it for binding during code generation
778
+ nodeInfo := v.analysis.ensureNodeData(n)
779
+ nodeInfo.IsMethodValue = true
699
780
  }
700
- v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
781
+ }
782
+ return v
783
+ }
701
784
 
702
- return v
785
+ // visitAssignStmt handles assignment statement analysis
786
+ func (v *analysisVisitor) visitAssignStmt(n *ast.AssignStmt) ast.Visitor {
787
+ // Handle variable assignment tracking and generate shadowing information
788
+ shadowingInfo := v.detectVariableShadowing(n)
703
789
 
704
- case *ast.SelectorExpr:
705
- // Initialize NodeData for this selector expression
706
- if v.analysis.NodeData[n] == nil {
707
- v.analysis.NodeData[n] = &NodeInfo{}
708
- }
709
- v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
790
+ // Store shadowing information if needed for code generation
791
+ if shadowingInfo != nil {
792
+ nodeInfo := v.analysis.ensureNodeData(n)
793
+ nodeInfo.ShadowingInfo = shadowingInfo
794
+ }
710
795
 
711
- // Check if this is a method value (method being used as a value, not called immediately)
712
- if selection := v.pkg.TypesInfo.Selections[n]; selection != nil {
713
- if selection.Kind() == types.MethodVal {
714
- // This is a method value - mark it for binding during code generation
715
- v.analysis.NodeData[n].IsMethodValue = true
716
- }
796
+ // Track assignment relationships for pointer analysis
797
+ for i, lhsExpr := range n.Lhs {
798
+ if i < len(n.Rhs) {
799
+ v.analyzeAssignment(lhsExpr, n.Rhs[i])
717
800
  }
718
- return v // Continue traversal
801
+ }
719
802
 
720
- case *ast.AssignStmt:
721
- // Detect variable shadowing in any := assignment
722
- if n.Tok == token.DEFINE {
723
- shadowingInfo := v.detectVariableShadowing(n)
724
- if shadowingInfo != nil {
725
- // Store shadowing info on the assignment statement itself
726
- if v.analysis.NodeData[n] == nil {
727
- v.analysis.NodeData[n] = &NodeInfo{}
803
+ // Track interface implementations for assignments to interface variables
804
+ v.trackInterfaceAssignments(n)
805
+
806
+ // Track function assignments (function literals assigned to variables)
807
+ if len(n.Lhs) == 1 && len(n.Rhs) == 1 {
808
+ if lhsIdent, ok := n.Lhs[0].(*ast.Ident); ok {
809
+ if rhsFuncLit, ok := n.Rhs[0].(*ast.FuncLit); ok {
810
+ // Get the object for the LHS variable
811
+ if obj := v.pkg.TypesInfo.ObjectOf(lhsIdent); obj != nil {
812
+ v.analysis.FunctionAssignments[obj] = rhsFuncLit
728
813
  }
729
- v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
730
814
  }
731
815
  }
816
+ }
732
817
 
733
- // Continue with the existing assignment analysis logic
734
- for i, currentLHSExpr := range n.Lhs {
735
- if i >= len(n.Rhs) {
736
- break // Should not happen in valid Go
737
- }
738
- currentRHSExpr := n.Rhs[i]
739
-
740
- // --- Analyze RHS to determine assignment type and source object (if any) ---
741
- rhsAssignmentType := DirectAssignment
742
- var rhsSourceObj types.Object // The variable object on the RHS (e.g., 'y' in x = y or x = &y)
743
-
744
- if unaryExpr, ok := currentRHSExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
745
- // RHS is &some_expr
746
- rhsAssignmentType = AddressOfAssignment
747
- if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
748
- // RHS is &variable
749
- rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
750
- }
751
- // If RHS is &structLit{} or &array[0], rhsSourceObj remains nil.
752
- // _, ok := unaryExpr.X.(*ast.CompositeLit); ok
753
- } else if rhsIdent, ok := currentRHSExpr.(*ast.Ident); ok {
754
- // RHS is variable
755
- rhsAssignmentType = DirectAssignment
756
- rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
757
- }
758
- // If RHS is a literal, function call, etc., rhsSourceObj remains nil.
818
+ return v
819
+ }
759
820
 
760
- // --- Determine the LHS object (if it's a simple variable or a known field) ---
761
- var lhsTrackedObj types.Object // The object on the LHS we might record info *for* (e.g. its sources)
821
+ // visitReturnStmt handles return statement analysis
822
+ func (v *analysisVisitor) visitReturnStmt(n *ast.ReturnStmt) ast.Visitor {
823
+ nodeInfo := v.analysis.ensureNodeData(n)
762
824
 
763
- if lhsIdent, ok := currentLHSExpr.(*ast.Ident); ok {
764
- if lhsIdent.Name == "_" {
765
- continue // Skip blank identifier assignments
766
- }
767
- lhsTrackedObj = v.pkg.TypesInfo.ObjectOf(lhsIdent)
825
+ // Record the enclosing function/literal for this return statement
826
+ if v.currentFuncDecl != nil {
827
+ nodeInfo.EnclosingFuncDecl = v.currentFuncDecl
828
+ } else if v.currentFuncLit != nil {
829
+ nodeInfo.EnclosingFuncLit = v.currentFuncLit
830
+ }
768
831
 
769
- // Check if RHS is a function literal and track the assignment
770
- if funcLit, ok := currentRHSExpr.(*ast.FuncLit); ok {
771
- v.analysis.FunctionAssignments[lhsTrackedObj] = funcLit
772
- }
773
- } else if selExpr, ok := currentLHSExpr.(*ast.SelectorExpr); ok {
774
- // LHS is struct.field or package.Var
775
- if selection := v.pkg.TypesInfo.Selections[selExpr]; selection != nil {
776
- lhsTrackedObj = selection.Obj() // This is the field or selected var object
777
- }
778
- } /* else if _, ok := currentLHSExpr.(*ast.StarExpr); ok {
779
- // LHS is *pointer.
780
- // We don't try to get a types.Object for the dereferenced entity itself to store in VariableUsage.
781
- // lhsTrackedObj remains nil. The effect on rhsSourceObj (if its address is taken) is handled below.
782
- } */
783
- // For other complex LHS (e.g., map_expr[key_expr]), lhsTrackedObj remains nil.
784
-
785
- // --- Record Usage Information ---
786
-
787
- // 1. If LHS is a trackable variable/field, record what's assigned to it (its sources).
788
- // We only want to create VariableUsage entries for actual variables/fields.
789
- if _, isVar := lhsTrackedObj.(*types.Var); isVar {
790
- lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
791
- if rhsSourceObj != nil {
792
- // Case: var1 = var2 OR var1 = &var2 OR field1 = var2 OR field1 = &var2
793
- lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
794
- Object: rhsSourceObj,
795
- Type: rhsAssignmentType,
796
- })
797
- } else if rhsAssignmentType == AddressOfAssignment {
798
- // Case: var1 = &non_ident_expr (e.g., &T{}) OR field1 = &non_ident_expr
799
- // lhsTrackedObj is assigned an address, but not of a named variable.
800
- lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
801
- Object: nil, // No specific source variable object
802
- Type: rhsAssignmentType,
803
- })
832
+ // Check if it's a bare return
833
+ if len(n.Results) == 0 {
834
+ if v.currentFuncDecl != nil {
835
+ // Check if the enclosing function declaration has named returns
836
+ if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
837
+ if _, ok := v.analysis.FunctionData[obj]; ok {
838
+ nodeInfo.IsBareReturn = true
804
839
  }
805
- // If rhsSourceObj is nil and rhsAssignmentType is DirectAssignment (e.g. var1 = 10),
806
- // no source object to record for LHS sources.
807
840
  }
808
-
809
- // 2. If RHS involved a source variable (rhsSourceObj is not nil),
810
- // record that this source variable was used (its destinations).
811
- // This is CRITICAL for varRefing analysis (e.g., if &rhsSourceObj was assigned).
812
- if rhsSourceObj != nil {
813
- sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
814
- // The 'Object' in DestinationInfo is what/where rhsSourceObj (or its address) was assigned TO.
815
- // This can be lhsTrackedObj (if LHS was an ident or field).
816
- // If LHS was complex (e.g., *ptr, map[k]), lhsTrackedObj might be nil for that DestinationInfo.Object.
817
- // Even if lhsTrackedObj is nil for the DestinationInfo.Object, if rhsAssignmentType is AddressOfAssignment,
818
- // it's important to record that rhsSourceObj's address was taken.
819
- sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
820
- Object: lhsTrackedObj, // This can be nil if LHS is complex (*p, map[k])
821
- Type: rhsAssignmentType,
822
- })
823
- }
824
- }
825
- return v // Continue traversal
826
-
827
- case *ast.ReturnStmt:
828
- // Initialize NodeData for return statement
829
- if v.analysis.NodeData[n] == nil {
830
- v.analysis.NodeData[n] = &NodeInfo{}
831
- }
832
-
833
- // Record the enclosing function/literal for this return statement
834
- if v.currentFuncDecl != nil {
835
- v.analysis.NodeData[n].EnclosingFuncDecl = v.currentFuncDecl
836
841
  } else if v.currentFuncLit != nil {
837
- v.analysis.NodeData[n].EnclosingFuncLit = v.currentFuncLit
838
- }
839
-
840
- // Check if it's a bare return
841
- if len(n.Results) == 0 {
842
- if v.currentFuncDecl != nil {
843
- // Check if the enclosing function declaration has named returns
844
- if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
845
- if _, ok := v.analysis.FunctionData[obj]; ok {
846
- if v.analysis.NodeData[n] == nil {
847
- v.analysis.NodeData[n] = &NodeInfo{}
848
- }
849
- v.analysis.NodeData[n].IsBareReturn = true
850
- }
851
- }
852
- } else if v.currentFuncLit != nil {
853
- // Check if the enclosing function literal has named returns
854
- if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
855
- if v.analysis.NodeData[n] == nil {
856
- v.analysis.NodeData[n] = &NodeInfo{}
857
- }
858
- v.analysis.NodeData[n].IsBareReturn = true
859
- }
842
+ // Check if the enclosing function literal has named returns
843
+ if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
844
+ nodeInfo.IsBareReturn = true
860
845
  }
861
846
  }
862
- return v // Continue traversal
847
+ }
848
+ return v
849
+ }
863
850
 
864
- case *ast.DeclStmt:
865
- // Handle declarations inside functions (const, var, type declarations within function bodies)
866
- // These should not have export modifiers in TypeScript
867
- if genDecl, ok := n.Decl.(*ast.GenDecl); ok {
868
- // Check if we're inside a function (either FuncDecl or FuncLit)
869
- isInsideFunction := v.currentFuncDecl != nil || v.currentFuncLit != nil
870
-
871
- if isInsideFunction {
872
- // Mark all specs in this declaration as being inside a function
873
- for _, spec := range genDecl.Specs {
874
- if v.analysis.NodeData[spec] == nil {
875
- v.analysis.NodeData[spec] = &NodeInfo{}
876
- }
877
- v.analysis.NodeData[spec].IsInsideFunction = true
878
- }
851
+ // visitDeclStmt handles declaration statement analysis
852
+ func (v *analysisVisitor) visitDeclStmt(n *ast.DeclStmt) ast.Visitor {
853
+ // Handle declarations inside functions (const, var, type declarations within function bodies)
854
+ // These should not have export modifiers in TypeScript
855
+ if genDecl, ok := n.Decl.(*ast.GenDecl); ok {
856
+ // Check if we're inside a function (either FuncDecl or FuncLit)
857
+ isInsideFunction := v.currentFuncDecl != nil || v.currentFuncLit != nil
858
+
859
+ if isInsideFunction {
860
+ // Mark all specs in this declaration as being inside a function
861
+ for _, spec := range genDecl.Specs {
862
+ nodeInfo := v.analysis.ensureNodeData(spec)
863
+ nodeInfo.IsInsideFunction = true
879
864
  }
880
865
  }
881
- return v // Continue traversal
866
+ }
867
+ return v
868
+ }
882
869
 
883
- case *ast.IfStmt:
884
- // Detect variable shadowing in if statement initializations
885
- if n.Init != nil {
886
- if assignStmt, ok := n.Init.(*ast.AssignStmt); ok && assignStmt.Tok == token.DEFINE {
887
- shadowingInfo := v.detectVariableShadowing(assignStmt)
888
- if shadowingInfo != nil {
889
- // Initialize NodeData for this if statement
890
- if v.analysis.NodeData[n] == nil {
891
- v.analysis.NodeData[n] = &NodeInfo{}
892
- }
893
- v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
894
- }
870
+ // visitIfStmt handles if statement analysis
871
+ func (v *analysisVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
872
+ // Detect variable shadowing in if statement initializations
873
+ if n.Init != nil {
874
+ if assignStmt, ok := n.Init.(*ast.AssignStmt); ok && assignStmt.Tok == token.DEFINE {
875
+ shadowingInfo := v.detectVariableShadowing(assignStmt)
876
+ if shadowingInfo != nil {
877
+ nodeInfo := v.analysis.ensureNodeData(n)
878
+ nodeInfo.ShadowingInfo = shadowingInfo
895
879
  }
896
880
  }
897
- return v // Continue traversal
898
881
  }
882
+ return v
883
+ }
899
884
 
900
- // For all other nodes, continue traversal
885
+ // visitTypeAssertExpr handles type assertion expression analysis
886
+ func (v *analysisVisitor) visitTypeAssertExpr(n *ast.TypeAssertExpr) ast.Visitor {
887
+ // Track interface implementations when we see type assertions
888
+ v.trackTypeAssertion(n)
901
889
  return v
902
890
  }
903
891
 
@@ -947,11 +935,11 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
947
935
 
948
936
  // Check if the type is from an imported package
949
937
  if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
950
- // Use the actual package name from the type information
951
- pkgName := typePkg.Name()
938
+ // Use the full package path from the type information (not just the package name)
939
+ pkgPath := typePkg.Path()
952
940
 
953
941
  // Check if this method is async based on metadata
954
- if v.analysis.IsMethodAsync(pkgName, typeName, methodName) {
942
+ if v.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
955
943
  hasAsync = true
956
944
  return false
957
945
  }
@@ -1057,99 +1045,113 @@ func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, recei
1057
1045
  return false
1058
1046
  }
1059
1047
 
1060
- // AnalyzeFile analyzes a Go source file AST and populates the Analysis struct with information
1061
- // that will be used during code generation to properly handle pointers, variables that need varRefing, etc.
1062
- func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap ast.CommentMap) {
1063
- // Store the comment map in the analysis object
1064
- analysis.Cmap = cmap
1048
+ // AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
1049
+ // with information that will be used during code generation to properly handle pointers,
1050
+ // variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
1051
+ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages.Package) *Analysis {
1052
+ analysis := NewAnalysis(allPackages)
1065
1053
 
1066
1054
  // Load package metadata for async function detection
1067
1055
  analysis.LoadPackageMetadata()
1068
1056
 
1069
- // Process imports from the file
1070
- for _, imp := range file.Imports {
1071
- path := ""
1072
- if imp.Path != nil {
1073
- path = imp.Path.Value
1074
- // Remove quotes from the import path string
1075
- path = path[1 : len(path)-1]
1057
+ // Process imports from all files in the package
1058
+ for _, file := range pkg.Syntax {
1059
+ // Create comment map for each file and store it (we'll merge them if needed)
1060
+ cmap := ast.NewCommentMap(pkg.Fset, file, file.Comments)
1061
+ if len(analysis.Cmap) == 0 {
1062
+ analysis.Cmap = cmap
1063
+ } else {
1064
+ // Merge comment maps from multiple files
1065
+ for node, comments := range cmap {
1066
+ analysis.Cmap[node] = append(analysis.Cmap[node], comments...)
1067
+ }
1076
1068
  }
1077
1069
 
1078
- // Store the import in the analysis
1079
- if path != "" {
1080
- name := ""
1081
- if imp.Name != nil {
1082
- name = imp.Name.Name
1070
+ // Process imports from this file
1071
+ for _, imp := range file.Imports {
1072
+ path := ""
1073
+ if imp.Path != nil {
1074
+ path = imp.Path.Value
1075
+ // Remove quotes from the import path string
1076
+ path = path[1 : len(path)-1]
1083
1077
  }
1084
1078
 
1085
- fileImp := &fileImport{
1086
- importPath: path,
1087
- importVars: make(map[string]struct{}),
1088
- }
1079
+ // Store the import in the analysis
1080
+ if path != "" {
1081
+ name := ""
1082
+ if imp.Name != nil {
1083
+ name = imp.Name.Name
1084
+ }
1089
1085
 
1090
- // Use the import name or the actual package name as the key
1091
- var key string
1092
- if name != "" {
1093
- // Explicit alias provided
1094
- key = name
1095
- } else {
1096
- // No explicit alias, use the actual package name from type information
1097
- // This handles cases where package name differs from the last path segment
1098
- if actualName, err := getActualPackageName(path, pkg.Imports); err == nil {
1099
- key = actualName
1086
+ fileImp := &fileImport{
1087
+ importPath: path,
1088
+ importVars: make(map[string]struct{}),
1089
+ }
1090
+
1091
+ // Use the import name or the actual package name as the key
1092
+ var key string
1093
+ if name != "" {
1094
+ // Explicit alias provided
1095
+ key = name
1100
1096
  } else {
1101
- // Fallback to last segment of path if package not found in type information
1102
- pts := strings.Split(path, "/")
1103
- key = pts[len(pts)-1]
1097
+ // No explicit alias, use the actual package name from type information
1098
+ // This handles cases where package name differs from the last path segment
1099
+ if actualName, err := getActualPackageName(path, pkg.Imports); err == nil {
1100
+ key = actualName
1101
+ } else {
1102
+ // Fallback to last segment of path if package not found in type information
1103
+ pts := strings.Split(path, "/")
1104
+ key = pts[len(pts)-1]
1105
+ }
1104
1106
  }
1105
- }
1106
1107
 
1107
- analysis.Imports[key] = fileImp
1108
+ analysis.Imports[key] = fileImp
1109
+ }
1108
1110
  }
1109
1111
  }
1110
1112
 
1111
- // Create an analysis visitor to traverse the AST
1113
+ // Create visitor for the entire package
1112
1114
  visitor := &analysisVisitor{
1113
- analysis: analysis,
1114
- pkg: pkg,
1115
- inAsyncFunction: false,
1116
- currentReceiver: nil, // Initialize currentReceiver
1115
+ analysis: analysis,
1116
+ pkg: pkg,
1117
1117
  }
1118
1118
 
1119
- // Walk the AST with our visitor
1120
- ast.Walk(visitor, file)
1119
+ // First pass: analyze all declarations and statements across all files
1120
+ for _, file := range pkg.Syntax {
1121
+ ast.Walk(visitor, file)
1122
+ }
1121
1123
 
1122
1124
  // Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
1123
1125
  // This distinguishes between method calls (obj.Method()) and method values (obj.Method)
1124
- ast.Inspect(file, func(n ast.Node) bool {
1125
- if callExpr, ok := n.(*ast.CallExpr); ok {
1126
- if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
1127
- // This SelectorExpr is the function being called, so it's NOT a method value
1128
- if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
1129
- nodeInfo.IsMethodValue = false
1126
+ for _, file := range pkg.Syntax {
1127
+ ast.Inspect(file, func(n ast.Node) bool {
1128
+ if callExpr, ok := n.(*ast.CallExpr); ok {
1129
+ if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
1130
+ // This SelectorExpr is the function being called, so it's NOT a method value
1131
+ if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
1132
+ nodeInfo.IsMethodValue = false
1133
+ }
1130
1134
  }
1131
1135
  }
1132
- }
1133
- return true
1134
- })
1135
- }
1136
+ return true
1137
+ })
1138
+ }
1136
1139
 
1137
- // getNamedReturns retrieves the named returns for a function
1138
- func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
1139
- var namedReturns []string
1140
- if funcDecl.Type != nil && funcDecl.Type.Results != nil {
1141
- for _, field := range funcDecl.Type.Results.List {
1142
- for _, name := range field.Names {
1143
- namedReturns = append(namedReturns, name.Name)
1144
- }
1145
- }
1140
+ // Second pass: analyze interface implementations now that all function async status is determined
1141
+ interfaceVisitor := &interfaceImplementationVisitor{
1142
+ analysis: analysis,
1143
+ pkg: pkg,
1146
1144
  }
1147
- return namedReturns
1145
+ for _, file := range pkg.Syntax {
1146
+ ast.Walk(interfaceVisitor, file)
1147
+ }
1148
+
1149
+ return analysis
1148
1150
  }
1149
1151
 
1150
- // AnalyzePackage performs package-level analysis to collect function definitions
1152
+ // AnalyzePackageImports performs package-level analysis to collect function definitions
1151
1153
  // and calls across all files in the package for auto-import generation
1152
- func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
1154
+ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1153
1155
  analysis := NewPackageAnalysis()
1154
1156
 
1155
1157
  // First pass: collect all function definitions per file
@@ -1315,10 +1317,6 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
1315
1317
 
1316
1318
  // LoadPackageMetadata loads metadata from gs packages using embedded JSON files
1317
1319
  func (a *Analysis) LoadPackageMetadata() {
1318
- // Use the embedded filesystem from gs.go
1319
- // The GsOverrides embed.FS should be available but we need to import it
1320
- // For now, let's iterate through known packages and load their meta.json files
1321
-
1322
1320
  // Discover all packages in the embedded gs/ directory
1323
1321
  packagePaths := a.discoverEmbeddedGsPackages()
1324
1322
 
@@ -1336,10 +1334,16 @@ func (a *Analysis) LoadPackageMetadata() {
1336
1334
  typeName := parts[0]
1337
1335
  methodName := parts[1]
1338
1336
  // The key format is "pkgName.TypeNameMethodNameInfo"
1339
- key := pkgPath + "." + typeName + methodName + "Info"
1337
+ key := PackageMetadataKey{
1338
+ PackagePath: pkgPath,
1339
+ TypeName: typeName,
1340
+ MethodName: methodName,
1341
+ }
1340
1342
 
1341
1343
  // Store the async value directly in PackageMetadata
1342
- a.PackageMetadata[key] = isAsync
1344
+ a.PackageMetadata[key] = MethodMetadata{
1345
+ IsAsync: isAsync,
1346
+ }
1343
1347
  }
1344
1348
  }
1345
1349
  }
@@ -1391,20 +1395,122 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
1391
1395
  }
1392
1396
 
1393
1397
  // IsMethodAsync checks if a method call is async based on package metadata
1394
- func (a *Analysis) IsMethodAsync(pkgName, typeName, methodName string) bool {
1398
+ func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
1399
+ // First, check existing metadata for internal packages
1395
1400
  // The metadata keys are stored as "pkgName.TypeNameMethodNameInfo"
1396
1401
  // e.g., "sync.MutexLockInfo", "sync.WaitGroupWaitInfo", etc.
1397
- key := pkgName + "." + typeName + methodName + "Info"
1402
+ key := PackageMetadataKey{
1403
+ PackagePath: pkgPath,
1404
+ TypeName: typeName,
1405
+ MethodName: methodName,
1406
+ }
1398
1407
 
1399
- if asyncValue, exists := a.PackageMetadata[key]; exists {
1400
- if isAsync, ok := asyncValue.(bool); ok {
1401
- return isAsync
1408
+ if metadata, exists := a.PackageMetadata[key]; exists {
1409
+ return metadata.IsAsync
1410
+ }
1411
+
1412
+ // If no metadata found, check if we can analyze the method from dependency packages
1413
+ return a.analyzeExternalMethodAsync(pkgPath, typeName, methodName)
1414
+ }
1415
+
1416
+ // analyzeExternalMethodAsync analyzes external package methods to determine if they're async
1417
+ // using the same logic we use for internal methods
1418
+ func (a *Analysis) analyzeExternalMethodAsync(pkgPath, typeName, methodName string) bool {
1419
+ // Look up the package in our loaded packages
1420
+ pkg, exists := a.AllPackages[pkgPath]
1421
+ if !exists {
1422
+ return false
1423
+ }
1424
+
1425
+ // Find the method definition in the package
1426
+ for _, syntax := range pkg.Syntax {
1427
+ for _, decl := range syntax.Decls {
1428
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
1429
+ // Check if this is a method with the right name and receiver type
1430
+ if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
1431
+ // Check the receiver type
1432
+ if len(funcDecl.Recv.List) > 0 {
1433
+ receiverType := funcDecl.Recv.List[0].Type
1434
+
1435
+ // Handle pointer receivers
1436
+ if starExpr, ok := receiverType.(*ast.StarExpr); ok {
1437
+ receiverType = starExpr.X
1438
+ }
1439
+
1440
+ if ident, ok := receiverType.(*ast.Ident); ok {
1441
+ if ident.Name == typeName {
1442
+ // Found the method! Now check if it's async
1443
+ if funcDecl.Body != nil {
1444
+ return a.containsAsyncOperations(funcDecl.Body, pkg)
1445
+ }
1446
+ }
1447
+ }
1448
+ }
1449
+ }
1450
+ }
1402
1451
  }
1403
1452
  }
1404
1453
 
1405
1454
  return false
1406
1455
  }
1407
1456
 
1457
+ // containsAsyncOperations checks if a node contains any async operations for a specific package
1458
+ func (a *Analysis) containsAsyncOperations(node ast.Node, pkg *packages.Package) bool {
1459
+ var hasAsync bool
1460
+
1461
+ ast.Inspect(node, func(n ast.Node) bool {
1462
+ if n == nil {
1463
+ return false
1464
+ }
1465
+
1466
+ switch s := n.(type) {
1467
+ case *ast.SendStmt:
1468
+ // Channel send operation (ch <- value)
1469
+ hasAsync = true
1470
+ return false
1471
+
1472
+ case *ast.UnaryExpr:
1473
+ // Channel receive operation (<-ch)
1474
+ if s.Op == token.ARROW {
1475
+ hasAsync = true
1476
+ return false
1477
+ }
1478
+
1479
+ case *ast.CallExpr:
1480
+ // Check for select statements and other async operations
1481
+ if ident, ok := s.Fun.(*ast.Ident); ok {
1482
+ // Look for calls to select-related functions or other async operations
1483
+ if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
1484
+ // Check if this function is from the context package (like context.Background, etc.)
1485
+ if obj.Pkg() != nil && obj.Pkg().Path() == "context" {
1486
+ hasAsync = true
1487
+ return false
1488
+ }
1489
+ }
1490
+ }
1491
+
1492
+ // Check if the method takes a context parameter, which usually indicates async behavior
1493
+ if len(s.Args) > 0 {
1494
+ for _, arg := range s.Args {
1495
+ if argType := pkg.TypesInfo.TypeOf(arg); argType != nil {
1496
+ if named, ok := argType.(*types.Named); ok {
1497
+ if named.Obj() != nil && named.Obj().Pkg() != nil &&
1498
+ named.Obj().Pkg().Path() == "context" && named.Obj().Name() == "Context" {
1499
+ hasAsync = true
1500
+ return false
1501
+ }
1502
+ }
1503
+ }
1504
+ }
1505
+ }
1506
+ }
1507
+
1508
+ return true
1509
+ })
1510
+
1511
+ return hasAsync
1512
+ }
1513
+
1408
1514
  // NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
1409
1515
  func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
1410
1516
  if node == nil {
@@ -1602,117 +1708,106 @@ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map
1602
1708
  }
1603
1709
  }
1604
1710
 
1605
- // IsWrapperType returns whether the given type should be implemented as a wrapper class
1606
- // This includes both local types with methods and imported types that are known wrapper types
1607
- func (a *Analysis) IsWrapperType(t types.Type) bool {
1608
- if t == nil {
1609
- return false
1711
+ // trackInterfaceImplementation records that a struct type implements an interface method
1712
+ func (a *Analysis) trackInterfaceImplementation(interfaceType *types.Interface, structType *types.Named, method *types.Func, isAsync bool) {
1713
+ key := InterfaceMethodKey{
1714
+ InterfaceType: interfaceType.String(),
1715
+ MethodName: method.Name(),
1610
1716
  }
1611
1717
 
1612
- // Check if we've already determined this type is a wrapper type
1613
- if isWrapper, exists := a.WrapperTypes[t]; exists {
1614
- return isWrapper
1718
+ implementation := ImplementationInfo{
1719
+ StructType: structType,
1720
+ Method: method,
1721
+ IsAsyncByFlow: isAsync,
1615
1722
  }
1616
1723
 
1617
- // For named types, check if they have methods and underlying primitive types
1618
- if namedType, ok := t.(*types.Named); ok {
1619
- // Exclude struct types - they should remain as classes, not wrapper types
1620
- if _, isStruct := namedType.Underlying().(*types.Struct); isStruct {
1621
- a.WrapperTypes[t] = false
1622
- return false
1623
- }
1724
+ a.InterfaceImplementations[key] = append(a.InterfaceImplementations[key], implementation)
1725
+ }
1624
1726
 
1625
- // Exclude interface types
1626
- if _, isInterface := namedType.Underlying().(*types.Interface); isInterface {
1627
- a.WrapperTypes[t] = false
1628
- return false
1629
- }
1727
+ // IsInterfaceMethodAsync determines if an interface method should be async based on its implementations
1728
+ func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, methodName string) bool {
1729
+ key := InterfaceMethodKey{
1730
+ InterfaceType: interfaceType.String(),
1731
+ MethodName: methodName,
1732
+ }
1630
1733
 
1631
- // Check if this type has methods defined on it
1632
- if namedType.NumMethods() > 0 {
1633
- // Additional check: the underlying type should be a basic type (primitive)
1634
- // This distinguishes wrapper types from complex types with methods
1635
- underlying := namedType.Underlying()
1734
+ // Check if we've already computed this
1735
+ if result, exists := a.InterfaceMethodAsyncStatus[key]; exists {
1736
+ return result
1737
+ }
1636
1738
 
1637
- if isBasicType(underlying) {
1638
- a.WrapperTypes[t] = true
1639
- return true
1640
- }
1739
+ // Find all implementations of this interface method
1740
+ implementations, exists := a.InterfaceImplementations[key]
1741
+ if !exists {
1742
+ // No implementations found, default to sync
1743
+ a.InterfaceMethodAsyncStatus[key] = false
1744
+ return false
1745
+ }
1641
1746
 
1642
- // For non-basic underlying types with methods, still consider them wrapper types
1643
- // if they're not structs or interfaces (already excluded above)
1644
- a.WrapperTypes[t] = true
1747
+ // If ANY implementation is async, the interface method is async
1748
+ for _, impl := range implementations {
1749
+ if impl.IsAsyncByFlow {
1750
+ a.InterfaceMethodAsyncStatus[key] = true
1645
1751
  return true
1646
1752
  }
1647
1753
  }
1648
1754
 
1649
- // Handle type aliases (Go 1.9+)
1650
- if aliasType, ok := t.(*types.Alias); ok {
1651
- // For type aliases, we need to check the RHS (right-hand side) type
1652
- // For example, if we have: type FileMode = fs.FileMode
1653
- // We need to check if fs.FileMode has methods
1654
- rhs := aliasType.Rhs()
1655
-
1656
- if rhsNamed, ok := rhs.(*types.Named); ok {
1657
- // Check if the RHS named type has methods and a basic underlying type
1658
- if rhsNamed.NumMethods() > 0 {
1659
- underlying := rhsNamed.Underlying()
1660
-
1661
- // Exclude struct types
1662
- if _, isStruct := underlying.(*types.Struct); isStruct {
1663
- a.WrapperTypes[t] = false
1664
- return false
1665
- }
1755
+ // All implementations are sync
1756
+ a.InterfaceMethodAsyncStatus[key] = false
1757
+ return false
1758
+ }
1666
1759
 
1667
- // Exclude interface types
1668
- if _, isInterface := underlying.(*types.Interface); isInterface {
1669
- a.WrapperTypes[t] = false
1670
- return false
1671
- }
1760
+ // MustBeAsyncDueToInterface checks if a struct method must be async due to interface constraints
1761
+ func (a *Analysis) MustBeAsyncDueToInterface(structType *types.Named, methodName string) bool {
1762
+ // Find all interfaces that this struct implements
1763
+ for key, implementations := range a.InterfaceImplementations {
1764
+ if key.MethodName != methodName {
1765
+ continue
1766
+ }
1672
1767
 
1673
- if isBasicType(underlying) {
1674
- a.WrapperTypes[t] = true
1768
+ // Check if this struct is among the implementations
1769
+ for _, impl := range implementations {
1770
+ if impl.StructType == structType {
1771
+ // This struct implements this interface method
1772
+ // Check if the interface method is marked as async
1773
+ interfaceType := a.findInterfaceTypeByString(key.InterfaceType)
1774
+ if interfaceType != nil && a.IsInterfaceMethodAsync(interfaceType, methodName) {
1675
1775
  return true
1676
1776
  }
1677
-
1678
- // For non-basic underlying types with methods, still consider them wrapper types
1679
- // if they're not structs or interfaces (already excluded above)
1680
- a.WrapperTypes[t] = true
1681
- return true
1682
- }
1683
- }
1684
-
1685
- // Fallback: check the underlying type for older Go versions or different alias patterns
1686
- underlying := aliasType.Underlying()
1687
- if namedUnderlying, ok := underlying.(*types.Named); ok {
1688
- if namedUnderlying.NumMethods() > 0 && isBasicType(namedUnderlying.Underlying()) {
1689
- a.WrapperTypes[t] = true
1690
- return true
1691
1777
  }
1692
1778
  }
1693
1779
  }
1694
1780
 
1695
- // Cache negative result
1696
- a.WrapperTypes[t] = false
1697
1781
  return false
1698
1782
  }
1699
1783
 
1700
- // isBasicType checks if a type is a basic/primitive type
1701
- func isBasicType(t types.Type) bool {
1702
- if t == nil {
1703
- return false
1704
- }
1705
-
1706
- switch underlying := t.(type) {
1707
- case *types.Basic:
1708
- // Basic types like int, string, bool, etc.
1709
- return true
1710
- case *types.Pointer:
1711
- // Pointers to basic types could also be considered for wrapper types
1712
- return isBasicType(underlying.Elem())
1713
- default:
1714
- return false
1784
+ // findInterfaceTypeByString finds an interface type by its string representation
1785
+ // This is a helper method for MustBeAsyncDueToInterface
1786
+ func (a *Analysis) findInterfaceTypeByString(interfaceString string) *types.Interface {
1787
+ // This is a simplified implementation - in practice, we might need to store
1788
+ // the actual interface types in our tracking data structure
1789
+ for _, pkg := range a.AllPackages {
1790
+ for _, syntax := range pkg.Syntax {
1791
+ for _, decl := range syntax.Decls {
1792
+ if genDecl, ok := decl.(*ast.GenDecl); ok {
1793
+ for _, spec := range genDecl.Specs {
1794
+ if typeSpec, ok := spec.(*ast.TypeSpec); ok {
1795
+ if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
1796
+ if goType := pkg.TypesInfo.TypeOf(interfaceType); goType != nil {
1797
+ if iface, ok := goType.(*types.Interface); ok {
1798
+ if iface.String() == interfaceString {
1799
+ return iface
1800
+ }
1801
+ }
1802
+ }
1803
+ }
1804
+ }
1805
+ }
1806
+ }
1807
+ }
1808
+ }
1715
1809
  }
1810
+ return nil
1716
1811
  }
1717
1812
 
1718
1813
  // GetReceiverMapping returns the receiver variable mapping for a function declaration
@@ -1742,3 +1837,354 @@ func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
1742
1837
 
1743
1838
  return ""
1744
1839
  }
1840
+
1841
+ // trackTypeAssertion analyzes type assertions and records interface implementations
1842
+ func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
1843
+ // Get the type being asserted to
1844
+ assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
1845
+ if assertedType == nil {
1846
+ return
1847
+ }
1848
+
1849
+ // Check if the asserted type is an interface
1850
+ interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
1851
+ if !isInterface {
1852
+ return
1853
+ }
1854
+
1855
+ // Get the type of the expression being asserted
1856
+ exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
1857
+ if exprType == nil {
1858
+ return
1859
+ }
1860
+
1861
+ // Handle pointer types by getting the element type
1862
+ if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
1863
+ exprType = ptrType.Elem()
1864
+ }
1865
+
1866
+ // Check if the expression type is a named struct type
1867
+ namedType, isNamed := exprType.(*types.Named)
1868
+ if !isNamed {
1869
+ return
1870
+ }
1871
+
1872
+ // For each method in the interface, check if the struct implements it
1873
+ for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
1874
+ interfaceMethod := interfaceType.ExplicitMethod(i)
1875
+
1876
+ // Find the corresponding method in the struct type
1877
+ structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
1878
+ if structMethod != nil {
1879
+ // Determine if this struct method is async based on control flow analysis
1880
+ isAsync := false
1881
+ if obj := structMethod; obj != nil {
1882
+ if funcInfo := v.analysis.FunctionData[obj]; funcInfo != nil {
1883
+ isAsync = funcInfo.IsAsync
1884
+ }
1885
+ }
1886
+
1887
+ // Track this interface implementation
1888
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
1889
+ }
1890
+ }
1891
+ }
1892
+
1893
+ // findStructMethod finds a method with the given name on a named type
1894
+ func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
1895
+ // Check methods directly on the type
1896
+ for i := 0; i < namedType.NumMethods(); i++ {
1897
+ method := namedType.Method(i)
1898
+ if method.Name() == methodName {
1899
+ return method
1900
+ }
1901
+ }
1902
+ return nil
1903
+ }
1904
+
1905
+ // analyzeAssignment analyzes a single assignment for pointer analysis
1906
+ func (v *analysisVisitor) analyzeAssignment(lhsExpr, rhsExpr ast.Expr) {
1907
+ // Determine RHS assignment type and source object
1908
+ rhsAssignmentType := DirectAssignment
1909
+ var rhsSourceObj types.Object
1910
+
1911
+ if unaryExpr, ok := rhsExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
1912
+ // RHS is &some_expr
1913
+ rhsAssignmentType = AddressOfAssignment
1914
+ if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
1915
+ rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
1916
+ }
1917
+ } else if rhsIdent, ok := rhsExpr.(*ast.Ident); ok {
1918
+ // RHS is variable
1919
+ rhsAssignmentType = DirectAssignment
1920
+ rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
1921
+ }
1922
+
1923
+ // Determine LHS object
1924
+ var lhsTrackedObj types.Object
1925
+
1926
+ if lhsIdent, ok := lhsExpr.(*ast.Ident); ok {
1927
+ if lhsIdent.Name != "_" {
1928
+ lhsTrackedObj = v.pkg.TypesInfo.ObjectOf(lhsIdent)
1929
+ }
1930
+ } else if selExpr, ok := lhsExpr.(*ast.SelectorExpr); ok {
1931
+ if selection := v.pkg.TypesInfo.Selections[selExpr]; selection != nil {
1932
+ lhsTrackedObj = selection.Obj()
1933
+ }
1934
+ }
1935
+
1936
+ // Record usage information
1937
+ if _, isVar := lhsTrackedObj.(*types.Var); isVar {
1938
+ lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
1939
+ if rhsSourceObj != nil {
1940
+ lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
1941
+ Object: rhsSourceObj,
1942
+ Type: rhsAssignmentType,
1943
+ })
1944
+ } else if rhsAssignmentType == AddressOfAssignment {
1945
+ lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
1946
+ Object: nil,
1947
+ Type: rhsAssignmentType,
1948
+ })
1949
+ }
1950
+ }
1951
+
1952
+ if rhsSourceObj != nil {
1953
+ sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
1954
+ sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
1955
+ Object: lhsTrackedObj,
1956
+ Type: rhsAssignmentType,
1957
+ })
1958
+ }
1959
+ }
1960
+
1961
+ // trackInterfaceAssignments tracks interface implementations in assignment statements
1962
+ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt) {
1963
+ // For each assignment, check if we're assigning a struct to an interface variable
1964
+ for i, lhsExpr := range assignStmt.Lhs {
1965
+ if i >= len(assignStmt.Rhs) {
1966
+ continue
1967
+ }
1968
+ rhsExpr := assignStmt.Rhs[i]
1969
+
1970
+ // Get the type of the LHS (destination)
1971
+ lhsType := v.pkg.TypesInfo.TypeOf(lhsExpr)
1972
+ if lhsType == nil {
1973
+ continue
1974
+ }
1975
+
1976
+ // Check if LHS is an interface type
1977
+ interfaceType, isInterface := lhsType.Underlying().(*types.Interface)
1978
+ if !isInterface {
1979
+ continue
1980
+ }
1981
+
1982
+ // Get the type of the RHS (source)
1983
+ rhsType := v.pkg.TypesInfo.TypeOf(rhsExpr)
1984
+ if rhsType == nil {
1985
+ continue
1986
+ }
1987
+
1988
+ // Handle pointer types
1989
+ if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
1990
+ rhsType = ptrType.Elem()
1991
+ }
1992
+
1993
+ // Check if RHS is a named struct type
1994
+ namedType, isNamed := rhsType.(*types.Named)
1995
+ if !isNamed {
1996
+ continue
1997
+ }
1998
+
1999
+ // Track implementations for all interface methods
2000
+ for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
2001
+ interfaceMethod := interfaceType.ExplicitMethod(j)
2002
+
2003
+ structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2004
+ if structMethod != nil {
2005
+ // Determine if this struct method is async
2006
+ isAsync := false
2007
+ if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
2008
+ isAsync = funcInfo.IsAsync
2009
+ }
2010
+
2011
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2012
+ }
2013
+ }
2014
+ }
2015
+ }
2016
+
2017
+ // IsNamedBasicType returns whether the given type should be implemented as a type alias with standalone functions
2018
+ // This applies to named types with basic underlying types (like uint32, string, etc.) that have methods
2019
+ // It excludes struct types, which should remain as classes
2020
+ func (a *Analysis) IsNamedBasicType(t types.Type) bool {
2021
+ if t == nil {
2022
+ return false
2023
+ }
2024
+
2025
+ // Check if we already have this result cached
2026
+ if result, exists := a.NamedBasicTypes[t]; exists {
2027
+ return result
2028
+ }
2029
+
2030
+ var originalType types.Type = t
2031
+ var foundMethods bool
2032
+
2033
+ // Traverse the type chain to find any type with methods
2034
+ for {
2035
+ switch typed := t.(type) {
2036
+ case *types.Named:
2037
+ // Built-in types cannot be named basic types
2038
+ if typed.Obj().Pkg() == nil {
2039
+ return false
2040
+ }
2041
+
2042
+ // Check if this named type has methods
2043
+ if typed.NumMethods() > 0 {
2044
+ foundMethods = true
2045
+ }
2046
+
2047
+ // Check underlying type
2048
+ underlying := typed.Underlying()
2049
+ switch underlying.(type) {
2050
+ case *types.Struct, *types.Interface:
2051
+ return false
2052
+ }
2053
+ t = underlying
2054
+
2055
+ case *types.Alias:
2056
+ // Built-in types cannot be named basic types
2057
+ if typed.Obj().Pkg() == nil {
2058
+ return false
2059
+ }
2060
+ t = typed.Underlying()
2061
+
2062
+ default:
2063
+ // We've reached a non-named, non-alias type
2064
+ // Check if it's a supported type with methods
2065
+ switch t.(type) {
2066
+ case *types.Basic, *types.Slice, *types.Array, *types.Map:
2067
+ if foundMethods {
2068
+ a.NamedBasicTypes[originalType] = true
2069
+ return true
2070
+ }
2071
+ return false
2072
+ default:
2073
+ return false
2074
+ }
2075
+ }
2076
+ }
2077
+ }
2078
+
2079
+ // interfaceImplementationVisitor performs a second pass to analyze interface implementations
2080
+ type interfaceImplementationVisitor struct {
2081
+ analysis *Analysis
2082
+ pkg *packages.Package
2083
+ }
2084
+
2085
+ func (v *interfaceImplementationVisitor) Visit(node ast.Node) ast.Visitor {
2086
+ switch n := node.(type) {
2087
+ case *ast.GenDecl:
2088
+ // Look for interface type specifications
2089
+ for _, spec := range n.Specs {
2090
+ if typeSpec, ok := spec.(*ast.TypeSpec); ok {
2091
+ if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
2092
+ // This is an interface declaration, find all potential implementations
2093
+ v.findInterfaceImplementations(interfaceType)
2094
+ }
2095
+ }
2096
+ }
2097
+ }
2098
+ return v
2099
+ }
2100
+
2101
+ // findInterfaceImplementations finds all struct types that implement the given interface
2102
+ func (v *interfaceImplementationVisitor) findInterfaceImplementations(interfaceAST *ast.InterfaceType) {
2103
+ // Get the interface type from TypesInfo
2104
+ interfaceGoType := v.pkg.TypesInfo.TypeOf(interfaceAST)
2105
+ if interfaceGoType == nil {
2106
+ return
2107
+ }
2108
+
2109
+ interfaceType, ok := interfaceGoType.(*types.Interface)
2110
+ if !ok {
2111
+ return
2112
+ }
2113
+
2114
+ // Look through all packages for potential implementations
2115
+ for _, pkg := range v.analysis.AllPackages {
2116
+ v.findImplementationsInPackage(interfaceType, pkg)
2117
+ }
2118
+ }
2119
+
2120
+ // findImplementationsInPackage finds implementations of an interface in a specific package
2121
+ func (v *interfaceImplementationVisitor) findImplementationsInPackage(interfaceType *types.Interface, pkg *packages.Package) {
2122
+ // Get all named types in the package
2123
+ scope := pkg.Types.Scope()
2124
+ for _, name := range scope.Names() {
2125
+ obj := scope.Lookup(name)
2126
+ if obj == nil {
2127
+ continue
2128
+ }
2129
+
2130
+ // Check if this is a type name
2131
+ typeName, ok := obj.(*types.TypeName)
2132
+ if !ok {
2133
+ continue
2134
+ }
2135
+
2136
+ namedType, ok := typeName.Type().(*types.Named)
2137
+ if !ok {
2138
+ continue
2139
+ }
2140
+
2141
+ // Check if this type implements the interface
2142
+ if types.Implements(namedType, interfaceType) || types.Implements(types.NewPointer(namedType), interfaceType) {
2143
+ v.trackImplementation(interfaceType, namedType)
2144
+ }
2145
+ }
2146
+ }
2147
+
2148
+ // trackImplementation records that a named type implements an interface
2149
+ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *types.Interface, namedType *types.Named) {
2150
+ // For each method in the interface, find the corresponding implementation
2151
+ for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
2152
+ interfaceMethod := interfaceType.ExplicitMethod(i)
2153
+
2154
+ // Find the method in the implementing type
2155
+ structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
2156
+ if structMethod != nil {
2157
+ // Determine if this implementation is async
2158
+ isAsync := false
2159
+ if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
2160
+ isAsync = funcInfo.IsAsync
2161
+ }
2162
+
2163
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2164
+ }
2165
+ }
2166
+ }
2167
+
2168
+ // findMethodInType finds a method with the given name in a named type
2169
+ func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named, methodName string) *types.Func {
2170
+ for i := 0; i < namedType.NumMethods(); i++ {
2171
+ method := namedType.Method(i)
2172
+ if method.Name() == methodName {
2173
+ return method
2174
+ }
2175
+ }
2176
+ return nil
2177
+ }
2178
+
2179
+ // getNamedReturns retrieves the named returns for a function
2180
+ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
2181
+ var namedReturns []string
2182
+ if funcDecl.Type != nil && funcDecl.Type.Results != nil {
2183
+ for _, field := range funcDecl.Type.Results.List {
2184
+ for _, name := range field.Names {
2185
+ namedReturns = append(namedReturns, name.Name)
2186
+ }
2187
+ }
2188
+ }
2189
+ return namedReturns
2190
+ }