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.
- package/compiler/analysis.go +513 -339
- package/compiler/compiler.go +35 -6
- package/compiler/expr-call-async.go +71 -209
- package/compiler/expr.go +0 -44
- package/compiler/stmt-assign.go +0 -6
- package/compiler/stmt-select.go +5 -5
- package/compiler/type-assert.go +6 -6
- package/dist/gs/builtin/builtin.d.ts +0 -1
- package/dist/gs/builtin/builtin.js +0 -9
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.d.ts +3 -1
- package/dist/gs/builtin/channel.js +2 -10
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/context/context.js +2 -2
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -1
- package/dist/gs/runtime/runtime.js +1 -1
- package/dist/gs/time/time.d.ts +2 -2
- package/dist/gs/time/time.js +12 -9
- package/dist/gs/time/time.js.map +1 -1
- package/go.mod +1 -1
- package/gs/builtin/builtin.ts +0 -11
- package/gs/builtin/channel.ts +23 -7
- package/gs/context/context.ts +6 -2
- package/gs/context/meta.json +16 -0
- package/gs/runtime/runtime.ts +1 -1
- package/gs/syscall/constants.ts +1 -1
- package/gs/syscall/env.ts +1 -1
- package/gs/syscall/errors.ts +1 -1
- package/gs/syscall/fs.ts +1 -1
- package/gs/syscall/rawconn.ts +1 -1
- package/gs/syscall/types.ts +1 -1
- package/gs/time/meta.json +6 -0
- package/gs/time/time.ts +16 -13
- package/gs/unicode/meta.json +24 -0
- package/package.json +1 -1
- package/gs/unicode/unicode.go +0 -38
package/compiler/analysis.go
CHANGED
|
@@ -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:
|
|
197
|
-
Imports:
|
|
198
|
-
FunctionData:
|
|
199
|
-
NodeData:
|
|
200
|
-
FuncLitData:
|
|
201
|
-
ReflectedFunctions:
|
|
202
|
-
FunctionAssignments:
|
|
203
|
-
PackageMetadata
|
|
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
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
746
|
-
|
|
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:
|
|
1130
|
-
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:
|
|
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
|
|
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
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
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
|
-
|
|
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
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
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
|
-
|
|
1440
|
-
|
|
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
|
|
1673
|
+
// Determine if this struct method is async using unified system
|
|
1894
1674
|
isAsync := false
|
|
1895
1675
|
if obj := structMethod; obj != nil {
|
|
1896
|
-
|
|
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 :=
|
|
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 :=
|
|
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
|
+
}
|