goscript 0.0.48 → 0.0.50
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/cmd/goscript/deps.go +1 -4
- package/compiler/analysis.go +1120 -513
- package/compiler/analysis_test.go +113 -4
- package/compiler/compiler.go +88 -124
- package/compiler/decl.go +22 -0
- package/compiler/expr-call-async.go +46 -52
- package/compiler/expr-call-type-conversion.go +144 -59
- package/compiler/expr-call.go +235 -12
- package/compiler/expr.go +5 -82
- package/compiler/gs_dependencies_test.go +60 -1
- package/compiler/spec-value.go +73 -51
- package/compiler/spec.go +337 -151
- package/compiler/stmt-assign.go +7 -4
- package/compiler/stmt.go +250 -81
- package/compiler/type.go +13 -0
- package/dist/gs/builtin/builtin.d.ts +1 -5
- package/dist/gs/builtin/builtin.js +1 -34
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/context/context.d.ts +16 -18
- package/dist/gs/context/context.js +23 -13
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/fmt/fmt.js +22 -4
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/io/fs/fs.d.ts +6 -12
- package/dist/gs/io/fs/fs.js +52 -67
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/os/index.d.ts +2 -1
- package/dist/gs/os/index.js +1 -1
- package/dist/gs/os/index.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +7 -1
- package/dist/gs/os/types_js.gs.js +16 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +2 -2
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +3 -3
- package/dist/gs/reflect/index.js +2 -2
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js +2 -2
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +8 -9
- package/dist/gs/reflect/type.js +98 -103
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +1 -10
- package/dist/gs/reflect/types.js +3 -26
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.js +23 -23
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js +3 -3
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/time/time.d.ts +13 -23
- package/dist/gs/time/time.js +57 -75
- package/dist/gs/time/time.js.map +1 -1
- package/gs/builtin/builtin.ts +3 -47
- package/gs/builtin/slice.ts +1 -1
- package/gs/bytes/meta.json +10 -0
- package/gs/context/context.ts +63 -45
- package/gs/fmt/fmt.ts +22 -4
- package/gs/fmt/meta.json +5 -0
- package/gs/internal/meta.json +5 -0
- package/gs/io/fs/fs.ts +58 -73
- package/gs/io/meta.json +9 -0
- package/gs/maps/meta.json +6 -0
- package/gs/math/meta.json +5 -0
- package/gs/os/index.ts +8 -1
- package/gs/os/meta.json +15 -0
- package/gs/os/types_js.gs.ts +22 -1
- package/gs/os/types_unix.gs.ts +2 -2
- package/gs/path/meta.json +6 -0
- package/gs/reflect/function-types.test.ts +10 -10
- package/gs/reflect/index.ts +6 -6
- package/gs/reflect/map.ts +2 -2
- package/gs/reflect/meta.json +5 -0
- package/gs/reflect/type.ts +105 -105
- package/gs/reflect/types.ts +2 -28
- package/gs/reflect/value.ts +23 -23
- package/gs/reflect/visiblefields.ts +3 -3
- package/gs/strconv/meta.json +5 -0
- package/gs/strings/meta.json +9 -0
- package/gs/sync/meta.json +19 -0
- package/gs/time/time.ts +65 -84
- package/package.json +2 -2
- package/dist/gs/builtin/io.d.ts +0 -16
- package/dist/gs/builtin/io.js +0 -15
- package/dist/gs/builtin/io.js.map +0 -1
- package/dist/gs/internal/testlog/index.d.ts +0 -1
- package/dist/gs/internal/testlog/index.js +0 -5
- package/dist/gs/internal/testlog/index.js.map +0 -1
- package/dist/gs/maps/iter.gs.d.ts +0 -7
- package/dist/gs/maps/iter.gs.js +0 -65
- package/dist/gs/maps/iter.gs.js.map +0 -1
- package/dist/gs/maps/maps.gs.d.ts +0 -7
- package/dist/gs/maps/maps.gs.js +0 -79
- package/dist/gs/maps/maps.gs.js.map +0 -1
- package/dist/gs/reflect/abi.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.d.ts +0 -59
- package/dist/gs/reflect/abi.gs.js +0 -79
- package/dist/gs/reflect/abi.gs.js.map +0 -1
- package/dist/gs/reflect/abi.js +0 -79
- package/dist/gs/reflect/abi.js.map +0 -1
- package/dist/gs/reflect/badlinkname.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.d.ts +0 -52
- package/dist/gs/reflect/badlinkname.gs.js +0 -72
- package/dist/gs/reflect/badlinkname.gs.js.map +0 -1
- package/dist/gs/reflect/badlinkname.js +0 -72
- package/dist/gs/reflect/badlinkname.js.map +0 -1
- package/dist/gs/reflect/deepequal.gs.d.ts +0 -25
- package/dist/gs/reflect/deepequal.gs.js +0 -308
- package/dist/gs/reflect/deepequal.gs.js.map +0 -1
- package/dist/gs/reflect/float32reg_generic.gs.d.ts +0 -2
- package/dist/gs/reflect/float32reg_generic.gs.js +0 -10
- package/dist/gs/reflect/float32reg_generic.gs.js.map +0 -1
- package/dist/gs/reflect/index.gs.d.ts +0 -1
- package/dist/gs/reflect/index.gs.js +0 -3
- package/dist/gs/reflect/index.gs.js.map +0 -1
- package/dist/gs/reflect/iter.gs.d.ts +0 -3
- package/dist/gs/reflect/iter.gs.js +0 -24
- package/dist/gs/reflect/iter.gs.js.map +0 -1
- package/dist/gs/reflect/makefunc.gs.d.ts +0 -34
- package/dist/gs/reflect/makefunc.gs.js +0 -288
- package/dist/gs/reflect/makefunc.gs.js.map +0 -1
- package/dist/gs/reflect/map_swiss.gs.d.ts +0 -14
- package/dist/gs/reflect/map_swiss.gs.js +0 -70
- package/dist/gs/reflect/map_swiss.gs.js.map +0 -1
- package/dist/gs/reflect/reflect.gs.d.ts +0 -132
- package/dist/gs/reflect/reflect.gs.js +0 -437
- package/dist/gs/reflect/reflect.gs.js.map +0 -1
- package/dist/gs/reflect/swapper.gs.d.ts +0 -1
- package/dist/gs/reflect/swapper.gs.js +0 -32
- package/dist/gs/reflect/swapper.gs.js.map +0 -1
- package/dist/gs/reflect/type.gs.d.ts +0 -4
- package/dist/gs/reflect/type.gs.js +0 -21
- package/dist/gs/reflect/type.gs.js.map +0 -1
- package/dist/gs/reflect/value.gs.d.ts +0 -4
- package/dist/gs/reflect/value.gs.js +0 -12
- package/dist/gs/reflect/value.gs.js.map +0 -1
- package/dist/gs/reflect/visiblefields.gs.d.ts +0 -3
- package/dist/gs/reflect/visiblefields.gs.js +0 -123
- package/dist/gs/reflect/visiblefields.gs.js.map +0 -1
- package/dist/gs/stringslite/index.d.ts +0 -1
- package/dist/gs/stringslite/index.js +0 -2
- package/dist/gs/stringslite/index.js.map +0 -1
- package/dist/gs/stringslite/strings.d.ts +0 -11
- package/dist/gs/stringslite/strings.js +0 -67
- package/dist/gs/stringslite/strings.js.map +0 -1
- package/gs/bytes/metadata.go +0 -12
- package/gs/fmt/metadata.go +0 -7
- package/gs/internal/metadata.go +0 -7
- package/gs/io/io.go +0 -75
- package/gs/io/metadata.go +0 -11
- package/gs/maps/metadata.go +0 -8
- package/gs/math/metadata.go +0 -7
- package/gs/os/metadata.go +0 -17
- package/gs/path/metadata.go +0 -8
- package/gs/reflect/metadata.go +0 -7
- package/gs/strconv/metadata.go +0 -7
- package/gs/strings/metadata.go +0 -11
- package/gs/sync/metadata.go +0 -7
- package/gs/sync/sync.go +0 -64
package/compiler/analysis.go
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
package compiler
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"encoding/json"
|
|
4
5
|
"go/ast"
|
|
5
6
|
"go/token"
|
|
6
7
|
"go/types"
|
|
7
8
|
"path/filepath"
|
|
8
9
|
"strings"
|
|
9
10
|
|
|
11
|
+
"github.com/aperturerobotics/goscript"
|
|
10
12
|
"golang.org/x/tools/go/packages"
|
|
11
13
|
)
|
|
12
14
|
|
|
@@ -81,6 +83,38 @@ type NodeInfo struct {
|
|
|
81
83
|
IsInsideFunction bool // true if this declaration is inside a function body
|
|
82
84
|
IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
|
|
83
85
|
ShadowingInfo *ShadowingInfo // variable shadowing information for if statements
|
|
86
|
+
IdentifierMapping string // replacement name for this identifier (e.g., receiver -> "receiver")
|
|
87
|
+
}
|
|
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
|
+
// GsMetadata represents the structure of a meta.json file in gs/ packages
|
|
102
|
+
type GsMetadata struct {
|
|
103
|
+
Dependencies []string `json:"dependencies,omitempty"`
|
|
104
|
+
AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// InterfaceMethodKey uniquely identifies an interface method
|
|
108
|
+
type InterfaceMethodKey struct {
|
|
109
|
+
InterfaceType string // The string representation of the interface type
|
|
110
|
+
MethodName string // The method name
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ImplementationInfo tracks information about a struct that implements an interface method
|
|
114
|
+
type ImplementationInfo struct {
|
|
115
|
+
StructType *types.Named // The struct type that implements the interface
|
|
116
|
+
Method *types.Func // The method object
|
|
117
|
+
IsAsyncByFlow bool // Whether this implementation is async based on control flow analysis
|
|
84
118
|
}
|
|
85
119
|
|
|
86
120
|
// Analysis holds information gathered during the analysis phase of the Go code compilation.
|
|
@@ -113,8 +147,24 @@ type Analysis struct {
|
|
|
113
147
|
// FunctionAssignments tracks which function literals are assigned to which variables
|
|
114
148
|
FunctionAssignments map[types.Object]ast.Node
|
|
115
149
|
|
|
116
|
-
// PackageMetadata holds package-level metadata
|
|
117
|
-
PackageMetadata map[
|
|
150
|
+
// PackageMetadata holds package-level metadata with structured keys
|
|
151
|
+
PackageMetadata map[PackageMetadataKey]MethodMetadata
|
|
152
|
+
|
|
153
|
+
// NamedBasicTypes tracks types that should be implemented as type aliases with standalone functions
|
|
154
|
+
// This includes named types with basic underlying types (like uint32, string) that have methods
|
|
155
|
+
NamedBasicTypes map[types.Type]bool
|
|
156
|
+
|
|
157
|
+
// AllPackages stores all loaded packages for dependency analysis
|
|
158
|
+
// Key: package path, Value: package data
|
|
159
|
+
AllPackages map[string]*packages.Package
|
|
160
|
+
|
|
161
|
+
// InterfaceImplementations tracks which struct types implement which interface methods
|
|
162
|
+
// This is used to determine interface method async status based on implementations
|
|
163
|
+
InterfaceImplementations map[InterfaceMethodKey][]ImplementationInfo
|
|
164
|
+
|
|
165
|
+
// InterfaceMethodAsyncStatus caches the async status determination for interface methods
|
|
166
|
+
// This is computed once during analysis and reused during code generation
|
|
167
|
+
InterfaceMethodAsyncStatus map[InterfaceMethodKey]bool
|
|
118
168
|
}
|
|
119
169
|
|
|
120
170
|
// PackageAnalysis holds cross-file analysis data for a package
|
|
@@ -137,16 +187,24 @@ type PackageAnalysis struct {
|
|
|
137
187
|
}
|
|
138
188
|
|
|
139
189
|
// NewAnalysis creates a new Analysis instance.
|
|
140
|
-
func NewAnalysis() *Analysis {
|
|
190
|
+
func NewAnalysis(allPackages map[string]*packages.Package) *Analysis {
|
|
191
|
+
if allPackages == nil {
|
|
192
|
+
allPackages = make(map[string]*packages.Package)
|
|
193
|
+
}
|
|
194
|
+
|
|
141
195
|
return &Analysis{
|
|
142
|
-
VariableUsage:
|
|
143
|
-
Imports:
|
|
144
|
-
FunctionData:
|
|
145
|
-
NodeData:
|
|
146
|
-
FuncLitData:
|
|
147
|
-
ReflectedFunctions:
|
|
148
|
-
FunctionAssignments:
|
|
149
|
-
PackageMetadata:
|
|
196
|
+
VariableUsage: make(map[types.Object]*VariableUsageInfo),
|
|
197
|
+
Imports: make(map[string]*fileImport),
|
|
198
|
+
FunctionData: make(map[types.Object]*FunctionInfo),
|
|
199
|
+
NodeData: make(map[ast.Node]*NodeInfo),
|
|
200
|
+
FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
|
|
201
|
+
ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
|
|
202
|
+
FunctionAssignments: make(map[types.Object]ast.Node),
|
|
203
|
+
PackageMetadata: make(map[PackageMetadataKey]MethodMetadata),
|
|
204
|
+
NamedBasicTypes: make(map[types.Type]bool),
|
|
205
|
+
AllPackages: allPackages,
|
|
206
|
+
InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
|
|
207
|
+
InterfaceMethodAsyncStatus: make(map[InterfaceMethodKey]bool),
|
|
150
208
|
}
|
|
151
209
|
}
|
|
152
210
|
|
|
@@ -160,6 +218,28 @@ func NewPackageAnalysis() *PackageAnalysis {
|
|
|
160
218
|
}
|
|
161
219
|
}
|
|
162
220
|
|
|
221
|
+
// ensureNodeData ensures that NodeData exists for a given node and returns it
|
|
222
|
+
func (a *Analysis) ensureNodeData(node ast.Node) *NodeInfo {
|
|
223
|
+
if node == nil {
|
|
224
|
+
return nil
|
|
225
|
+
}
|
|
226
|
+
if a.NodeData[node] == nil {
|
|
227
|
+
a.NodeData[node] = &NodeInfo{}
|
|
228
|
+
}
|
|
229
|
+
return a.NodeData[node]
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ensureFunctionData ensures that FunctionData exists for a given object and returns it
|
|
233
|
+
func (a *Analysis) ensureFunctionData(obj types.Object) *FunctionInfo {
|
|
234
|
+
if obj == nil {
|
|
235
|
+
return nil
|
|
236
|
+
}
|
|
237
|
+
if a.FunctionData[obj] == nil {
|
|
238
|
+
a.FunctionData[obj] = &FunctionInfo{}
|
|
239
|
+
}
|
|
240
|
+
return a.FunctionData[obj]
|
|
241
|
+
}
|
|
242
|
+
|
|
163
243
|
// NeedsDefer returns whether the given node needs defer handling.
|
|
164
244
|
func (a *Analysis) NeedsDefer(node ast.Node) bool {
|
|
165
245
|
if node == nil {
|
|
@@ -373,10 +453,8 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
373
453
|
}
|
|
374
454
|
|
|
375
455
|
// Initialize and store async state for the current node
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
v.analysis.NodeData[node].InAsyncContext = v.inAsyncFunction
|
|
456
|
+
nodeInfo := v.analysis.ensureNodeData(node)
|
|
457
|
+
nodeInfo.InAsyncContext = v.inAsyncFunction
|
|
380
458
|
|
|
381
459
|
switch n := node.(type) {
|
|
382
460
|
case *ast.GenDecl:
|
|
@@ -449,441 +527,365 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
449
527
|
}
|
|
450
528
|
}
|
|
451
529
|
}
|
|
452
|
-
// Continue traversal AFTER processing the declaration itself
|
|
453
|
-
// to handle expressions within initial values if needed.
|
|
454
|
-
// However, the core usage tracking is done above.
|
|
455
|
-
// Let standard traversal handle children.
|
|
456
530
|
return v
|
|
457
531
|
|
|
458
532
|
case *ast.FuncDecl:
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
v
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
533
|
+
return v.visitFuncDecl(n)
|
|
534
|
+
|
|
535
|
+
case *ast.FuncLit:
|
|
536
|
+
return v.visitFuncLit(n)
|
|
537
|
+
|
|
538
|
+
case *ast.BlockStmt:
|
|
539
|
+
return v.visitBlockStmt(n)
|
|
540
|
+
|
|
541
|
+
case *ast.UnaryExpr:
|
|
542
|
+
// We handle address-of (&) within AssignStmt where it's actually used.
|
|
543
|
+
return v
|
|
544
|
+
|
|
545
|
+
case *ast.CallExpr:
|
|
546
|
+
return v.visitCallExpr(n)
|
|
547
|
+
|
|
548
|
+
case *ast.SelectorExpr:
|
|
549
|
+
return v.visitSelectorExpr(n)
|
|
550
|
+
|
|
551
|
+
case *ast.AssignStmt:
|
|
552
|
+
return v.visitAssignStmt(n)
|
|
553
|
+
|
|
554
|
+
case *ast.ReturnStmt:
|
|
555
|
+
return v.visitReturnStmt(n)
|
|
556
|
+
|
|
557
|
+
case *ast.DeclStmt:
|
|
558
|
+
return v.visitDeclStmt(n)
|
|
559
|
+
|
|
560
|
+
case *ast.IfStmt:
|
|
561
|
+
return v.visitIfStmt(n)
|
|
562
|
+
|
|
563
|
+
case *ast.TypeAssertExpr:
|
|
564
|
+
return v.visitTypeAssertExpr(n)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// For all other nodes, continue traversal
|
|
568
|
+
return v
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// visitFuncDecl handles function declaration analysis
|
|
572
|
+
func (v *analysisVisitor) visitFuncDecl(n *ast.FuncDecl) ast.Visitor {
|
|
573
|
+
// Save original states to restore after visiting
|
|
574
|
+
originalInAsync := v.inAsyncFunction
|
|
575
|
+
originalFuncObj := v.currentFuncObj
|
|
576
|
+
originalFuncDecl := v.currentFuncDecl
|
|
577
|
+
originalFuncLit := v.currentFuncLit
|
|
578
|
+
originalReceiver := v.currentReceiver
|
|
579
|
+
|
|
580
|
+
// Reset for current function
|
|
581
|
+
v.currentFuncName = n.Name.Name
|
|
582
|
+
v.currentFuncDecl = n
|
|
583
|
+
v.currentFuncLit = nil
|
|
584
|
+
v.currentReceiver = nil
|
|
585
|
+
|
|
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
|
|
485
597
|
}
|
|
486
598
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if
|
|
496
|
-
if
|
|
497
|
-
if
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
} else {
|
|
510
|
-
receiverUsed = v.containsReceiverUsage(n.Body, vr)
|
|
511
|
-
}
|
|
599
|
+
}
|
|
600
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
601
|
+
nodeInfo.InAsyncContext = isAsync
|
|
602
|
+
|
|
603
|
+
// Set current receiver if this is a method
|
|
604
|
+
if n.Recv != nil && len(n.Recv.List) > 0 {
|
|
605
|
+
// Assuming a single receiver for simplicity for now
|
|
606
|
+
if len(n.Recv.List[0].Names) > 0 {
|
|
607
|
+
if ident := n.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
|
|
608
|
+
if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
|
|
609
|
+
if vr, ok := def.(*types.Var); ok {
|
|
610
|
+
v.currentReceiver = vr
|
|
611
|
+
// Add the receiver variable to the VariableUsage map
|
|
612
|
+
v.getOrCreateUsageInfo(v.currentReceiver)
|
|
613
|
+
|
|
614
|
+
// Check if receiver is used in method body
|
|
615
|
+
receiverUsed := false
|
|
616
|
+
if n.Body != nil {
|
|
617
|
+
if v.isInterfaceMethod(n) {
|
|
618
|
+
receiverUsed = true
|
|
619
|
+
} else {
|
|
620
|
+
receiverUsed = v.containsReceiverUsage(n.Body, vr)
|
|
512
621
|
}
|
|
622
|
+
}
|
|
513
623
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
v.analysis.FunctionData[obj].ReceiverUsed = receiverUsed
|
|
520
|
-
}
|
|
624
|
+
// Update function data with receiver usage info
|
|
625
|
+
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
626
|
+
funcInfo := v.analysis.ensureFunctionData(obj)
|
|
627
|
+
funcInfo.ReceiverUsed = receiverUsed
|
|
521
628
|
}
|
|
522
629
|
}
|
|
523
630
|
}
|
|
524
631
|
}
|
|
525
632
|
}
|
|
633
|
+
}
|
|
526
634
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
if len(namedReturns) > 0 {
|
|
536
|
-
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
537
|
-
if v.analysis.FunctionData[obj] == nil {
|
|
538
|
-
v.analysis.FunctionData[obj] = &FunctionInfo{}
|
|
539
|
-
}
|
|
540
|
-
v.analysis.FunctionData[obj].NamedReturns = namedReturns
|
|
541
|
-
}
|
|
635
|
+
// Store named return variables (sanitized for TypeScript)
|
|
636
|
+
if n.Type != nil && n.Type.Results != nil {
|
|
637
|
+
var namedReturns []string
|
|
638
|
+
for _, field := range n.Type.Results.List {
|
|
639
|
+
for _, name := range field.Names {
|
|
640
|
+
namedReturns = append(namedReturns, sanitizeIdentifier(name.Name))
|
|
542
641
|
}
|
|
543
642
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
v.analysis.NodeData[n].InAsyncContext = isAsync // Ensure FuncDecl node itself is marked
|
|
549
|
-
|
|
550
|
-
if n.Body != nil {
|
|
551
|
-
// Check if the body contains any defer statements
|
|
552
|
-
if v.containsDefer(n.Body) {
|
|
553
|
-
if v.analysis.NodeData[n] == nil {
|
|
554
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
555
|
-
}
|
|
556
|
-
v.analysis.NodeData[n].NeedsDefer = true
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Visit the body with updated state
|
|
560
|
-
ast.Walk(v, n.Body)
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// Restore states after visiting
|
|
564
|
-
defer func() {
|
|
565
|
-
v.currentFuncName = ""
|
|
566
|
-
v.inAsyncFunction = originalInAsync
|
|
567
|
-
v.currentReceiver = originalReceiver
|
|
568
|
-
v.currentFuncObj = originalFuncObj
|
|
569
|
-
v.currentFuncDecl = originalFuncDecl
|
|
570
|
-
v.currentFuncLit = originalFuncLit
|
|
571
|
-
}()
|
|
572
|
-
return nil // Stop traversal here, ast.Walk handled the body
|
|
573
|
-
|
|
574
|
-
case *ast.FuncLit:
|
|
575
|
-
// Save original inAsyncFunction state to restore after visiting
|
|
576
|
-
originalInAsync := v.inAsyncFunction
|
|
577
|
-
originalFuncDecl := v.currentFuncDecl
|
|
578
|
-
originalFuncLit := v.currentFuncLit
|
|
579
|
-
|
|
580
|
-
// Set current function literal
|
|
581
|
-
v.currentFuncDecl = nil
|
|
582
|
-
v.currentFuncLit = n
|
|
583
|
-
|
|
584
|
-
// Determine if this function literal is async based on its body
|
|
585
|
-
isAsync := v.containsAsyncOperations(n.Body)
|
|
586
|
-
if v.analysis.NodeData[n] == nil {
|
|
587
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
588
|
-
}
|
|
589
|
-
v.analysis.NodeData[n].InAsyncContext = isAsync
|
|
590
|
-
|
|
591
|
-
// Store named return variables for function literal
|
|
592
|
-
if n.Type != nil && n.Type.Results != nil {
|
|
593
|
-
var namedReturns []string
|
|
594
|
-
for _, field := range n.Type.Results.List {
|
|
595
|
-
for _, name := range field.Names {
|
|
596
|
-
namedReturns = append(namedReturns, name.Name)
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
if len(namedReturns) > 0 {
|
|
600
|
-
v.analysis.FuncLitData[n] = &FunctionInfo{
|
|
601
|
-
IsAsync: isAsync,
|
|
602
|
-
NamedReturns: namedReturns,
|
|
603
|
-
}
|
|
643
|
+
if len(namedReturns) > 0 {
|
|
644
|
+
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
645
|
+
funcInfo := v.analysis.ensureFunctionData(obj)
|
|
646
|
+
funcInfo.NamedReturns = namedReturns
|
|
604
647
|
}
|
|
605
648
|
}
|
|
649
|
+
}
|
|
606
650
|
|
|
607
|
-
|
|
651
|
+
// Update visitor state for this function
|
|
652
|
+
v.inAsyncFunction = isAsync
|
|
653
|
+
v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
|
|
608
654
|
|
|
655
|
+
if n.Body != nil {
|
|
609
656
|
// Check if the body contains any defer statements
|
|
610
|
-
if
|
|
611
|
-
|
|
612
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
613
|
-
}
|
|
614
|
-
v.analysis.NodeData[n].NeedsDefer = true
|
|
657
|
+
if v.containsDefer(n.Body) {
|
|
658
|
+
nodeInfo.NeedsDefer = true
|
|
615
659
|
}
|
|
616
660
|
|
|
617
661
|
// Visit the body with updated state
|
|
618
662
|
ast.Walk(v, n.Body)
|
|
663
|
+
}
|
|
619
664
|
|
|
620
|
-
|
|
665
|
+
// Restore states after visiting
|
|
666
|
+
defer func() {
|
|
667
|
+
v.currentFuncName = ""
|
|
621
668
|
v.inAsyncFunction = originalInAsync
|
|
669
|
+
v.currentReceiver = originalReceiver
|
|
670
|
+
v.currentFuncObj = originalFuncObj
|
|
622
671
|
v.currentFuncDecl = originalFuncDecl
|
|
623
672
|
v.currentFuncLit = originalFuncLit
|
|
624
|
-
|
|
673
|
+
}()
|
|
674
|
+
return nil // Stop traversal here, ast.Walk handled the body
|
|
675
|
+
}
|
|
625
676
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
677
|
+
// visitFuncLit handles function literal analysis
|
|
678
|
+
func (v *analysisVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor {
|
|
679
|
+
// Save original inAsyncFunction state to restore after visiting
|
|
680
|
+
originalInAsync := v.inAsyncFunction
|
|
681
|
+
originalFuncDecl := v.currentFuncDecl
|
|
682
|
+
originalFuncLit := v.currentFuncLit
|
|
683
|
+
|
|
684
|
+
// Set current function literal
|
|
685
|
+
v.currentFuncDecl = nil
|
|
686
|
+
v.currentFuncLit = n
|
|
687
|
+
|
|
688
|
+
// Determine if this function literal is async based on its body
|
|
689
|
+
isAsync := v.containsAsyncOperations(n.Body)
|
|
690
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
691
|
+
nodeInfo.InAsyncContext = isAsync
|
|
692
|
+
|
|
693
|
+
// Store named return variables for function literal
|
|
694
|
+
if n.Type != nil && n.Type.Results != nil {
|
|
695
|
+
var namedReturns []string
|
|
696
|
+
for _, field := range n.Type.Results.List {
|
|
697
|
+
for _, name := range field.Names {
|
|
698
|
+
namedReturns = append(namedReturns, name.Name)
|
|
699
|
+
}
|
|
629
700
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
701
|
+
if len(namedReturns) > 0 {
|
|
702
|
+
v.analysis.FuncLitData[n] = &FunctionInfo{
|
|
703
|
+
IsAsync: isAsync,
|
|
704
|
+
NamedReturns: namedReturns,
|
|
705
|
+
}
|
|
634
706
|
}
|
|
707
|
+
}
|
|
635
708
|
|
|
636
|
-
|
|
637
|
-
if v.containsDefer(n) {
|
|
638
|
-
v.analysis.NodeData[n].NeedsDefer = true
|
|
639
|
-
}
|
|
709
|
+
v.inAsyncFunction = isAsync
|
|
640
710
|
|
|
641
|
-
|
|
642
|
-
|
|
711
|
+
// Check if the body contains any defer statements
|
|
712
|
+
if n.Body != nil && v.containsDefer(n.Body) {
|
|
713
|
+
nodeInfo.NeedsDefer = true
|
|
714
|
+
}
|
|
643
715
|
|
|
644
|
-
|
|
716
|
+
// Visit the body with updated state
|
|
717
|
+
ast.Walk(v, n.Body)
|
|
645
718
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
719
|
+
// Restore inAsyncFunction state after visiting
|
|
720
|
+
v.inAsyncFunction = originalInAsync
|
|
721
|
+
v.currentFuncDecl = originalFuncDecl
|
|
722
|
+
v.currentFuncLit = originalFuncLit
|
|
723
|
+
return nil // Stop traversal here, ast.Walk handled the body
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// visitBlockStmt handles block statement analysis
|
|
727
|
+
func (v *analysisVisitor) visitBlockStmt(n *ast.BlockStmt) ast.Visitor {
|
|
728
|
+
if n == nil || len(n.List) == 0 {
|
|
652
729
|
return v
|
|
730
|
+
}
|
|
653
731
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
732
|
+
// Initialize NodeData for this block
|
|
733
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
734
|
+
|
|
735
|
+
// Check for defer statements in this block
|
|
736
|
+
if v.containsDefer(n) {
|
|
737
|
+
nodeInfo.NeedsDefer = true
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
return v
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// visitCallExpr handles call expression analysis
|
|
744
|
+
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
|
|
674
760
|
}
|
|
675
761
|
}
|
|
676
762
|
}
|
|
677
763
|
}
|
|
764
|
+
}
|
|
678
765
|
|
|
679
|
-
|
|
680
|
-
|
|
766
|
+
// Check for reflect function calls that operate on functions
|
|
767
|
+
v.checkReflectUsage(n)
|
|
681
768
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
769
|
+
return v
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// visitSelectorExpr handles selector expression analysis
|
|
773
|
+
func (v *analysisVisitor) visitSelectorExpr(n *ast.SelectorExpr) ast.Visitor {
|
|
774
|
+
// Check if this is a method value (method being used as a value, not called immediately)
|
|
775
|
+
if selection := v.pkg.TypesInfo.Selections[n]; selection != nil {
|
|
776
|
+
if selection.Kind() == types.MethodVal {
|
|
777
|
+
// This is a method value - mark it for binding during code generation
|
|
778
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
779
|
+
nodeInfo.IsMethodValue = true
|
|
685
780
|
}
|
|
686
|
-
|
|
781
|
+
}
|
|
782
|
+
return v
|
|
783
|
+
}
|
|
687
784
|
|
|
688
|
-
|
|
785
|
+
// visitAssignStmt handles assignment statement analysis
|
|
786
|
+
func (v *analysisVisitor) visitAssignStmt(n *ast.AssignStmt) ast.Visitor {
|
|
787
|
+
// Handle variable assignment tracking and generate shadowing information
|
|
788
|
+
shadowingInfo := v.detectVariableShadowing(n)
|
|
689
789
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
|
|
790
|
+
// Store shadowing information if needed for code generation
|
|
791
|
+
if shadowingInfo != nil {
|
|
792
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
793
|
+
nodeInfo.ShadowingInfo = shadowingInfo
|
|
794
|
+
}
|
|
696
795
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
v.analysis.NodeData[n].IsMethodValue = true
|
|
702
|
-
}
|
|
796
|
+
// Track assignment relationships for pointer analysis
|
|
797
|
+
for i, lhsExpr := range n.Lhs {
|
|
798
|
+
if i < len(n.Rhs) {
|
|
799
|
+
v.analyzeAssignment(lhsExpr, n.Rhs[i])
|
|
703
800
|
}
|
|
704
|
-
|
|
801
|
+
}
|
|
705
802
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
803
|
+
// Track interface implementations for assignments to interface variables
|
|
804
|
+
v.trackInterfaceAssignments(n)
|
|
805
|
+
|
|
806
|
+
// Track function assignments (function literals assigned to variables)
|
|
807
|
+
if len(n.Lhs) == 1 && len(n.Rhs) == 1 {
|
|
808
|
+
if lhsIdent, ok := n.Lhs[0].(*ast.Ident); ok {
|
|
809
|
+
if rhsFuncLit, ok := n.Rhs[0].(*ast.FuncLit); ok {
|
|
810
|
+
// Get the object for the LHS variable
|
|
811
|
+
if obj := v.pkg.TypesInfo.ObjectOf(lhsIdent); obj != nil {
|
|
812
|
+
v.analysis.FunctionAssignments[obj] = rhsFuncLit
|
|
714
813
|
}
|
|
715
|
-
v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
|
|
716
814
|
}
|
|
717
815
|
}
|
|
816
|
+
}
|
|
718
817
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
if i >= len(n.Rhs) {
|
|
722
|
-
break // Should not happen in valid Go
|
|
723
|
-
}
|
|
724
|
-
currentRHSExpr := n.Rhs[i]
|
|
725
|
-
|
|
726
|
-
// --- Analyze RHS to determine assignment type and source object (if any) ---
|
|
727
|
-
rhsAssignmentType := DirectAssignment
|
|
728
|
-
var rhsSourceObj types.Object // The variable object on the RHS (e.g., 'y' in x = y or x = &y)
|
|
729
|
-
|
|
730
|
-
if unaryExpr, ok := currentRHSExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
|
|
731
|
-
// RHS is &some_expr
|
|
732
|
-
rhsAssignmentType = AddressOfAssignment
|
|
733
|
-
if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
|
|
734
|
-
// RHS is &variable
|
|
735
|
-
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
736
|
-
}
|
|
737
|
-
// If RHS is &structLit{} or &array[0], rhsSourceObj remains nil.
|
|
738
|
-
// _, ok := unaryExpr.X.(*ast.CompositeLit); ok
|
|
739
|
-
} else if rhsIdent, ok := currentRHSExpr.(*ast.Ident); ok {
|
|
740
|
-
// RHS is variable
|
|
741
|
-
rhsAssignmentType = DirectAssignment
|
|
742
|
-
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
743
|
-
}
|
|
744
|
-
// If RHS is a literal, function call, etc., rhsSourceObj remains nil.
|
|
818
|
+
return v
|
|
819
|
+
}
|
|
745
820
|
|
|
746
|
-
|
|
747
|
-
|
|
821
|
+
// visitReturnStmt handles return statement analysis
|
|
822
|
+
func (v *analysisVisitor) visitReturnStmt(n *ast.ReturnStmt) ast.Visitor {
|
|
823
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
748
824
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
825
|
+
// Record the enclosing function/literal for this return statement
|
|
826
|
+
if v.currentFuncDecl != nil {
|
|
827
|
+
nodeInfo.EnclosingFuncDecl = v.currentFuncDecl
|
|
828
|
+
} else if v.currentFuncLit != nil {
|
|
829
|
+
nodeInfo.EnclosingFuncLit = v.currentFuncLit
|
|
830
|
+
}
|
|
754
831
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
lhsTrackedObj = selection.Obj() // This is the field or selected var object
|
|
763
|
-
}
|
|
764
|
-
} /* else if _, ok := currentLHSExpr.(*ast.StarExpr); ok {
|
|
765
|
-
// LHS is *pointer.
|
|
766
|
-
// We don't try to get a types.Object for the dereferenced entity itself to store in VariableUsage.
|
|
767
|
-
// lhsTrackedObj remains nil. The effect on rhsSourceObj (if its address is taken) is handled below.
|
|
768
|
-
} */
|
|
769
|
-
// For other complex LHS (e.g., map_expr[key_expr]), lhsTrackedObj remains nil.
|
|
770
|
-
|
|
771
|
-
// --- Record Usage Information ---
|
|
772
|
-
|
|
773
|
-
// 1. If LHS is a trackable variable/field, record what's assigned to it (its sources).
|
|
774
|
-
// We only want to create VariableUsage entries for actual variables/fields.
|
|
775
|
-
if _, isVar := lhsTrackedObj.(*types.Var); isVar {
|
|
776
|
-
lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
|
|
777
|
-
if rhsSourceObj != nil {
|
|
778
|
-
// Case: var1 = var2 OR var1 = &var2 OR field1 = var2 OR field1 = &var2
|
|
779
|
-
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
780
|
-
Object: rhsSourceObj,
|
|
781
|
-
Type: rhsAssignmentType,
|
|
782
|
-
})
|
|
783
|
-
} else if rhsAssignmentType == AddressOfAssignment {
|
|
784
|
-
// Case: var1 = &non_ident_expr (e.g., &T{}) OR field1 = &non_ident_expr
|
|
785
|
-
// lhsTrackedObj is assigned an address, but not of a named variable.
|
|
786
|
-
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
787
|
-
Object: nil, // No specific source variable object
|
|
788
|
-
Type: rhsAssignmentType,
|
|
789
|
-
})
|
|
832
|
+
// Check if it's a bare return
|
|
833
|
+
if len(n.Results) == 0 {
|
|
834
|
+
if v.currentFuncDecl != nil {
|
|
835
|
+
// Check if the enclosing function declaration has named returns
|
|
836
|
+
if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
|
|
837
|
+
if _, ok := v.analysis.FunctionData[obj]; ok {
|
|
838
|
+
nodeInfo.IsBareReturn = true
|
|
790
839
|
}
|
|
791
|
-
// If rhsSourceObj is nil and rhsAssignmentType is DirectAssignment (e.g. var1 = 10),
|
|
792
|
-
// no source object to record for LHS sources.
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
// 2. If RHS involved a source variable (rhsSourceObj is not nil),
|
|
796
|
-
// record that this source variable was used (its destinations).
|
|
797
|
-
// This is CRITICAL for varRefing analysis (e.g., if &rhsSourceObj was assigned).
|
|
798
|
-
if rhsSourceObj != nil {
|
|
799
|
-
sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
|
|
800
|
-
// The 'Object' in DestinationInfo is what/where rhsSourceObj (or its address) was assigned TO.
|
|
801
|
-
// This can be lhsTrackedObj (if LHS was an ident or field).
|
|
802
|
-
// If LHS was complex (e.g., *ptr, map[k]), lhsTrackedObj might be nil for that DestinationInfo.Object.
|
|
803
|
-
// Even if lhsTrackedObj is nil for the DestinationInfo.Object, if rhsAssignmentType is AddressOfAssignment,
|
|
804
|
-
// it's important to record that rhsSourceObj's address was taken.
|
|
805
|
-
sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
|
|
806
|
-
Object: lhsTrackedObj, // This can be nil if LHS is complex (*p, map[k])
|
|
807
|
-
Type: rhsAssignmentType,
|
|
808
|
-
})
|
|
809
840
|
}
|
|
810
|
-
}
|
|
811
|
-
return v // Continue traversal
|
|
812
|
-
|
|
813
|
-
case *ast.ReturnStmt:
|
|
814
|
-
// Initialize NodeData for return statement
|
|
815
|
-
if v.analysis.NodeData[n] == nil {
|
|
816
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Record the enclosing function/literal for this return statement
|
|
820
|
-
if v.currentFuncDecl != nil {
|
|
821
|
-
v.analysis.NodeData[n].EnclosingFuncDecl = v.currentFuncDecl
|
|
822
841
|
} else if v.currentFuncLit != nil {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
// Check if it's a bare return
|
|
827
|
-
if len(n.Results) == 0 {
|
|
828
|
-
if v.currentFuncDecl != nil {
|
|
829
|
-
// Check if the enclosing function declaration has named returns
|
|
830
|
-
if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
|
|
831
|
-
if _, ok := v.analysis.FunctionData[obj]; ok {
|
|
832
|
-
if v.analysis.NodeData[n] == nil {
|
|
833
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
834
|
-
}
|
|
835
|
-
v.analysis.NodeData[n].IsBareReturn = true
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
} else if v.currentFuncLit != nil {
|
|
839
|
-
// Check if the enclosing function literal has named returns
|
|
840
|
-
if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
|
|
841
|
-
if v.analysis.NodeData[n] == nil {
|
|
842
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
843
|
-
}
|
|
844
|
-
v.analysis.NodeData[n].IsBareReturn = true
|
|
845
|
-
}
|
|
842
|
+
// Check if the enclosing function literal has named returns
|
|
843
|
+
if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
|
|
844
|
+
nodeInfo.IsBareReturn = true
|
|
846
845
|
}
|
|
847
846
|
}
|
|
848
|
-
|
|
847
|
+
}
|
|
848
|
+
return v
|
|
849
|
+
}
|
|
849
850
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
v.analysis.NodeData[spec].IsInsideFunction = true
|
|
864
|
-
}
|
|
851
|
+
// visitDeclStmt handles declaration statement analysis
|
|
852
|
+
func (v *analysisVisitor) visitDeclStmt(n *ast.DeclStmt) ast.Visitor {
|
|
853
|
+
// Handle declarations inside functions (const, var, type declarations within function bodies)
|
|
854
|
+
// These should not have export modifiers in TypeScript
|
|
855
|
+
if genDecl, ok := n.Decl.(*ast.GenDecl); ok {
|
|
856
|
+
// Check if we're inside a function (either FuncDecl or FuncLit)
|
|
857
|
+
isInsideFunction := v.currentFuncDecl != nil || v.currentFuncLit != nil
|
|
858
|
+
|
|
859
|
+
if isInsideFunction {
|
|
860
|
+
// Mark all specs in this declaration as being inside a function
|
|
861
|
+
for _, spec := range genDecl.Specs {
|
|
862
|
+
nodeInfo := v.analysis.ensureNodeData(spec)
|
|
863
|
+
nodeInfo.IsInsideFunction = true
|
|
865
864
|
}
|
|
866
865
|
}
|
|
867
|
-
|
|
866
|
+
}
|
|
867
|
+
return v
|
|
868
|
+
}
|
|
868
869
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
|
|
880
|
-
}
|
|
870
|
+
// visitIfStmt handles if statement analysis
|
|
871
|
+
func (v *analysisVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
|
|
872
|
+
// Detect variable shadowing in if statement initializations
|
|
873
|
+
if n.Init != nil {
|
|
874
|
+
if assignStmt, ok := n.Init.(*ast.AssignStmt); ok && assignStmt.Tok == token.DEFINE {
|
|
875
|
+
shadowingInfo := v.detectVariableShadowing(assignStmt)
|
|
876
|
+
if shadowingInfo != nil {
|
|
877
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
878
|
+
nodeInfo.ShadowingInfo = shadowingInfo
|
|
881
879
|
}
|
|
882
880
|
}
|
|
883
|
-
return v // Continue traversal
|
|
884
881
|
}
|
|
882
|
+
return v
|
|
883
|
+
}
|
|
885
884
|
|
|
886
|
-
|
|
885
|
+
// visitTypeAssertExpr handles type assertion expression analysis
|
|
886
|
+
func (v *analysisVisitor) visitTypeAssertExpr(n *ast.TypeAssertExpr) ast.Visitor {
|
|
887
|
+
// Track interface implementations when we see type assertions
|
|
888
|
+
v.trackTypeAssertion(n)
|
|
887
889
|
return v
|
|
888
890
|
}
|
|
889
891
|
|
|
@@ -933,11 +935,11 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
|
|
|
933
935
|
|
|
934
936
|
// Check if the type is from an imported package
|
|
935
937
|
if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
|
|
936
|
-
// Use the
|
|
937
|
-
|
|
938
|
+
// Use the full package path from the type information (not just the package name)
|
|
939
|
+
pkgPath := typePkg.Path()
|
|
938
940
|
|
|
939
941
|
// Check if this method is async based on metadata
|
|
940
|
-
if v.analysis.IsMethodAsync(
|
|
942
|
+
if v.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
|
|
941
943
|
hasAsync = true
|
|
942
944
|
return false
|
|
943
945
|
}
|
|
@@ -1043,99 +1045,113 @@ func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, recei
|
|
|
1043
1045
|
return false
|
|
1044
1046
|
}
|
|
1045
1047
|
|
|
1046
|
-
//
|
|
1047
|
-
// that will be used during code generation to properly handle pointers,
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
analysis
|
|
1048
|
+
// AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
|
|
1049
|
+
// with information that will be used during code generation to properly handle pointers,
|
|
1050
|
+
// variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
|
|
1051
|
+
func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages.Package) *Analysis {
|
|
1052
|
+
analysis := NewAnalysis(allPackages)
|
|
1051
1053
|
|
|
1052
1054
|
// Load package metadata for async function detection
|
|
1053
1055
|
analysis.LoadPackageMetadata()
|
|
1054
1056
|
|
|
1055
|
-
// Process imports from the
|
|
1056
|
-
for _,
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1057
|
+
// Process imports from all files in the package
|
|
1058
|
+
for _, file := range pkg.Syntax {
|
|
1059
|
+
// Create comment map for each file and store it (we'll merge them if needed)
|
|
1060
|
+
cmap := ast.NewCommentMap(pkg.Fset, file, file.Comments)
|
|
1061
|
+
if len(analysis.Cmap) == 0 {
|
|
1062
|
+
analysis.Cmap = cmap
|
|
1063
|
+
} else {
|
|
1064
|
+
// Merge comment maps from multiple files
|
|
1065
|
+
for node, comments := range cmap {
|
|
1066
|
+
analysis.Cmap[node] = append(analysis.Cmap[node], comments...)
|
|
1067
|
+
}
|
|
1062
1068
|
}
|
|
1063
1069
|
|
|
1064
|
-
//
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
if imp.
|
|
1068
|
-
|
|
1070
|
+
// Process imports from this file
|
|
1071
|
+
for _, imp := range file.Imports {
|
|
1072
|
+
path := ""
|
|
1073
|
+
if imp.Path != nil {
|
|
1074
|
+
path = imp.Path.Value
|
|
1075
|
+
// Remove quotes from the import path string
|
|
1076
|
+
path = path[1 : len(path)-1]
|
|
1069
1077
|
}
|
|
1070
1078
|
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1079
|
+
// Store the import in the analysis
|
|
1080
|
+
if path != "" {
|
|
1081
|
+
name := ""
|
|
1082
|
+
if imp.Name != nil {
|
|
1083
|
+
name = imp.Name.Name
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
fileImp := &fileImport{
|
|
1087
|
+
importPath: path,
|
|
1088
|
+
importVars: make(map[string]struct{}),
|
|
1089
|
+
}
|
|
1075
1090
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
} else {
|
|
1082
|
-
// No explicit alias, use the actual package name from type information
|
|
1083
|
-
// This handles cases where package name differs from the last path segment
|
|
1084
|
-
if actualName, err := getActualPackageName(path, pkg.Imports); err == nil {
|
|
1085
|
-
key = actualName
|
|
1091
|
+
// Use the import name or the actual package name as the key
|
|
1092
|
+
var key string
|
|
1093
|
+
if name != "" {
|
|
1094
|
+
// Explicit alias provided
|
|
1095
|
+
key = name
|
|
1086
1096
|
} else {
|
|
1087
|
-
//
|
|
1088
|
-
|
|
1089
|
-
|
|
1097
|
+
// No explicit alias, use the actual package name from type information
|
|
1098
|
+
// This handles cases where package name differs from the last path segment
|
|
1099
|
+
if actualName, err := getActualPackageName(path, pkg.Imports); err == nil {
|
|
1100
|
+
key = actualName
|
|
1101
|
+
} else {
|
|
1102
|
+
// Fallback to last segment of path if package not found in type information
|
|
1103
|
+
pts := strings.Split(path, "/")
|
|
1104
|
+
key = pts[len(pts)-1]
|
|
1105
|
+
}
|
|
1090
1106
|
}
|
|
1091
|
-
}
|
|
1092
1107
|
|
|
1093
|
-
|
|
1108
|
+
analysis.Imports[key] = fileImp
|
|
1109
|
+
}
|
|
1094
1110
|
}
|
|
1095
1111
|
}
|
|
1096
1112
|
|
|
1097
|
-
// Create
|
|
1113
|
+
// Create visitor for the entire package
|
|
1098
1114
|
visitor := &analysisVisitor{
|
|
1099
|
-
analysis:
|
|
1100
|
-
pkg:
|
|
1101
|
-
inAsyncFunction: false,
|
|
1102
|
-
currentReceiver: nil, // Initialize currentReceiver
|
|
1115
|
+
analysis: analysis,
|
|
1116
|
+
pkg: pkg,
|
|
1103
1117
|
}
|
|
1104
1118
|
|
|
1105
|
-
//
|
|
1106
|
-
|
|
1119
|
+
// First pass: analyze all declarations and statements across all files
|
|
1120
|
+
for _, file := range pkg.Syntax {
|
|
1121
|
+
ast.Walk(visitor, file)
|
|
1122
|
+
}
|
|
1107
1123
|
|
|
1108
1124
|
// Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
|
|
1109
1125
|
// This distinguishes between method calls (obj.Method()) and method values (obj.Method)
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
if
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
nodeInfo.
|
|
1126
|
+
for _, file := range pkg.Syntax {
|
|
1127
|
+
ast.Inspect(file, func(n ast.Node) bool {
|
|
1128
|
+
if callExpr, ok := n.(*ast.CallExpr); ok {
|
|
1129
|
+
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
|
1130
|
+
// This SelectorExpr is the function being called, so it's NOT a method value
|
|
1131
|
+
if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
|
|
1132
|
+
nodeInfo.IsMethodValue = false
|
|
1133
|
+
}
|
|
1116
1134
|
}
|
|
1117
1135
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1136
|
+
return true
|
|
1137
|
+
})
|
|
1138
|
+
}
|
|
1122
1139
|
|
|
1123
|
-
//
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
for _, field := range funcDecl.Type.Results.List {
|
|
1128
|
-
for _, name := range field.Names {
|
|
1129
|
-
namedReturns = append(namedReturns, name.Name)
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1140
|
+
// Second pass: analyze interface implementations now that all function async status is determined
|
|
1141
|
+
interfaceVisitor := &interfaceImplementationVisitor{
|
|
1142
|
+
analysis: analysis,
|
|
1143
|
+
pkg: pkg,
|
|
1132
1144
|
}
|
|
1133
|
-
|
|
1145
|
+
for _, file := range pkg.Syntax {
|
|
1146
|
+
ast.Walk(interfaceVisitor, file)
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
return analysis
|
|
1134
1150
|
}
|
|
1135
1151
|
|
|
1136
|
-
//
|
|
1152
|
+
// AnalyzePackageImports performs package-level analysis to collect function definitions
|
|
1137
1153
|
// and calls across all files in the package for auto-import generation
|
|
1138
|
-
func
|
|
1154
|
+
func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
1139
1155
|
analysis := NewPackageAnalysis()
|
|
1140
1156
|
|
|
1141
1157
|
// First pass: collect all function definitions per file
|
|
@@ -1299,92 +1315,202 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
|
|
|
1299
1315
|
return analysis
|
|
1300
1316
|
}
|
|
1301
1317
|
|
|
1302
|
-
// LoadPackageMetadata loads metadata from gs packages
|
|
1318
|
+
// LoadPackageMetadata loads metadata from gs packages using embedded JSON files
|
|
1303
1319
|
func (a *Analysis) LoadPackageMetadata() {
|
|
1304
|
-
//
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1320
|
+
// Discover all packages in the embedded gs/ directory
|
|
1321
|
+
packagePaths := a.discoverEmbeddedGsPackages()
|
|
1322
|
+
|
|
1323
|
+
for _, pkgPath := range packagePaths {
|
|
1324
|
+
metaFilePath := filepath.Join("gs", pkgPath, "meta.json")
|
|
1325
|
+
|
|
1326
|
+
// Try to read the meta.json file from embedded filesystem
|
|
1327
|
+
// We need access to the embedded FS, which should be imported from the parent package
|
|
1328
|
+
if metadata := a.loadGsMetadata(metaFilePath); metadata != nil {
|
|
1329
|
+
// Store async method information
|
|
1330
|
+
for methodKey, isAsync := range metadata.AsyncMethods {
|
|
1331
|
+
// Convert "Type.Method" format to our internal key format
|
|
1332
|
+
parts := strings.Split(methodKey, ".")
|
|
1333
|
+
if len(parts) == 2 {
|
|
1334
|
+
typeName := parts[0]
|
|
1335
|
+
methodName := parts[1]
|
|
1336
|
+
// The key format is "pkgName.TypeNameMethodNameInfo"
|
|
1337
|
+
key := PackageMetadataKey{
|
|
1338
|
+
PackagePath: pkgPath,
|
|
1339
|
+
TypeName: typeName,
|
|
1340
|
+
MethodName: methodName,
|
|
1341
|
+
}
|
|
1309
1342
|
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1343
|
+
// Store the async value directly in PackageMetadata
|
|
1344
|
+
a.PackageMetadata[key] = MethodMetadata{
|
|
1345
|
+
IsAsync: isAsync,
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1313
1349
|
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1314
1352
|
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1353
|
+
// discoverEmbeddedGsPackages finds all packages in the embedded gs/ directory
|
|
1354
|
+
func (a *Analysis) discoverEmbeddedGsPackages() []string {
|
|
1355
|
+
var packageList []string
|
|
1319
1356
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1357
|
+
// Read the gs/ directory from the embedded filesystem
|
|
1358
|
+
entries, err := goscript.GsOverrides.ReadDir("gs")
|
|
1359
|
+
if err != nil {
|
|
1360
|
+
// If we can't read the gs/ directory, return empty list
|
|
1361
|
+
return packageList
|
|
1362
|
+
}
|
|
1324
1363
|
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1364
|
+
// Iterate through all entries in gs/
|
|
1365
|
+
for _, entry := range entries {
|
|
1366
|
+
if entry.IsDir() {
|
|
1367
|
+
packageName := entry.Name()
|
|
1328
1368
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
for _, name := range scope.Names() {
|
|
1332
|
-
obj := scope.Lookup(name)
|
|
1333
|
-
if obj == nil {
|
|
1369
|
+
// Skip special directories like github.com
|
|
1370
|
+
if strings.Contains(packageName, ".") {
|
|
1334
1371
|
continue
|
|
1335
1372
|
}
|
|
1336
1373
|
|
|
1337
|
-
|
|
1338
|
-
if strings.HasSuffix(name, "Info") {
|
|
1339
|
-
if varObj, ok := obj.(*types.Var); ok {
|
|
1340
|
-
// Store the metadata with a key like "sync.MutexLock"
|
|
1341
|
-
methodName := strings.TrimSuffix(name, "Info")
|
|
1342
|
-
key := pkgName + "." + methodName
|
|
1343
|
-
a.PackageMetadata[key] = varObj
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1374
|
+
packageList = append(packageList, packageName)
|
|
1346
1375
|
}
|
|
1347
1376
|
}
|
|
1377
|
+
|
|
1378
|
+
return packageList
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// loadGsMetadata loads metadata from a meta.json file in the embedded filesystem
|
|
1382
|
+
func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
|
|
1383
|
+
// Read the meta.json file from the embedded filesystem
|
|
1384
|
+
content, err := goscript.GsOverrides.ReadFile(metaFilePath)
|
|
1385
|
+
if err != nil {
|
|
1386
|
+
return nil // No metadata file found
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
var metadata GsMetadata
|
|
1390
|
+
if err := json.Unmarshal(content, &metadata); err != nil {
|
|
1391
|
+
return nil // Invalid JSON
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
return &metadata
|
|
1348
1395
|
}
|
|
1349
1396
|
|
|
1350
1397
|
// IsMethodAsync checks if a method call is async based on package metadata
|
|
1351
|
-
func (a *Analysis) IsMethodAsync(
|
|
1352
|
-
//
|
|
1353
|
-
//
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1398
|
+
func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
|
|
1399
|
+
// First, check existing metadata for internal packages
|
|
1400
|
+
// The metadata keys are stored as "pkgName.TypeNameMethodNameInfo"
|
|
1401
|
+
// e.g., "sync.MutexLockInfo", "sync.WaitGroupWaitInfo", etc.
|
|
1402
|
+
key := PackageMetadataKey{
|
|
1403
|
+
PackagePath: pkgPath,
|
|
1404
|
+
TypeName: typeName,
|
|
1405
|
+
MethodName: methodName,
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
if metadata, exists := a.PackageMetadata[key]; exists {
|
|
1409
|
+
return metadata.IsAsync
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// If no metadata found, check if we can analyze the method from dependency packages
|
|
1413
|
+
return a.analyzeExternalMethodAsync(pkgPath, typeName, methodName)
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// analyzeExternalMethodAsync analyzes external package methods to determine if they're async
|
|
1417
|
+
// using the same logic we use for internal methods
|
|
1418
|
+
func (a *Analysis) analyzeExternalMethodAsync(pkgPath, typeName, methodName string) bool {
|
|
1419
|
+
// Look up the package in our loaded packages
|
|
1420
|
+
pkg, exists := a.AllPackages[pkgPath]
|
|
1421
|
+
if !exists {
|
|
1422
|
+
return false
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// Find the method definition in the package
|
|
1426
|
+
for _, syntax := range pkg.Syntax {
|
|
1427
|
+
for _, decl := range syntax.Decls {
|
|
1428
|
+
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
1429
|
+
// Check if this is a method with the right name and receiver type
|
|
1430
|
+
if funcDecl.Name.Name == methodName && funcDecl.Recv != nil {
|
|
1431
|
+
// Check the receiver type
|
|
1432
|
+
if len(funcDecl.Recv.List) > 0 {
|
|
1433
|
+
receiverType := funcDecl.Recv.List[0].Type
|
|
1434
|
+
|
|
1435
|
+
// Handle pointer receivers
|
|
1436
|
+
if starExpr, ok := receiverType.(*ast.StarExpr); ok {
|
|
1437
|
+
receiverType = starExpr.X
|
|
1438
|
+
}
|
|
1379
1439
|
|
|
1380
|
-
|
|
1381
|
-
|
|
1440
|
+
if ident, ok := receiverType.(*ast.Ident); ok {
|
|
1441
|
+
if ident.Name == typeName {
|
|
1442
|
+
// Found the method! Now check if it's async
|
|
1443
|
+
if funcDecl.Body != nil {
|
|
1444
|
+
return a.containsAsyncOperations(funcDecl.Body, pkg)
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1382
1451
|
}
|
|
1383
1452
|
}
|
|
1384
1453
|
|
|
1385
1454
|
return false
|
|
1386
1455
|
}
|
|
1387
1456
|
|
|
1457
|
+
// containsAsyncOperations checks if a node contains any async operations for a specific package
|
|
1458
|
+
func (a *Analysis) containsAsyncOperations(node ast.Node, pkg *packages.Package) bool {
|
|
1459
|
+
var hasAsync bool
|
|
1460
|
+
|
|
1461
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
1462
|
+
if n == nil {
|
|
1463
|
+
return false
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
switch s := n.(type) {
|
|
1467
|
+
case *ast.SendStmt:
|
|
1468
|
+
// Channel send operation (ch <- value)
|
|
1469
|
+
hasAsync = true
|
|
1470
|
+
return false
|
|
1471
|
+
|
|
1472
|
+
case *ast.UnaryExpr:
|
|
1473
|
+
// Channel receive operation (<-ch)
|
|
1474
|
+
if s.Op == token.ARROW {
|
|
1475
|
+
hasAsync = true
|
|
1476
|
+
return false
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
case *ast.CallExpr:
|
|
1480
|
+
// Check for select statements and other async operations
|
|
1481
|
+
if ident, ok := s.Fun.(*ast.Ident); ok {
|
|
1482
|
+
// Look for calls to select-related functions or other async operations
|
|
1483
|
+
if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
1484
|
+
// Check if this function is from the context package (like context.Background, etc.)
|
|
1485
|
+
if obj.Pkg() != nil && obj.Pkg().Path() == "context" {
|
|
1486
|
+
hasAsync = true
|
|
1487
|
+
return false
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
// Check if the method takes a context parameter, which usually indicates async behavior
|
|
1493
|
+
if len(s.Args) > 0 {
|
|
1494
|
+
for _, arg := range s.Args {
|
|
1495
|
+
if argType := pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
1496
|
+
if named, ok := argType.(*types.Named); ok {
|
|
1497
|
+
if named.Obj() != nil && named.Obj().Pkg() != nil &&
|
|
1498
|
+
named.Obj().Pkg().Path() == "context" && named.Obj().Name() == "Context" {
|
|
1499
|
+
hasAsync = true
|
|
1500
|
+
return false
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
return true
|
|
1509
|
+
})
|
|
1510
|
+
|
|
1511
|
+
return hasAsync
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1388
1514
|
// NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
|
|
1389
1515
|
func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
|
|
1390
1516
|
if node == nil {
|
|
@@ -1581,3 +1707,484 @@ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map
|
|
|
1581
1707
|
// For now, we'll ignore them as they're less common in shadowing scenarios
|
|
1582
1708
|
}
|
|
1583
1709
|
}
|
|
1710
|
+
|
|
1711
|
+
// trackInterfaceImplementation records that a struct type implements an interface method
|
|
1712
|
+
func (a *Analysis) trackInterfaceImplementation(interfaceType *types.Interface, structType *types.Named, method *types.Func, isAsync bool) {
|
|
1713
|
+
key := InterfaceMethodKey{
|
|
1714
|
+
InterfaceType: interfaceType.String(),
|
|
1715
|
+
MethodName: method.Name(),
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
implementation := ImplementationInfo{
|
|
1719
|
+
StructType: structType,
|
|
1720
|
+
Method: method,
|
|
1721
|
+
IsAsyncByFlow: isAsync,
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
a.InterfaceImplementations[key] = append(a.InterfaceImplementations[key], implementation)
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// IsInterfaceMethodAsync determines if an interface method should be async based on its implementations
|
|
1728
|
+
func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, methodName string) bool {
|
|
1729
|
+
key := InterfaceMethodKey{
|
|
1730
|
+
InterfaceType: interfaceType.String(),
|
|
1731
|
+
MethodName: methodName,
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// Check if we've already computed this
|
|
1735
|
+
if result, exists := a.InterfaceMethodAsyncStatus[key]; exists {
|
|
1736
|
+
return result
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
// Find all implementations of this interface method
|
|
1740
|
+
implementations, exists := a.InterfaceImplementations[key]
|
|
1741
|
+
if !exists {
|
|
1742
|
+
// No implementations found, default to sync
|
|
1743
|
+
a.InterfaceMethodAsyncStatus[key] = false
|
|
1744
|
+
return false
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
// If ANY implementation is async, the interface method is async
|
|
1748
|
+
for _, impl := range implementations {
|
|
1749
|
+
if impl.IsAsyncByFlow {
|
|
1750
|
+
a.InterfaceMethodAsyncStatus[key] = true
|
|
1751
|
+
return true
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
// All implementations are sync
|
|
1756
|
+
a.InterfaceMethodAsyncStatus[key] = false
|
|
1757
|
+
return false
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// MustBeAsyncDueToInterface checks if a struct method must be async due to interface constraints
|
|
1761
|
+
func (a *Analysis) MustBeAsyncDueToInterface(structType *types.Named, methodName string) bool {
|
|
1762
|
+
// Find all interfaces that this struct implements
|
|
1763
|
+
for key, implementations := range a.InterfaceImplementations {
|
|
1764
|
+
if key.MethodName != methodName {
|
|
1765
|
+
continue
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
// Check if this struct is among the implementations
|
|
1769
|
+
for _, impl := range implementations {
|
|
1770
|
+
if impl.StructType == structType {
|
|
1771
|
+
// This struct implements this interface method
|
|
1772
|
+
// Check if the interface method is marked as async
|
|
1773
|
+
interfaceType := a.findInterfaceTypeByString(key.InterfaceType)
|
|
1774
|
+
if interfaceType != nil && a.IsInterfaceMethodAsync(interfaceType, methodName) {
|
|
1775
|
+
return true
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
return false
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
// findInterfaceTypeByString finds an interface type by its string representation
|
|
1785
|
+
// This is a helper method for MustBeAsyncDueToInterface
|
|
1786
|
+
func (a *Analysis) findInterfaceTypeByString(interfaceString string) *types.Interface {
|
|
1787
|
+
// This is a simplified implementation - in practice, we might need to store
|
|
1788
|
+
// the actual interface types in our tracking data structure
|
|
1789
|
+
for _, pkg := range a.AllPackages {
|
|
1790
|
+
for _, syntax := range pkg.Syntax {
|
|
1791
|
+
for _, decl := range syntax.Decls {
|
|
1792
|
+
if genDecl, ok := decl.(*ast.GenDecl); ok {
|
|
1793
|
+
for _, spec := range genDecl.Specs {
|
|
1794
|
+
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
1795
|
+
if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
|
|
1796
|
+
if goType := pkg.TypesInfo.TypeOf(interfaceType); goType != nil {
|
|
1797
|
+
if iface, ok := goType.(*types.Interface); ok {
|
|
1798
|
+
if iface.String() == interfaceString {
|
|
1799
|
+
return iface
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
return nil
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// GetReceiverMapping returns the receiver variable mapping for a function declaration
|
|
1814
|
+
func (a *Analysis) GetReceiverMapping(funcDecl *ast.FuncDecl) string {
|
|
1815
|
+
if funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
|
|
1816
|
+
for _, field := range funcDecl.Recv.List {
|
|
1817
|
+
for _, name := range field.Names {
|
|
1818
|
+
if name != nil && name.Name != "_" {
|
|
1819
|
+
return "receiver"
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
return ""
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
// GetIdentifierMapping returns the replacement name for an identifier
|
|
1828
|
+
func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
|
|
1829
|
+
if ident == nil {
|
|
1830
|
+
return ""
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
// Check if this identifier has a mapping in NodeData
|
|
1834
|
+
if nodeInfo := a.NodeData[ident]; nodeInfo != nil {
|
|
1835
|
+
return nodeInfo.IdentifierMapping
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
return ""
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// trackTypeAssertion analyzes type assertions and records interface implementations
|
|
1842
|
+
func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
|
|
1843
|
+
// Get the type being asserted to
|
|
1844
|
+
assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
|
|
1845
|
+
if assertedType == nil {
|
|
1846
|
+
return
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
// Check if the asserted type is an interface
|
|
1850
|
+
interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
|
|
1851
|
+
if !isInterface {
|
|
1852
|
+
return
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
// Get the type of the expression being asserted
|
|
1856
|
+
exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
|
|
1857
|
+
if exprType == nil {
|
|
1858
|
+
return
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
// Handle pointer types by getting the element type
|
|
1862
|
+
if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
|
|
1863
|
+
exprType = ptrType.Elem()
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// Check if the expression type is a named struct type
|
|
1867
|
+
namedType, isNamed := exprType.(*types.Named)
|
|
1868
|
+
if !isNamed {
|
|
1869
|
+
return
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// For each method in the interface, check if the struct implements it
|
|
1873
|
+
for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
|
|
1874
|
+
interfaceMethod := interfaceType.ExplicitMethod(i)
|
|
1875
|
+
|
|
1876
|
+
// Find the corresponding method in the struct type
|
|
1877
|
+
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1878
|
+
if structMethod != nil {
|
|
1879
|
+
// Determine if this struct method is async based on control flow analysis
|
|
1880
|
+
isAsync := false
|
|
1881
|
+
if obj := structMethod; obj != nil {
|
|
1882
|
+
if funcInfo := v.analysis.FunctionData[obj]; funcInfo != nil {
|
|
1883
|
+
isAsync = funcInfo.IsAsync
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
// Track this interface implementation
|
|
1888
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// findStructMethod finds a method with the given name on a named type
|
|
1894
|
+
func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
|
|
1895
|
+
// Check methods directly on the type
|
|
1896
|
+
for i := 0; i < namedType.NumMethods(); i++ {
|
|
1897
|
+
method := namedType.Method(i)
|
|
1898
|
+
if method.Name() == methodName {
|
|
1899
|
+
return method
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
return nil
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// analyzeAssignment analyzes a single assignment for pointer analysis
|
|
1906
|
+
func (v *analysisVisitor) analyzeAssignment(lhsExpr, rhsExpr ast.Expr) {
|
|
1907
|
+
// Determine RHS assignment type and source object
|
|
1908
|
+
rhsAssignmentType := DirectAssignment
|
|
1909
|
+
var rhsSourceObj types.Object
|
|
1910
|
+
|
|
1911
|
+
if unaryExpr, ok := rhsExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
|
|
1912
|
+
// RHS is &some_expr
|
|
1913
|
+
rhsAssignmentType = AddressOfAssignment
|
|
1914
|
+
if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
|
|
1915
|
+
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
1916
|
+
}
|
|
1917
|
+
} else if rhsIdent, ok := rhsExpr.(*ast.Ident); ok {
|
|
1918
|
+
// RHS is variable
|
|
1919
|
+
rhsAssignmentType = DirectAssignment
|
|
1920
|
+
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// Determine LHS object
|
|
1924
|
+
var lhsTrackedObj types.Object
|
|
1925
|
+
|
|
1926
|
+
if lhsIdent, ok := lhsExpr.(*ast.Ident); ok {
|
|
1927
|
+
if lhsIdent.Name != "_" {
|
|
1928
|
+
lhsTrackedObj = v.pkg.TypesInfo.ObjectOf(lhsIdent)
|
|
1929
|
+
}
|
|
1930
|
+
} else if selExpr, ok := lhsExpr.(*ast.SelectorExpr); ok {
|
|
1931
|
+
if selection := v.pkg.TypesInfo.Selections[selExpr]; selection != nil {
|
|
1932
|
+
lhsTrackedObj = selection.Obj()
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// Record usage information
|
|
1937
|
+
if _, isVar := lhsTrackedObj.(*types.Var); isVar {
|
|
1938
|
+
lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
|
|
1939
|
+
if rhsSourceObj != nil {
|
|
1940
|
+
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
1941
|
+
Object: rhsSourceObj,
|
|
1942
|
+
Type: rhsAssignmentType,
|
|
1943
|
+
})
|
|
1944
|
+
} else if rhsAssignmentType == AddressOfAssignment {
|
|
1945
|
+
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
1946
|
+
Object: nil,
|
|
1947
|
+
Type: rhsAssignmentType,
|
|
1948
|
+
})
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
if rhsSourceObj != nil {
|
|
1953
|
+
sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
|
|
1954
|
+
sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
|
|
1955
|
+
Object: lhsTrackedObj,
|
|
1956
|
+
Type: rhsAssignmentType,
|
|
1957
|
+
})
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// trackInterfaceAssignments tracks interface implementations in assignment statements
|
|
1962
|
+
func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt) {
|
|
1963
|
+
// For each assignment, check if we're assigning a struct to an interface variable
|
|
1964
|
+
for i, lhsExpr := range assignStmt.Lhs {
|
|
1965
|
+
if i >= len(assignStmt.Rhs) {
|
|
1966
|
+
continue
|
|
1967
|
+
}
|
|
1968
|
+
rhsExpr := assignStmt.Rhs[i]
|
|
1969
|
+
|
|
1970
|
+
// Get the type of the LHS (destination)
|
|
1971
|
+
lhsType := v.pkg.TypesInfo.TypeOf(lhsExpr)
|
|
1972
|
+
if lhsType == nil {
|
|
1973
|
+
continue
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
// Check if LHS is an interface type
|
|
1977
|
+
interfaceType, isInterface := lhsType.Underlying().(*types.Interface)
|
|
1978
|
+
if !isInterface {
|
|
1979
|
+
continue
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
// Get the type of the RHS (source)
|
|
1983
|
+
rhsType := v.pkg.TypesInfo.TypeOf(rhsExpr)
|
|
1984
|
+
if rhsType == nil {
|
|
1985
|
+
continue
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
// Handle pointer types
|
|
1989
|
+
if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
|
|
1990
|
+
rhsType = ptrType.Elem()
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// Check if RHS is a named struct type
|
|
1994
|
+
namedType, isNamed := rhsType.(*types.Named)
|
|
1995
|
+
if !isNamed {
|
|
1996
|
+
continue
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// Track implementations for all interface methods
|
|
2000
|
+
for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
|
|
2001
|
+
interfaceMethod := interfaceType.ExplicitMethod(j)
|
|
2002
|
+
|
|
2003
|
+
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
2004
|
+
if structMethod != nil {
|
|
2005
|
+
// Determine if this struct method is async
|
|
2006
|
+
isAsync := false
|
|
2007
|
+
if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
|
|
2008
|
+
isAsync = funcInfo.IsAsync
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
// IsNamedBasicType returns whether the given type should be implemented as a type alias with standalone functions
|
|
2018
|
+
// This applies to named types with basic underlying types (like uint32, string, etc.) that have methods
|
|
2019
|
+
// It excludes struct types, which should remain as classes
|
|
2020
|
+
func (a *Analysis) IsNamedBasicType(t types.Type) bool {
|
|
2021
|
+
if t == nil {
|
|
2022
|
+
return false
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
// Check if we already have this result cached
|
|
2026
|
+
if result, exists := a.NamedBasicTypes[t]; exists {
|
|
2027
|
+
return result
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
var originalType types.Type = t
|
|
2031
|
+
var foundMethods bool
|
|
2032
|
+
|
|
2033
|
+
// Traverse the type chain to find any type with methods
|
|
2034
|
+
for {
|
|
2035
|
+
switch typed := t.(type) {
|
|
2036
|
+
case *types.Named:
|
|
2037
|
+
// Built-in types cannot be named basic types
|
|
2038
|
+
if typed.Obj().Pkg() == nil {
|
|
2039
|
+
return false
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
// Check if this named type has methods
|
|
2043
|
+
if typed.NumMethods() > 0 {
|
|
2044
|
+
foundMethods = true
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// Check underlying type
|
|
2048
|
+
underlying := typed.Underlying()
|
|
2049
|
+
switch underlying.(type) {
|
|
2050
|
+
case *types.Struct, *types.Interface:
|
|
2051
|
+
return false
|
|
2052
|
+
}
|
|
2053
|
+
t = underlying
|
|
2054
|
+
|
|
2055
|
+
case *types.Alias:
|
|
2056
|
+
// Built-in types cannot be named basic types
|
|
2057
|
+
if typed.Obj().Pkg() == nil {
|
|
2058
|
+
return false
|
|
2059
|
+
}
|
|
2060
|
+
t = typed.Underlying()
|
|
2061
|
+
|
|
2062
|
+
default:
|
|
2063
|
+
// We've reached a non-named, non-alias type
|
|
2064
|
+
// Check if it's a supported type with methods
|
|
2065
|
+
switch t.(type) {
|
|
2066
|
+
case *types.Basic, *types.Slice, *types.Array, *types.Map:
|
|
2067
|
+
if foundMethods {
|
|
2068
|
+
a.NamedBasicTypes[originalType] = true
|
|
2069
|
+
return true
|
|
2070
|
+
}
|
|
2071
|
+
return false
|
|
2072
|
+
default:
|
|
2073
|
+
return false
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// interfaceImplementationVisitor performs a second pass to analyze interface implementations
|
|
2080
|
+
type interfaceImplementationVisitor struct {
|
|
2081
|
+
analysis *Analysis
|
|
2082
|
+
pkg *packages.Package
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
func (v *interfaceImplementationVisitor) Visit(node ast.Node) ast.Visitor {
|
|
2086
|
+
switch n := node.(type) {
|
|
2087
|
+
case *ast.GenDecl:
|
|
2088
|
+
// Look for interface type specifications
|
|
2089
|
+
for _, spec := range n.Specs {
|
|
2090
|
+
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
2091
|
+
if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
|
|
2092
|
+
// This is an interface declaration, find all potential implementations
|
|
2093
|
+
v.findInterfaceImplementations(interfaceType)
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
return v
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
// findInterfaceImplementations finds all struct types that implement the given interface
|
|
2102
|
+
func (v *interfaceImplementationVisitor) findInterfaceImplementations(interfaceAST *ast.InterfaceType) {
|
|
2103
|
+
// Get the interface type from TypesInfo
|
|
2104
|
+
interfaceGoType := v.pkg.TypesInfo.TypeOf(interfaceAST)
|
|
2105
|
+
if interfaceGoType == nil {
|
|
2106
|
+
return
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
interfaceType, ok := interfaceGoType.(*types.Interface)
|
|
2110
|
+
if !ok {
|
|
2111
|
+
return
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
// Look through all packages for potential implementations
|
|
2115
|
+
for _, pkg := range v.analysis.AllPackages {
|
|
2116
|
+
v.findImplementationsInPackage(interfaceType, pkg)
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
// findImplementationsInPackage finds implementations of an interface in a specific package
|
|
2121
|
+
func (v *interfaceImplementationVisitor) findImplementationsInPackage(interfaceType *types.Interface, pkg *packages.Package) {
|
|
2122
|
+
// Get all named types in the package
|
|
2123
|
+
scope := pkg.Types.Scope()
|
|
2124
|
+
for _, name := range scope.Names() {
|
|
2125
|
+
obj := scope.Lookup(name)
|
|
2126
|
+
if obj == nil {
|
|
2127
|
+
continue
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
// Check if this is a type name
|
|
2131
|
+
typeName, ok := obj.(*types.TypeName)
|
|
2132
|
+
if !ok {
|
|
2133
|
+
continue
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
namedType, ok := typeName.Type().(*types.Named)
|
|
2137
|
+
if !ok {
|
|
2138
|
+
continue
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
// Check if this type implements the interface
|
|
2142
|
+
if types.Implements(namedType, interfaceType) || types.Implements(types.NewPointer(namedType), interfaceType) {
|
|
2143
|
+
v.trackImplementation(interfaceType, namedType)
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
// trackImplementation records that a named type implements an interface
|
|
2149
|
+
func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *types.Interface, namedType *types.Named) {
|
|
2150
|
+
// For each method in the interface, find the corresponding implementation
|
|
2151
|
+
for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
|
|
2152
|
+
interfaceMethod := interfaceType.ExplicitMethod(i)
|
|
2153
|
+
|
|
2154
|
+
// Find the method in the implementing type
|
|
2155
|
+
structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
|
|
2156
|
+
if structMethod != nil {
|
|
2157
|
+
// Determine if this implementation is async
|
|
2158
|
+
isAsync := false
|
|
2159
|
+
if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
|
|
2160
|
+
isAsync = funcInfo.IsAsync
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// findMethodInType finds a method with the given name in a named type
|
|
2169
|
+
func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named, methodName string) *types.Func {
|
|
2170
|
+
for i := 0; i < namedType.NumMethods(); i++ {
|
|
2171
|
+
method := namedType.Method(i)
|
|
2172
|
+
if method.Name() == methodName {
|
|
2173
|
+
return method
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
return nil
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
// getNamedReturns retrieves the named returns for a function
|
|
2180
|
+
func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
|
|
2181
|
+
var namedReturns []string
|
|
2182
|
+
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
|
|
2183
|
+
for _, field := range funcDecl.Type.Results.List {
|
|
2184
|
+
for _, name := range field.Names {
|
|
2185
|
+
namedReturns = append(namedReturns, name.Name)
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
return namedReturns
|
|
2190
|
+
}
|