goscript 0.0.51 → 0.0.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/analysis.go +633 -340
- package/compiler/compiler.go +35 -6
- package/compiler/expr-call-async.go +82 -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/reflect/index.d.ts +8 -8
- package/dist/gs/reflect/index.js +6 -6
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/iter.d.ts +1 -1
- package/dist/gs/reflect/iter.js +1 -1
- package/dist/gs/reflect/iter.js.map +1 -1
- package/dist/gs/reflect/swapper.d.ts +1 -1
- package/dist/gs/reflect/value.d.ts +1 -2
- package/dist/gs/reflect/value.js +1 -3
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -1
- package/dist/gs/runtime/runtime.js +1 -1
- package/dist/gs/sort/index.d.ts +4 -4
- package/dist/gs/sort/index.js +3 -3
- package/dist/gs/sort/index.js.map +1 -1
- package/dist/gs/strings/index.js +0 -1
- package/dist/gs/strings/index.js.map +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 +4 -4
- package/go.sum +6 -6
- 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/reflect/index.ts +8 -8
- package/gs/reflect/iter.ts +1 -11
- package/gs/reflect/swapper.ts +1 -1
- package/gs/reflect/value.ts +1 -4
- package/gs/runtime/runtime.ts +1 -1
- package/gs/sort/index.ts +4 -4
- package/gs/strings/index.ts +0 -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,30 +747,12 @@ func (v *analysisVisitor) visitBlockStmt(n *ast.BlockStmt) ast.Visitor {
|
|
|
742
747
|
|
|
743
748
|
// visitCallExpr handles call expression analysis
|
|
744
749
|
func (v *analysisVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor {
|
|
745
|
-
// Check if this is a function call that might be async
|
|
746
|
-
if funcIdent, ok := n.Fun.(*ast.Ident); ok {
|
|
747
|
-
// Get the object for this function call
|
|
748
|
-
if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
|
|
749
|
-
// We're calling an async function, so mark current function as async if we're in one
|
|
750
|
-
if v.currentFuncObj != nil {
|
|
751
|
-
funcInfo := v.analysis.ensureFunctionData(v.currentFuncObj)
|
|
752
|
-
funcInfo.IsAsync = true
|
|
753
|
-
funcInfo.NamedReturns = v.getNamedReturns(v.currentFuncDecl)
|
|
754
|
-
v.inAsyncFunction = true // Update visitor state
|
|
755
|
-
// Mark the FuncDecl node itself if possible
|
|
756
|
-
for nodeAst := range v.analysis.NodeData {
|
|
757
|
-
if fd, ok := nodeAst.(*ast.FuncDecl); ok && v.pkg.TypesInfo.ObjectOf(fd.Name) == v.currentFuncObj {
|
|
758
|
-
nodeInfo := v.analysis.ensureNodeData(nodeAst)
|
|
759
|
-
nodeInfo.InAsyncContext = true
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
750
|
// Check for reflect function calls that operate on functions
|
|
767
751
|
v.checkReflectUsage(n)
|
|
768
752
|
|
|
753
|
+
// Track interface implementations from function call arguments
|
|
754
|
+
v.trackInterfaceCallArguments(n)
|
|
755
|
+
|
|
769
756
|
return v
|
|
770
757
|
}
|
|
771
758
|
|
|
@@ -890,88 +877,6 @@ func (v *analysisVisitor) visitTypeAssertExpr(n *ast.TypeAssertExpr) ast.Visitor
|
|
|
890
877
|
}
|
|
891
878
|
|
|
892
879
|
// containsAsyncOperations checks if a node contains any async operations like channel operations.
|
|
893
|
-
func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
|
|
894
|
-
var hasAsync bool
|
|
895
|
-
|
|
896
|
-
ast.Inspect(node, func(n ast.Node) bool {
|
|
897
|
-
if n == nil {
|
|
898
|
-
return false
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
switch s := n.(type) {
|
|
902
|
-
case *ast.SendStmt:
|
|
903
|
-
// Channel send operation (ch <- value)
|
|
904
|
-
hasAsync = true
|
|
905
|
-
return false
|
|
906
|
-
|
|
907
|
-
case *ast.UnaryExpr:
|
|
908
|
-
// Channel receive operation (<-ch)
|
|
909
|
-
if s.Op == token.ARROW {
|
|
910
|
-
hasAsync = true
|
|
911
|
-
return false
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
case *ast.SelectStmt:
|
|
915
|
-
// Select statement with channel operations
|
|
916
|
-
hasAsync = true
|
|
917
|
-
return false
|
|
918
|
-
|
|
919
|
-
case *ast.CallExpr:
|
|
920
|
-
// Check if we're calling a function known to be async
|
|
921
|
-
if funcIdent, ok := s.Fun.(*ast.Ident); ok {
|
|
922
|
-
// Get the object for this function call
|
|
923
|
-
if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
|
|
924
|
-
hasAsync = true
|
|
925
|
-
return false
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
// Check for method calls on imported types (e.g., sync.Mutex.Lock())
|
|
930
|
-
if selExpr, ok := s.Fun.(*ast.SelectorExpr); ok {
|
|
931
|
-
// Check if this is a method call on a variable (e.g., mu.Lock())
|
|
932
|
-
if ident, ok := selExpr.X.(*ast.Ident); ok {
|
|
933
|
-
// Get the type of the receiver
|
|
934
|
-
if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
935
|
-
if varObj, ok := obj.(*types.Var); ok {
|
|
936
|
-
// Handle both direct named types and pointer to named types
|
|
937
|
-
var namedType *types.Named
|
|
938
|
-
switch t := varObj.Type().(type) {
|
|
939
|
-
case *types.Named:
|
|
940
|
-
namedType = t
|
|
941
|
-
case *types.Pointer:
|
|
942
|
-
if nt, isNamed := t.Elem().(*types.Named); isNamed {
|
|
943
|
-
namedType = nt
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
if namedType != nil {
|
|
948
|
-
typeName := namedType.Obj().Name()
|
|
949
|
-
methodName := selExpr.Sel.Name
|
|
950
|
-
|
|
951
|
-
// Check if the type is from an imported package
|
|
952
|
-
if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
|
|
953
|
-
// Use the full package path from the type information (not just the package name)
|
|
954
|
-
pkgPath := typePkg.Path()
|
|
955
|
-
|
|
956
|
-
// Check if this method is async based on metadata
|
|
957
|
-
if v.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
|
|
958
|
-
hasAsync = true
|
|
959
|
-
return false
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
// Note: Local method async detection is handled during code generation to avoid circular dependencies
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
return true
|
|
971
|
-
})
|
|
972
|
-
|
|
973
|
-
return hasAsync
|
|
974
|
-
}
|
|
975
880
|
|
|
976
881
|
// containsDefer checks if a block contains any defer statements.
|
|
977
882
|
func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
|
|
@@ -1027,38 +932,6 @@ func (v *analysisVisitor) containsReceiverUsage(node ast.Node, receiver *types.V
|
|
|
1027
932
|
return hasReceiverUsage
|
|
1028
933
|
}
|
|
1029
934
|
|
|
1030
|
-
func (v *analysisVisitor) isInterfaceMethod(decl *ast.FuncDecl) bool {
|
|
1031
|
-
if decl.Recv == nil {
|
|
1032
|
-
return false
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
// Get the method name
|
|
1036
|
-
methodName := decl.Name.Name
|
|
1037
|
-
|
|
1038
|
-
// Get the receiver variable
|
|
1039
|
-
var receiver *types.Var
|
|
1040
|
-
if len(decl.Recv.List) > 0 && len(decl.Recv.List[0].Names) > 0 {
|
|
1041
|
-
if ident := decl.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
|
|
1042
|
-
if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
|
|
1043
|
-
if vr, ok := def.(*types.Var); ok {
|
|
1044
|
-
receiver = vr
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
return v.couldImplementInterfaceMethod(methodName, receiver)
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, receiver *types.Var) bool {
|
|
1054
|
-
// Check if method is exported (interface methods must be exported)
|
|
1055
|
-
if !ast.IsExported(methodName) {
|
|
1056
|
-
return false
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
return false
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
935
|
// AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
|
|
1063
936
|
// with information that will be used during code generation to properly handle pointers,
|
|
1064
937
|
// variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
|
|
@@ -1126,8 +999,9 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
|
|
|
1126
999
|
|
|
1127
1000
|
// Create visitor for the entire package
|
|
1128
1001
|
visitor := &analysisVisitor{
|
|
1129
|
-
analysis:
|
|
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,7 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
|
|
|
1151
1025
|
})
|
|
1152
1026
|
}
|
|
1153
1027
|
|
|
1154
|
-
// Second pass: analyze interface implementations
|
|
1028
|
+
// Second pass: analyze interface implementations first
|
|
1155
1029
|
interfaceVisitor := &interfaceImplementationVisitor{
|
|
1156
1030
|
analysis: analysis,
|
|
1157
1031
|
pkg: pkg,
|
|
@@ -1160,6 +1034,10 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
|
|
|
1160
1034
|
ast.Walk(interfaceVisitor, file)
|
|
1161
1035
|
}
|
|
1162
1036
|
|
|
1037
|
+
// Third pass: comprehensive async analysis for all methods
|
|
1038
|
+
// Interface implementation async status is now updated on-demand in IsInterfaceMethodAsync
|
|
1039
|
+
visitor.analyzeAllMethodsAsync()
|
|
1040
|
+
|
|
1163
1041
|
return analysis
|
|
1164
1042
|
}
|
|
1165
1043
|
|
|
@@ -1342,23 +1220,32 @@ func (a *Analysis) LoadPackageMetadata() {
|
|
|
1342
1220
|
if metadata := a.loadGsMetadata(metaFilePath); metadata != nil {
|
|
1343
1221
|
// Store async method information
|
|
1344
1222
|
for methodKey, isAsync := range metadata.AsyncMethods {
|
|
1345
|
-
// Convert
|
|
1223
|
+
// Convert method key to our internal key format
|
|
1346
1224
|
parts := strings.Split(methodKey, ".")
|
|
1225
|
+
var typeName, methodName string
|
|
1226
|
+
|
|
1347
1227
|
if len(parts) == 2 {
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1228
|
+
// "Type.Method" format for methods
|
|
1229
|
+
typeName = parts[0]
|
|
1230
|
+
methodName = parts[1]
|
|
1231
|
+
} else if len(parts) == 1 {
|
|
1232
|
+
// "Function" format for package-level functions
|
|
1233
|
+
typeName = "" // Empty type name for package-level functions
|
|
1234
|
+
methodName = parts[0]
|
|
1235
|
+
} else {
|
|
1236
|
+
// Skip invalid formats
|
|
1237
|
+
continue
|
|
1238
|
+
}
|
|
1356
1239
|
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1240
|
+
// Use MethodKey instead of PackageMetadataKey for consistency
|
|
1241
|
+
key := MethodKey{
|
|
1242
|
+
PackagePath: pkgPath,
|
|
1243
|
+
ReceiverType: typeName,
|
|
1244
|
+
MethodName: methodName,
|
|
1361
1245
|
}
|
|
1246
|
+
|
|
1247
|
+
// Store the async value directly in MethodAsyncStatus
|
|
1248
|
+
a.MethodAsyncStatus[key] = isAsync
|
|
1362
1249
|
}
|
|
1363
1250
|
}
|
|
1364
1251
|
}
|
|
@@ -1378,14 +1265,7 @@ func (a *Analysis) discoverEmbeddedGsPackages() []string {
|
|
|
1378
1265
|
// Iterate through all entries in gs/
|
|
1379
1266
|
for _, entry := range entries {
|
|
1380
1267
|
if entry.IsDir() {
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
// Skip special directories like github.com
|
|
1384
|
-
if strings.Contains(packageName, ".") {
|
|
1385
|
-
continue
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
packageList = append(packageList, packageName)
|
|
1268
|
+
packageList = append(packageList, entry.Name())
|
|
1389
1269
|
}
|
|
1390
1270
|
}
|
|
1391
1271
|
|
|
@@ -1410,121 +1290,22 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
|
|
|
1410
1290
|
|
|
1411
1291
|
// IsMethodAsync checks if a method call is async based on package metadata
|
|
1412
1292
|
func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
|
|
1413
|
-
// First, check
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
TypeName: typeName,
|
|
1419
|
-
MethodName: methodName,
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
if metadata, exists := a.PackageMetadata[key]; exists {
|
|
1423
|
-
return metadata.IsAsync
|
|
1293
|
+
// First, check pre-computed method async status
|
|
1294
|
+
methodKey := MethodKey{
|
|
1295
|
+
PackagePath: pkgPath,
|
|
1296
|
+
ReceiverType: typeName,
|
|
1297
|
+
MethodName: methodName,
|
|
1424
1298
|
}
|
|
1425
1299
|
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
// analyzeExternalMethodAsync analyzes external package methods to determine if they're async
|
|
1431
|
-
// using the same logic we use for internal methods
|
|
1432
|
-
func (a *Analysis) analyzeExternalMethodAsync(pkgPath, typeName, methodName string) bool {
|
|
1433
|
-
// Look up the package in our loaded packages
|
|
1434
|
-
pkg, exists := a.AllPackages[pkgPath]
|
|
1435
|
-
if !exists {
|
|
1436
|
-
return false
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
// Find the method definition in the package
|
|
1440
|
-
for _, syntax := range pkg.Syntax {
|
|
1441
|
-
for _, decl := range syntax.Decls {
|
|
1442
|
-
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
1443
|
-
// Check if this is a method with the right name and receiver type
|
|
1444
|
-
if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
|
|
1445
|
-
// Check the receiver type
|
|
1446
|
-
if len(funcDecl.Recv.List) > 0 {
|
|
1447
|
-
receiverType := funcDecl.Recv.List[0].Type
|
|
1448
|
-
|
|
1449
|
-
// Handle pointer receivers
|
|
1450
|
-
if starExpr, ok := receiverType.(*ast.StarExpr); ok {
|
|
1451
|
-
receiverType = starExpr.X
|
|
1452
|
-
}
|
|
1453
|
-
|
|
1454
|
-
if ident, ok := receiverType.(*ast.Ident); ok {
|
|
1455
|
-
if ident.Name == typeName {
|
|
1456
|
-
// Found the method! Now check if it's async
|
|
1457
|
-
if funcDecl.Body != nil {
|
|
1458
|
-
return a.containsAsyncOperations(funcDecl.Body, pkg)
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1300
|
+
if status, exists := a.MethodAsyncStatus[methodKey]; exists {
|
|
1301
|
+
return status
|
|
1466
1302
|
}
|
|
1467
1303
|
|
|
1304
|
+
// If no pre-computed status found, external methods default to sync
|
|
1305
|
+
// Comprehensive analysis should have already analyzed all packages and loaded metadata
|
|
1468
1306
|
return false
|
|
1469
1307
|
}
|
|
1470
1308
|
|
|
1471
|
-
// containsAsyncOperations checks if a node contains any async operations for a specific package
|
|
1472
|
-
func (a *Analysis) containsAsyncOperations(node ast.Node, pkg *packages.Package) bool {
|
|
1473
|
-
var hasAsync bool
|
|
1474
|
-
|
|
1475
|
-
ast.Inspect(node, func(n ast.Node) bool {
|
|
1476
|
-
if n == nil {
|
|
1477
|
-
return false
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
switch s := n.(type) {
|
|
1481
|
-
case *ast.SendStmt:
|
|
1482
|
-
// Channel send operation (ch <- value)
|
|
1483
|
-
hasAsync = true
|
|
1484
|
-
return false
|
|
1485
|
-
|
|
1486
|
-
case *ast.UnaryExpr:
|
|
1487
|
-
// Channel receive operation (<-ch)
|
|
1488
|
-
if s.Op == token.ARROW {
|
|
1489
|
-
hasAsync = true
|
|
1490
|
-
return false
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
case *ast.CallExpr:
|
|
1494
|
-
// Check for select statements and other async operations
|
|
1495
|
-
if ident, ok := s.Fun.(*ast.Ident); ok {
|
|
1496
|
-
// Look for calls to select-related functions or other async operations
|
|
1497
|
-
if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
1498
|
-
// Check if this function is from the context package (like context.Background, etc.)
|
|
1499
|
-
if obj.Pkg() != nil && obj.Pkg().Path() == "context" {
|
|
1500
|
-
hasAsync = true
|
|
1501
|
-
return false
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
// Check if the method takes a context parameter, which usually indicates async behavior
|
|
1507
|
-
if len(s.Args) > 0 {
|
|
1508
|
-
for _, arg := range s.Args {
|
|
1509
|
-
if argType := pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
1510
|
-
if named, ok := argType.(*types.Named); ok {
|
|
1511
|
-
if named.Obj() != nil && named.Obj().Pkg() != nil &&
|
|
1512
|
-
named.Obj().Pkg().Path() == "context" && named.Obj().Name() == "Context" {
|
|
1513
|
-
hasAsync = true
|
|
1514
|
-
return false
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
return true
|
|
1523
|
-
})
|
|
1524
|
-
|
|
1525
|
-
return hasAsync
|
|
1526
|
-
}
|
|
1527
|
-
|
|
1528
1309
|
// NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
|
|
1529
1310
|
func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
|
|
1530
1311
|
if node == nil {
|
|
@@ -1758,6 +1539,26 @@ func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, method
|
|
|
1758
1539
|
return false
|
|
1759
1540
|
}
|
|
1760
1541
|
|
|
1542
|
+
// Update implementations with current async status before checking
|
|
1543
|
+
for i := range implementations {
|
|
1544
|
+
impl := &implementations[i]
|
|
1545
|
+
|
|
1546
|
+
// Create method key for this implementation
|
|
1547
|
+
methodKey := MethodKey{
|
|
1548
|
+
PackagePath: impl.StructType.Obj().Pkg().Path(),
|
|
1549
|
+
ReceiverType: impl.StructType.Obj().Name(),
|
|
1550
|
+
MethodName: impl.Method.Name(),
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
// Update with current async status from method analysis
|
|
1554
|
+
if isAsync, exists := a.MethodAsyncStatus[methodKey]; exists {
|
|
1555
|
+
impl.IsAsyncByFlow = isAsync
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// Store the updated implementations back to the map
|
|
1560
|
+
a.InterfaceImplementations[key] = implementations
|
|
1561
|
+
|
|
1761
1562
|
// If ANY implementation is async, the interface method is async
|
|
1762
1563
|
for _, impl := range implementations {
|
|
1763
1564
|
if impl.IsAsyncByFlow {
|
|
@@ -1890,12 +1691,10 @@ func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
|
|
|
1890
1691
|
// Find the corresponding method in the struct type
|
|
1891
1692
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1892
1693
|
if structMethod != nil {
|
|
1893
|
-
// Determine if this struct method is async
|
|
1694
|
+
// Determine if this struct method is async using unified system
|
|
1894
1695
|
isAsync := false
|
|
1895
1696
|
if obj := structMethod; obj != nil {
|
|
1896
|
-
|
|
1897
|
-
isAsync = funcInfo.IsAsync
|
|
1898
|
-
}
|
|
1697
|
+
isAsync = v.analysis.IsAsyncFunc(obj)
|
|
1899
1698
|
}
|
|
1900
1699
|
|
|
1901
1700
|
// Track this interface implementation
|
|
@@ -2016,11 +1815,8 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
|
|
|
2016
1815
|
|
|
2017
1816
|
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
2018
1817
|
if structMethod != nil {
|
|
2019
|
-
// Determine if this struct method is async
|
|
2020
|
-
isAsync :=
|
|
2021
|
-
if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
|
|
2022
|
-
isAsync = funcInfo.IsAsync
|
|
2023
|
-
}
|
|
1818
|
+
// Determine if this struct method is async using unified system
|
|
1819
|
+
isAsync := v.analysis.IsAsyncFunc(structMethod)
|
|
2024
1820
|
|
|
2025
1821
|
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2026
1822
|
}
|
|
@@ -2028,6 +1824,67 @@ func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt)
|
|
|
2028
1824
|
}
|
|
2029
1825
|
}
|
|
2030
1826
|
|
|
1827
|
+
// trackInterfaceCallArguments analyzes function call arguments to detect interface implementations
|
|
1828
|
+
func (v *analysisVisitor) trackInterfaceCallArguments(callExpr *ast.CallExpr) {
|
|
1829
|
+
// Get the function signature to determine parameter types
|
|
1830
|
+
var funcType *types.Signature
|
|
1831
|
+
|
|
1832
|
+
if callFunType := v.pkg.TypesInfo.TypeOf(callExpr.Fun); callFunType != nil {
|
|
1833
|
+
if sig, ok := callFunType.(*types.Signature); ok {
|
|
1834
|
+
funcType = sig
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
if funcType == nil {
|
|
1839
|
+
return
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// Check each argument against its corresponding parameter
|
|
1843
|
+
params := funcType.Params()
|
|
1844
|
+
for i, arg := range callExpr.Args {
|
|
1845
|
+
if i >= params.Len() {
|
|
1846
|
+
break // More arguments than parameters (variadic case)
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
paramType := params.At(i).Type()
|
|
1850
|
+
|
|
1851
|
+
// Check if the parameter is an interface type
|
|
1852
|
+
interfaceType, isInterface := paramType.Underlying().(*types.Interface)
|
|
1853
|
+
if !isInterface {
|
|
1854
|
+
continue
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
// Get the type of the argument
|
|
1858
|
+
argType := v.pkg.TypesInfo.TypeOf(arg)
|
|
1859
|
+
if argType == nil {
|
|
1860
|
+
continue
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// Handle pointer types
|
|
1864
|
+
if ptrType, isPtr := argType.(*types.Pointer); isPtr {
|
|
1865
|
+
argType = ptrType.Elem()
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
// Check if argument is a named struct type
|
|
1869
|
+
namedType, isNamed := argType.(*types.Named)
|
|
1870
|
+
if !isNamed {
|
|
1871
|
+
continue
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
// Track implementations for all interface methods
|
|
1875
|
+
for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
|
|
1876
|
+
interfaceMethod := interfaceType.ExplicitMethod(j)
|
|
1877
|
+
|
|
1878
|
+
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1879
|
+
if structMethod != nil {
|
|
1880
|
+
// Note: Don't determine async status here - it will be determined later after method analysis
|
|
1881
|
+
// For now, just track the implementation relationship without async status
|
|
1882
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, false)
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
2031
1888
|
// IsNamedBasicType returns whether the given type should be implemented as a type alias with standalone functions
|
|
2032
1889
|
// This applies to named types with basic underlying types (like uint32, string, etc.) that have methods
|
|
2033
1890
|
// It excludes struct types, which should remain as classes
|
|
@@ -2168,11 +2025,8 @@ func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *type
|
|
|
2168
2025
|
// Find the method in the implementing type
|
|
2169
2026
|
structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
|
|
2170
2027
|
if structMethod != nil {
|
|
2171
|
-
// Determine if this implementation is async
|
|
2172
|
-
isAsync :=
|
|
2173
|
-
if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
|
|
2174
|
-
isAsync = funcInfo.IsAsync
|
|
2175
|
-
}
|
|
2028
|
+
// Determine if this implementation is async using unified system
|
|
2029
|
+
isAsync := v.analysis.IsAsyncFunc(structMethod)
|
|
2176
2030
|
|
|
2177
2031
|
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2178
2032
|
}
|
|
@@ -2202,3 +2056,442 @@ func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
|
|
|
2202
2056
|
}
|
|
2203
2057
|
return namedReturns
|
|
2204
2058
|
}
|
|
2059
|
+
|
|
2060
|
+
// analyzeAllMethodsAsync performs comprehensive async analysis on all methods in all packages
|
|
2061
|
+
func (v *analysisVisitor) analyzeAllMethodsAsync() {
|
|
2062
|
+
// Initialize visitingMethods map
|
|
2063
|
+
v.visitingMethods = make(map[MethodKey]bool)
|
|
2064
|
+
|
|
2065
|
+
// Analyze methods in current package
|
|
2066
|
+
v.analyzePackageMethodsAsync(v.pkg)
|
|
2067
|
+
|
|
2068
|
+
// Analyze methods in all dependency packages
|
|
2069
|
+
for _, pkg := range v.analysis.AllPackages {
|
|
2070
|
+
if pkg != v.pkg {
|
|
2071
|
+
v.analyzePackageMethodsAsync(pkg)
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// Finally, analyze function literals in the current package only
|
|
2076
|
+
// (external packages' function literals are not accessible)
|
|
2077
|
+
v.analyzeFunctionLiteralsAsync(v.pkg)
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
// analyzePackageMethodsAsync analyzes all methods in a specific package
|
|
2081
|
+
func (v *analysisVisitor) analyzePackageMethodsAsync(pkg *packages.Package) {
|
|
2082
|
+
// Analyze function declarations
|
|
2083
|
+
for _, file := range pkg.Syntax {
|
|
2084
|
+
for _, decl := range file.Decls {
|
|
2085
|
+
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
2086
|
+
v.analyzeMethodAsync(funcDecl, pkg)
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
// analyzeFunctionLiteralsAsync analyzes all function literals in a package for async operations
|
|
2093
|
+
func (v *analysisVisitor) analyzeFunctionLiteralsAsync(pkg *packages.Package) {
|
|
2094
|
+
for _, file := range pkg.Syntax {
|
|
2095
|
+
ast.Inspect(file, func(n ast.Node) bool {
|
|
2096
|
+
if funcLit, ok := n.(*ast.FuncLit); ok {
|
|
2097
|
+
v.analyzeFunctionLiteralAsync(funcLit, pkg)
|
|
2098
|
+
}
|
|
2099
|
+
return true
|
|
2100
|
+
})
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
// analyzeFunctionLiteralAsync determines if a function literal is async and stores the result
|
|
2105
|
+
func (v *analysisVisitor) analyzeFunctionLiteralAsync(funcLit *ast.FuncLit, pkg *packages.Package) {
|
|
2106
|
+
// Check if already analyzed
|
|
2107
|
+
nodeInfo := v.analysis.NodeData[funcLit]
|
|
2108
|
+
if nodeInfo != nil && nodeInfo.InAsyncContext {
|
|
2109
|
+
// Already marked as async, skip
|
|
2110
|
+
return
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
// Analyze function literal body for async operations
|
|
2114
|
+
isAsync := false
|
|
2115
|
+
if funcLit.Body != nil {
|
|
2116
|
+
isAsync = v.containsAsyncOperationsComplete(funcLit.Body, pkg)
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// Store result in NodeData
|
|
2120
|
+
if nodeInfo == nil {
|
|
2121
|
+
nodeInfo = v.analysis.ensureNodeData(funcLit)
|
|
2122
|
+
}
|
|
2123
|
+
nodeInfo.InAsyncContext = isAsync
|
|
2124
|
+
}
|
|
2125
|
+
|
|
2126
|
+
// analyzeMethodAsync determines if a method is async and stores the result
|
|
2127
|
+
func (v *analysisVisitor) analyzeMethodAsync(funcDecl *ast.FuncDecl, pkg *packages.Package) {
|
|
2128
|
+
methodKey := v.getMethodKey(funcDecl, pkg)
|
|
2129
|
+
|
|
2130
|
+
// Check if already analyzed
|
|
2131
|
+
if _, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2132
|
+
return
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2135
|
+
// Check for cycles
|
|
2136
|
+
if v.visitingMethods[methodKey] {
|
|
2137
|
+
// Cycle detected, assume sync to break recursion
|
|
2138
|
+
v.analysis.MethodAsyncStatus[methodKey] = false
|
|
2139
|
+
return
|
|
2140
|
+
}
|
|
2141
|
+
|
|
2142
|
+
// Mark as visiting
|
|
2143
|
+
v.visitingMethods[methodKey] = true
|
|
2144
|
+
|
|
2145
|
+
// Determine if method is async
|
|
2146
|
+
isAsync := false
|
|
2147
|
+
|
|
2148
|
+
// Determine if this is a truly external package vs a package being compiled locally
|
|
2149
|
+
isExternalPackage := pkg.Types != v.pkg.Types && v.analysis.AllPackages[pkg.Types.Path()] == nil
|
|
2150
|
+
|
|
2151
|
+
if isExternalPackage {
|
|
2152
|
+
// Truly external package: check metadata first, fall back to body analysis
|
|
2153
|
+
isAsync = v.checkExternalMethodMetadata(methodKey.PackagePath, methodKey.ReceiverType, methodKey.MethodName)
|
|
2154
|
+
} else {
|
|
2155
|
+
// Local package or package being compiled: analyze method body
|
|
2156
|
+
if funcDecl.Body != nil {
|
|
2157
|
+
isAsync = v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
// Store result in MethodAsyncStatus
|
|
2162
|
+
v.analysis.MethodAsyncStatus[methodKey] = isAsync
|
|
2163
|
+
|
|
2164
|
+
// Unmark as visiting
|
|
2165
|
+
delete(v.visitingMethods, methodKey)
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// getMethodKey creates a unique key for a method
|
|
2169
|
+
func (v *analysisVisitor) getMethodKey(funcDecl *ast.FuncDecl, pkg *packages.Package) MethodKey {
|
|
2170
|
+
packagePath := pkg.Types.Path()
|
|
2171
|
+
methodName := funcDecl.Name.Name
|
|
2172
|
+
receiverType := ""
|
|
2173
|
+
|
|
2174
|
+
if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
|
|
2175
|
+
// Get receiver type name
|
|
2176
|
+
if len(funcDecl.Recv.List[0].Names) > 0 {
|
|
2177
|
+
if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
|
|
2178
|
+
if vr, ok := def.(*types.Var); ok {
|
|
2179
|
+
receiverType = v.getTypeName(vr.Type())
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
return MethodKey{
|
|
2186
|
+
PackagePath: packagePath,
|
|
2187
|
+
ReceiverType: receiverType,
|
|
2188
|
+
MethodName: methodName,
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
// getTypeName extracts a clean type name from a types.Type
|
|
2193
|
+
func (v *analysisVisitor) getTypeName(t types.Type) string {
|
|
2194
|
+
switch typ := t.(type) {
|
|
2195
|
+
case *types.Named:
|
|
2196
|
+
return typ.Obj().Name()
|
|
2197
|
+
case *types.Pointer:
|
|
2198
|
+
return v.getTypeName(typ.Elem())
|
|
2199
|
+
default:
|
|
2200
|
+
return typ.String()
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
// containsAsyncOperationsComplete is a comprehensive async detection that handles method calls
|
|
2205
|
+
func (v *analysisVisitor) containsAsyncOperationsComplete(node ast.Node, pkg *packages.Package) bool {
|
|
2206
|
+
var hasAsync bool
|
|
2207
|
+
var asyncReasons []string
|
|
2208
|
+
|
|
2209
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
2210
|
+
if n == nil {
|
|
2211
|
+
return false
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
switch s := n.(type) {
|
|
2215
|
+
case *ast.SendStmt:
|
|
2216
|
+
// Channel send operation (ch <- value)
|
|
2217
|
+
hasAsync = true
|
|
2218
|
+
asyncReasons = append(asyncReasons, "channel send")
|
|
2219
|
+
return false
|
|
2220
|
+
|
|
2221
|
+
case *ast.UnaryExpr:
|
|
2222
|
+
// Channel receive operation (<-ch)
|
|
2223
|
+
if s.Op == token.ARROW {
|
|
2224
|
+
hasAsync = true
|
|
2225
|
+
asyncReasons = append(asyncReasons, "channel receive")
|
|
2226
|
+
return false
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
case *ast.SelectStmt:
|
|
2230
|
+
// Select statement with channel operations
|
|
2231
|
+
hasAsync = true
|
|
2232
|
+
asyncReasons = append(asyncReasons, "select statement")
|
|
2233
|
+
return false
|
|
2234
|
+
|
|
2235
|
+
case *ast.CallExpr:
|
|
2236
|
+
// Check if we're calling a function known to be async
|
|
2237
|
+
isCallAsyncResult := v.isCallAsync(s, pkg)
|
|
2238
|
+
if isCallAsyncResult {
|
|
2239
|
+
hasAsync = true
|
|
2240
|
+
callName := ""
|
|
2241
|
+
if ident, ok := s.Fun.(*ast.Ident); ok {
|
|
2242
|
+
callName = ident.Name
|
|
2243
|
+
} else if sel, ok := s.Fun.(*ast.SelectorExpr); ok {
|
|
2244
|
+
callName = sel.Sel.Name
|
|
2245
|
+
}
|
|
2246
|
+
asyncReasons = append(asyncReasons, fmt.Sprintf("async call: %s", callName))
|
|
2247
|
+
return false
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
return true
|
|
2252
|
+
})
|
|
2253
|
+
|
|
2254
|
+
return hasAsync
|
|
2255
|
+
}
|
|
2256
|
+
|
|
2257
|
+
// isCallAsync determines if a call expression is async
|
|
2258
|
+
func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Package) bool {
|
|
2259
|
+
switch fun := callExpr.Fun.(type) {
|
|
2260
|
+
case *ast.Ident:
|
|
2261
|
+
// Direct function call
|
|
2262
|
+
if obj := pkg.TypesInfo.Uses[fun]; obj != nil {
|
|
2263
|
+
if funcObj, ok := obj.(*types.Func); ok {
|
|
2264
|
+
return v.isFunctionAsync(funcObj, pkg)
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
case *ast.SelectorExpr:
|
|
2269
|
+
// Handle package-level function calls (e.g., time.Sleep)
|
|
2270
|
+
if ident, ok := fun.X.(*ast.Ident); ok {
|
|
2271
|
+
if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
2272
|
+
if pkgName, isPkg := obj.(*types.PkgName); isPkg {
|
|
2273
|
+
methodName := fun.Sel.Name
|
|
2274
|
+
pkgPath := pkgName.Imported().Path()
|
|
2275
|
+
// Check if this package-level function is async (empty TypeName)
|
|
2276
|
+
isAsync := v.analysis.IsMethodAsync(pkgPath, "", methodName)
|
|
2277
|
+
return isAsync
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
// Check if this is an interface method call
|
|
2283
|
+
if receiverType := pkg.TypesInfo.TypeOf(fun.X); receiverType != nil {
|
|
2284
|
+
if interfaceType, isInterface := receiverType.Underlying().(*types.Interface); isInterface {
|
|
2285
|
+
methodName := fun.Sel.Name
|
|
2286
|
+
// For interface method calls, check if the interface method is async
|
|
2287
|
+
return v.analysis.IsInterfaceMethodAsync(interfaceType, methodName)
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
// Method call on concrete objects
|
|
2292
|
+
if selection := pkg.TypesInfo.Selections[fun]; selection != nil {
|
|
2293
|
+
if methodObj := selection.Obj(); methodObj != nil {
|
|
2294
|
+
return v.isMethodAsyncFromSelection(fun, methodObj, pkg)
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
return false
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
// isFunctionAsync checks if a function object is async
|
|
2303
|
+
func (v *analysisVisitor) isFunctionAsync(funcObj *types.Func, pkg *packages.Package) bool {
|
|
2304
|
+
// Check if it's from external package metadata
|
|
2305
|
+
if funcObj.Pkg() != nil && funcObj.Pkg() != pkg.Types {
|
|
2306
|
+
return v.analysis.IsMethodAsync(funcObj.Pkg().Path(), "", funcObj.Name())
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
// Check internal method status
|
|
2310
|
+
methodKey := MethodKey{
|
|
2311
|
+
PackagePath: pkg.Types.Path(),
|
|
2312
|
+
ReceiverType: "",
|
|
2313
|
+
MethodName: funcObj.Name(),
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2317
|
+
return status
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
// Not analyzed yet, analyze now
|
|
2321
|
+
if funcDecl := v.findFunctionDecl(funcObj.Name(), pkg); funcDecl != nil {
|
|
2322
|
+
v.analyzeMethodAsync(funcDecl, pkg)
|
|
2323
|
+
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2324
|
+
return status
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
return false
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// isMethodAsyncFromSelection checks if a method call is async based on selection
|
|
2332
|
+
func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr, methodObj types.Object, pkg *packages.Package) bool {
|
|
2333
|
+
// Get receiver type - handle both direct identifiers and field access
|
|
2334
|
+
var receiverType string
|
|
2335
|
+
var methodPkgPath string
|
|
2336
|
+
|
|
2337
|
+
// Handle different receiver patterns
|
|
2338
|
+
switch x := selExpr.X.(type) {
|
|
2339
|
+
case *ast.Ident:
|
|
2340
|
+
// Direct variable (e.g., mtx.Lock())
|
|
2341
|
+
if obj := pkg.TypesInfo.Uses[x]; obj != nil {
|
|
2342
|
+
if varObj, ok := obj.(*types.Var); ok {
|
|
2343
|
+
receiverType = v.getTypeName(varObj.Type())
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
case *ast.SelectorExpr:
|
|
2347
|
+
// Field access (e.g., l.m.Lock())
|
|
2348
|
+
if typeExpr := pkg.TypesInfo.TypeOf(x); typeExpr != nil {
|
|
2349
|
+
receiverType = v.getTypeName(typeExpr)
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
// Get the method's package path
|
|
2354
|
+
if methodFunc, ok := methodObj.(*types.Func); ok {
|
|
2355
|
+
if methodFunc.Pkg() != nil {
|
|
2356
|
+
methodPkgPath = methodFunc.Pkg().Path()
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
// If no package path found, use current package
|
|
2361
|
+
if methodPkgPath == "" {
|
|
2362
|
+
methodPkgPath = pkg.Types.Path()
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
// For external packages, check unified MethodAsyncStatus first
|
|
2366
|
+
// For internal packages, try analysis first, then fallback to lookup
|
|
2367
|
+
methodKey := MethodKey{
|
|
2368
|
+
PackagePath: methodPkgPath,
|
|
2369
|
+
ReceiverType: receiverType,
|
|
2370
|
+
MethodName: methodObj.Name(),
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2374
|
+
return status
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
// Only try to analyze methods for packages that don't have metadata loaded
|
|
2378
|
+
// If a package has metadata, we should rely solely on that metadata
|
|
2379
|
+
if targetPkg := v.analysis.AllPackages[methodPkgPath]; targetPkg != nil {
|
|
2380
|
+
// Check if this package has metadata loaded by checking if any method from this package
|
|
2381
|
+
// exists in MethodAsyncStatus. If so, don't analyze - rely on metadata only.
|
|
2382
|
+
hasMetadata := false
|
|
2383
|
+
for key := range v.analysis.MethodAsyncStatus {
|
|
2384
|
+
if key.PackagePath == methodPkgPath {
|
|
2385
|
+
hasMetadata = true
|
|
2386
|
+
break
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
// Only analyze if no metadata exists for this package
|
|
2391
|
+
if !hasMetadata {
|
|
2392
|
+
if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), targetPkg); funcDecl != nil {
|
|
2393
|
+
v.analyzeMethodAsync(funcDecl, targetPkg)
|
|
2394
|
+
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2395
|
+
return status
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
return false
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
// findFunctionDecl finds a function declaration by name in a package
|
|
2405
|
+
func (v *analysisVisitor) findFunctionDecl(funcName string, pkg *packages.Package) *ast.FuncDecl {
|
|
2406
|
+
for _, file := range pkg.Syntax {
|
|
2407
|
+
for _, decl := range file.Decls {
|
|
2408
|
+
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
2409
|
+
if funcDecl.Name.Name == funcName && funcDecl.Recv == nil {
|
|
2410
|
+
return funcDecl
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
return nil
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// findMethodDecl finds a method declaration by receiver type and method name
|
|
2419
|
+
func (v *analysisVisitor) findMethodDecl(receiverType, methodName string, pkg *packages.Package) *ast.FuncDecl {
|
|
2420
|
+
for _, file := range pkg.Syntax {
|
|
2421
|
+
for _, decl := range file.Decls {
|
|
2422
|
+
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
2423
|
+
if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
|
|
2424
|
+
if len(funcDecl.Recv.List) > 0 && len(funcDecl.Recv.List[0].Names) > 0 {
|
|
2425
|
+
if def := pkg.TypesInfo.Defs[funcDecl.Recv.List[0].Names[0]]; def != nil {
|
|
2426
|
+
if vr, ok := def.(*types.Var); ok {
|
|
2427
|
+
if v.getTypeName(vr.Type()) == receiverType {
|
|
2428
|
+
return funcDecl
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
return nil
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
// checkExternalMethodMetadata checks if an external method is async based on pre-loaded metadata
|
|
2441
|
+
func (v *analysisVisitor) checkExternalMethodMetadata(pkgPath, receiverType, methodName string) bool {
|
|
2442
|
+
// Use MethodKey to check pre-loaded metadata in MethodAsyncStatus
|
|
2443
|
+
key := MethodKey{
|
|
2444
|
+
PackagePath: pkgPath,
|
|
2445
|
+
ReceiverType: receiverType,
|
|
2446
|
+
MethodName: methodName,
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
if isAsync, exists := v.analysis.MethodAsyncStatus[key]; exists {
|
|
2450
|
+
return isAsync
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
return false
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
// IsLocalMethodAsync checks if a local method is async using pre-computed analysis
|
|
2457
|
+
func (a *Analysis) IsLocalMethodAsync(pkgPath, receiverType, methodName string) bool {
|
|
2458
|
+
methodKey := MethodKey{
|
|
2459
|
+
PackagePath: pkgPath,
|
|
2460
|
+
ReceiverType: receiverType,
|
|
2461
|
+
MethodName: methodName,
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
if status, exists := a.MethodAsyncStatus[methodKey]; exists {
|
|
2465
|
+
return status
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
return false
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
// updateInterfaceImplementationAsyncStatus updates interface implementations with correct async status
|
|
2472
|
+
// This runs after method async analysis is complete
|
|
2473
|
+
func (v *analysisVisitor) updateInterfaceImplementationAsyncStatus() {
|
|
2474
|
+
// Iterate through all tracked interface implementations and update their async status
|
|
2475
|
+
for key, implementations := range v.analysis.InterfaceImplementations {
|
|
2476
|
+
// Remove duplicates first
|
|
2477
|
+
seenMethods := make(map[string]bool)
|
|
2478
|
+
uniqueImplementations := []ImplementationInfo{}
|
|
2479
|
+
|
|
2480
|
+
for _, impl := range implementations {
|
|
2481
|
+
methodKey := impl.StructType.Obj().Name() + "." + key.MethodName
|
|
2482
|
+
if !seenMethods[methodKey] {
|
|
2483
|
+
seenMethods[methodKey] = true
|
|
2484
|
+
|
|
2485
|
+
// Now that method async analysis is complete, get the correct async status
|
|
2486
|
+
isAsync := v.analysis.IsAsyncFunc(impl.Method)
|
|
2487
|
+
|
|
2488
|
+
// Update the implementation with the correct async status
|
|
2489
|
+
impl.IsAsyncByFlow = isAsync
|
|
2490
|
+
uniqueImplementations = append(uniqueImplementations, impl)
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
// Store the updated implementations without duplicates
|
|
2495
|
+
v.analysis.InterfaceImplementations[key] = uniqueImplementations
|
|
2496
|
+
}
|
|
2497
|
+
}
|