goscript 0.0.51 → 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.
@@ -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,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,10 @@ 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: comprehensive async analysis for all methods
1029
+ visitor.analyzeAllMethodsAsync()
1030
+
1031
+ // Third pass: analyze interface implementations now that all function async status is determined
1155
1032
  interfaceVisitor := &interfaceImplementationVisitor{
1156
1033
  analysis: analysis,
1157
1034
  pkg: pkg,
@@ -1342,23 +1219,32 @@ func (a *Analysis) LoadPackageMetadata() {
1342
1219
  if metadata := a.loadGsMetadata(metaFilePath); metadata != nil {
1343
1220
  // Store async method information
1344
1221
  for methodKey, isAsync := range metadata.AsyncMethods {
1345
- // Convert "Type.Method" format to our internal key format
1222
+ // Convert method key to our internal key format
1346
1223
  parts := strings.Split(methodKey, ".")
1224
+ var typeName, methodName string
1225
+
1347
1226
  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
- }
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
+ }
1356
1238
 
1357
- // Store the async value directly in PackageMetadata
1358
- a.PackageMetadata[key] = MethodMetadata{
1359
- IsAsync: isAsync,
1360
- }
1239
+ // Use MethodKey instead of PackageMetadataKey for consistency
1240
+ key := MethodKey{
1241
+ PackagePath: pkgPath,
1242
+ ReceiverType: typeName,
1243
+ MethodName: methodName,
1361
1244
  }
1245
+
1246
+ // Store the async value directly in MethodAsyncStatus
1247
+ a.MethodAsyncStatus[key] = isAsync
1362
1248
  }
1363
1249
  }
1364
1250
  }
@@ -1378,14 +1264,7 @@ func (a *Analysis) discoverEmbeddedGsPackages() []string {
1378
1264
  // Iterate through all entries in gs/
1379
1265
  for _, entry := range entries {
1380
1266
  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)
1267
+ packageList = append(packageList, entry.Name())
1389
1268
  }
1390
1269
  }
1391
1270
 
@@ -1410,121 +1289,22 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
1410
1289
 
1411
1290
  // IsMethodAsync checks if a method call is async based on package metadata
1412
1291
  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
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
- }
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
1292
+ // First, check pre-computed method async status
1293
+ methodKey := MethodKey{
1294
+ PackagePath: pkgPath,
1295
+ ReceiverType: typeName,
1296
+ MethodName: methodName,
1437
1297
  }
1438
1298
 
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
- }
1299
+ if status, exists := a.MethodAsyncStatus[methodKey]; exists {
1300
+ return status
1466
1301
  }
1467
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
1468
1305
  return false
1469
1306
  }
1470
1307
 
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
1308
  // NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
1529
1309
  func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
1530
1310
  if node == nil {
@@ -1890,12 +1670,10 @@ func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
1890
1670
  // Find the corresponding method in the struct type
1891
1671
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
1892
1672
  if structMethod != nil {
1893
- // Determine if this struct method is async based on control flow analysis
1673
+ // Determine if this struct method is async using unified system
1894
1674
  isAsync := false
1895
1675
  if obj := structMethod; obj != nil {
1896
- if funcInfo := v.analysis.FunctionData[obj]; funcInfo != nil {
1897
- isAsync = funcInfo.IsAsync
1898
- }
1676
+ isAsync = v.analysis.IsAsyncFunc(obj)
1899
1677
  }
1900
1678
 
1901
1679
  // Track this interface implementation
@@ -2016,11 +1794,8 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
2016
1794
 
2017
1795
  structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
2018
1796
  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
- }
1797
+ // Determine if this struct method is async using unified system
1798
+ isAsync := v.analysis.IsAsyncFunc(structMethod)
2024
1799
 
2025
1800
  v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2026
1801
  }
@@ -2168,11 +1943,8 @@ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *type
2168
1943
  // Find the method in the implementing type
2169
1944
  structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
2170
1945
  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
- }
1946
+ // Determine if this implementation is async using unified system
1947
+ isAsync := v.analysis.IsAsyncFunc(structMethod)
2176
1948
 
2177
1949
  v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
2178
1950
  }
@@ -2202,3 +1974,405 @@ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
2202
1974
  }
2203
1975
  return namedReturns
2204
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
+ }