goscript 0.0.50 → 0.0.52

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 (63) hide show
  1. package/compiler/analysis.go +513 -325
  2. package/compiler/compiler.go +39 -6
  3. package/compiler/expr-call-async.go +90 -23
  4. package/compiler/expr.go +0 -44
  5. package/compiler/sanitize.go +1 -2
  6. package/compiler/spec-struct.go +3 -3
  7. package/compiler/spec.go +0 -21
  8. package/compiler/stmt-assign.go +0 -6
  9. package/compiler/stmt-select.go +52 -1
  10. package/compiler/type-assert.go +6 -6
  11. package/compiler/type.go +3 -3
  12. package/dist/gs/builtin/builtin.d.ts +0 -1
  13. package/dist/gs/builtin/builtin.js +0 -9
  14. package/dist/gs/builtin/builtin.js.map +1 -1
  15. package/dist/gs/builtin/channel.d.ts +5 -3
  16. package/dist/gs/builtin/channel.js +14 -17
  17. package/dist/gs/builtin/channel.js.map +1 -1
  18. package/dist/gs/context/context.js +2 -2
  19. package/dist/gs/context/context.js.map +1 -1
  20. package/dist/gs/runtime/runtime.d.ts +1 -1
  21. package/dist/gs/runtime/runtime.js +1 -1
  22. package/dist/gs/syscall/constants.d.ts +24 -0
  23. package/dist/gs/syscall/constants.js +27 -0
  24. package/dist/gs/syscall/constants.js.map +1 -0
  25. package/dist/gs/syscall/env.d.ts +6 -0
  26. package/dist/gs/syscall/env.js +43 -0
  27. package/dist/gs/syscall/env.js.map +1 -0
  28. package/dist/gs/syscall/errors.d.ts +111 -0
  29. package/dist/gs/syscall/errors.js +547 -0
  30. package/dist/gs/syscall/errors.js.map +1 -0
  31. package/dist/gs/syscall/fs.d.ts +29 -0
  32. package/dist/gs/syscall/fs.js +53 -0
  33. package/dist/gs/syscall/fs.js.map +1 -0
  34. package/dist/gs/syscall/index.d.ts +6 -80
  35. package/dist/gs/syscall/index.js +12 -168
  36. package/dist/gs/syscall/index.js.map +1 -1
  37. package/dist/gs/syscall/rawconn.d.ts +7 -0
  38. package/dist/gs/syscall/rawconn.js +19 -0
  39. package/dist/gs/syscall/rawconn.js.map +1 -0
  40. package/dist/gs/syscall/types.d.ts +12 -0
  41. package/dist/gs/syscall/types.js +2 -0
  42. package/dist/gs/syscall/types.js.map +1 -0
  43. package/dist/gs/time/time.d.ts +2 -2
  44. package/dist/gs/time/time.js +12 -9
  45. package/dist/gs/time/time.js.map +1 -1
  46. package/go.mod +1 -1
  47. package/gs/builtin/builtin.ts +0 -11
  48. package/gs/builtin/channel.ts +39 -17
  49. package/gs/context/context.ts +6 -2
  50. package/gs/context/meta.json +16 -0
  51. package/gs/runtime/runtime.ts +1 -1
  52. package/gs/syscall/constants.ts +29 -0
  53. package/gs/syscall/env.ts +47 -0
  54. package/gs/syscall/errors.ts +658 -0
  55. package/gs/syscall/fs.ts +62 -0
  56. package/gs/syscall/index.ts +12 -207
  57. package/gs/syscall/rawconn.ts +23 -0
  58. package/gs/syscall/types.ts +18 -0
  59. package/gs/time/meta.json +6 -0
  60. package/gs/time/time.ts +16 -13
  61. package/gs/unicode/meta.json +24 -0
  62. package/package.json +1 -1
  63. 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,26 +747,8 @@ 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
- }
750
+ // Note: Async function call detection is now handled by the second analysis phase
751
+ // This ensures consistent handling of all async patterns including external packages
765
752
 
766
753
  // Check for reflect function calls that operate on functions
767
754
  v.checkReflectUsage(n)
@@ -890,74 +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.CallExpr:
915
- // Check if we're calling a function known to be async
916
- if funcIdent, ok := s.Fun.(*ast.Ident); ok {
917
- // Get the object for this function call
918
- if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
919
- hasAsync = true
920
- return false
921
- }
922
- }
923
-
924
- // Check for method calls on imported types (e.g., sync.Mutex.Lock())
925
- if selExpr, ok := s.Fun.(*ast.SelectorExpr); ok {
926
- // Check if this is a method call on a variable (e.g., mu.Lock())
927
- if ident, ok := selExpr.X.(*ast.Ident); ok {
928
- // Get the type of the receiver
929
- if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
930
- if varObj, ok := obj.(*types.Var); ok {
931
- // Get the type name and package
932
- if namedType, ok := varObj.Type().(*types.Named); ok {
933
- typeName := namedType.Obj().Name()
934
- methodName := selExpr.Sel.Name
935
-
936
- // Check if the type is from an imported package
937
- if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
938
- // Use the full package path from the type information (not just the package name)
939
- pkgPath := typePkg.Path()
940
-
941
- // Check if this method is async based on metadata
942
- if v.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
943
- hasAsync = true
944
- return false
945
- }
946
- }
947
- }
948
- }
949
- }
950
- }
951
- }
952
-
953
- // TODO: Add detection of method calls on async types
954
- }
955
-
956
- return true
957
- })
958
-
959
- return hasAsync
960
- }
961
880
 
962
881
  // containsDefer checks if a block contains any defer statements.
963
882
  func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
@@ -1013,38 +932,6 @@ func (v *analysisVisitor) containsReceiverUsage(node ast.Node, receiver *types.V
1013
932
  return hasReceiverUsage
1014
933
  }
1015
934
 
1016
- func (v *analysisVisitor) isInterfaceMethod(decl *ast.FuncDecl) bool {
1017
- if decl.Recv == nil {
1018
- return false
1019
- }
1020
-
1021
- // Get the method name
1022
- methodName := decl.Name.Name
1023
-
1024
- // Get the receiver variable
1025
- var receiver *types.Var
1026
- if len(decl.Recv.List) > 0 && len(decl.Recv.List[0].Names) > 0 {
1027
- if ident := decl.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
1028
- if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
1029
- if vr, ok := def.(*types.Var); ok {
1030
- receiver = vr
1031
- }
1032
- }
1033
- }
1034
- }
1035
-
1036
- return v.couldImplementInterfaceMethod(methodName, receiver)
1037
- }
1038
-
1039
- func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, receiver *types.Var) bool {
1040
- // Check if method is exported (interface methods must be exported)
1041
- if !ast.IsExported(methodName) {
1042
- return false
1043
- }
1044
-
1045
- return false
1046
- }
1047
-
1048
935
  // AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
1049
936
  // with information that will be used during code generation to properly handle pointers,
1050
937
  // variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
@@ -1112,8 +999,9 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
1112
999
 
1113
1000
  // Create visitor for the entire package
1114
1001
  visitor := &analysisVisitor{
1115
- analysis: analysis,
1116
- pkg: pkg,
1002
+ analysis: analysis,
1003
+ pkg: pkg,
1004
+ visitingMethods: make(map[MethodKey]bool),
1117
1005
  }
1118
1006
 
1119
1007
  // First pass: analyze all declarations and statements across all files
@@ -1137,7 +1025,10 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
1137
1025
  })
1138
1026
  }
1139
1027
 
1140
- // Second pass: analyze interface implementations now that all function async status is determined
1028
+ // Second pass: comprehensive async analysis for all methods
1029
+ visitor.analyzeAllMethodsAsync()
1030
+
1031
+ // Third pass: analyze interface implementations now that all function async status is determined
1141
1032
  interfaceVisitor := &interfaceImplementationVisitor{
1142
1033
  analysis: analysis,
1143
1034
  pkg: pkg,
@@ -1328,23 +1219,32 @@ func (a *Analysis) LoadPackageMetadata() {
1328
1219
  if metadata := a.loadGsMetadata(metaFilePath); metadata != nil {
1329
1220
  // Store async method information
1330
1221
  for methodKey, isAsync := range metadata.AsyncMethods {
1331
- // Convert "Type.Method" format to our internal key format
1222
+ // Convert method key to our internal key format
1332
1223
  parts := strings.Split(methodKey, ".")
1224
+ var typeName, methodName string
1225
+
1333
1226
  if len(parts) == 2 {
1334
- typeName := parts[0]
1335
- methodName := parts[1]
1336
- // The key format is "pkgName.TypeNameMethodNameInfo"
1337
- key := PackageMetadataKey{
1338
- PackagePath: pkgPath,
1339
- TypeName: typeName,
1340
- MethodName: methodName,
1341
- }
1227
+ // "Type.Method" format for methods
1228
+ typeName = parts[0]
1229
+ methodName = parts[1]
1230
+ } else if len(parts) == 1 {
1231
+ // "Function" format for package-level functions
1232
+ typeName = "" // Empty type name for package-level functions
1233
+ methodName = parts[0]
1234
+ } else {
1235
+ // Skip invalid formats
1236
+ continue
1237
+ }
1342
1238
 
1343
- // Store the async value directly in PackageMetadata
1344
- a.PackageMetadata[key] = MethodMetadata{
1345
- IsAsync: isAsync,
1346
- }
1239
+ // Use MethodKey instead of PackageMetadataKey for consistency
1240
+ key := MethodKey{
1241
+ PackagePath: pkgPath,
1242
+ ReceiverType: typeName,
1243
+ MethodName: methodName,
1347
1244
  }
1245
+
1246
+ // Store the async value directly in MethodAsyncStatus
1247
+ a.MethodAsyncStatus[key] = isAsync
1348
1248
  }
1349
1249
  }
1350
1250
  }
@@ -1364,14 +1264,7 @@ func (a *Analysis) discoverEmbeddedGsPackages() []string {
1364
1264
  // Iterate through all entries in gs/
1365
1265
  for _, entry := range entries {
1366
1266
  if entry.IsDir() {
1367
- packageName := entry.Name()
1368
-
1369
- // Skip special directories like github.com
1370
- if strings.Contains(packageName, ".") {
1371
- continue
1372
- }
1373
-
1374
- packageList = append(packageList, packageName)
1267
+ packageList = append(packageList, entry.Name())
1375
1268
  }
1376
1269
  }
1377
1270
 
@@ -1396,121 +1289,22 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
1396
1289
 
1397
1290
  // IsMethodAsync checks if a method call is async based on package metadata
1398
1291
  func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
1399
- // First, check existing metadata for internal packages
1400
- // The metadata keys are stored as "pkgName.TypeNameMethodNameInfo"
1401
- // e.g., "sync.MutexLockInfo", "sync.WaitGroupWaitInfo", etc.
1402
- key := PackageMetadataKey{
1403
- PackagePath: pkgPath,
1404
- TypeName: typeName,
1405
- MethodName: methodName,
1292
+ // First, check pre-computed method async status
1293
+ methodKey := MethodKey{
1294
+ PackagePath: pkgPath,
1295
+ ReceiverType: typeName,
1296
+ MethodName: methodName,
1406
1297
  }
1407
1298
 
1408
- if metadata, exists := a.PackageMetadata[key]; exists {
1409
- return metadata.IsAsync
1410
- }
1411
-
1412
- // If no metadata found, check if we can analyze the method from dependency packages
1413
- return a.analyzeExternalMethodAsync(pkgPath, typeName, methodName)
1414
- }
1415
-
1416
- // analyzeExternalMethodAsync analyzes external package methods to determine if they're async
1417
- // using the same logic we use for internal methods
1418
- func (a *Analysis) analyzeExternalMethodAsync(pkgPath, typeName, methodName string) bool {
1419
- // Look up the package in our loaded packages
1420
- pkg, exists := a.AllPackages[pkgPath]
1421
- if !exists {
1422
- return false
1423
- }
1424
-
1425
- // Find the method definition in the package
1426
- for _, syntax := range pkg.Syntax {
1427
- for _, decl := range syntax.Decls {
1428
- if funcDecl, ok := decl.(*ast.FuncDecl); ok {
1429
- // Check if this is a method with the right name and receiver type
1430
- if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
1431
- // Check the receiver type
1432
- if len(funcDecl.Recv.List) > 0 {
1433
- receiverType := funcDecl.Recv.List[0].Type
1434
-
1435
- // Handle pointer receivers
1436
- if starExpr, ok := receiverType.(*ast.StarExpr); ok {
1437
- receiverType = starExpr.X
1438
- }
1439
-
1440
- if ident, ok := receiverType.(*ast.Ident); ok {
1441
- if ident.Name == typeName {
1442
- // Found the method! Now check if it's async
1443
- if funcDecl.Body != nil {
1444
- return a.containsAsyncOperations(funcDecl.Body, pkg)
1445
- }
1446
- }
1447
- }
1448
- }
1449
- }
1450
- }
1451
- }
1299
+ if status, exists := a.MethodAsyncStatus[methodKey]; exists {
1300
+ return status
1452
1301
  }
1453
1302
 
1303
+ // If no pre-computed status found, external methods default to sync
1304
+ // Comprehensive analysis should have already analyzed all packages and loaded metadata
1454
1305
  return false
1455
1306
  }
1456
1307
 
1457
- // containsAsyncOperations checks if a node contains any async operations for a specific package
1458
- func (a *Analysis) containsAsyncOperations(node ast.Node, pkg *packages.Package) bool {
1459
- var hasAsync bool
1460
-
1461
- ast.Inspect(node, func(n ast.Node) bool {
1462
- if n == nil {
1463
- return false
1464
- }
1465
-
1466
- switch s := n.(type) {
1467
- case *ast.SendStmt:
1468
- // Channel send operation (ch <- value)
1469
- hasAsync = true
1470
- return false
1471
-
1472
- case *ast.UnaryExpr:
1473
- // Channel receive operation (<-ch)
1474
- if s.Op == token.ARROW {
1475
- hasAsync = true
1476
- return false
1477
- }
1478
-
1479
- case *ast.CallExpr:
1480
- // Check for select statements and other async operations
1481
- if ident, ok := s.Fun.(*ast.Ident); ok {
1482
- // Look for calls to select-related functions or other async operations
1483
- if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
1484
- // Check if this function is from the context package (like context.Background, etc.)
1485
- if obj.Pkg() != nil && obj.Pkg().Path() == "context" {
1486
- hasAsync = true
1487
- return false
1488
- }
1489
- }
1490
- }
1491
-
1492
- // Check if the method takes a context parameter, which usually indicates async behavior
1493
- if len(s.Args) > 0 {
1494
- for _, arg := range s.Args {
1495
- if argType := pkg.TypesInfo.TypeOf(arg); argType != nil {
1496
- if named, ok := argType.(*types.Named); ok {
1497
- if named.Obj() != nil && named.Obj().Pkg() != nil &&
1498
- named.Obj().Pkg().Path() == "context" && named.Obj().Name() == "Context" {
1499
- hasAsync = true
1500
- return false
1501
- }
1502
- }
1503
- }
1504
- }
1505
- }
1506
- }
1507
-
1508
- return true
1509
- })
1510
-
1511
- return hasAsync
1512
- }
1513
-
1514
1308
  // NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
1515
1309
  func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
1516
1310
  if node == nil {
@@ -1876,12 +1670,10 @@ func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
1876
1670
  // Find the corresponding method in the struct type
1877
1671
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
1878
1672
  if structMethod != nil {
1879
- // Determine if this struct method is async based on control flow analysis
1673
+ // Determine if this struct method is async using unified system
1880
1674
  isAsync := false
1881
1675
  if obj := structMethod; obj != nil {
1882
- if funcInfo := v.analysis.FunctionData[obj]; funcInfo != nil {
1883
- isAsync = funcInfo.IsAsync
1884
- }
1676
+ isAsync = v.analysis.IsAsyncFunc(obj)
1885
1677
  }
1886
1678
 
1887
1679
  // Track this interface implementation
@@ -2002,11 +1794,8 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
2002
1794
 
2003
1795
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2004
1796
  if structMethod != nil {
2005
- // Determine if this struct method is async
2006
- isAsync := false
2007
- if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
2008
- isAsync = funcInfo.IsAsync
2009
- }
1797
+ // Determine if this struct method is async using unified system
1798
+ isAsync := v.analysis.IsAsyncFunc(structMethod)
2010
1799
 
2011
1800
  v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2012
1801
  }
@@ -2154,11 +1943,8 @@ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *type
2154
1943
  // Find the method in the implementing type
2155
1944
  structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
2156
1945
  if structMethod != nil {
2157
- // Determine if this implementation is async
2158
- isAsync := false
2159
- if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
2160
- isAsync = funcInfo.IsAsync
2161
- }
1946
+ // Determine if this implementation is async using unified system
1947
+ isAsync := v.analysis.IsAsyncFunc(structMethod)
2162
1948
 
2163
1949
  v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2164
1950
  }
@@ -2188,3 +1974,405 @@ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
2188
1974
  }
2189
1975
  return namedReturns
2190
1976
  }
1977
+
1978
+ // analyzeAllMethodsAsync performs comprehensive async analysis on all methods in all packages
1979
+ func (v *analysisVisitor) analyzeAllMethodsAsync() {
1980
+ // Initialize visitingMethods map
1981
+ v.visitingMethods = make(map[MethodKey]bool)
1982
+
1983
+ // Analyze methods in current package
1984
+ v.analyzePackageMethodsAsync(v.pkg)
1985
+
1986
+ // Analyze methods in all dependency packages
1987
+ for _, pkg := range v.analysis.AllPackages {
1988
+ if pkg != v.pkg {
1989
+ v.analyzePackageMethodsAsync(pkg)
1990
+ }
1991
+ }
1992
+
1993
+ // Finally, analyze function literals in the current package only
1994
+ // (external packages' function literals are not accessible)
1995
+ v.analyzeFunctionLiteralsAsync(v.pkg)
1996
+ }
1997
+
1998
+ // analyzePackageMethodsAsync analyzes all methods in a specific package
1999
+ func (v *analysisVisitor) analyzePackageMethodsAsync(pkg *packages.Package) {
2000
+ // Analyze function declarations
2001
+ for _, file := range pkg.Syntax {
2002
+ for _, decl := range file.Decls {
2003
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
2004
+ v.analyzeMethodAsync(funcDecl, pkg)
2005
+ }
2006
+ }
2007
+ }
2008
+ }
2009
+
2010
+ // analyzeFunctionLiteralsAsync analyzes all function literals in a package for async operations
2011
+ func (v *analysisVisitor) analyzeFunctionLiteralsAsync(pkg *packages.Package) {
2012
+ for _, file := range pkg.Syntax {
2013
+ ast.Inspect(file, func(n ast.Node) bool {
2014
+ if funcLit, ok := n.(*ast.FuncLit); ok {
2015
+ v.analyzeFunctionLiteralAsync(funcLit, pkg)
2016
+ }
2017
+ return true
2018
+ })
2019
+ }
2020
+ }
2021
+
2022
+ // analyzeFunctionLiteralAsync determines if a function literal is async and stores the result
2023
+ func (v *analysisVisitor) analyzeFunctionLiteralAsync(funcLit *ast.FuncLit, pkg *packages.Package) {
2024
+ // Check if already analyzed
2025
+ nodeInfo := v.analysis.NodeData[funcLit]
2026
+ if nodeInfo != nil && nodeInfo.InAsyncContext {
2027
+ // Already marked as async, skip
2028
+ return
2029
+ }
2030
+
2031
+ // Analyze function literal body for async operations
2032
+ isAsync := false
2033
+ if funcLit.Body != nil {
2034
+ isAsync = v.containsAsyncOperationsComplete(funcLit.Body, pkg)
2035
+ }
2036
+
2037
+ // Store result in NodeData
2038
+ if nodeInfo == nil {
2039
+ nodeInfo = v.analysis.ensureNodeData(funcLit)
2040
+ }
2041
+ nodeInfo.InAsyncContext = isAsync
2042
+ }
2043
+
2044
+ // analyzeMethodAsync determines if a method is async and stores the result
2045
+ func (v *analysisVisitor) analyzeMethodAsync(funcDecl *ast.FuncDecl, pkg *packages.Package) {
2046
+ methodKey := v.getMethodKey(funcDecl, pkg)
2047
+
2048
+ // Check if already analyzed
2049
+ if _, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2050
+ return
2051
+ }
2052
+
2053
+ // Check for cycles
2054
+ if v.visitingMethods[methodKey] {
2055
+ // Cycle detected, assume sync to break recursion
2056
+ v.analysis.MethodAsyncStatus[methodKey] = false
2057
+ return
2058
+ }
2059
+
2060
+ // Mark as visiting
2061
+ v.visitingMethods[methodKey] = true
2062
+
2063
+ // Determine if method is async
2064
+ isAsync := false
2065
+
2066
+ // Determine if this is a truly external package vs a package being compiled locally
2067
+ isExternalPackage := pkg.Types != v.pkg.Types && v.analysis.AllPackages[pkg.Types.Path()] == nil
2068
+
2069
+ if isExternalPackage {
2070
+ // Truly external package: check metadata first, fall back to body analysis
2071
+ isAsync = v.checkExternalMethodMetadata(methodKey.PackagePath, methodKey.ReceiverType, methodKey.MethodName)
2072
+ } else {
2073
+ // Local package or package being compiled: analyze method body
2074
+ if funcDecl.Body != nil {
2075
+ isAsync = v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
2076
+ }
2077
+ }
2078
+
2079
+ // Store result in MethodAsyncStatus
2080
+ v.analysis.MethodAsyncStatus[methodKey] = isAsync
2081
+
2082
+ // Unmark as visiting
2083
+ delete(v.visitingMethods, methodKey)
2084
+ }
2085
+
2086
+ // getMethodKey creates a unique key for a method
2087
+ func (v *analysisVisitor) getMethodKey(funcDecl *ast.FuncDecl, pkg *packages.Package) MethodKey {
2088
+ packagePath := pkg.Types.Path()
2089
+ methodName := funcDecl.Name.Name
2090
+ receiverType := ""
2091
+
2092
+ if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
2093
+ // Get receiver type name
2094
+ if len(funcDecl.Recv.List[0].Names) > 0 {
2095
+ if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
2096
+ if vr, ok := def.(*types.Var); ok {
2097
+ receiverType = v.getTypeName(vr.Type())
2098
+ }
2099
+ }
2100
+ }
2101
+ }
2102
+
2103
+ return MethodKey{
2104
+ PackagePath: packagePath,
2105
+ ReceiverType: receiverType,
2106
+ MethodName: methodName,
2107
+ }
2108
+ }
2109
+
2110
+ // getTypeName extracts a clean type name from a types.Type
2111
+ func (v *analysisVisitor) getTypeName(t types.Type) string {
2112
+ switch typ := t.(type) {
2113
+ case *types.Named:
2114
+ return typ.Obj().Name()
2115
+ case *types.Pointer:
2116
+ return v.getTypeName(typ.Elem())
2117
+ default:
2118
+ return typ.String()
2119
+ }
2120
+ }
2121
+
2122
+ // containsAsyncOperationsComplete is a comprehensive async detection that handles method calls
2123
+ func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *packages.Package) bool {
2124
+ var hasAsync bool
2125
+ var asyncReasons []string
2126
+
2127
+ ast.Inspect(node, func(n ast.Node) bool {
2128
+ if n == nil {
2129
+ return false
2130
+ }
2131
+
2132
+ switch s := n.(type) {
2133
+ case *ast.SendStmt:
2134
+ // Channel send operation (ch <- value)
2135
+ hasAsync = true
2136
+ asyncReasons = append(asyncReasons, "channel send")
2137
+ return false
2138
+
2139
+ case *ast.UnaryExpr:
2140
+ // Channel receive operation (<-ch)
2141
+ if s.Op == token.ARROW {
2142
+ hasAsync = true
2143
+ asyncReasons = append(asyncReasons, "channel receive")
2144
+ return false
2145
+ }
2146
+
2147
+ case *ast.SelectStmt:
2148
+ // Select statement with channel operations
2149
+ hasAsync = true
2150
+ asyncReasons = append(asyncReasons, "select statement")
2151
+ return false
2152
+
2153
+ case *ast.CallExpr:
2154
+ // Check if we're calling a function known to be async
2155
+ isCallAsyncResult := v.isCallAsync(s, pkg)
2156
+ if isCallAsyncResult {
2157
+ hasAsync = true
2158
+ callName := ""
2159
+ if ident, ok := s.Fun.(*ast.Ident); ok {
2160
+ callName = ident.Name
2161
+ } else if sel, ok := s.Fun.(*ast.SelectorExpr); ok {
2162
+ callName = sel.Sel.Name
2163
+ }
2164
+ asyncReasons = append(asyncReasons, fmt.Sprintf("async call: %s", callName))
2165
+ return false
2166
+ }
2167
+ }
2168
+
2169
+ return true
2170
+ })
2171
+
2172
+ return hasAsync
2173
+ }
2174
+
2175
+ // isCallAsync determines if a call expression is async
2176
+ func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Package) bool {
2177
+ switch fun := callExpr.Fun.(type) {
2178
+ case *ast.Ident:
2179
+ // Direct function call
2180
+ if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
2181
+ if funcObj, ok := obj.(*types.Func); ok {
2182
+ return v.isFunctionAsync(funcObj, pkg)
2183
+ }
2184
+ }
2185
+
2186
+ case *ast.SelectorExpr:
2187
+ // Handle package-level function calls (e.g., time.Sleep)
2188
+ if ident, ok := fun.X.(*ast.Ident); ok {
2189
+ if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
2190
+ if pkgName, isPkg := obj.(*types.PkgName); isPkg {
2191
+ methodName := fun.Sel.Name
2192
+ pkgPath := pkgName.Imported().Path()
2193
+ // Check if this package-level function is async (empty TypeName)
2194
+ isAsync := v.analysis.IsMethodAsync(pkgPath, "", methodName)
2195
+ return isAsync
2196
+ }
2197
+ }
2198
+ }
2199
+
2200
+ // Method call on objects
2201
+ if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
2202
+ if methodObj := selection.Obj(); methodObj != nil {
2203
+ return v.isMethodAsyncFromSelection(fun, methodObj, pkg)
2204
+ }
2205
+ }
2206
+ }
2207
+
2208
+ return false
2209
+ }
2210
+
2211
+ // isFunctionAsync checks if a function object is async
2212
+ func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Package) bool {
2213
+ // Check if it's from external package metadata
2214
+ if funcObj.Pkg() != nil && funcObj.Pkg() != pkg.Types {
2215
+ return v.analysis.IsMethodAsync(funcObj.Pkg().Path(), "", funcObj.Name())
2216
+ }
2217
+
2218
+ // Check internal method status
2219
+ methodKey := MethodKey{
2220
+ PackagePath: pkg.Types.Path(),
2221
+ ReceiverType: "",
2222
+ MethodName: funcObj.Name(),
2223
+ }
2224
+
2225
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2226
+ return status
2227
+ }
2228
+
2229
+ // Not analyzed yet, analyze now
2230
+ if funcDecl := v.findFunctionDecl(funcObj.Name(), pkg); funcDecl != nil {
2231
+ v.analyzeMethodAsync(funcDecl, pkg)
2232
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2233
+ return status
2234
+ }
2235
+ }
2236
+
2237
+ return false
2238
+ }
2239
+
2240
+ // isMethodAsyncFromSelection checks if a method call is async based on selection
2241
+ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr, methodObj types.Object, pkg *packages.Package) bool {
2242
+ // Get receiver type - handle both direct identifiers and field access
2243
+ var receiverType string
2244
+ var methodPkgPath string
2245
+
2246
+ // Handle different receiver patterns
2247
+ switch x := selExpr.X.(type) {
2248
+ case *ast.Ident:
2249
+ // Direct variable (e.g., mtx.Lock())
2250
+ if obj := pkg.TypesInfo.Uses[x]; obj != nil {
2251
+ if varObj, ok := obj.(*types.Var); ok {
2252
+ receiverType = v.getTypeName(varObj.Type())
2253
+ }
2254
+ }
2255
+ case *ast.SelectorExpr:
2256
+ // Field access (e.g., l.m.Lock())
2257
+ if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
2258
+ receiverType = v.getTypeName(typeExpr)
2259
+ }
2260
+ }
2261
+
2262
+ // Get the method's package path
2263
+ if methodFunc, ok := methodObj.(*types.Func); ok {
2264
+ if methodFunc.Pkg() != nil {
2265
+ methodPkgPath = methodFunc.Pkg().Path()
2266
+ }
2267
+ }
2268
+
2269
+ // If no package path found, use current package
2270
+ if methodPkgPath == "" {
2271
+ methodPkgPath = pkg.Types.Path()
2272
+ }
2273
+
2274
+ // For external packages, check unified MethodAsyncStatus first
2275
+ // For internal packages, try analysis first, then fallback to lookup
2276
+ methodKey := MethodKey{
2277
+ PackagePath: methodPkgPath,
2278
+ ReceiverType: receiverType,
2279
+ MethodName: methodObj.Name(),
2280
+ }
2281
+
2282
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2283
+ return status
2284
+ }
2285
+
2286
+ // Only try to analyze methods for packages that don't have metadata loaded
2287
+ // If a package has metadata, we should rely solely on that metadata
2288
+ if targetPkg := v.analysis.AllPackages[methodPkgPath]; targetPkg != nil {
2289
+ // Check if this package has metadata loaded by checking if any method from this package
2290
+ // exists in MethodAsyncStatus. If so, don't analyze - rely on metadata only.
2291
+ hasMetadata := false
2292
+ for key := range v.analysis.MethodAsyncStatus {
2293
+ if key.PackagePath == methodPkgPath {
2294
+ hasMetadata = true
2295
+ break
2296
+ }
2297
+ }
2298
+
2299
+ // Only analyze if no metadata exists for this package
2300
+ if !hasMetadata {
2301
+ if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), targetPkg); funcDecl != nil {
2302
+ v.analyzeMethodAsync(funcDecl, targetPkg)
2303
+ if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
2304
+ return status
2305
+ }
2306
+ }
2307
+ }
2308
+ }
2309
+
2310
+ return false
2311
+ }
2312
+
2313
+ // findFunctionDecl finds a function declaration by name in a package
2314
+ func (v *analysisVisitor) findFunctionDecl(funcName string, pkg *packages.Package) *ast.FuncDecl {
2315
+ for _, file := range pkg.Syntax {
2316
+ for _, decl := range file.Decls {
2317
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
2318
+ if funcDecl.Name.Name == funcName && funcDecl.Recv == nil {
2319
+ return funcDecl
2320
+ }
2321
+ }
2322
+ }
2323
+ }
2324
+ return nil
2325
+ }
2326
+
2327
+ // findMethodDecl finds a method declaration by receiver type and method name
2328
+ func (v *analysisVisitor) findMethodDecl(receiverType, methodName string, pkg *packages.Package) *ast.FuncDecl {
2329
+ for _, file := range pkg.Syntax {
2330
+ for _, decl := range file.Decls {
2331
+ if funcDecl, ok := decl.(*ast.FuncDecl); ok {
2332
+ if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
2333
+ if len(funcDecl.Recv.List) > 0 && len(funcDecl.Recv.List[0].Names) > 0 {
2334
+ if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
2335
+ if vr, ok := def.(*types.Var); ok {
2336
+ if v.getTypeName(vr.Type()) == receiverType {
2337
+ return funcDecl
2338
+ }
2339
+ }
2340
+ }
2341
+ }
2342
+ }
2343
+ }
2344
+ }
2345
+ }
2346
+ return nil
2347
+ }
2348
+
2349
+ // checkExternalMethodMetadata checks if an external method is async based on pre-loaded metadata
2350
+ func (v *analysisVisitor) checkExternalMethodMetadata(pkgPath, receiverType, methodName string) bool {
2351
+ // Use MethodKey to check pre-loaded metadata in MethodAsyncStatus
2352
+ key := MethodKey{
2353
+ PackagePath: pkgPath,
2354
+ ReceiverType: receiverType,
2355
+ MethodName: methodName,
2356
+ }
2357
+
2358
+ if isAsync, exists := v.analysis.MethodAsyncStatus[key]; exists {
2359
+ return isAsync
2360
+ }
2361
+
2362
+ return false
2363
+ }
2364
+
2365
+ // IsLocalMethodAsync checks if a local method is async using pre-computed analysis
2366
+ func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string) bool {
2367
+ methodKey := MethodKey{
2368
+ PackagePath: pkgPath,
2369
+ ReceiverType: receiverType,
2370
+ MethodName: methodName,
2371
+ }
2372
+
2373
+ if status, exists := a.MethodAsyncStatus[methodKey]; exists {
2374
+ return status
2375
+ }
2376
+
2377
+ return false
2378
+ }