goscript 0.0.51 → 0.0.53

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 (59) hide show
  1. package/compiler/analysis.go +633 -340
  2. package/compiler/compiler.go +35 -6
  3. package/compiler/expr-call-async.go +82 -209
  4. package/compiler/expr.go +0 -44
  5. package/compiler/stmt-assign.go +0 -6
  6. package/compiler/stmt-select.go +5 -5
  7. package/compiler/type-assert.go +6 -6
  8. package/dist/gs/builtin/builtin.d.ts +0 -1
  9. package/dist/gs/builtin/builtin.js +0 -9
  10. package/dist/gs/builtin/builtin.js.map +1 -1
  11. package/dist/gs/builtin/channel.d.ts +3 -1
  12. package/dist/gs/builtin/channel.js +2 -10
  13. package/dist/gs/builtin/channel.js.map +1 -1
  14. package/dist/gs/context/context.js +2 -2
  15. package/dist/gs/context/context.js.map +1 -1
  16. package/dist/gs/reflect/index.d.ts +8 -8
  17. package/dist/gs/reflect/index.js +6 -6
  18. package/dist/gs/reflect/index.js.map +1 -1
  19. package/dist/gs/reflect/iter.d.ts +1 -1
  20. package/dist/gs/reflect/iter.js +1 -1
  21. package/dist/gs/reflect/iter.js.map +1 -1
  22. package/dist/gs/reflect/swapper.d.ts +1 -1
  23. package/dist/gs/reflect/value.d.ts +1 -2
  24. package/dist/gs/reflect/value.js +1 -3
  25. package/dist/gs/reflect/value.js.map +1 -1
  26. package/dist/gs/runtime/runtime.d.ts +1 -1
  27. package/dist/gs/runtime/runtime.js +1 -1
  28. package/dist/gs/sort/index.d.ts +4 -4
  29. package/dist/gs/sort/index.js +3 -3
  30. package/dist/gs/sort/index.js.map +1 -1
  31. package/dist/gs/strings/index.js +0 -1
  32. package/dist/gs/strings/index.js.map +1 -1
  33. package/dist/gs/time/time.d.ts +2 -2
  34. package/dist/gs/time/time.js +12 -9
  35. package/dist/gs/time/time.js.map +1 -1
  36. package/go.mod +4 -4
  37. package/go.sum +6 -6
  38. package/gs/builtin/builtin.ts +0 -11
  39. package/gs/builtin/channel.ts +23 -7
  40. package/gs/context/context.ts +6 -2
  41. package/gs/context/meta.json +16 -0
  42. package/gs/reflect/index.ts +8 -8
  43. package/gs/reflect/iter.ts +1 -11
  44. package/gs/reflect/swapper.ts +1 -1
  45. package/gs/reflect/value.ts +1 -4
  46. package/gs/runtime/runtime.ts +1 -1
  47. package/gs/sort/index.ts +4 -4
  48. package/gs/strings/index.ts +0 -1
  49. package/gs/syscall/constants.ts +1 -1
  50. package/gs/syscall/env.ts +1 -1
  51. package/gs/syscall/errors.ts +1 -1
  52. package/gs/syscall/fs.ts +1 -1
  53. package/gs/syscall/rawconn.ts +1 -1
  54. package/gs/syscall/types.ts +1 -1
  55. package/gs/time/meta.json +6 -0
  56. package/gs/time/time.ts +16 -13
  57. package/gs/unicode/meta.json +24 -0
  58. package/package.json +1 -1
  59. package/gs/unicode/unicode.go +0 -38
@@ -2,6 +2,7 @@ package compiler
2
2
 
3
3
  import (
4
4
  "encoding/json"
5
+ "fmt"
5
6
  "go/ast"
6
7
  "go/token"
7
8
  "go/types"
@@ -62,7 +63,6 @@ type FunctionTypeInfo struct {
62
63
 
63
64
  // FunctionInfo consolidates function-related tracking data.
64
65
  type FunctionInfo struct {
65
- IsAsync bool
66
66
  ReceiverUsed bool
67
67
  NamedReturns []string
68
68
  }
@@ -86,18 +86,6 @@ 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
89
  // GsMetadata represents the structure of a meta.json file in gs/ packages
102
90
  type GsMetadata struct {
103
91
  Dependencies []string `json:"dependencies,omitempty"`
@@ -110,6 +98,13 @@ type InterfaceMethodKey struct {
110
98
  MethodName string // The method name
111
99
  }
112
100
 
101
+ // MethodKey uniquely identifies a method for async analysis
102
+ type MethodKey struct {
103
+ PackagePath string // Package path
104
+ ReceiverType string // Receiver type name (empty for package-level functions)
105
+ MethodName string // Method or function name
106
+ }
107
+
113
108
  // ImplementationInfo tracks information about a struct that implements an interface method
114
109
  type ImplementationInfo struct {
115
110
  StructType *types.Named // The struct type that implements the interface
@@ -147,9 +142,6 @@ type Analysis struct {
147
142
  // FunctionAssignments tracks which function literals are assigned to which variables
148
143
  FunctionAssignments map[types.Object]ast.Node
149
144
 
150
- // PackageMetadata holds package-level metadata with structured keys
151
- PackageMetadata map[PackageMetadataKey]MethodMetadata
152
-
153
145
  // NamedBasicTypes tracks types that should be implemented as type aliases with standalone functions
154
146
  // This includes named types with basic underlying types (like uint32, string) that have methods
155
147
  NamedBasicTypes map[types.Type]bool
@@ -165,6 +157,10 @@ type Analysis struct {
165
157
  // InterfaceMethodAsyncStatus caches the async status determination for interface methods
166
158
  // This is computed once during analysis and reused during code generation
167
159
  InterfaceMethodAsyncStatus map[InterfaceMethodKey]bool
160
+
161
+ // MethodAsyncStatus stores the async status of all methods analyzed
162
+ // This is computed once during analysis and reused during code generation
163
+ MethodAsyncStatus map[MethodKey]bool
168
164
  }
169
165
 
170
166
  // PackageAnalysis holds cross-file analysis data for a package
@@ -193,18 +189,19 @@ func NewAnalysis(allPackages map[string]*packages.Package) *Analysis {
193
189
  }
194
190
 
195
191
  return &Analysis{
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),
192
+ VariableUsage: make(map[types.Object]*VariableUsageInfo),
193
+ Imports: make(map[string]*fileImport),
194
+ FunctionData: make(map[types.Object]*FunctionInfo),
195
+ NodeData: make(map[ast.Node]*NodeInfo),
196
+ FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
197
+ ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
198
+ FunctionAssignments: make(map[types.Object]ast.Node),
199
+ // PackageMetadata removed - using MethodAsyncStatus only
204
200
  NamedBasicTypes: make(map[types.Type]bool),
205
201
  AllPackages: allPackages,
206
202
  InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
207
203
  InterfaceMethodAsyncStatus: make(map[InterfaceMethodKey]bool),
204
+ MethodAsyncStatus: make(map[MethodKey]bool),
208
205
  }
209
206
  }
210
207
 
@@ -269,11 +266,41 @@ func (a *Analysis) IsAsyncFunc(obj types.Object) bool {
269
266
  if obj == nil {
270
267
  return false
271
268
  }
272
- funcInfo := a.FunctionData[obj]
273
- if funcInfo == nil {
269
+
270
+ // Use MethodAsyncStatus for all async status lookups
271
+ funcObj, ok := obj.(*types.Func)
272
+ if !ok {
274
273
  return false
275
274
  }
276
- return funcInfo.IsAsync
275
+
276
+ // Create MethodKey for lookup
277
+ methodKey := MethodKey{
278
+ PackagePath: funcObj.Pkg().Path(),
279
+ ReceiverType: "", // Functions have no receiver, methods are handled separately
280
+ MethodName: funcObj.Name(),
281
+ }
282
+
283
+ // Check if it's a method with receiver
284
+ if sig, ok := funcObj.Type().(*types.Signature); ok && sig.Recv() != nil {
285
+ // For methods, get the receiver type name using same format as analysis
286
+ recv := sig.Recv()
287
+ recvType := recv.Type()
288
+ // Handle pointer receivers
289
+ if ptr, isPtr := recvType.(*types.Pointer); isPtr {
290
+ recvType = ptr.Elem()
291
+ }
292
+ // Use short type name, not full path (consistent with analysis)
293
+ if named, ok := recvType.(*types.Named); ok {
294
+ methodKey.ReceiverType = named.Obj().Name()
295
+ } else {
296
+ methodKey.ReceiverType = recvType.String()
297
+ }
298
+ }
299
+
300
+ if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists {
301
+ return isAsync
302
+ }
303
+ return false
277
304
  }
278
305
 
279
306
  func (a *Analysis) IsReceiverUsed(obj types.Object) bool {
@@ -292,11 +319,8 @@ func (a *Analysis) IsFuncLitAsync(funcLit *ast.FuncLit) bool {
292
319
  if funcLit == nil {
293
320
  return false
294
321
  }
295
- // Check function literal specific data first
296
- if funcInfo := a.FuncLitData[funcLit]; funcInfo != nil {
297
- return funcInfo.IsAsync
298
- }
299
- // Fall back to node data for backwards compatibility
322
+ // Check function literal specific data first - but IsAsync field was removed
323
+ // Function literals don't have types.Object, so fall back to node data
300
324
  nodeInfo := a.NodeData[funcLit]
301
325
  if nodeInfo == nil {
302
326
  return false
@@ -430,6 +454,9 @@ type analysisVisitor struct {
430
454
 
431
455
  // currentFuncLit tracks the *ast.FuncLit of the function literal we're currently analyzing.
432
456
  currentFuncLit *ast.FuncLit
457
+
458
+ // visitingMethods tracks methods currently being analyzed to prevent infinite recursion
459
+ visitingMethods map[MethodKey]bool
433
460
  }
434
461
 
435
462
  // getOrCreateUsageInfo retrieves or creates the VariableUsageInfo for a given object.
@@ -583,22 +610,8 @@ func (v *analysisVisitor) visitFuncDecl(n *ast.FuncDecl) ast.Visitor {
583
610
  v.currentFuncLit = nil
584
611
  v.currentReceiver = nil
585
612
 
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
597
- }
598
- }
599
- }
600
613
  nodeInfo := v.analysis.ensureNodeData(n)
601
- nodeInfo.InAsyncContext = isAsync
614
+ // InAsyncContext will be set by the second analysis phase
602
615
 
603
616
  // Set current receiver if this is a method
604
617
  if n.Recv != nil && len(n.Recv.List) > 0 {
@@ -614,11 +627,7 @@ func (v *analysisVisitor) visitFuncDecl(n *ast.FuncDecl) ast.Visitor {
614
627
  // Check if receiver is used in method body
615
628
  receiverUsed := false
616
629
  if n.Body != nil {
617
- if v.isInterfaceMethod(n) {
618
- receiverUsed = true
619
- } else {
620
- receiverUsed = v.containsReceiverUsage(n.Body, vr)
621
- }
630
+ receiverUsed = v.containsReceiverUsage(n.Body, vr)
622
631
  }
623
632
 
624
633
  // Update function data with receiver usage info
@@ -649,7 +658,7 @@ func (v *analysisVisitor) visitFuncDecl(n *ast.FuncDecl) ast.Visitor {
649
658
  }
650
659
 
651
660
  // Update visitor state for this function
652
- v.inAsyncFunction = isAsync
661
+ // Note: inAsyncFunction will be determined later by comprehensive analysis phase
653
662
  v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
654
663
 
655
664
  if n.Body != nil {
@@ -685,10 +694,8 @@ func (v *analysisVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor {
685
694
  v.currentFuncDecl = nil
686
695
  v.currentFuncLit = n
687
696
 
688
- // Determine if this function literal is async based on its body
689
- isAsync := v.containsAsyncOperations(n.Body)
697
+ // Note: Function literal async analysis is handled by comprehensive analysis phase
690
698
  nodeInfo := v.analysis.ensureNodeData(n)
691
- nodeInfo.InAsyncContext = isAsync
692
699
 
693
700
  // Store named return variables for function literal
694
701
  if n.Type != nil && n.Type.Results != nil {
@@ -700,14 +707,12 @@ func (v *analysisVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor {
700
707
  }
701
708
  if len(namedReturns) > 0 {
702
709
  v.analysis.FuncLitData[n] = &FunctionInfo{
703
- IsAsync: isAsync,
704
710
  NamedReturns: namedReturns,
711
+ // IsAsync will be set by comprehensive analysis
705
712
  }
706
713
  }
707
714
  }
708
715
 
709
- v.inAsyncFunction = isAsync
710
-
711
716
  // Check if the body contains any defer statements
712
717
  if n.Body != nil && v.containsDefer(n.Body) {
713
718
  nodeInfo.NeedsDefer = true
@@ -742,30 +747,12 @@ func (v *analysisVisitor) visitBlockStmt(n *ast.BlockStmt) ast.Visitor {
742
747
 
743
748
  // visitCallExpr handles call expression analysis
744
749
  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
760
- }
761
- }
762
- }
763
- }
764
- }
765
-
766
750
  // Check for reflect function calls that operate on functions
767
751
  v.checkReflectUsage(n)
768
752
 
753
+ // Track interface implementations from function call arguments
754
+ v.trackInterfaceCallArguments(n)
755
+
769
756
  return v
770
757
  }
771
758
 
@@ -890,88 +877,6 @@ func (v *analysisVisitor) visitTypeAssertExpr(n *ast.TypeAssertExpr) ast.Visitor
890
877
  }
891
878
 
892
879
  // containsAsyncOperations checks if a node contains any async operations like channel operations.
893
- func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
894
- var hasAsync bool
895
-
896
- ast.Inspect(node, func(n ast.Node) bool {
897
- if n == nil {
898
- return false
899
- }
900
-
901
- switch s := n.(type) {
902
- case *ast.SendStmt:
903
- // Channel send operation (ch <- value)
904
- hasAsync = true
905
- return false
906
-
907
- case *ast.UnaryExpr:
908
- // Channel receive operation (<-ch)
909
- if s.Op == token.ARROW {
910
- hasAsync = true
911
- return false
912
- }
913
-
914
- case *ast.SelectStmt:
915
- // Select statement with channel operations
916
- hasAsync = true
917
- return false
918
-
919
- case *ast.CallExpr:
920
- // Check if we're calling a function known to be async
921
- if funcIdent, ok := s.Fun.(*ast.Ident); ok {
922
- // Get the object for this function call
923
- if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
924
- hasAsync = true
925
- return false
926
- }
927
- }
928
-
929
- // Check for method calls on imported types (e.g., sync.Mutex.Lock())
930
- if selExpr, ok := s.Fun.(*ast.SelectorExpr); ok {
931
- // Check if this is a method call on a variable (e.g., mu.Lock())
932
- if ident, ok := selExpr.X.(*ast.Ident); ok {
933
- // Get the type of the receiver
934
- if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
935
- if varObj, ok := obj.(*types.Var); 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 {
948
- typeName := namedType.Obj().Name()
949
- methodName := selExpr.Sel.Name
950
-
951
- // Check if the type is from an imported package
952
- if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
953
- // Use the full package path from the type information (not just the package name)
954
- pkgPath := typePkg.Path()
955
-
956
- // Check if this method is async based on metadata
957
- if v.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
958
- hasAsync = true
959
- return false
960
- }
961
- }
962
- // Note: Local method async detection is handled during code generation to avoid circular dependencies
963
- }
964
- }
965
- }
966
- }
967
- }
968
- }
969
-
970
- return true
971
- })
972
-
973
- return hasAsync
974
- }
975
880
 
976
881
  // containsDefer checks if a block contains any defer statements.
977
882
  func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
@@ -1027,38 +932,6 @@ func (v *analysisVisitor) containsReceiverUsage(node ast.Node, receiver *types.V
1027
932
  return hasReceiverUsage
1028
933
  }
1029
934
 
1030
- func (v *analysisVisitor) isInterfaceMethod(decl *ast.FuncDecl) bool {
1031
- if decl.Recv == nil {
1032
- return false
1033
- }
1034
-
1035
- // Get the method name
1036
- methodName := decl.Name.Name
1037
-
1038
- // Get the receiver variable
1039
- var receiver *types.Var
1040
- if len(decl.Recv.List) > 0 && len(decl.Recv.List[0].Names) > 0 {
1041
- if ident := decl.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
1042
- if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
1043
- if vr, ok := def.(*types.Var); ok {
1044
- receiver = vr
1045
- }
1046
- }
1047
- }
1048
- }
1049
-
1050
- return v.couldImplementInterfaceMethod(methodName, receiver)
1051
- }
1052
-
1053
- func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, receiver *types.Var) bool {
1054
- // Check if method is exported (interface methods must be exported)
1055
- if !ast.IsExported(methodName) {
1056
- return false
1057
- }
1058
-
1059
- return false
1060
- }
1061
-
1062
935
  // AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
1063
936
  // with information that will be used during code generation to properly handle pointers,
1064
937
  // variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
@@ -1126,8 +999,9 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
1126
999
 
1127
1000
  // Create visitor for the entire package
1128
1001
  visitor := &analysisVisitor{
1129
- analysis: analysis,
1130
- pkg: pkg,
1002
+ analysis: analysis,
1003
+ pkg: pkg,
1004
+ visitingMethods: make(map[MethodKey]bool),
1131
1005
  }
1132
1006
 
1133
1007
  // First pass: analyze all declarations and statements across all files
@@ -1151,7 +1025,7 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
1151
1025
  })
1152
1026
  }
1153
1027
 
1154
- // Second pass: analyze interface implementations now that all function async status is determined
1028
+ // Second pass: analyze interface implementations first
1155
1029
  interfaceVisitor := &interfaceImplementationVisitor{
1156
1030
  analysis: analysis,
1157
1031
  pkg: pkg,
@@ -1160,6 +1034,10 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
1160
1034
  ast.Walk(interfaceVisitor, file)
1161
1035
  }
1162
1036
 
1037
+ // Third pass: comprehensive async analysis for all methods
1038
+ // Interface implementation async status is now updated on-demand in IsInterfaceMethodAsync
1039
+ visitor.analyzeAllMethodsAsync()
1040
+
1163
1041
  return analysis
1164
1042
  }
1165
1043
 
@@ -1342,23 +1220,32 @@ func (a *Analysis) LoadPackageMetadata() {
1342
1220
  if metadata := a.loadGsMetadata(metaFilePath); metadata != nil {
1343
1221
  // Store async method information
1344
1222
  for methodKey, isAsync := range metadata.AsyncMethods {
1345
- // Convert "Type.Method" format to our internal key format
1223
+ // Convert method key to our internal key format
1346
1224
  parts := strings.Split(methodKey, ".")
1225
+ var typeName, methodName string
1226
+
1347
1227
  if len(parts) == 2 {
1348
- typeName := parts[0]
1349
- methodName := parts[1]
1350
- // The key format is "pkgName.TypeNameMethodNameInfo"
1351
- key := PackageMetadataKey{
1352
- PackagePath: pkgPath,
1353
- TypeName: typeName,
1354
- MethodName: methodName,
1355
- }
1228
+ // "Type.Method" format for methods
1229
+ typeName = parts[0]
1230
+ methodName = parts[1]
1231
+ } else if len(parts) == 1 {
1232
+ // "Function" format for package-level functions
1233
+ typeName = "" // Empty type name for package-level functions
1234
+ methodName = parts[0]
1235
+ } else {
1236
+ // Skip invalid formats
1237
+ continue
1238
+ }
1356
1239
 
1357
- // Store the async value directly in PackageMetadata
1358
- a.PackageMetadata[key] = MethodMetadata{
1359
- IsAsync: isAsync,
1360
- }
1240
+ // Use MethodKey instead of PackageMetadataKey for consistency
1241
+ key := MethodKey{
1242
+ PackagePath: pkgPath,
1243
+ ReceiverType: typeName,
1244
+ MethodName: methodName,
1361
1245
  }
1246
+
1247
+ // Store the async value directly in MethodAsyncStatus
1248
+ a.MethodAsyncStatus[key] = isAsync
1362
1249
  }
1363
1250
  }
1364
1251
  }
@@ -1378,14 +1265,7 @@ func (a *Analysis) discoverEmbeddedGsPackages() []string {
1378
1265
  // Iterate through all entries in gs/
1379
1266
  for _, entry := range entries {
1380
1267
  if entry.IsDir() {
1381
- packageName := entry.Name()
1382
-
1383
- // Skip special directories like github.com
1384
- if strings.Contains(packageName, ".") {
1385
- continue
1386
- }
1387
-
1388
- packageList = append(packageList, packageName)
1268
+ packageList = append(packageList, entry.Name())
1389
1269
  }
1390
1270
  }
1391
1271
 
@@ -1410,121 +1290,22 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
1410
1290
 
1411
1291
  // IsMethodAsync checks if a method call is async based on package metadata
1412
1292
  func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
1413
- // First, check existing metadata for internal packages
1414
- // The metadata keys are stored as "pkgName.TypeNameMethodNameInfo"
1415
- // e.g., "sync.MutexLockInfo", "sync.WaitGroupWaitInfo", etc.
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
1293
+ // First, check pre-computed method async status
1294
+ methodKey := MethodKey{
1295
+ PackagePath: pkgPath,
1296
+ ReceiverType: typeName,
1297
+ MethodName: methodName,
1424
1298
  }
1425
1299
 
1426
- // If no metadata found, check if we can analyze the method from dependency packages
1427
- return a.analyzeExternalMethodAsync(pkgPath, typeName, methodName)
1428
- }
1429
-
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
- }
1465
- }
1300
+ if status, exists := a.MethodAsyncStatus[methodKey]; exists {
1301
+ return status
1466
1302
  }
1467
1303
 
1304
+ // If no pre-computed status found, external methods default to sync
1305
+ // Comprehensive analysis should have already analyzed all packages and loaded metadata
1468
1306
  return false
1469
1307
  }
1470
1308
 
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
-
1528
1309
  // NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
1529
1310
  func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
1530
1311
  if node == nil {
@@ -1758,6 +1539,26 @@ func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, method
1758
1539
  return false
1759
1540
  }
1760
1541
 
1542
+ // Update implementations with current async status before checking
1543
+ for i := range implementations {
1544
+ impl := &implementations[i]
1545
+
1546
+ // Create method key for this implementation
1547
+ methodKey := MethodKey{
1548
+ PackagePath: impl.StructType.Obj().Pkg().Path(),
1549
+ ReceiverType: impl.StructType.Obj().Name(),
1550
+ MethodName: impl.Method.Name(),
1551
+ }
1552
+
1553
+ // Update with current async status from method analysis
1554
+ if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists {
1555
+ impl.IsAsyncByFlow = isAsync
1556
+ }
1557
+ }
1558
+
1559
+ // Store the updated implementations back to the map
1560
+ a.InterfaceImplementations[key] = implementations
1561
+
1761
1562
  // If ANY implementation is async, the interface method is async
1762
1563
  for _, impl := range implementations {
1763
1564
  if impl.IsAsyncByFlow {
@@ -1890,12 +1691,10 @@ func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
1890
1691
  // Find the corresponding method in the struct type
1891
1692
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
1892
1693
  if structMethod != nil {
1893
- // Determine if this struct method is async based on control flow analysis
1694
+ // Determine if this struct method is async using unified system
1894
1695
  isAsync := false
1895
1696
  if obj := structMethod; obj != nil {
1896
- if funcInfo := v.analysis.FunctionData[obj]; funcInfo != nil {
1897
- isAsync = funcInfo.IsAsync
1898
- }
1697
+ isAsync = v.analysis.IsAsyncFunc(obj)
1899
1698
  }
1900
1699
 
1901
1700
  // Track this interface implementation
@@ -2016,11 +1815,8 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
2016
1815
 
2017
1816
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2018
1817
  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
- }
1818
+ // Determine if this struct method is async using unified system
1819
+ isAsync := v.analysis.IsAsyncFunc(structMethod)
2024
1820
 
2025
1821
  v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2026
1822
  }
@@ -2028,6 +1824,67 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
2028
1824
  }
2029
1825
  }
2030
1826
 
1827
+ // trackInterfaceCallArguments analyzes function call arguments to detect interface implementations
1828
+ func (v *analysisVisitor) trackInterfaceCallArguments(callExpr *ast.CallExpr) {
1829
+ // Get the function signature to determine parameter types
1830
+ var funcType *types.Signature
1831
+
1832
+ if callFunType := v.pkg.TypesInfo.TypeOf(callExpr.Fun); callFunType != nil {
1833
+ if sig, ok := callFunType.(*types.Signature); ok {
1834
+ funcType = sig
1835
+ }
1836
+ }
1837
+
1838
+ if funcType == nil {
1839
+ return
1840
+ }
1841
+
1842
+ // Check each argument against its corresponding parameter
1843
+ params := funcType.Params()
1844
+ for i, arg := range callExpr.Args {
1845
+ if i >= params.Len() {
1846
+ break // More arguments than parameters (variadic case)
1847
+ }
1848
+
1849
+ paramType := params.At(i).Type()
1850
+
1851
+ // Check if the parameter is an interface type
1852
+ interfaceType, isInterface := paramType.Underlying().(*types.Interface)
1853
+ if !isInterface {
1854
+ continue
1855
+ }
1856
+
1857
+ // Get the type of the argument
1858
+ argType := v.pkg.TypesInfo.TypeOf(arg)
1859
+ if argType == nil {
1860
+ continue
1861
+ }
1862
+
1863
+ // Handle pointer types
1864
+ if ptrType, isPtr := argType.(*types.Pointer); isPtr {
1865
+ argType = ptrType.Elem()
1866
+ }
1867
+
1868
+ // Check if argument is a named struct type
1869
+ namedType, isNamed := argType.(*types.Named)
1870
+ if !isNamed {
1871
+ continue
1872
+ }
1873
+
1874
+ // Track implementations for all interface methods
1875
+ for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
1876
+ interfaceMethod := interfaceType.ExplicitMethod(j)
1877
+
1878
+ structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
1879
+ if structMethod != nil {
1880
+ // Note: Don't determine async status here - it will be determined later after method analysis
1881
+ // For now, just track the implementation relationship without async status
1882
+ v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, false)
1883
+ }
1884
+ }
1885
+ }
1886
+ }
1887
+
2031
1888
  // IsNamedBasicType returns whether the given type should be implemented as a type alias with standalone functions
2032
1889
  // This applies to named types with basic underlying types (like uint32, string, etc.) that have methods
2033
1890
  // It excludes struct types, which should remain as classes
@@ -2168,11 +2025,8 @@ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *type
2168
2025
  // Find the method in the implementing type
2169
2026
  structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
2170
2027
  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
- }
2028
+ // Determine if this implementation is async using unified system
2029
+ isAsync := v.analysis.IsAsyncFunc(structMethod)
2176
2030
 
2177
2031
  v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2178
2032
  }
@@ -2202,3 +2056,442 @@ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
2202
2056
  }
2203
2057
  return namedReturns
2204
2058
  }
2059
+
2060
+ // analyzeAllMethodsAsync performs comprehensive async analysis on all methods in all packages
2061
+ func (v *analysisVisitor) analyzeAllMethodsAsync() {
2062
+ // Initialize visitingMethods map
2063
+ v.visitingMethods = make(map[MethodKey]bool)
2064
+
2065
+ // Analyze methods in current package
2066
+ v.analyzePackageMethodsAsync(v.pkg)
2067
+
2068
+ // Analyze methods in all dependency packages
2069
+ for _, pkg := range v.analysis.AllPackages {
2070
+ if pkg != v.pkg {
2071
+ v.analyzePackageMethodsAsync(pkg)
2072
+ }
2073
+ }
2074
+
2075
+ // Finally, analyze function literals in the current package only
2076
+ // (external packages' function literals are not accessible)
2077
+ v.analyzeFunctionLiteralsAsync(v.pkg)
2078
+ }
2079
+
2080
+ // analyzePackageMethodsAsync analyzes all methods in a specific package
2081
+ func (v *analysisVisitor) analyzePackageMethodsAsync(pkg *packages.Package) {
2082
+ // Analyze function declarations
2083
+ for _, file := range pkg.Syntax {
2084
+ for _, decl := range file.Decls {
2085
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
2086
+ v.analyzeMethodAsync(funcDecl, pkg)
2087
+ }
2088
+ }
2089
+ }
2090
+ }
2091
+
2092
+ // analyzeFunctionLiteralsAsync analyzes all function literals in a package for async operations
2093
+ func (v *analysisVisitor) analyzeFunctionLiteralsAsync(pkg *packages.Package) {
2094
+ for _, file := range pkg.Syntax {
2095
+ ast.Inspect(file, func(n ast.Node) bool {
2096
+ if funcLit, ok := n.(*ast.FuncLit); ok {
2097
+ v.analyzeFunctionLiteralAsync(funcLit, pkg)
2098
+ }
2099
+ return true
2100
+ })
2101
+ }
2102
+ }
2103
+
2104
+ // analyzeFunctionLiteralAsync determines if a function literal is async and stores the result
2105
+ func (v *analysisVisitor) analyzeFunctionLiteralAsync(funcLit *ast.FuncLit, pkg *packages.Package) {
2106
+ // Check if already analyzed
2107
+ nodeInfo := v.analysis.NodeData[funcLit]
2108
+ if nodeInfo != nil && nodeInfo.InAsyncContext {
2109
+ // Already marked as async, skip
2110
+ return
2111
+ }
2112
+
2113
+ // Analyze function literal body for async operations
2114
+ isAsync := false
2115
+ if funcLit.Body != nil {
2116
+ isAsync = v.containsAsyncOperationsComplete(funcLit.Body, pkg)
2117
+ }
2118
+
2119
+ // Store result in NodeData
2120
+ if nodeInfo == nil {
2121
+ nodeInfo = v.analysis.ensureNodeData(funcLit)
2122
+ }
2123
+ nodeInfo.InAsyncContext = isAsync
2124
+ }
2125
+
2126
+ // analyzeMethodAsync determines if a method is async and stores the result
2127
+ func (v *analysisVisitor) analyzeMethodAsync(funcDecl *ast.FuncDecl, pkg *packages.Package) {
2128
+ methodKey := v.getMethodKey(funcDecl, pkg)
2129
+
2130
+ // Check if already analyzed
2131
+ if _, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2132
+ return
2133
+ }
2134
+
2135
+ // Check for cycles
2136
+ if v.visitingMethods[methodKey] {
2137
+ // Cycle detected, assume sync to break recursion
2138
+ v.analysis.MethodAsyncStatus[methodKey] = false
2139
+ return
2140
+ }
2141
+
2142
+ // Mark as visiting
2143
+ v.visitingMethods[methodKey] = true
2144
+
2145
+ // Determine if method is async
2146
+ isAsync := false
2147
+
2148
+ // Determine if this is a truly external package vs a package being compiled locally
2149
+ isExternalPackage := pkg.Types != v.pkg.Types && v.analysis.AllPackages[pkg.Types.Path()] == nil
2150
+
2151
+ if isExternalPackage {
2152
+ // Truly external package: check metadata first, fall back to body analysis
2153
+ isAsync = v.checkExternalMethodMetadata(methodKey.PackagePath, methodKey.ReceiverType, methodKey.MethodName)
2154
+ } else {
2155
+ // Local package or package being compiled: analyze method body
2156
+ if funcDecl.Body != nil {
2157
+ isAsync = v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
2158
+ }
2159
+ }
2160
+
2161
+ // Store result in MethodAsyncStatus
2162
+ v.analysis.MethodAsyncStatus[methodKey] = isAsync
2163
+
2164
+ // Unmark as visiting
2165
+ delete(v.visitingMethods, methodKey)
2166
+ }
2167
+
2168
+ // getMethodKey creates a unique key for a method
2169
+ func (v *analysisVisitor) getMethodKey(funcDecl *ast.FuncDecl, pkg *packages.Package) MethodKey {
2170
+ packagePath := pkg.Types.Path()
2171
+ methodName := funcDecl.Name.Name
2172
+ receiverType := ""
2173
+
2174
+ if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
2175
+ // Get receiver type name
2176
+ if len(funcDecl.Recv.List[0].Names) > 0 {
2177
+ if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
2178
+ if vr, ok := def.(*types.Var); ok {
2179
+ receiverType = v.getTypeName(vr.Type())
2180
+ }
2181
+ }
2182
+ }
2183
+ }
2184
+
2185
+ return MethodKey{
2186
+ PackagePath: packagePath,
2187
+ ReceiverType: receiverType,
2188
+ MethodName: methodName,
2189
+ }
2190
+ }
2191
+
2192
+ // getTypeName extracts a clean type name from a types.Type
2193
+ func (v *analysisVisitor) getTypeName(t types.Type) string {
2194
+ switch typ := t.(type) {
2195
+ case *types.Named:
2196
+ return typ.Obj().Name()
2197
+ case *types.Pointer:
2198
+ return v.getTypeName(typ.Elem())
2199
+ default:
2200
+ return typ.String()
2201
+ }
2202
+ }
2203
+
2204
+ // containsAsyncOperationsComplete is a comprehensive async detection that handles method calls
2205
+ func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *packages.Package) bool {
2206
+ var hasAsync bool
2207
+ var asyncReasons []string
2208
+
2209
+ ast.Inspect(node, func(n ast.Node) bool {
2210
+ if n == nil {
2211
+ return false
2212
+ }
2213
+
2214
+ switch s := n.(type) {
2215
+ case *ast.SendStmt:
2216
+ // Channel send operation (ch <- value)
2217
+ hasAsync = true
2218
+ asyncReasons = append(asyncReasons, "channel send")
2219
+ return false
2220
+
2221
+ case *ast.UnaryExpr:
2222
+ // Channel receive operation (<-ch)
2223
+ if s.Op == token.ARROW {
2224
+ hasAsync = true
2225
+ asyncReasons = append(asyncReasons, "channel receive")
2226
+ return false
2227
+ }
2228
+
2229
+ case *ast.SelectStmt:
2230
+ // Select statement with channel operations
2231
+ hasAsync = true
2232
+ asyncReasons = append(asyncReasons, "select statement")
2233
+ return false
2234
+
2235
+ case *ast.CallExpr:
2236
+ // Check if we're calling a function known to be async
2237
+ isCallAsyncResult := v.isCallAsync(s, pkg)
2238
+ if isCallAsyncResult {
2239
+ hasAsync = true
2240
+ callName := ""
2241
+ if ident, ok := s.Fun.(*ast.Ident); ok {
2242
+ callName = ident.Name
2243
+ } else if sel, ok := s.Fun.(*ast.SelectorExpr); ok {
2244
+ callName = sel.Sel.Name
2245
+ }
2246
+ asyncReasons = append(asyncReasons, fmt.Sprintf("async call: %s", callName))
2247
+ return false
2248
+ }
2249
+ }
2250
+
2251
+ return true
2252
+ })
2253
+
2254
+ return hasAsync
2255
+ }
2256
+
2257
+ // isCallAsync determines if a call expression is async
2258
+ func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Package) bool {
2259
+ switch fun := callExpr.Fun.(type) {
2260
+ case *ast.Ident:
2261
+ // Direct function call
2262
+ if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
2263
+ if funcObj, ok := obj.(*types.Func); ok {
2264
+ return v.isFunctionAsync(funcObj, pkg)
2265
+ }
2266
+ }
2267
+
2268
+ case *ast.SelectorExpr:
2269
+ // Handle package-level function calls (e.g., time.Sleep)
2270
+ if ident, ok := fun.X.(*ast.Ident); ok {
2271
+ if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
2272
+ if pkgName, isPkg := obj.(*types.PkgName); isPkg {
2273
+ methodName := fun.Sel.Name
2274
+ pkgPath := pkgName.Imported().Path()
2275
+ // Check if this package-level function is async (empty TypeName)
2276
+ isAsync := v.analysis.IsMethodAsync(pkgPath, "", methodName)
2277
+ return isAsync
2278
+ }
2279
+ }
2280
+ }
2281
+
2282
+ // Check if this is an interface method call
2283
+ if receiverType := pkg.TypesInfo.TypeOf(fun.X); receiverType != nil {
2284
+ if interfaceType, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
2285
+ methodName := fun.Sel.Name
2286
+ // For interface method calls, check if the interface method is async
2287
+ return v.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
2288
+ }
2289
+ }
2290
+
2291
+ // Method call on concrete objects
2292
+ if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
2293
+ if methodObj := selection.Obj(); methodObj != nil {
2294
+ return v.isMethodAsyncFromSelection(fun, methodObj, pkg)
2295
+ }
2296
+ }
2297
+ }
2298
+
2299
+ return false
2300
+ }
2301
+
2302
+ // isFunctionAsync checks if a function object is async
2303
+ func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Package) bool {
2304
+ // Check if it's from external package metadata
2305
+ if funcObj.Pkg() != nil && funcObj.Pkg() != pkg.Types {
2306
+ return v.analysis.IsMethodAsync(funcObj.Pkg().Path(), "", funcObj.Name())
2307
+ }
2308
+
2309
+ // Check internal method status
2310
+ methodKey := MethodKey{
2311
+ PackagePath: pkg.Types.Path(),
2312
+ ReceiverType: "",
2313
+ MethodName: funcObj.Name(),
2314
+ }
2315
+
2316
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2317
+ return status
2318
+ }
2319
+
2320
+ // Not analyzed yet, analyze now
2321
+ if funcDecl := v.findFunctionDecl(funcObj.Name(), pkg); funcDecl != nil {
2322
+ v.analyzeMethodAsync(funcDecl, pkg)
2323
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2324
+ return status
2325
+ }
2326
+ }
2327
+
2328
+ return false
2329
+ }
2330
+
2331
+ // isMethodAsyncFromSelection checks if a method call is async based on selection
2332
+ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr, methodObj types.Object, pkg *packages.Package) bool {
2333
+ // Get receiver type - handle both direct identifiers and field access
2334
+ var receiverType string
2335
+ var methodPkgPath string
2336
+
2337
+ // Handle different receiver patterns
2338
+ switch x := selExpr.X.(type) {
2339
+ case *ast.Ident:
2340
+ // Direct variable (e.g., mtx.Lock())
2341
+ if obj := pkg.TypesInfo.Uses[x]; obj != nil {
2342
+ if varObj, ok := obj.(*types.Var); ok {
2343
+ receiverType = v.getTypeName(varObj.Type())
2344
+ }
2345
+ }
2346
+ case *ast.SelectorExpr:
2347
+ // Field access (e.g., l.m.Lock())
2348
+ if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
2349
+ receiverType = v.getTypeName(typeExpr)
2350
+ }
2351
+ }
2352
+
2353
+ // Get the method's package path
2354
+ if methodFunc, ok := methodObj.(*types.Func); ok {
2355
+ if methodFunc.Pkg() != nil {
2356
+ methodPkgPath = methodFunc.Pkg().Path()
2357
+ }
2358
+ }
2359
+
2360
+ // If no package path found, use current package
2361
+ if methodPkgPath == "" {
2362
+ methodPkgPath = pkg.Types.Path()
2363
+ }
2364
+
2365
+ // For external packages, check unified MethodAsyncStatus first
2366
+ // For internal packages, try analysis first, then fallback to lookup
2367
+ methodKey := MethodKey{
2368
+ PackagePath: methodPkgPath,
2369
+ ReceiverType: receiverType,
2370
+ MethodName: methodObj.Name(),
2371
+ }
2372
+
2373
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2374
+ return status
2375
+ }
2376
+
2377
+ // Only try to analyze methods for packages that don't have metadata loaded
2378
+ // If a package has metadata, we should rely solely on that metadata
2379
+ if targetPkg := v.analysis.AllPackages[methodPkgPath]; targetPkg != nil {
2380
+ // Check if this package has metadata loaded by checking if any method from this package
2381
+ // exists in MethodAsyncStatus. If so, don't analyze - rely on metadata only.
2382
+ hasMetadata := false
2383
+ for key := range v.analysis.MethodAsyncStatus {
2384
+ if key.PackagePath == methodPkgPath {
2385
+ hasMetadata = true
2386
+ break
2387
+ }
2388
+ }
2389
+
2390
+ // Only analyze if no metadata exists for this package
2391
+ if !hasMetadata {
2392
+ if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), targetPkg); funcDecl != nil {
2393
+ v.analyzeMethodAsync(funcDecl, targetPkg)
2394
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2395
+ return status
2396
+ }
2397
+ }
2398
+ }
2399
+ }
2400
+
2401
+ return false
2402
+ }
2403
+
2404
+ // findFunctionDecl finds a function declaration by name in a package
2405
+ func (v *analysisVisitor) findFunctionDecl(funcName string, pkg *packages.Package) *ast.FuncDecl {
2406
+ for _, file := range pkg.Syntax {
2407
+ for _, decl := range file.Decls {
2408
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
2409
+ if funcDecl.Name.Name == funcName && funcDecl.Recv == nil {
2410
+ return funcDecl
2411
+ }
2412
+ }
2413
+ }
2414
+ }
2415
+ return nil
2416
+ }
2417
+
2418
+ // findMethodDecl finds a method declaration by receiver type and method name
2419
+ func (v *analysisVisitor) findMethodDecl(receiverType, methodName string, pkg *packages.Package) *ast.FuncDecl {
2420
+ for _, file := range pkg.Syntax {
2421
+ for _, decl := range file.Decls {
2422
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
2423
+ if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
2424
+ if len(funcDecl.Recv.List) > 0 && len(funcDecl.Recv.List[0].Names) > 0 {
2425
+ if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
2426
+ if vr, ok := def.(*types.Var); ok {
2427
+ if v.getTypeName(vr.Type()) == receiverType {
2428
+ return funcDecl
2429
+ }
2430
+ }
2431
+ }
2432
+ }
2433
+ }
2434
+ }
2435
+ }
2436
+ }
2437
+ return nil
2438
+ }
2439
+
2440
+ // checkExternalMethodMetadata checks if an external method is async based on pre-loaded metadata
2441
+ func (v *analysisVisitor) checkExternalMethodMetadata(pkgPath, receiverType, methodName string) bool {
2442
+ // Use MethodKey to check pre-loaded metadata in MethodAsyncStatus
2443
+ key := MethodKey{
2444
+ PackagePath: pkgPath,
2445
+ ReceiverType: receiverType,
2446
+ MethodName: methodName,
2447
+ }
2448
+
2449
+ if isAsync, exists := v.analysis.MethodAsyncStatus[key]; exists {
2450
+ return isAsync
2451
+ }
2452
+
2453
+ return false
2454
+ }
2455
+
2456
+ // IsLocalMethodAsync checks if a local method is async using pre-computed analysis
2457
+ func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string) bool {
2458
+ methodKey := MethodKey{
2459
+ PackagePath: pkgPath,
2460
+ ReceiverType: receiverType,
2461
+ MethodName: methodName,
2462
+ }
2463
+
2464
+ if status, exists := a.MethodAsyncStatus[methodKey]; exists {
2465
+ return status
2466
+ }
2467
+
2468
+ return false
2469
+ }
2470
+
2471
+ // updateInterfaceImplementationAsyncStatus updates interface implementations with correct async status
2472
+ // This runs after method async analysis is complete
2473
+ func (v *analysisVisitor) updateInterfaceImplementationAsyncStatus() {
2474
+ // Iterate through all tracked interface implementations and update their async status
2475
+ for key, implementations := range v.analysis.InterfaceImplementations {
2476
+ // Remove duplicates first
2477
+ seenMethods := make(map[string]bool)
2478
+ uniqueImplementations := []ImplementationInfo{}
2479
+
2480
+ for _, impl := range implementations {
2481
+ methodKey := impl.StructType.Obj().Name() + "." + key.MethodName
2482
+ if !seenMethods[methodKey] {
2483
+ seenMethods[methodKey] = true
2484
+
2485
+ // Now that method async analysis is complete, get the correct async status
2486
+ isAsync := v.analysis.IsAsyncFunc(impl.Method)
2487
+
2488
+ // Update the implementation with the correct async status
2489
+ impl.IsAsyncByFlow = isAsync
2490
+ uniqueImplementations = append(uniqueImplementations, impl)
2491
+ }
2492
+ }
2493
+
2494
+ // Store the updated implementations without duplicates
2495
+ v.analysis.InterfaceImplementations[key] = uniqueImplementations
2496
+ }
2497
+ }