goscript 0.0.49 → 0.0.51

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.
Files changed (65) hide show
  1. package/compiler/analysis.go +1021 -561
  2. package/compiler/analysis_test.go +12 -15
  3. package/compiler/compiler.go +75 -39
  4. package/compiler/decl.go +22 -0
  5. package/compiler/expr-call-async.go +239 -40
  6. package/compiler/expr-call-type-conversion.go +6 -10
  7. package/compiler/expr-call.go +58 -27
  8. package/compiler/sanitize.go +1 -2
  9. package/compiler/spec-struct.go +3 -3
  10. package/compiler/spec-value.go +2 -2
  11. package/compiler/spec.go +66 -43
  12. package/compiler/stmt-assign.go +7 -4
  13. package/compiler/stmt-select.go +52 -1
  14. package/compiler/stmt.go +63 -5
  15. package/compiler/type.go +16 -3
  16. package/dist/gs/builtin/builtin.js.map +1 -1
  17. package/dist/gs/builtin/channel.d.ts +2 -2
  18. package/dist/gs/builtin/channel.js +12 -7
  19. package/dist/gs/builtin/channel.js.map +1 -1
  20. package/dist/gs/context/context.d.ts +16 -18
  21. package/dist/gs/context/context.js +23 -13
  22. package/dist/gs/context/context.js.map +1 -1
  23. package/dist/gs/fmt/fmt.js +3 -1
  24. package/dist/gs/fmt/fmt.js.map +1 -1
  25. package/dist/gs/reflect/type.js +5 -8
  26. package/dist/gs/reflect/type.js.map +1 -1
  27. package/dist/gs/syscall/constants.d.ts +24 -0
  28. package/dist/gs/syscall/constants.js +27 -0
  29. package/dist/gs/syscall/constants.js.map +1 -0
  30. package/dist/gs/syscall/env.d.ts +6 -0
  31. package/dist/gs/syscall/env.js +43 -0
  32. package/dist/gs/syscall/env.js.map +1 -0
  33. package/dist/gs/syscall/errors.d.ts +111 -0
  34. package/dist/gs/syscall/errors.js +547 -0
  35. package/dist/gs/syscall/errors.js.map +1 -0
  36. package/dist/gs/syscall/fs.d.ts +29 -0
  37. package/dist/gs/syscall/fs.js +53 -0
  38. package/dist/gs/syscall/fs.js.map +1 -0
  39. package/dist/gs/syscall/index.d.ts +6 -80
  40. package/dist/gs/syscall/index.js +12 -168
  41. package/dist/gs/syscall/index.js.map +1 -1
  42. package/dist/gs/syscall/rawconn.d.ts +7 -0
  43. package/dist/gs/syscall/rawconn.js +19 -0
  44. package/dist/gs/syscall/rawconn.js.map +1 -0
  45. package/dist/gs/syscall/types.d.ts +12 -0
  46. package/dist/gs/syscall/types.js +2 -0
  47. package/dist/gs/syscall/types.js.map +1 -0
  48. package/dist/gs/time/time.d.ts +2 -1
  49. package/dist/gs/time/time.js +29 -19
  50. package/dist/gs/time/time.js.map +1 -1
  51. package/gs/builtin/builtin.ts +1 -7
  52. package/gs/builtin/channel.ts +18 -12
  53. package/gs/context/context.ts +63 -45
  54. package/gs/fmt/fmt.ts +5 -1
  55. package/gs/reflect/type.ts +5 -10
  56. package/gs/syscall/constants.ts +29 -0
  57. package/gs/syscall/env.ts +47 -0
  58. package/gs/syscall/errors.ts +658 -0
  59. package/gs/syscall/fs.ts +62 -0
  60. package/gs/syscall/index.ts +12 -207
  61. package/gs/syscall/rawconn.ts +23 -0
  62. package/gs/syscall/types.ts +18 -0
  63. package/gs/time/time.ts +35 -21
  64. package/package.json +2 -2
  65. package/gs/TODO.md +0 -129
@@ -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
- }
526
- }
527
-
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
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)
534
621
  }
535
622
  }
536
- }
537
- }
538
- }
539
- }
540
623
 
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{}
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
628
+ }
553
629
  }
554
- v.analysis.FunctionData[obj].NamedReturns = namedReturns
555
630
  }
556
631
  }
557
632
  }
633
+ }
558
634
 
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
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))
571
641
  }
572
-
573
- // Visit the body with updated state
574
- ast.Walk(v, n.Body)
575
642
  }
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
+ }
765
+
766
+ // Check for reflect function calls that operate on functions
767
+ v.checkReflectUsage(n)
692
768
 
693
- // Check for reflect function calls that operate on functions
694
- v.checkReflectUsage(n)
769
+ return v
770
+ }
695
771
 
696
- // Store async state for this call expression
697
- if v.analysis.NodeData[n] == nil {
698
- v.analysis.NodeData[n] = &NodeInfo{}
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
- }
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
840
  }
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
 
@@ -923,6 +911,11 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
923
911
  return false
924
912
  }
925
913
 
914
+ case *ast.SelectStmt:
915
+ // Select statement with channel operations
916
+ hasAsync = true
917
+ return false
918
+
926
919
  case *ast.CallExpr:
927
920
  // Check if we're calling a function known to be async
928
921
  if funcIdent, ok := s.Fun.(*ast.Ident); ok {
@@ -940,29 +933,38 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
940
933
  // Get the type of the receiver
941
934
  if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
942
935
  if varObj, ok := obj.(*types.Var); ok {
943
- // Get the type name and package
944
- if namedType, ok := varObj.Type().(*types.Named); ok {
936
+ // Handle both direct named types and pointer to named types
937
+ var namedType *types.Named
938
+ switch t := varObj.Type().(type) {
939
+ case *types.Named:
940
+ namedType = t
941
+ case *types.Pointer:
942
+ if nt, isNamed := t.Elem().(*types.Named); isNamed {
943
+ namedType = nt
944
+ }
945
+ }
946
+
947
+ if namedType != nil {
945
948
  typeName := namedType.Obj().Name()
946
949
  methodName := selExpr.Sel.Name
947
950
 
948
951
  // Check if the type is from an imported package
949
952
  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()
953
+ // Use the full package path from the type information (not just the package name)
954
+ pkgPath := typePkg.Path()
952
955
 
953
956
  // Check if this method is async based on metadata
954
- if v.analysis.IsMethodAsync(pkgName, typeName, methodName) {
957
+ if v.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
955
958
  hasAsync = true
956
959
  return false
957
960
  }
958
961
  }
962
+ // Note: Local method async detection is handled during code generation to avoid circular dependencies
959
963
  }
960
964
  }
961
965
  }
962
966
  }
963
967
  }
964
-
965
- // TODO: Add detection of method calls on async types
966
968
  }
967
969
 
968
970
  return true
@@ -1057,99 +1059,113 @@ func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, recei
1057
1059
  return false
1058
1060
  }
1059
1061
 
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
1062
+ // AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
1063
+ // with information that will be used during code generation to properly handle pointers,
1064
+ // variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
1065
+ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages.Package) *Analysis {
1066
+ analysis := NewAnalysis(allPackages)
1065
1067
 
1066
1068
  // Load package metadata for async function detection
1067
1069
  analysis.LoadPackageMetadata()
1068
1070
 
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]
1071
+ // Process imports from all files in the package
1072
+ for _, file := range pkg.Syntax {
1073
+ // Create comment map for each file and store it (we'll merge them if needed)
1074
+ cmap := ast.NewCommentMap(pkg.Fset, file, file.Comments)
1075
+ if len(analysis.Cmap) == 0 {
1076
+ analysis.Cmap = cmap
1077
+ } else {
1078
+ // Merge comment maps from multiple files
1079
+ for node, comments := range cmap {
1080
+ analysis.Cmap[node] = append(analysis.Cmap[node], comments...)
1081
+ }
1076
1082
  }
1077
1083
 
1078
- // Store the import in the analysis
1079
- if path != "" {
1080
- name := ""
1081
- if imp.Name != nil {
1082
- name = imp.Name.Name
1084
+ // Process imports from this file
1085
+ for _, imp := range file.Imports {
1086
+ path := ""
1087
+ if imp.Path != nil {
1088
+ path = imp.Path.Value
1089
+ // Remove quotes from the import path string
1090
+ path = path[1 : len(path)-1]
1083
1091
  }
1084
1092
 
1085
- fileImp := &fileImport{
1086
- importPath: path,
1087
- importVars: make(map[string]struct{}),
1088
- }
1093
+ // Store the import in the analysis
1094
+ if path != "" {
1095
+ name := ""
1096
+ if imp.Name != nil {
1097
+ name = imp.Name.Name
1098
+ }
1089
1099
 
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
1100
+ fileImp := &fileImport{
1101
+ importPath: path,
1102
+ importVars: make(map[string]struct{}),
1103
+ }
1104
+
1105
+ // Use the import name or the actual package name as the key
1106
+ var key string
1107
+ if name != "" {
1108
+ // Explicit alias provided
1109
+ key = name
1100
1110
  } 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]
1111
+ // No explicit alias, use the actual package name from type information
1112
+ // This handles cases where package name differs from the last path segment
1113
+ if actualName, err := getActualPackageName(path, pkg.Imports); err == nil {
1114
+ key = actualName
1115
+ } else {
1116
+ // Fallback to last segment of path if package not found in type information
1117
+ pts := strings.Split(path, "/")
1118
+ key = pts[len(pts)-1]
1119
+ }
1104
1120
  }
1105
- }
1106
1121
 
1107
- analysis.Imports[key] = fileImp
1122
+ analysis.Imports[key] = fileImp
1123
+ }
1108
1124
  }
1109
1125
  }
1110
1126
 
1111
- // Create an analysis visitor to traverse the AST
1127
+ // Create visitor for the entire package
1112
1128
  visitor := &analysisVisitor{
1113
- analysis: analysis,
1114
- pkg: pkg,
1115
- inAsyncFunction: false,
1116
- currentReceiver: nil, // Initialize currentReceiver
1129
+ analysis: analysis,
1130
+ pkg: pkg,
1117
1131
  }
1118
1132
 
1119
- // Walk the AST with our visitor
1120
- ast.Walk(visitor, file)
1133
+ // First pass: analyze all declarations and statements across all files
1134
+ for _, file := range pkg.Syntax {
1135
+ ast.Walk(visitor, file)
1136
+ }
1121
1137
 
1122
1138
  // Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
1123
1139
  // 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
1140
+ for _, file := range pkg.Syntax {
1141
+ ast.Inspect(file, func(n ast.Node) bool {
1142
+ if callExpr, ok := n.(*ast.CallExpr); ok {
1143
+ if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
1144
+ // This SelectorExpr is the function being called, so it's NOT a method value
1145
+ if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
1146
+ nodeInfo.IsMethodValue = false
1147
+ }
1130
1148
  }
1131
1149
  }
1132
- }
1133
- return true
1134
- })
1135
- }
1150
+ return true
1151
+ })
1152
+ }
1136
1153
 
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
- }
1154
+ // Second pass: analyze interface implementations now that all function async status is determined
1155
+ interfaceVisitor := &interfaceImplementationVisitor{
1156
+ analysis: analysis,
1157
+ pkg: pkg,
1146
1158
  }
1147
- return namedReturns
1159
+ for _, file := range pkg.Syntax {
1160
+ ast.Walk(interfaceVisitor, file)
1161
+ }
1162
+
1163
+ return analysis
1148
1164
  }
1149
1165
 
1150
- // AnalyzePackage performs package-level analysis to collect function definitions
1166
+ // AnalyzePackageImports performs package-level analysis to collect function definitions
1151
1167
  // and calls across all files in the package for auto-import generation
1152
- func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
1168
+ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1153
1169
  analysis := NewPackageAnalysis()
1154
1170
 
1155
1171
  // First pass: collect all function definitions per file
@@ -1315,10 +1331,6 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
1315
1331
 
1316
1332
  // LoadPackageMetadata loads metadata from gs packages using embedded JSON files
1317
1333
  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
1334
  // Discover all packages in the embedded gs/ directory
1323
1335
  packagePaths := a.discoverEmbeddedGsPackages()
1324
1336
 
@@ -1336,10 +1348,16 @@ func (a *Analysis) LoadPackageMetadata() {
1336
1348
  typeName := parts[0]
1337
1349
  methodName := parts[1]
1338
1350
  // The key format is "pkgName.TypeNameMethodNameInfo"
1339
- key := pkgPath + "." + typeName + methodName + "Info"
1351
+ key := PackageMetadataKey{
1352
+ PackagePath: pkgPath,
1353
+ TypeName: typeName,
1354
+ MethodName: methodName,
1355
+ }
1340
1356
 
1341
1357
  // Store the async value directly in PackageMetadata
1342
- a.PackageMetadata[key] = isAsync
1358
+ a.PackageMetadata[key] = MethodMetadata{
1359
+ IsAsync: isAsync,
1360
+ }
1343
1361
  }
1344
1362
  }
1345
1363
  }
@@ -1391,20 +1409,122 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
1391
1409
  }
1392
1410
 
1393
1411
  // IsMethodAsync checks if a method call is async based on package metadata
1394
- func (a *Analysis) IsMethodAsync(pkgName, typeName, methodName string) bool {
1412
+ func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
1413
+ // First, check existing metadata for internal packages
1395
1414
  // The metadata keys are stored as "pkgName.TypeNameMethodNameInfo"
1396
1415
  // e.g., "sync.MutexLockInfo", "sync.WaitGroupWaitInfo", etc.
1397
- key := pkgName + "." + typeName + methodName + "Info"
1416
+ key := PackageMetadataKey{
1417
+ PackagePath: pkgPath,
1418
+ TypeName: typeName,
1419
+ MethodName: methodName,
1420
+ }
1421
+
1422
+ if metadata, exists := a.PackageMetadata[key]; exists {
1423
+ return metadata.IsAsync
1424
+ }
1425
+
1426
+ // If no metadata found, check if we can analyze the method from dependency packages
1427
+ return a.analyzeExternalMethodAsync(pkgPath, typeName, methodName)
1428
+ }
1398
1429
 
1399
- if asyncValue, exists := a.PackageMetadata[key]; exists {
1400
- if isAsync, ok := asyncValue.(bool); ok {
1401
- return isAsync
1430
+ // analyzeExternalMethodAsync analyzes external package methods to determine if they're async
1431
+ // using the same logic we use for internal methods
1432
+ func (a *Analysis) analyzeExternalMethodAsync(pkgPath, typeName, methodName string) bool {
1433
+ // Look up the package in our loaded packages
1434
+ pkg, exists := a.AllPackages[pkgPath]
1435
+ if !exists {
1436
+ return false
1437
+ }
1438
+
1439
+ // Find the method definition in the package
1440
+ for _, syntax := range pkg.Syntax {
1441
+ for _, decl := range syntax.Decls {
1442
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
1443
+ // Check if this is a method with the right name and receiver type
1444
+ if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
1445
+ // Check the receiver type
1446
+ if len(funcDecl.Recv.List) > 0 {
1447
+ receiverType := funcDecl.Recv.List[0].Type
1448
+
1449
+ // Handle pointer receivers
1450
+ if starExpr, ok := receiverType.(*ast.StarExpr); ok {
1451
+ receiverType = starExpr.X
1452
+ }
1453
+
1454
+ if ident, ok := receiverType.(*ast.Ident); ok {
1455
+ if ident.Name == typeName {
1456
+ // Found the method! Now check if it's async
1457
+ if funcDecl.Body != nil {
1458
+ return a.containsAsyncOperations(funcDecl.Body, pkg)
1459
+ }
1460
+ }
1461
+ }
1462
+ }
1463
+ }
1464
+ }
1402
1465
  }
1403
1466
  }
1404
1467
 
1405
1468
  return false
1406
1469
  }
1407
1470
 
1471
+ // containsAsyncOperations checks if a node contains any async operations for a specific package
1472
+ func (a *Analysis) containsAsyncOperations(node ast.Node, pkg *packages.Package) bool {
1473
+ var hasAsync bool
1474
+
1475
+ ast.Inspect(node, func(n ast.Node) bool {
1476
+ if n == nil {
1477
+ return false
1478
+ }
1479
+
1480
+ switch s := n.(type) {
1481
+ case *ast.SendStmt:
1482
+ // Channel send operation (ch <- value)
1483
+ hasAsync = true
1484
+ return false
1485
+
1486
+ case *ast.UnaryExpr:
1487
+ // Channel receive operation (<-ch)
1488
+ if s.Op == token.ARROW {
1489
+ hasAsync = true
1490
+ return false
1491
+ }
1492
+
1493
+ case *ast.CallExpr:
1494
+ // Check for select statements and other async operations
1495
+ if ident, ok := s.Fun.(*ast.Ident); ok {
1496
+ // Look for calls to select-related functions or other async operations
1497
+ if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
1498
+ // Check if this function is from the context package (like context.Background, etc.)
1499
+ if obj.Pkg() != nil && obj.Pkg().Path() == "context" {
1500
+ hasAsync = true
1501
+ return false
1502
+ }
1503
+ }
1504
+ }
1505
+
1506
+ // Check if the method takes a context parameter, which usually indicates async behavior
1507
+ if len(s.Args) > 0 {
1508
+ for _, arg := range s.Args {
1509
+ if argType := pkg.TypesInfo.TypeOf(arg); argType != nil {
1510
+ if named, ok := argType.(*types.Named); ok {
1511
+ if named.Obj() != nil && named.Obj().Pkg() != nil &&
1512
+ named.Obj().Pkg().Path() == "context" && named.Obj().Name() == "Context" {
1513
+ hasAsync = true
1514
+ return false
1515
+ }
1516
+ }
1517
+ }
1518
+ }
1519
+ }
1520
+ }
1521
+
1522
+ return true
1523
+ })
1524
+
1525
+ return hasAsync
1526
+ }
1527
+
1408
1528
  // NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
1409
1529
  func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
1410
1530
  if node == nil {
@@ -1602,117 +1722,106 @@ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map
1602
1722
  }
1603
1723
  }
1604
1724
 
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
1725
+ // trackInterfaceImplementation records that a struct type implements an interface method
1726
+ func (a *Analysis) trackInterfaceImplementation(interfaceType *types.Interface, structType *types.Named, method *types.Func, isAsync bool) {
1727
+ key := InterfaceMethodKey{
1728
+ InterfaceType: interfaceType.String(),
1729
+ MethodName: method.Name(),
1610
1730
  }
1611
1731
 
1612
- // Check if we've already determined this type is a wrapper type
1613
- if isWrapper, exists := a.WrapperTypes[t]; exists {
1614
- return isWrapper
1732
+ implementation := ImplementationInfo{
1733
+ StructType: structType,
1734
+ Method: method,
1735
+ IsAsyncByFlow: isAsync,
1615
1736
  }
1616
1737
 
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
- }
1738
+ a.InterfaceImplementations[key] = append(a.InterfaceImplementations[key], implementation)
1739
+ }
1624
1740
 
1625
- // Exclude interface types
1626
- if _, isInterface := namedType.Underlying().(*types.Interface); isInterface {
1627
- a.WrapperTypes[t] = false
1628
- return false
1629
- }
1741
+ // IsInterfaceMethodAsync determines if an interface method should be async based on its implementations
1742
+ func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, methodName string) bool {
1743
+ key := InterfaceMethodKey{
1744
+ InterfaceType: interfaceType.String(),
1745
+ MethodName: methodName,
1746
+ }
1630
1747
 
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()
1748
+ // Check if we've already computed this
1749
+ if result, exists := a.InterfaceMethodAsyncStatus[key]; exists {
1750
+ return result
1751
+ }
1636
1752
 
1637
- if isBasicType(underlying) {
1638
- a.WrapperTypes[t] = true
1639
- return true
1640
- }
1753
+ // Find all implementations of this interface method
1754
+ implementations, exists := a.InterfaceImplementations[key]
1755
+ if !exists {
1756
+ // No implementations found, default to sync
1757
+ a.InterfaceMethodAsyncStatus[key] = false
1758
+ return false
1759
+ }
1641
1760
 
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
1761
+ // If ANY implementation is async, the interface method is async
1762
+ for _, impl := range implementations {
1763
+ if impl.IsAsyncByFlow {
1764
+ a.InterfaceMethodAsyncStatus[key] = true
1645
1765
  return true
1646
1766
  }
1647
1767
  }
1648
1768
 
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
- }
1769
+ // All implementations are sync
1770
+ a.InterfaceMethodAsyncStatus[key] = false
1771
+ return false
1772
+ }
1666
1773
 
1667
- // Exclude interface types
1668
- if _, isInterface := underlying.(*types.Interface); isInterface {
1669
- a.WrapperTypes[t] = false
1670
- return false
1671
- }
1774
+ // MustBeAsyncDueToInterface checks if a struct method must be async due to interface constraints
1775
+ func (a *Analysis) MustBeAsyncDueToInterface(structType *types.Named, methodName string) bool {
1776
+ // Find all interfaces that this struct implements
1777
+ for key, implementations := range a.InterfaceImplementations {
1778
+ if key.MethodName != methodName {
1779
+ continue
1780
+ }
1672
1781
 
1673
- if isBasicType(underlying) {
1674
- a.WrapperTypes[t] = true
1782
+ // Check if this struct is among the implementations
1783
+ for _, impl := range implementations {
1784
+ if impl.StructType == structType {
1785
+ // This struct implements this interface method
1786
+ // Check if the interface method is marked as async
1787
+ interfaceType := a.findInterfaceTypeByString(key.InterfaceType)
1788
+ if interfaceType != nil && a.IsInterfaceMethodAsync(interfaceType, methodName) {
1675
1789
  return true
1676
1790
  }
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
1791
  }
1692
1792
  }
1693
1793
  }
1694
1794
 
1695
- // Cache negative result
1696
- a.WrapperTypes[t] = false
1697
1795
  return false
1698
1796
  }
1699
1797
 
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
1798
+ // findInterfaceTypeByString finds an interface type by its string representation
1799
+ // This is a helper method for MustBeAsyncDueToInterface
1800
+ func (a *Analysis) findInterfaceTypeByString(interfaceString string) *types.Interface {
1801
+ // This is a simplified implementation - in practice, we might need to store
1802
+ // the actual interface types in our tracking data structure
1803
+ for _, pkg := range a.AllPackages {
1804
+ for _, syntax := range pkg.Syntax {
1805
+ for _, decl := range syntax.Decls {
1806
+ if genDecl, ok := decl.(*ast.GenDecl); ok {
1807
+ for _, spec := range genDecl.Specs {
1808
+ if typeSpec, ok := spec.(*ast.TypeSpec); ok {
1809
+ if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
1810
+ if goType := pkg.TypesInfo.TypeOf(interfaceType); goType != nil {
1811
+ if iface, ok := goType.(*types.Interface); ok {
1812
+ if iface.String() == interfaceString {
1813
+ return iface
1814
+ }
1815
+ }
1816
+ }
1817
+ }
1818
+ }
1819
+ }
1820
+ }
1821
+ }
1822
+ }
1715
1823
  }
1824
+ return nil
1716
1825
  }
1717
1826
 
1718
1827
  // GetReceiverMapping returns the receiver variable mapping for a function declaration
@@ -1742,3 +1851,354 @@ func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
1742
1851
 
1743
1852
  return ""
1744
1853
  }
1854
+
1855
+ // trackTypeAssertion analyzes type assertions and records interface implementations
1856
+ func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
1857
+ // Get the type being asserted to
1858
+ assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
1859
+ if assertedType == nil {
1860
+ return
1861
+ }
1862
+
1863
+ // Check if the asserted type is an interface
1864
+ interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
1865
+ if !isInterface {
1866
+ return
1867
+ }
1868
+
1869
+ // Get the type of the expression being asserted
1870
+ exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
1871
+ if exprType == nil {
1872
+ return
1873
+ }
1874
+
1875
+ // Handle pointer types by getting the element type
1876
+ if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
1877
+ exprType = ptrType.Elem()
1878
+ }
1879
+
1880
+ // Check if the expression type is a named struct type
1881
+ namedType, isNamed := exprType.(*types.Named)
1882
+ if !isNamed {
1883
+ return
1884
+ }
1885
+
1886
+ // For each method in the interface, check if the struct implements it
1887
+ for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
1888
+ interfaceMethod := interfaceType.ExplicitMethod(i)
1889
+
1890
+ // Find the corresponding method in the struct type
1891
+ structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
1892
+ if structMethod != nil {
1893
+ // Determine if this struct method is async based on control flow analysis
1894
+ isAsync := false
1895
+ if obj := structMethod; obj != nil {
1896
+ if funcInfo := v.analysis.FunctionData[obj]; funcInfo != nil {
1897
+ isAsync = funcInfo.IsAsync
1898
+ }
1899
+ }
1900
+
1901
+ // Track this interface implementation
1902
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
1903
+ }
1904
+ }
1905
+ }
1906
+
1907
+ // findStructMethod finds a method with the given name on a named type
1908
+ func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
1909
+ // Check methods directly on the type
1910
+ for i := 0; i < namedType.NumMethods(); i++ {
1911
+ method := namedType.Method(i)
1912
+ if method.Name() == methodName {
1913
+ return method
1914
+ }
1915
+ }
1916
+ return nil
1917
+ }
1918
+
1919
+ // analyzeAssignment analyzes a single assignment for pointer analysis
1920
+ func (v *analysisVisitor) analyzeAssignment(lhsExpr, rhsExpr ast.Expr) {
1921
+ // Determine RHS assignment type and source object
1922
+ rhsAssignmentType := DirectAssignment
1923
+ var rhsSourceObj types.Object
1924
+
1925
+ if unaryExpr, ok := rhsExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
1926
+ // RHS is &some_expr
1927
+ rhsAssignmentType = AddressOfAssignment
1928
+ if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
1929
+ rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
1930
+ }
1931
+ } else if rhsIdent, ok := rhsExpr.(*ast.Ident); ok {
1932
+ // RHS is variable
1933
+ rhsAssignmentType = DirectAssignment
1934
+ rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
1935
+ }
1936
+
1937
+ // Determine LHS object
1938
+ var lhsTrackedObj types.Object
1939
+
1940
+ if lhsIdent, ok := lhsExpr.(*ast.Ident); ok {
1941
+ if lhsIdent.Name != "_" {
1942
+ lhsTrackedObj = v.pkg.TypesInfo.ObjectOf(lhsIdent)
1943
+ }
1944
+ } else if selExpr, ok := lhsExpr.(*ast.SelectorExpr); ok {
1945
+ if selection := v.pkg.TypesInfo.Selections[selExpr]; selection != nil {
1946
+ lhsTrackedObj = selection.Obj()
1947
+ }
1948
+ }
1949
+
1950
+ // Record usage information
1951
+ if _, isVar := lhsTrackedObj.(*types.Var); isVar {
1952
+ lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
1953
+ if rhsSourceObj != nil {
1954
+ lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
1955
+ Object: rhsSourceObj,
1956
+ Type: rhsAssignmentType,
1957
+ })
1958
+ } else if rhsAssignmentType == AddressOfAssignment {
1959
+ lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
1960
+ Object: nil,
1961
+ Type: rhsAssignmentType,
1962
+ })
1963
+ }
1964
+ }
1965
+
1966
+ if rhsSourceObj != nil {
1967
+ sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
1968
+ sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
1969
+ Object: lhsTrackedObj,
1970
+ Type: rhsAssignmentType,
1971
+ })
1972
+ }
1973
+ }
1974
+
1975
+ // trackInterfaceAssignments tracks interface implementations in assignment statements
1976
+ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt) {
1977
+ // For each assignment, check if we're assigning a struct to an interface variable
1978
+ for i, lhsExpr := range assignStmt.Lhs {
1979
+ if i >= len(assignStmt.Rhs) {
1980
+ continue
1981
+ }
1982
+ rhsExpr := assignStmt.Rhs[i]
1983
+
1984
+ // Get the type of the LHS (destination)
1985
+ lhsType := v.pkg.TypesInfo.TypeOf(lhsExpr)
1986
+ if lhsType == nil {
1987
+ continue
1988
+ }
1989
+
1990
+ // Check if LHS is an interface type
1991
+ interfaceType, isInterface := lhsType.Underlying().(*types.Interface)
1992
+ if !isInterface {
1993
+ continue
1994
+ }
1995
+
1996
+ // Get the type of the RHS (source)
1997
+ rhsType := v.pkg.TypesInfo.TypeOf(rhsExpr)
1998
+ if rhsType == nil {
1999
+ continue
2000
+ }
2001
+
2002
+ // Handle pointer types
2003
+ if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
2004
+ rhsType = ptrType.Elem()
2005
+ }
2006
+
2007
+ // Check if RHS is a named struct type
2008
+ namedType, isNamed := rhsType.(*types.Named)
2009
+ if !isNamed {
2010
+ continue
2011
+ }
2012
+
2013
+ // Track implementations for all interface methods
2014
+ for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
2015
+ interfaceMethod := interfaceType.ExplicitMethod(j)
2016
+
2017
+ structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2018
+ if structMethod != nil {
2019
+ // Determine if this struct method is async
2020
+ isAsync := false
2021
+ if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
2022
+ isAsync = funcInfo.IsAsync
2023
+ }
2024
+
2025
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2026
+ }
2027
+ }
2028
+ }
2029
+ }
2030
+
2031
+ // IsNamedBasicType returns whether the given type should be implemented as a type alias with standalone functions
2032
+ // This applies to named types with basic underlying types (like uint32, string, etc.) that have methods
2033
+ // It excludes struct types, which should remain as classes
2034
+ func (a *Analysis) IsNamedBasicType(t types.Type) bool {
2035
+ if t == nil {
2036
+ return false
2037
+ }
2038
+
2039
+ // Check if we already have this result cached
2040
+ if result, exists := a.NamedBasicTypes[t]; exists {
2041
+ return result
2042
+ }
2043
+
2044
+ var originalType types.Type = t
2045
+ var foundMethods bool
2046
+
2047
+ // Traverse the type chain to find any type with methods
2048
+ for {
2049
+ switch typed := t.(type) {
2050
+ case *types.Named:
2051
+ // Built-in types cannot be named basic types
2052
+ if typed.Obj().Pkg() == nil {
2053
+ return false
2054
+ }
2055
+
2056
+ // Check if this named type has methods
2057
+ if typed.NumMethods() > 0 {
2058
+ foundMethods = true
2059
+ }
2060
+
2061
+ // Check underlying type
2062
+ underlying := typed.Underlying()
2063
+ switch underlying.(type) {
2064
+ case *types.Struct, *types.Interface:
2065
+ return false
2066
+ }
2067
+ t = underlying
2068
+
2069
+ case *types.Alias:
2070
+ // Built-in types cannot be named basic types
2071
+ if typed.Obj().Pkg() == nil {
2072
+ return false
2073
+ }
2074
+ t = typed.Underlying()
2075
+
2076
+ default:
2077
+ // We've reached a non-named, non-alias type
2078
+ // Check if it's a supported type with methods
2079
+ switch t.(type) {
2080
+ case *types.Basic, *types.Slice, *types.Array, *types.Map:
2081
+ if foundMethods {
2082
+ a.NamedBasicTypes[originalType] = true
2083
+ return true
2084
+ }
2085
+ return false
2086
+ default:
2087
+ return false
2088
+ }
2089
+ }
2090
+ }
2091
+ }
2092
+
2093
+ // interfaceImplementationVisitor performs a second pass to analyze interface implementations
2094
+ type interfaceImplementationVisitor struct {
2095
+ analysis *Analysis
2096
+ pkg *packages.Package
2097
+ }
2098
+
2099
+ func (v *interfaceImplementationVisitor) Visit(node ast.Node) ast.Visitor {
2100
+ switch n := node.(type) {
2101
+ case *ast.GenDecl:
2102
+ // Look for interface type specifications
2103
+ for _, spec := range n.Specs {
2104
+ if typeSpec, ok := spec.(*ast.TypeSpec); ok {
2105
+ if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
2106
+ // This is an interface declaration, find all potential implementations
2107
+ v.findInterfaceImplementations(interfaceType)
2108
+ }
2109
+ }
2110
+ }
2111
+ }
2112
+ return v
2113
+ }
2114
+
2115
+ // findInterfaceImplementations finds all struct types that implement the given interface
2116
+ func (v *interfaceImplementationVisitor) findInterfaceImplementations(interfaceAST *ast.InterfaceType) {
2117
+ // Get the interface type from TypesInfo
2118
+ interfaceGoType := v.pkg.TypesInfo.TypeOf(interfaceAST)
2119
+ if interfaceGoType == nil {
2120
+ return
2121
+ }
2122
+
2123
+ interfaceType, ok := interfaceGoType.(*types.Interface)
2124
+ if !ok {
2125
+ return
2126
+ }
2127
+
2128
+ // Look through all packages for potential implementations
2129
+ for _, pkg := range v.analysis.AllPackages {
2130
+ v.findImplementationsInPackage(interfaceType, pkg)
2131
+ }
2132
+ }
2133
+
2134
+ // findImplementationsInPackage finds implementations of an interface in a specific package
2135
+ func (v *interfaceImplementationVisitor) findImplementationsInPackage(interfaceType *types.Interface, pkg *packages.Package) {
2136
+ // Get all named types in the package
2137
+ scope := pkg.Types.Scope()
2138
+ for _, name := range scope.Names() {
2139
+ obj := scope.Lookup(name)
2140
+ if obj == nil {
2141
+ continue
2142
+ }
2143
+
2144
+ // Check if this is a type name
2145
+ typeName, ok := obj.(*types.TypeName)
2146
+ if !ok {
2147
+ continue
2148
+ }
2149
+
2150
+ namedType, ok := typeName.Type().(*types.Named)
2151
+ if !ok {
2152
+ continue
2153
+ }
2154
+
2155
+ // Check if this type implements the interface
2156
+ if types.Implements(namedType, interfaceType) || types.Implements(types.NewPointer(namedType), interfaceType) {
2157
+ v.trackImplementation(interfaceType, namedType)
2158
+ }
2159
+ }
2160
+ }
2161
+
2162
+ // trackImplementation records that a named type implements an interface
2163
+ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *types.Interface, namedType *types.Named) {
2164
+ // For each method in the interface, find the corresponding implementation
2165
+ for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
2166
+ interfaceMethod := interfaceType.ExplicitMethod(i)
2167
+
2168
+ // Find the method in the implementing type
2169
+ structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
2170
+ if structMethod != nil {
2171
+ // Determine if this implementation is async
2172
+ isAsync := false
2173
+ if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
2174
+ isAsync = funcInfo.IsAsync
2175
+ }
2176
+
2177
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2178
+ }
2179
+ }
2180
+ }
2181
+
2182
+ // findMethodInType finds a method with the given name in a named type
2183
+ func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named, methodName string) *types.Func {
2184
+ for i := 0; i < namedType.NumMethods(); i++ {
2185
+ method := namedType.Method(i)
2186
+ if method.Name() == methodName {
2187
+ return method
2188
+ }
2189
+ }
2190
+ return nil
2191
+ }
2192
+
2193
+ // getNamedReturns retrieves the named returns for a function
2194
+ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
2195
+ var namedReturns []string
2196
+ if funcDecl.Type != nil && funcDecl.Type.Results != nil {
2197
+ for _, field := range funcDecl.Type.Results.List {
2198
+ for _, name := range field.Names {
2199
+ namedReturns = append(namedReturns, name.Name)
2200
+ }
2201
+ }
2202
+ }
2203
+ return namedReturns
2204
+ }