goscript 0.0.49 → 0.0.51
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 +1021 -561
- package/compiler/analysis_test.go +12 -15
- package/compiler/compiler.go +75 -39
- package/compiler/decl.go +22 -0
- package/compiler/expr-call-async.go +239 -40
- package/compiler/expr-call-type-conversion.go +6 -10
- package/compiler/expr-call.go +58 -27
- package/compiler/sanitize.go +1 -2
- package/compiler/spec-struct.go +3 -3
- package/compiler/spec-value.go +2 -2
- package/compiler/spec.go +66 -43
- package/compiler/stmt-assign.go +7 -4
- package/compiler/stmt-select.go +52 -1
- package/compiler/stmt.go +63 -5
- package/compiler/type.go +16 -3
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.d.ts +2 -2
- package/dist/gs/builtin/channel.js +12 -7
- package/dist/gs/builtin/channel.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 +3 -1
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/reflect/type.js +5 -8
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/syscall/constants.d.ts +24 -0
- package/dist/gs/syscall/constants.js +27 -0
- package/dist/gs/syscall/constants.js.map +1 -0
- package/dist/gs/syscall/env.d.ts +6 -0
- package/dist/gs/syscall/env.js +43 -0
- package/dist/gs/syscall/env.js.map +1 -0
- package/dist/gs/syscall/errors.d.ts +111 -0
- package/dist/gs/syscall/errors.js +547 -0
- package/dist/gs/syscall/errors.js.map +1 -0
- package/dist/gs/syscall/fs.d.ts +29 -0
- package/dist/gs/syscall/fs.js +53 -0
- package/dist/gs/syscall/fs.js.map +1 -0
- package/dist/gs/syscall/index.d.ts +6 -80
- package/dist/gs/syscall/index.js +12 -168
- package/dist/gs/syscall/index.js.map +1 -1
- package/dist/gs/syscall/rawconn.d.ts +7 -0
- package/dist/gs/syscall/rawconn.js +19 -0
- package/dist/gs/syscall/rawconn.js.map +1 -0
- package/dist/gs/syscall/types.d.ts +12 -0
- package/dist/gs/syscall/types.js +2 -0
- package/dist/gs/syscall/types.js.map +1 -0
- package/dist/gs/time/time.d.ts +2 -1
- package/dist/gs/time/time.js +29 -19
- package/dist/gs/time/time.js.map +1 -1
- package/gs/builtin/builtin.ts +1 -7
- package/gs/builtin/channel.ts +18 -12
- package/gs/context/context.ts +63 -45
- package/gs/fmt/fmt.ts +5 -1
- package/gs/reflect/type.ts +5 -10
- package/gs/syscall/constants.ts +29 -0
- package/gs/syscall/env.ts +47 -0
- package/gs/syscall/errors.ts +658 -0
- package/gs/syscall/fs.ts +62 -0
- package/gs/syscall/index.ts +12 -207
- package/gs/syscall/rawconn.ts +23 -0
- package/gs/syscall/types.ts +18 -0
- package/gs/time/time.ts +35 -21
- package/package.json +2 -2
- package/gs/TODO.md +0 -129
package/compiler/analysis.go
CHANGED
|
@@ -86,6 +86,37 @@ 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
|
+
// 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
|
|
118
|
+
}
|
|
119
|
+
|
|
89
120
|
// Analysis holds information gathered during the analysis phase of the Go code compilation.
|
|
90
121
|
// This data is used to make decisions about how to generate TypeScript code.
|
|
91
122
|
// Analysis is read-only after being built and should not be modified during code generation.
|
|
@@ -116,12 +147,24 @@ type Analysis struct {
|
|
|
116
147
|
// FunctionAssignments tracks which function literals are assigned to which variables
|
|
117
148
|
FunctionAssignments map[types.Object]ast.Node
|
|
118
149
|
|
|
119
|
-
// PackageMetadata holds package-level metadata
|
|
120
|
-
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
|
|
121
156
|
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
|
|
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
|
|
125
168
|
}
|
|
126
169
|
|
|
127
170
|
// PackageAnalysis holds cross-file analysis data for a package
|
|
@@ -143,24 +186,25 @@ type PackageAnalysis struct {
|
|
|
143
186
|
TypeCalls map[string]map[string][]string
|
|
144
187
|
}
|
|
145
188
|
|
|
146
|
-
// GsMetadata represents the structure of a meta.json file in gs/ packages
|
|
147
|
-
type GsMetadata struct {
|
|
148
|
-
Dependencies []string `json:"dependencies,omitempty"`
|
|
149
|
-
AsyncMethods map[string]bool `json:"asyncMethods,omitempty"`
|
|
150
|
-
}
|
|
151
|
-
|
|
152
189
|
// NewAnalysis creates a new Analysis instance.
|
|
153
|
-
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
|
+
|
|
154
195
|
return &Analysis{
|
|
155
|
-
VariableUsage:
|
|
156
|
-
Imports:
|
|
157
|
-
FunctionData:
|
|
158
|
-
NodeData:
|
|
159
|
-
FuncLitData:
|
|
160
|
-
ReflectedFunctions:
|
|
161
|
-
FunctionAssignments:
|
|
162
|
-
PackageMetadata:
|
|
163
|
-
|
|
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),
|
|
164
208
|
}
|
|
165
209
|
}
|
|
166
210
|
|
|
@@ -174,6 +218,28 @@ func NewPackageAnalysis() *PackageAnalysis {
|
|
|
174
218
|
}
|
|
175
219
|
}
|
|
176
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
|
+
|
|
177
243
|
// NeedsDefer returns whether the given node needs defer handling.
|
|
178
244
|
func (a *Analysis) NeedsDefer(node ast.Node) bool {
|
|
179
245
|
if node == nil {
|
|
@@ -387,10 +453,8 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
387
453
|
}
|
|
388
454
|
|
|
389
455
|
// Initialize and store async state for the current node
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
v.analysis.NodeData[node].InAsyncContext = v.inAsyncFunction
|
|
456
|
+
nodeInfo := v.analysis.ensureNodeData(node)
|
|
457
|
+
nodeInfo.InAsyncContext = v.inAsyncFunction
|
|
394
458
|
|
|
395
459
|
switch n := node.(type) {
|
|
396
460
|
case *ast.GenDecl:
|
|
@@ -463,441 +527,365 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
463
527
|
}
|
|
464
528
|
}
|
|
465
529
|
}
|
|
466
|
-
// Continue traversal AFTER processing the declaration itself
|
|
467
|
-
// to handle expressions within initial values if needed.
|
|
468
|
-
// However, the core usage tracking is done above.
|
|
469
|
-
// Let standard traversal handle children.
|
|
470
530
|
return v
|
|
471
531
|
|
|
472
532
|
case *ast.FuncDecl:
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
v
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
|
499
597
|
}
|
|
500
598
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
if
|
|
510
|
-
if
|
|
511
|
-
if
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
} else {
|
|
524
|
-
receiverUsed = v.containsReceiverUsage(n.Body, vr)
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Update function data with receiver usage info
|
|
529
|
-
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
530
|
-
if v.analysis.FunctionData[obj] == nil {
|
|
531
|
-
v.analysis.FunctionData[obj] = &FunctionInfo{}
|
|
532
|
-
}
|
|
533
|
-
v.analysis.FunctionData[obj].ReceiverUsed = receiverUsed
|
|
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)
|
|
534
621
|
}
|
|
535
622
|
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
623
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
namedReturns = append(namedReturns, sanitizeIdentifier(name.Name))
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
if len(namedReturns) > 0 {
|
|
550
|
-
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
551
|
-
if v.analysis.FunctionData[obj] == nil {
|
|
552
|
-
v.analysis.FunctionData[obj] = &FunctionInfo{}
|
|
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
|
|
628
|
+
}
|
|
553
629
|
}
|
|
554
|
-
v.analysis.FunctionData[obj].NamedReturns = namedReturns
|
|
555
630
|
}
|
|
556
631
|
}
|
|
557
632
|
}
|
|
633
|
+
}
|
|
558
634
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
// Check if the body contains any defer statements
|
|
566
|
-
if v.containsDefer(n.Body) {
|
|
567
|
-
if v.analysis.NodeData[n] == nil {
|
|
568
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
569
|
-
}
|
|
570
|
-
v.analysis.NodeData[n].NeedsDefer = true
|
|
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))
|
|
571
641
|
}
|
|
572
|
-
|
|
573
|
-
// Visit the body with updated state
|
|
574
|
-
ast.Walk(v, n.Body)
|
|
575
642
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
v.inAsyncFunction = originalInAsync
|
|
581
|
-
v.currentReceiver = originalReceiver
|
|
582
|
-
v.currentFuncObj = originalFuncObj
|
|
583
|
-
v.currentFuncDecl = originalFuncDecl
|
|
584
|
-
v.currentFuncLit = originalFuncLit
|
|
585
|
-
}()
|
|
586
|
-
return nil // Stop traversal here, ast.Walk handled the body
|
|
587
|
-
|
|
588
|
-
case *ast.FuncLit:
|
|
589
|
-
// Save original inAsyncFunction state to restore after visiting
|
|
590
|
-
originalInAsync := v.inAsyncFunction
|
|
591
|
-
originalFuncDecl := v.currentFuncDecl
|
|
592
|
-
originalFuncLit := v.currentFuncLit
|
|
593
|
-
|
|
594
|
-
// Set current function literal
|
|
595
|
-
v.currentFuncDecl = nil
|
|
596
|
-
v.currentFuncLit = n
|
|
597
|
-
|
|
598
|
-
// Determine if this function literal is async based on its body
|
|
599
|
-
isAsync := v.containsAsyncOperations(n.Body)
|
|
600
|
-
if v.analysis.NodeData[n] == nil {
|
|
601
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
602
|
-
}
|
|
603
|
-
v.analysis.NodeData[n].InAsyncContext = isAsync
|
|
604
|
-
|
|
605
|
-
// Store named return variables for function literal
|
|
606
|
-
if n.Type != nil && n.Type.Results != nil {
|
|
607
|
-
var namedReturns []string
|
|
608
|
-
for _, field := range n.Type.Results.List {
|
|
609
|
-
for _, name := range field.Names {
|
|
610
|
-
namedReturns = append(namedReturns, name.Name)
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
if len(namedReturns) > 0 {
|
|
614
|
-
v.analysis.FuncLitData[n] = &FunctionInfo{
|
|
615
|
-
IsAsync: isAsync,
|
|
616
|
-
NamedReturns: namedReturns,
|
|
617
|
-
}
|
|
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
|
|
618
647
|
}
|
|
619
648
|
}
|
|
649
|
+
}
|
|
620
650
|
|
|
621
|
-
|
|
651
|
+
// Update visitor state for this function
|
|
652
|
+
v.inAsyncFunction = isAsync
|
|
653
|
+
v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
|
|
622
654
|
|
|
655
|
+
if n.Body != nil {
|
|
623
656
|
// Check if the body contains any defer statements
|
|
624
|
-
if
|
|
625
|
-
|
|
626
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
627
|
-
}
|
|
628
|
-
v.analysis.NodeData[n].NeedsDefer = true
|
|
657
|
+
if v.containsDefer(n.Body) {
|
|
658
|
+
nodeInfo.NeedsDefer = true
|
|
629
659
|
}
|
|
630
660
|
|
|
631
661
|
// Visit the body with updated state
|
|
632
662
|
ast.Walk(v, n.Body)
|
|
663
|
+
}
|
|
633
664
|
|
|
634
|
-
|
|
665
|
+
// Restore states after visiting
|
|
666
|
+
defer func() {
|
|
667
|
+
v.currentFuncName = ""
|
|
635
668
|
v.inAsyncFunction = originalInAsync
|
|
669
|
+
v.currentReceiver = originalReceiver
|
|
670
|
+
v.currentFuncObj = originalFuncObj
|
|
636
671
|
v.currentFuncDecl = originalFuncDecl
|
|
637
672
|
v.currentFuncLit = originalFuncLit
|
|
638
|
-
|
|
673
|
+
}()
|
|
674
|
+
return nil // Stop traversal here, ast.Walk handled the body
|
|
675
|
+
}
|
|
639
676
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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
|
+
}
|
|
643
700
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
701
|
+
if len(namedReturns) > 0 {
|
|
702
|
+
v.analysis.FuncLitData[n] = &FunctionInfo{
|
|
703
|
+
IsAsync: isAsync,
|
|
704
|
+
NamedReturns: namedReturns,
|
|
705
|
+
}
|
|
648
706
|
}
|
|
707
|
+
}
|
|
649
708
|
|
|
650
|
-
|
|
651
|
-
if v.containsDefer(n) {
|
|
652
|
-
v.analysis.NodeData[n].NeedsDefer = true
|
|
653
|
-
}
|
|
709
|
+
v.inAsyncFunction = isAsync
|
|
654
710
|
|
|
655
|
-
|
|
656
|
-
|
|
711
|
+
// Check if the body contains any defer statements
|
|
712
|
+
if n.Body != nil && v.containsDefer(n.Body) {
|
|
713
|
+
nodeInfo.NeedsDefer = true
|
|
714
|
+
}
|
|
657
715
|
|
|
658
|
-
|
|
716
|
+
// Visit the body with updated state
|
|
717
|
+
ast.Walk(v, n.Body)
|
|
659
718
|
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
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 {
|
|
666
729
|
return v
|
|
730
|
+
}
|
|
667
731
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
|
688
760
|
}
|
|
689
761
|
}
|
|
690
762
|
}
|
|
691
763
|
}
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Check for reflect function calls that operate on functions
|
|
767
|
+
v.checkReflectUsage(n)
|
|
692
768
|
|
|
693
|
-
|
|
694
|
-
|
|
769
|
+
return v
|
|
770
|
+
}
|
|
695
771
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
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
|
|
699
780
|
}
|
|
700
|
-
|
|
781
|
+
}
|
|
782
|
+
return v
|
|
783
|
+
}
|
|
701
784
|
|
|
702
|
-
|
|
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)
|
|
703
789
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
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
|
+
}
|
|
710
795
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
v.analysis.NodeData[n].IsMethodValue = true
|
|
716
|
-
}
|
|
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])
|
|
717
800
|
}
|
|
718
|
-
|
|
801
|
+
}
|
|
719
802
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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
|
|
728
813
|
}
|
|
729
|
-
v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
|
|
730
814
|
}
|
|
731
815
|
}
|
|
816
|
+
}
|
|
732
817
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
if i >= len(n.Rhs) {
|
|
736
|
-
break // Should not happen in valid Go
|
|
737
|
-
}
|
|
738
|
-
currentRHSExpr := n.Rhs[i]
|
|
739
|
-
|
|
740
|
-
// --- Analyze RHS to determine assignment type and source object (if any) ---
|
|
741
|
-
rhsAssignmentType := DirectAssignment
|
|
742
|
-
var rhsSourceObj types.Object // The variable object on the RHS (e.g., 'y' in x = y or x = &y)
|
|
743
|
-
|
|
744
|
-
if unaryExpr, ok := currentRHSExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
|
|
745
|
-
// RHS is &some_expr
|
|
746
|
-
rhsAssignmentType = AddressOfAssignment
|
|
747
|
-
if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
|
|
748
|
-
// RHS is &variable
|
|
749
|
-
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
750
|
-
}
|
|
751
|
-
// If RHS is &structLit{} or &array[0], rhsSourceObj remains nil.
|
|
752
|
-
// _, ok := unaryExpr.X.(*ast.CompositeLit); ok
|
|
753
|
-
} else if rhsIdent, ok := currentRHSExpr.(*ast.Ident); ok {
|
|
754
|
-
// RHS is variable
|
|
755
|
-
rhsAssignmentType = DirectAssignment
|
|
756
|
-
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
757
|
-
}
|
|
758
|
-
// If RHS is a literal, function call, etc., rhsSourceObj remains nil.
|
|
818
|
+
return v
|
|
819
|
+
}
|
|
759
820
|
|
|
760
|
-
|
|
761
|
-
|
|
821
|
+
// visitReturnStmt handles return statement analysis
|
|
822
|
+
func (v *analysisVisitor) visitReturnStmt(n *ast.ReturnStmt) ast.Visitor {
|
|
823
|
+
nodeInfo := v.analysis.ensureNodeData(n)
|
|
762
824
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
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
|
+
}
|
|
768
831
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
lhsTrackedObj = selection.Obj() // This is the field or selected var object
|
|
777
|
-
}
|
|
778
|
-
} /* else if _, ok := currentLHSExpr.(*ast.StarExpr); ok {
|
|
779
|
-
// LHS is *pointer.
|
|
780
|
-
// We don't try to get a types.Object for the dereferenced entity itself to store in VariableUsage.
|
|
781
|
-
// lhsTrackedObj remains nil. The effect on rhsSourceObj (if its address is taken) is handled below.
|
|
782
|
-
} */
|
|
783
|
-
// For other complex LHS (e.g., map_expr[key_expr]), lhsTrackedObj remains nil.
|
|
784
|
-
|
|
785
|
-
// --- Record Usage Information ---
|
|
786
|
-
|
|
787
|
-
// 1. If LHS is a trackable variable/field, record what's assigned to it (its sources).
|
|
788
|
-
// We only want to create VariableUsage entries for actual variables/fields.
|
|
789
|
-
if _, isVar := lhsTrackedObj.(*types.Var); isVar {
|
|
790
|
-
lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
|
|
791
|
-
if rhsSourceObj != nil {
|
|
792
|
-
// Case: var1 = var2 OR var1 = &var2 OR field1 = var2 OR field1 = &var2
|
|
793
|
-
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
794
|
-
Object: rhsSourceObj,
|
|
795
|
-
Type: rhsAssignmentType,
|
|
796
|
-
})
|
|
797
|
-
} else if rhsAssignmentType == AddressOfAssignment {
|
|
798
|
-
// Case: var1 = &non_ident_expr (e.g., &T{}) OR field1 = &non_ident_expr
|
|
799
|
-
// lhsTrackedObj is assigned an address, but not of a named variable.
|
|
800
|
-
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
801
|
-
Object: nil, // No specific source variable object
|
|
802
|
-
Type: rhsAssignmentType,
|
|
803
|
-
})
|
|
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
|
|
804
839
|
}
|
|
805
|
-
// If rhsSourceObj is nil and rhsAssignmentType is DirectAssignment (e.g. var1 = 10),
|
|
806
|
-
// no source object to record for LHS sources.
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
// 2. If RHS involved a source variable (rhsSourceObj is not nil),
|
|
810
|
-
// record that this source variable was used (its destinations).
|
|
811
|
-
// This is CRITICAL for varRefing analysis (e.g., if &rhsSourceObj was assigned).
|
|
812
|
-
if rhsSourceObj != nil {
|
|
813
|
-
sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
|
|
814
|
-
// The 'Object' in DestinationInfo is what/where rhsSourceObj (or its address) was assigned TO.
|
|
815
|
-
// This can be lhsTrackedObj (if LHS was an ident or field).
|
|
816
|
-
// If LHS was complex (e.g., *ptr, map[k]), lhsTrackedObj might be nil for that DestinationInfo.Object.
|
|
817
|
-
// Even if lhsTrackedObj is nil for the DestinationInfo.Object, if rhsAssignmentType is AddressOfAssignment,
|
|
818
|
-
// it's important to record that rhsSourceObj's address was taken.
|
|
819
|
-
sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
|
|
820
|
-
Object: lhsTrackedObj, // This can be nil if LHS is complex (*p, map[k])
|
|
821
|
-
Type: rhsAssignmentType,
|
|
822
|
-
})
|
|
823
840
|
}
|
|
824
|
-
}
|
|
825
|
-
return v // Continue traversal
|
|
826
|
-
|
|
827
|
-
case *ast.ReturnStmt:
|
|
828
|
-
// Initialize NodeData for return statement
|
|
829
|
-
if v.analysis.NodeData[n] == nil {
|
|
830
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
// Record the enclosing function/literal for this return statement
|
|
834
|
-
if v.currentFuncDecl != nil {
|
|
835
|
-
v.analysis.NodeData[n].EnclosingFuncDecl = v.currentFuncDecl
|
|
836
841
|
} else if v.currentFuncLit != nil {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
// Check if it's a bare return
|
|
841
|
-
if len(n.Results) == 0 {
|
|
842
|
-
if v.currentFuncDecl != nil {
|
|
843
|
-
// Check if the enclosing function declaration has named returns
|
|
844
|
-
if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
|
|
845
|
-
if _, ok := v.analysis.FunctionData[obj]; ok {
|
|
846
|
-
if v.analysis.NodeData[n] == nil {
|
|
847
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
848
|
-
}
|
|
849
|
-
v.analysis.NodeData[n].IsBareReturn = true
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
} else if v.currentFuncLit != nil {
|
|
853
|
-
// Check if the enclosing function literal has named returns
|
|
854
|
-
if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
|
|
855
|
-
if v.analysis.NodeData[n] == nil {
|
|
856
|
-
v.analysis.NodeData[n] = &NodeInfo{}
|
|
857
|
-
}
|
|
858
|
-
v.analysis.NodeData[n].IsBareReturn = true
|
|
859
|
-
}
|
|
842
|
+
// Check if the enclosing function literal has named returns
|
|
843
|
+
if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
|
|
844
|
+
nodeInfo.IsBareReturn = true
|
|
860
845
|
}
|
|
861
846
|
}
|
|
862
|
-
|
|
847
|
+
}
|
|
848
|
+
return v
|
|
849
|
+
}
|
|
863
850
|
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
v.analysis.NodeData[spec].IsInsideFunction = true
|
|
878
|
-
}
|
|
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
|
|
879
864
|
}
|
|
880
865
|
}
|
|
881
|
-
|
|
866
|
+
}
|
|
867
|
+
return v
|
|
868
|
+
}
|
|
882
869
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
893
|
-
v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
|
|
894
|
-
}
|
|
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
|
|
895
879
|
}
|
|
896
880
|
}
|
|
897
|
-
return v // Continue traversal
|
|
898
881
|
}
|
|
882
|
+
return v
|
|
883
|
+
}
|
|
899
884
|
|
|
900
|
-
|
|
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)
|
|
901
889
|
return v
|
|
902
890
|
}
|
|
903
891
|
|
|
@@ -923,6 +911,11 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
|
|
|
923
911
|
return false
|
|
924
912
|
}
|
|
925
913
|
|
|
914
|
+
case *ast.SelectStmt:
|
|
915
|
+
// Select statement with channel operations
|
|
916
|
+
hasAsync = true
|
|
917
|
+
return false
|
|
918
|
+
|
|
926
919
|
case *ast.CallExpr:
|
|
927
920
|
// Check if we're calling a function known to be async
|
|
928
921
|
if funcIdent, ok := s.Fun.(*ast.Ident); ok {
|
|
@@ -940,29 +933,38 @@ func (v *analysisVisitor) containsAsyncOperations(node ast.Node) bool {
|
|
|
940
933
|
// Get the type of the receiver
|
|
941
934
|
if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
942
935
|
if varObj, ok := obj.(*types.Var); ok {
|
|
943
|
-
//
|
|
944
|
-
|
|
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 {
|
|
945
948
|
typeName := namedType.Obj().Name()
|
|
946
949
|
methodName := selExpr.Sel.Name
|
|
947
950
|
|
|
948
951
|
// Check if the type is from an imported package
|
|
949
952
|
if typePkg := namedType.Obj().Pkg(); typePkg != nil && typePkg != v.pkg.Types {
|
|
950
|
-
// Use the
|
|
951
|
-
|
|
953
|
+
// Use the full package path from the type information (not just the package name)
|
|
954
|
+
pkgPath := typePkg.Path()
|
|
952
955
|
|
|
953
956
|
// Check if this method is async based on metadata
|
|
954
|
-
if v.analysis.IsMethodAsync(
|
|
957
|
+
if v.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
|
|
955
958
|
hasAsync = true
|
|
956
959
|
return false
|
|
957
960
|
}
|
|
958
961
|
}
|
|
962
|
+
// Note: Local method async detection is handled during code generation to avoid circular dependencies
|
|
959
963
|
}
|
|
960
964
|
}
|
|
961
965
|
}
|
|
962
966
|
}
|
|
963
967
|
}
|
|
964
|
-
|
|
965
|
-
// TODO: Add detection of method calls on async types
|
|
966
968
|
}
|
|
967
969
|
|
|
968
970
|
return true
|
|
@@ -1057,99 +1059,113 @@ func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, recei
|
|
|
1057
1059
|
return false
|
|
1058
1060
|
}
|
|
1059
1061
|
|
|
1060
|
-
//
|
|
1061
|
-
// that will be used during code generation to properly handle pointers,
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
analysis
|
|
1062
|
+
// AnalyzePackageFiles analyzes all Go source files in a package and populates the Analysis struct
|
|
1063
|
+
// with information that will be used during code generation to properly handle pointers,
|
|
1064
|
+
// variables that need varRefing, receiver usage, etc. This replaces the old file-by-file analysis.
|
|
1065
|
+
func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages.Package) *Analysis {
|
|
1066
|
+
analysis := NewAnalysis(allPackages)
|
|
1065
1067
|
|
|
1066
1068
|
// Load package metadata for async function detection
|
|
1067
1069
|
analysis.LoadPackageMetadata()
|
|
1068
1070
|
|
|
1069
|
-
// Process imports from the
|
|
1070
|
-
for _,
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1071
|
+
// Process imports from all files in the package
|
|
1072
|
+
for _, file := range pkg.Syntax {
|
|
1073
|
+
// Create comment map for each file and store it (we'll merge them if needed)
|
|
1074
|
+
cmap := ast.NewCommentMap(pkg.Fset, file, file.Comments)
|
|
1075
|
+
if len(analysis.Cmap) == 0 {
|
|
1076
|
+
analysis.Cmap = cmap
|
|
1077
|
+
} else {
|
|
1078
|
+
// Merge comment maps from multiple files
|
|
1079
|
+
for node, comments := range cmap {
|
|
1080
|
+
analysis.Cmap[node] = append(analysis.Cmap[node], comments...)
|
|
1081
|
+
}
|
|
1076
1082
|
}
|
|
1077
1083
|
|
|
1078
|
-
//
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
if imp.
|
|
1082
|
-
|
|
1084
|
+
// Process imports from this file
|
|
1085
|
+
for _, imp := range file.Imports {
|
|
1086
|
+
path := ""
|
|
1087
|
+
if imp.Path != nil {
|
|
1088
|
+
path = imp.Path.Value
|
|
1089
|
+
// Remove quotes from the import path string
|
|
1090
|
+
path = path[1 : len(path)-1]
|
|
1083
1091
|
}
|
|
1084
1092
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1093
|
+
// Store the import in the analysis
|
|
1094
|
+
if path != "" {
|
|
1095
|
+
name := ""
|
|
1096
|
+
if imp.Name != nil {
|
|
1097
|
+
name = imp.Name.Name
|
|
1098
|
+
}
|
|
1089
1099
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
key =
|
|
1100
|
+
fileImp := &fileImport{
|
|
1101
|
+
importPath: path,
|
|
1102
|
+
importVars: make(map[string]struct{}),
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// Use the import name or the actual package name as the key
|
|
1106
|
+
var key string
|
|
1107
|
+
if name != "" {
|
|
1108
|
+
// Explicit alias provided
|
|
1109
|
+
key = name
|
|
1100
1110
|
} else {
|
|
1101
|
-
//
|
|
1102
|
-
|
|
1103
|
-
|
|
1111
|
+
// No explicit alias, use the actual package name from type information
|
|
1112
|
+
// This handles cases where package name differs from the last path segment
|
|
1113
|
+
if actualName, err := getActualPackageName(path, pkg.Imports); err == nil {
|
|
1114
|
+
key = actualName
|
|
1115
|
+
} else {
|
|
1116
|
+
// Fallback to last segment of path if package not found in type information
|
|
1117
|
+
pts := strings.Split(path, "/")
|
|
1118
|
+
key = pts[len(pts)-1]
|
|
1119
|
+
}
|
|
1104
1120
|
}
|
|
1105
|
-
}
|
|
1106
1121
|
|
|
1107
|
-
|
|
1122
|
+
analysis.Imports[key] = fileImp
|
|
1123
|
+
}
|
|
1108
1124
|
}
|
|
1109
1125
|
}
|
|
1110
1126
|
|
|
1111
|
-
// Create
|
|
1127
|
+
// Create visitor for the entire package
|
|
1112
1128
|
visitor := &analysisVisitor{
|
|
1113
|
-
analysis:
|
|
1114
|
-
pkg:
|
|
1115
|
-
inAsyncFunction: false,
|
|
1116
|
-
currentReceiver: nil, // Initialize currentReceiver
|
|
1129
|
+
analysis: analysis,
|
|
1130
|
+
pkg: pkg,
|
|
1117
1131
|
}
|
|
1118
1132
|
|
|
1119
|
-
//
|
|
1120
|
-
|
|
1133
|
+
// First pass: analyze all declarations and statements across all files
|
|
1134
|
+
for _, file := range pkg.Syntax {
|
|
1135
|
+
ast.Walk(visitor, file)
|
|
1136
|
+
}
|
|
1121
1137
|
|
|
1122
1138
|
// Post-processing: Find all CallExpr nodes and unmark their Fun SelectorExpr as method values
|
|
1123
1139
|
// This distinguishes between method calls (obj.Method()) and method values (obj.Method)
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
if
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
nodeInfo.
|
|
1140
|
+
for _, file := range pkg.Syntax {
|
|
1141
|
+
ast.Inspect(file, func(n ast.Node) bool {
|
|
1142
|
+
if callExpr, ok := n.(*ast.CallExpr); ok {
|
|
1143
|
+
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
|
1144
|
+
// This SelectorExpr is the function being called, so it's NOT a method value
|
|
1145
|
+
if nodeInfo := analysis.NodeData[selExpr]; nodeInfo != nil {
|
|
1146
|
+
nodeInfo.IsMethodValue = false
|
|
1147
|
+
}
|
|
1130
1148
|
}
|
|
1131
1149
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
}
|
|
1135
|
-
}
|
|
1150
|
+
return true
|
|
1151
|
+
})
|
|
1152
|
+
}
|
|
1136
1153
|
|
|
1137
|
-
//
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
for _, field := range funcDecl.Type.Results.List {
|
|
1142
|
-
for _, name := range field.Names {
|
|
1143
|
-
namedReturns = append(namedReturns, name.Name)
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1154
|
+
// Second pass: analyze interface implementations now that all function async status is determined
|
|
1155
|
+
interfaceVisitor := &interfaceImplementationVisitor{
|
|
1156
|
+
analysis: analysis,
|
|
1157
|
+
pkg: pkg,
|
|
1146
1158
|
}
|
|
1147
|
-
|
|
1159
|
+
for _, file := range pkg.Syntax {
|
|
1160
|
+
ast.Walk(interfaceVisitor, file)
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
return analysis
|
|
1148
1164
|
}
|
|
1149
1165
|
|
|
1150
|
-
//
|
|
1166
|
+
// AnalyzePackageImports performs package-level analysis to collect function definitions
|
|
1151
1167
|
// and calls across all files in the package for auto-import generation
|
|
1152
|
-
func
|
|
1168
|
+
func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
|
|
1153
1169
|
analysis := NewPackageAnalysis()
|
|
1154
1170
|
|
|
1155
1171
|
// First pass: collect all function definitions per file
|
|
@@ -1315,10 +1331,6 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
|
|
|
1315
1331
|
|
|
1316
1332
|
// LoadPackageMetadata loads metadata from gs packages using embedded JSON files
|
|
1317
1333
|
func (a *Analysis) LoadPackageMetadata() {
|
|
1318
|
-
// Use the embedded filesystem from gs.go
|
|
1319
|
-
// The GsOverrides embed.FS should be available but we need to import it
|
|
1320
|
-
// For now, let's iterate through known packages and load their meta.json files
|
|
1321
|
-
|
|
1322
1334
|
// Discover all packages in the embedded gs/ directory
|
|
1323
1335
|
packagePaths := a.discoverEmbeddedGsPackages()
|
|
1324
1336
|
|
|
@@ -1336,10 +1348,16 @@ func (a *Analysis) LoadPackageMetadata() {
|
|
|
1336
1348
|
typeName := parts[0]
|
|
1337
1349
|
methodName := parts[1]
|
|
1338
1350
|
// The key format is "pkgName.TypeNameMethodNameInfo"
|
|
1339
|
-
key :=
|
|
1351
|
+
key := PackageMetadataKey{
|
|
1352
|
+
PackagePath: pkgPath,
|
|
1353
|
+
TypeName: typeName,
|
|
1354
|
+
MethodName: methodName,
|
|
1355
|
+
}
|
|
1340
1356
|
|
|
1341
1357
|
// Store the async value directly in PackageMetadata
|
|
1342
|
-
a.PackageMetadata[key] =
|
|
1358
|
+
a.PackageMetadata[key] = MethodMetadata{
|
|
1359
|
+
IsAsync: isAsync,
|
|
1360
|
+
}
|
|
1343
1361
|
}
|
|
1344
1362
|
}
|
|
1345
1363
|
}
|
|
@@ -1391,20 +1409,122 @@ func (a *Analysis) loadGsMetadata(metaFilePath string) *GsMetadata {
|
|
|
1391
1409
|
}
|
|
1392
1410
|
|
|
1393
1411
|
// IsMethodAsync checks if a method call is async based on package metadata
|
|
1394
|
-
func (a *Analysis) IsMethodAsync(
|
|
1412
|
+
func (a *Analysis) IsMethodAsync(pkgPath, typeName, methodName string) bool {
|
|
1413
|
+
// First, check existing metadata for internal packages
|
|
1395
1414
|
// The metadata keys are stored as "pkgName.TypeNameMethodNameInfo"
|
|
1396
1415
|
// e.g., "sync.MutexLockInfo", "sync.WaitGroupWaitInfo", etc.
|
|
1397
|
-
key :=
|
|
1416
|
+
key := PackageMetadataKey{
|
|
1417
|
+
PackagePath: pkgPath,
|
|
1418
|
+
TypeName: typeName,
|
|
1419
|
+
MethodName: methodName,
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
if metadata, exists := a.PackageMetadata[key]; exists {
|
|
1423
|
+
return metadata.IsAsync
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
// If no metadata found, check if we can analyze the method from dependency packages
|
|
1427
|
+
return a.analyzeExternalMethodAsync(pkgPath, typeName, methodName)
|
|
1428
|
+
}
|
|
1398
1429
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
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
|
+
}
|
|
1402
1465
|
}
|
|
1403
1466
|
}
|
|
1404
1467
|
|
|
1405
1468
|
return false
|
|
1406
1469
|
}
|
|
1407
1470
|
|
|
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
|
+
|
|
1408
1528
|
// NeedsReflectionMetadata returns whether the given function node needs reflection type metadata
|
|
1409
1529
|
func (a *Analysis) NeedsReflectionMetadata(node ast.Node) bool {
|
|
1410
1530
|
if node == nil {
|
|
@@ -1602,117 +1722,106 @@ func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map
|
|
|
1602
1722
|
}
|
|
1603
1723
|
}
|
|
1604
1724
|
|
|
1605
|
-
//
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1725
|
+
// trackInterfaceImplementation records that a struct type implements an interface method
|
|
1726
|
+
func (a *Analysis) trackInterfaceImplementation(interfaceType *types.Interface, structType *types.Named, method *types.Func, isAsync bool) {
|
|
1727
|
+
key := InterfaceMethodKey{
|
|
1728
|
+
InterfaceType: interfaceType.String(),
|
|
1729
|
+
MethodName: method.Name(),
|
|
1610
1730
|
}
|
|
1611
1731
|
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1732
|
+
implementation := ImplementationInfo{
|
|
1733
|
+
StructType: structType,
|
|
1734
|
+
Method: method,
|
|
1735
|
+
IsAsyncByFlow: isAsync,
|
|
1615
1736
|
}
|
|
1616
1737
|
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
// Exclude struct types - they should remain as classes, not wrapper types
|
|
1620
|
-
if _, isStruct := namedType.Underlying().(*types.Struct); isStruct {
|
|
1621
|
-
a.WrapperTypes[t] = false
|
|
1622
|
-
return false
|
|
1623
|
-
}
|
|
1738
|
+
a.InterfaceImplementations[key] = append(a.InterfaceImplementations[key], implementation)
|
|
1739
|
+
}
|
|
1624
1740
|
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1741
|
+
// IsInterfaceMethodAsync determines if an interface method should be async based on its implementations
|
|
1742
|
+
func (a *Analysis) IsInterfaceMethodAsync(interfaceType *types.Interface, methodName string) bool {
|
|
1743
|
+
key := InterfaceMethodKey{
|
|
1744
|
+
InterfaceType: interfaceType.String(),
|
|
1745
|
+
MethodName: methodName,
|
|
1746
|
+
}
|
|
1630
1747
|
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
underlying := namedType.Underlying()
|
|
1748
|
+
// Check if we've already computed this
|
|
1749
|
+
if result, exists := a.InterfaceMethodAsyncStatus[key]; exists {
|
|
1750
|
+
return result
|
|
1751
|
+
}
|
|
1636
1752
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1753
|
+
// Find all implementations of this interface method
|
|
1754
|
+
implementations, exists := a.InterfaceImplementations[key]
|
|
1755
|
+
if !exists {
|
|
1756
|
+
// No implementations found, default to sync
|
|
1757
|
+
a.InterfaceMethodAsyncStatus[key] = false
|
|
1758
|
+
return false
|
|
1759
|
+
}
|
|
1641
1760
|
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1761
|
+
// If ANY implementation is async, the interface method is async
|
|
1762
|
+
for _, impl := range implementations {
|
|
1763
|
+
if impl.IsAsyncByFlow {
|
|
1764
|
+
a.InterfaceMethodAsyncStatus[key] = true
|
|
1645
1765
|
return true
|
|
1646
1766
|
}
|
|
1647
1767
|
}
|
|
1648
1768
|
|
|
1649
|
-
//
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
// We need to check if fs.FileMode has methods
|
|
1654
|
-
rhs := aliasType.Rhs()
|
|
1655
|
-
|
|
1656
|
-
if rhsNamed, ok := rhs.(*types.Named); ok {
|
|
1657
|
-
// Check if the RHS named type has methods and a basic underlying type
|
|
1658
|
-
if rhsNamed.NumMethods() > 0 {
|
|
1659
|
-
underlying := rhsNamed.Underlying()
|
|
1660
|
-
|
|
1661
|
-
// Exclude struct types
|
|
1662
|
-
if _, isStruct := underlying.(*types.Struct); isStruct {
|
|
1663
|
-
a.WrapperTypes[t] = false
|
|
1664
|
-
return false
|
|
1665
|
-
}
|
|
1769
|
+
// All implementations are sync
|
|
1770
|
+
a.InterfaceMethodAsyncStatus[key] = false
|
|
1771
|
+
return false
|
|
1772
|
+
}
|
|
1666
1773
|
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1774
|
+
// MustBeAsyncDueToInterface checks if a struct method must be async due to interface constraints
|
|
1775
|
+
func (a *Analysis) MustBeAsyncDueToInterface(structType *types.Named, methodName string) bool {
|
|
1776
|
+
// Find all interfaces that this struct implements
|
|
1777
|
+
for key, implementations := range a.InterfaceImplementations {
|
|
1778
|
+
if key.MethodName != methodName {
|
|
1779
|
+
continue
|
|
1780
|
+
}
|
|
1672
1781
|
|
|
1673
|
-
|
|
1674
|
-
|
|
1782
|
+
// Check if this struct is among the implementations
|
|
1783
|
+
for _, impl := range implementations {
|
|
1784
|
+
if impl.StructType == structType {
|
|
1785
|
+
// This struct implements this interface method
|
|
1786
|
+
// Check if the interface method is marked as async
|
|
1787
|
+
interfaceType := a.findInterfaceTypeByString(key.InterfaceType)
|
|
1788
|
+
if interfaceType != nil && a.IsInterfaceMethodAsync(interfaceType, methodName) {
|
|
1675
1789
|
return true
|
|
1676
1790
|
}
|
|
1677
|
-
|
|
1678
|
-
// For non-basic underlying types with methods, still consider them wrapper types
|
|
1679
|
-
// if they're not structs or interfaces (already excluded above)
|
|
1680
|
-
a.WrapperTypes[t] = true
|
|
1681
|
-
return true
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
// Fallback: check the underlying type for older Go versions or different alias patterns
|
|
1686
|
-
underlying := aliasType.Underlying()
|
|
1687
|
-
if namedUnderlying, ok := underlying.(*types.Named); ok {
|
|
1688
|
-
if namedUnderlying.NumMethods() > 0 && isBasicType(namedUnderlying.Underlying()) {
|
|
1689
|
-
a.WrapperTypes[t] = true
|
|
1690
|
-
return true
|
|
1691
1791
|
}
|
|
1692
1792
|
}
|
|
1693
1793
|
}
|
|
1694
1794
|
|
|
1695
|
-
// Cache negative result
|
|
1696
|
-
a.WrapperTypes[t] = false
|
|
1697
1795
|
return false
|
|
1698
1796
|
}
|
|
1699
1797
|
|
|
1700
|
-
//
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1798
|
+
// findInterfaceTypeByString finds an interface type by its string representation
|
|
1799
|
+
// This is a helper method for MustBeAsyncDueToInterface
|
|
1800
|
+
func (a *Analysis) findInterfaceTypeByString(interfaceString string) *types.Interface {
|
|
1801
|
+
// This is a simplified implementation - in practice, we might need to store
|
|
1802
|
+
// the actual interface types in our tracking data structure
|
|
1803
|
+
for _, pkg := range a.AllPackages {
|
|
1804
|
+
for _, syntax := range pkg.Syntax {
|
|
1805
|
+
for _, decl := range syntax.Decls {
|
|
1806
|
+
if genDecl, ok := decl.(*ast.GenDecl); ok {
|
|
1807
|
+
for _, spec := range genDecl.Specs {
|
|
1808
|
+
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
1809
|
+
if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
|
|
1810
|
+
if goType := pkg.TypesInfo.TypeOf(interfaceType); goType != nil {
|
|
1811
|
+
if iface, ok := goType.(*types.Interface); ok {
|
|
1812
|
+
if iface.String() == interfaceString {
|
|
1813
|
+
return iface
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1715
1823
|
}
|
|
1824
|
+
return nil
|
|
1716
1825
|
}
|
|
1717
1826
|
|
|
1718
1827
|
// GetReceiverMapping returns the receiver variable mapping for a function declaration
|
|
@@ -1742,3 +1851,354 @@ func (a *Analysis) GetIdentifierMapping(ident *ast.Ident) string {
|
|
|
1742
1851
|
|
|
1743
1852
|
return ""
|
|
1744
1853
|
}
|
|
1854
|
+
|
|
1855
|
+
// trackTypeAssertion analyzes type assertions and records interface implementations
|
|
1856
|
+
func (v *analysisVisitor) trackTypeAssertion(typeAssert *ast.TypeAssertExpr) {
|
|
1857
|
+
// Get the type being asserted to
|
|
1858
|
+
assertedType := v.pkg.TypesInfo.TypeOf(typeAssert.Type)
|
|
1859
|
+
if assertedType == nil {
|
|
1860
|
+
return
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// Check if the asserted type is an interface
|
|
1864
|
+
interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
|
|
1865
|
+
if !isInterface {
|
|
1866
|
+
return
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// Get the type of the expression being asserted
|
|
1870
|
+
exprType := v.pkg.TypesInfo.TypeOf(typeAssert.X)
|
|
1871
|
+
if exprType == nil {
|
|
1872
|
+
return
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// Handle pointer types by getting the element type
|
|
1876
|
+
if ptrType, isPtr := exprType.(*types.Pointer); isPtr {
|
|
1877
|
+
exprType = ptrType.Elem()
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
// Check if the expression type is a named struct type
|
|
1881
|
+
namedType, isNamed := exprType.(*types.Named)
|
|
1882
|
+
if !isNamed {
|
|
1883
|
+
return
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// For each method in the interface, check if the struct implements it
|
|
1887
|
+
for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
|
|
1888
|
+
interfaceMethod := interfaceType.ExplicitMethod(i)
|
|
1889
|
+
|
|
1890
|
+
// Find the corresponding method in the struct type
|
|
1891
|
+
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
1892
|
+
if structMethod != nil {
|
|
1893
|
+
// Determine if this struct method is async based on control flow analysis
|
|
1894
|
+
isAsync := false
|
|
1895
|
+
if obj := structMethod; obj != nil {
|
|
1896
|
+
if funcInfo := v.analysis.FunctionData[obj]; funcInfo != nil {
|
|
1897
|
+
isAsync = funcInfo.IsAsync
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
// Track this interface implementation
|
|
1902
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
// findStructMethod finds a method with the given name on a named type
|
|
1908
|
+
func (v *analysisVisitor) findStructMethod(namedType *types.Named, methodName string) *types.Func {
|
|
1909
|
+
// Check methods directly on the type
|
|
1910
|
+
for i := 0; i < namedType.NumMethods(); i++ {
|
|
1911
|
+
method := namedType.Method(i)
|
|
1912
|
+
if method.Name() == methodName {
|
|
1913
|
+
return method
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
return nil
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
// analyzeAssignment analyzes a single assignment for pointer analysis
|
|
1920
|
+
func (v *analysisVisitor) analyzeAssignment(lhsExpr, rhsExpr ast.Expr) {
|
|
1921
|
+
// Determine RHS assignment type and source object
|
|
1922
|
+
rhsAssignmentType := DirectAssignment
|
|
1923
|
+
var rhsSourceObj types.Object
|
|
1924
|
+
|
|
1925
|
+
if unaryExpr, ok := rhsExpr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
|
|
1926
|
+
// RHS is &some_expr
|
|
1927
|
+
rhsAssignmentType = AddressOfAssignment
|
|
1928
|
+
if rhsIdent, ok := unaryExpr.X.(*ast.Ident); ok {
|
|
1929
|
+
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
1930
|
+
}
|
|
1931
|
+
} else if rhsIdent, ok := rhsExpr.(*ast.Ident); ok {
|
|
1932
|
+
// RHS is variable
|
|
1933
|
+
rhsAssignmentType = DirectAssignment
|
|
1934
|
+
rhsSourceObj = v.pkg.TypesInfo.ObjectOf(rhsIdent)
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
// Determine LHS object
|
|
1938
|
+
var lhsTrackedObj types.Object
|
|
1939
|
+
|
|
1940
|
+
if lhsIdent, ok := lhsExpr.(*ast.Ident); ok {
|
|
1941
|
+
if lhsIdent.Name != "_" {
|
|
1942
|
+
lhsTrackedObj = v.pkg.TypesInfo.ObjectOf(lhsIdent)
|
|
1943
|
+
}
|
|
1944
|
+
} else if selExpr, ok := lhsExpr.(*ast.SelectorExpr); ok {
|
|
1945
|
+
if selection := v.pkg.TypesInfo.Selections[selExpr]; selection != nil {
|
|
1946
|
+
lhsTrackedObj = selection.Obj()
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
// Record usage information
|
|
1951
|
+
if _, isVar := lhsTrackedObj.(*types.Var); isVar {
|
|
1952
|
+
lhsUsageInfo := v.getOrCreateUsageInfo(lhsTrackedObj)
|
|
1953
|
+
if rhsSourceObj != nil {
|
|
1954
|
+
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
1955
|
+
Object: rhsSourceObj,
|
|
1956
|
+
Type: rhsAssignmentType,
|
|
1957
|
+
})
|
|
1958
|
+
} else if rhsAssignmentType == AddressOfAssignment {
|
|
1959
|
+
lhsUsageInfo.Sources = append(lhsUsageInfo.Sources, AssignmentInfo{
|
|
1960
|
+
Object: nil,
|
|
1961
|
+
Type: rhsAssignmentType,
|
|
1962
|
+
})
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
if rhsSourceObj != nil {
|
|
1967
|
+
sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
|
|
1968
|
+
sourceUsageInfo.Destinations = append(sourceUsageInfo.Destinations, AssignmentInfo{
|
|
1969
|
+
Object: lhsTrackedObj,
|
|
1970
|
+
Type: rhsAssignmentType,
|
|
1971
|
+
})
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
// trackInterfaceAssignments tracks interface implementations in assignment statements
|
|
1976
|
+
func (v *analysisVisitor) trackInterfaceAssignments(assignStmt *ast.AssignStmt) {
|
|
1977
|
+
// For each assignment, check if we're assigning a struct to an interface variable
|
|
1978
|
+
for i, lhsExpr := range assignStmt.Lhs {
|
|
1979
|
+
if i >= len(assignStmt.Rhs) {
|
|
1980
|
+
continue
|
|
1981
|
+
}
|
|
1982
|
+
rhsExpr := assignStmt.Rhs[i]
|
|
1983
|
+
|
|
1984
|
+
// Get the type of the LHS (destination)
|
|
1985
|
+
lhsType := v.pkg.TypesInfo.TypeOf(lhsExpr)
|
|
1986
|
+
if lhsType == nil {
|
|
1987
|
+
continue
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
// Check if LHS is an interface type
|
|
1991
|
+
interfaceType, isInterface := lhsType.Underlying().(*types.Interface)
|
|
1992
|
+
if !isInterface {
|
|
1993
|
+
continue
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
// Get the type of the RHS (source)
|
|
1997
|
+
rhsType := v.pkg.TypesInfo.TypeOf(rhsExpr)
|
|
1998
|
+
if rhsType == nil {
|
|
1999
|
+
continue
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
// Handle pointer types
|
|
2003
|
+
if ptrType, isPtr := rhsType.(*types.Pointer); isPtr {
|
|
2004
|
+
rhsType = ptrType.Elem()
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
// Check if RHS is a named struct type
|
|
2008
|
+
namedType, isNamed := rhsType.(*types.Named)
|
|
2009
|
+
if !isNamed {
|
|
2010
|
+
continue
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
// Track implementations for all interface methods
|
|
2014
|
+
for j := 0; j < interfaceType.NumExplicitMethods(); j++ {
|
|
2015
|
+
interfaceMethod := interfaceType.ExplicitMethod(j)
|
|
2016
|
+
|
|
2017
|
+
structMethod := v.findStructMethod(namedType, interfaceMethod.Name())
|
|
2018
|
+
if structMethod != nil {
|
|
2019
|
+
// Determine if this struct method is async
|
|
2020
|
+
isAsync := false
|
|
2021
|
+
if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
|
|
2022
|
+
isAsync = funcInfo.IsAsync
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
// IsNamedBasicType returns whether the given type should be implemented as a type alias with standalone functions
|
|
2032
|
+
// This applies to named types with basic underlying types (like uint32, string, etc.) that have methods
|
|
2033
|
+
// It excludes struct types, which should remain as classes
|
|
2034
|
+
func (a *Analysis) IsNamedBasicType(t types.Type) bool {
|
|
2035
|
+
if t == nil {
|
|
2036
|
+
return false
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
// Check if we already have this result cached
|
|
2040
|
+
if result, exists := a.NamedBasicTypes[t]; exists {
|
|
2041
|
+
return result
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
var originalType types.Type = t
|
|
2045
|
+
var foundMethods bool
|
|
2046
|
+
|
|
2047
|
+
// Traverse the type chain to find any type with methods
|
|
2048
|
+
for {
|
|
2049
|
+
switch typed := t.(type) {
|
|
2050
|
+
case *types.Named:
|
|
2051
|
+
// Built-in types cannot be named basic types
|
|
2052
|
+
if typed.Obj().Pkg() == nil {
|
|
2053
|
+
return false
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
// Check if this named type has methods
|
|
2057
|
+
if typed.NumMethods() > 0 {
|
|
2058
|
+
foundMethods = true
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
// Check underlying type
|
|
2062
|
+
underlying := typed.Underlying()
|
|
2063
|
+
switch underlying.(type) {
|
|
2064
|
+
case *types.Struct, *types.Interface:
|
|
2065
|
+
return false
|
|
2066
|
+
}
|
|
2067
|
+
t = underlying
|
|
2068
|
+
|
|
2069
|
+
case *types.Alias:
|
|
2070
|
+
// Built-in types cannot be named basic types
|
|
2071
|
+
if typed.Obj().Pkg() == nil {
|
|
2072
|
+
return false
|
|
2073
|
+
}
|
|
2074
|
+
t = typed.Underlying()
|
|
2075
|
+
|
|
2076
|
+
default:
|
|
2077
|
+
// We've reached a non-named, non-alias type
|
|
2078
|
+
// Check if it's a supported type with methods
|
|
2079
|
+
switch t.(type) {
|
|
2080
|
+
case *types.Basic, *types.Slice, *types.Array, *types.Map:
|
|
2081
|
+
if foundMethods {
|
|
2082
|
+
a.NamedBasicTypes[originalType] = true
|
|
2083
|
+
return true
|
|
2084
|
+
}
|
|
2085
|
+
return false
|
|
2086
|
+
default:
|
|
2087
|
+
return false
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
// interfaceImplementationVisitor performs a second pass to analyze interface implementations
|
|
2094
|
+
type interfaceImplementationVisitor struct {
|
|
2095
|
+
analysis *Analysis
|
|
2096
|
+
pkg *packages.Package
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
func (v *interfaceImplementationVisitor) Visit(node ast.Node) ast.Visitor {
|
|
2100
|
+
switch n := node.(type) {
|
|
2101
|
+
case *ast.GenDecl:
|
|
2102
|
+
// Look for interface type specifications
|
|
2103
|
+
for _, spec := range n.Specs {
|
|
2104
|
+
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
2105
|
+
if interfaceType, ok := typeSpec.Type.(*ast.InterfaceType); ok {
|
|
2106
|
+
// This is an interface declaration, find all potential implementations
|
|
2107
|
+
v.findInterfaceImplementations(interfaceType)
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return v
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// findInterfaceImplementations finds all struct types that implement the given interface
|
|
2116
|
+
func (v *interfaceImplementationVisitor) findInterfaceImplementations(interfaceAST *ast.InterfaceType) {
|
|
2117
|
+
// Get the interface type from TypesInfo
|
|
2118
|
+
interfaceGoType := v.pkg.TypesInfo.TypeOf(interfaceAST)
|
|
2119
|
+
if interfaceGoType == nil {
|
|
2120
|
+
return
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
interfaceType, ok := interfaceGoType.(*types.Interface)
|
|
2124
|
+
if !ok {
|
|
2125
|
+
return
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// Look through all packages for potential implementations
|
|
2129
|
+
for _, pkg := range v.analysis.AllPackages {
|
|
2130
|
+
v.findImplementationsInPackage(interfaceType, pkg)
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
// findImplementationsInPackage finds implementations of an interface in a specific package
|
|
2135
|
+
func (v *interfaceImplementationVisitor) findImplementationsInPackage(interfaceType *types.Interface, pkg *packages.Package) {
|
|
2136
|
+
// Get all named types in the package
|
|
2137
|
+
scope := pkg.Types.Scope()
|
|
2138
|
+
for _, name := range scope.Names() {
|
|
2139
|
+
obj := scope.Lookup(name)
|
|
2140
|
+
if obj == nil {
|
|
2141
|
+
continue
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// Check if this is a type name
|
|
2145
|
+
typeName, ok := obj.(*types.TypeName)
|
|
2146
|
+
if !ok {
|
|
2147
|
+
continue
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
namedType, ok := typeName.Type().(*types.Named)
|
|
2151
|
+
if !ok {
|
|
2152
|
+
continue
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
// Check if this type implements the interface
|
|
2156
|
+
if types.Implements(namedType, interfaceType) || types.Implements(types.NewPointer(namedType), interfaceType) {
|
|
2157
|
+
v.trackImplementation(interfaceType, namedType)
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
// trackImplementation records that a named type implements an interface
|
|
2163
|
+
func (v *interfaceImplementationVisitor) trackImplementation(interfaceType *types.Interface, namedType *types.Named) {
|
|
2164
|
+
// For each method in the interface, find the corresponding implementation
|
|
2165
|
+
for i := 0; i < interfaceType.NumExplicitMethods(); i++ {
|
|
2166
|
+
interfaceMethod := interfaceType.ExplicitMethod(i)
|
|
2167
|
+
|
|
2168
|
+
// Find the method in the implementing type
|
|
2169
|
+
structMethod := v.findMethodInType(namedType, interfaceMethod.Name())
|
|
2170
|
+
if structMethod != nil {
|
|
2171
|
+
// Determine if this implementation is async
|
|
2172
|
+
isAsync := false
|
|
2173
|
+
if funcInfo := v.analysis.FunctionData[structMethod]; funcInfo != nil {
|
|
2174
|
+
isAsync = funcInfo.IsAsync
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
v.analysis.trackInterfaceImplementation(interfaceType, namedType, structMethod, isAsync)
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
// findMethodInType finds a method with the given name in a named type
|
|
2183
|
+
func (v *interfaceImplementationVisitor) findMethodInType(namedType *types.Named, methodName string) *types.Func {
|
|
2184
|
+
for i := 0; i < namedType.NumMethods(); i++ {
|
|
2185
|
+
method := namedType.Method(i)
|
|
2186
|
+
if method.Name() == methodName {
|
|
2187
|
+
return method
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
return nil
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// getNamedReturns retrieves the named returns for a function
|
|
2194
|
+
func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
|
|
2195
|
+
var namedReturns []string
|
|
2196
|
+
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
|
|
2197
|
+
for _, field := range funcDecl.Type.Results.List {
|
|
2198
|
+
for _, name := range field.Names {
|
|
2199
|
+
namedReturns = append(namedReturns, name.Name)
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
return namedReturns
|
|
2204
|
+
}
|