goscript 0.0.22 → 0.0.24
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/README.md +1 -1
- package/cmd/goscript/cmd_compile.go +3 -3
- package/compiler/analysis.go +302 -182
- package/compiler/analysis_test.go +220 -0
- package/compiler/assignment.go +42 -43
- package/compiler/builtin_test.go +102 -0
- package/compiler/compiler.go +117 -29
- package/compiler/compiler_test.go +36 -8
- package/compiler/composite-lit.go +133 -53
- package/compiler/config.go +7 -3
- package/compiler/config_test.go +6 -33
- package/compiler/decl.go +36 -0
- package/compiler/expr-call.go +116 -60
- package/compiler/expr-selector.go +88 -43
- package/compiler/expr-star.go +57 -65
- package/compiler/expr-type.go +132 -5
- package/compiler/expr-value.go +8 -38
- package/compiler/expr.go +326 -30
- package/compiler/field.go +3 -3
- package/compiler/lit.go +34 -2
- package/compiler/primitive.go +19 -12
- package/compiler/spec-struct.go +140 -9
- package/compiler/spec-value.go +119 -41
- package/compiler/spec.go +21 -6
- package/compiler/stmt-assign.go +65 -3
- package/compiler/stmt-for.go +11 -0
- package/compiler/stmt-range.go +119 -11
- package/compiler/stmt-select.go +211 -0
- package/compiler/stmt-type-switch.go +147 -0
- package/compiler/stmt.go +175 -238
- package/compiler/type-assert.go +125 -379
- package/compiler/type.go +216 -129
- package/dist/gs/builtin/builtin.js +37 -0
- package/dist/gs/builtin/builtin.js.map +1 -0
- package/dist/gs/builtin/channel.js +471 -0
- package/dist/gs/builtin/channel.js.map +1 -0
- package/dist/gs/builtin/defer.js +54 -0
- package/dist/gs/builtin/defer.js.map +1 -0
- package/dist/gs/builtin/io.js +15 -0
- package/dist/gs/builtin/io.js.map +1 -0
- package/dist/gs/builtin/map.js +44 -0
- package/dist/gs/builtin/map.js.map +1 -0
- package/dist/gs/builtin/slice.js +799 -0
- package/dist/gs/builtin/slice.js.map +1 -0
- package/dist/gs/builtin/type.js +745 -0
- package/dist/gs/builtin/type.js.map +1 -0
- package/dist/gs/builtin/varRef.js +14 -0
- package/dist/gs/builtin/varRef.js.map +1 -0
- package/dist/gs/context/context.js +55 -0
- package/dist/gs/context/context.js.map +1 -0
- package/dist/gs/context/index.js +2 -0
- package/dist/gs/context/index.js.map +1 -0
- package/dist/gs/runtime/index.js +2 -0
- package/dist/gs/runtime/index.js.map +1 -0
- package/dist/gs/runtime/runtime.js +158 -0
- package/dist/gs/runtime/runtime.js.map +1 -0
- package/dist/gs/time/index.js +2 -0
- package/dist/gs/time/index.js.map +1 -0
- package/dist/gs/time/time.js +115 -0
- package/dist/gs/time/time.js.map +1 -0
- package/package.json +7 -6
- package/builtin/builtin.go +0 -11
- package/builtin/builtin.ts +0 -2379
- package/dist/builtin/builtin.d.ts +0 -513
- package/dist/builtin/builtin.js +0 -1686
- package/dist/builtin/builtin.js.map +0 -1
package/compiler/analysis.go
CHANGED
|
@@ -41,6 +41,21 @@ type VariableUsageInfo struct {
|
|
|
41
41
|
Destinations []AssignmentInfo
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// FunctionInfo consolidates function-related tracking data.
|
|
45
|
+
type FunctionInfo struct {
|
|
46
|
+
IsAsync bool
|
|
47
|
+
NamedReturns []string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// NodeInfo consolidates node-related tracking data.
|
|
51
|
+
type NodeInfo struct {
|
|
52
|
+
NeedsDefer bool
|
|
53
|
+
InAsyncContext bool
|
|
54
|
+
IsBareReturn bool
|
|
55
|
+
EnclosingFuncDecl *ast.FuncDecl
|
|
56
|
+
EnclosingFuncLit *ast.FuncLit
|
|
57
|
+
}
|
|
58
|
+
|
|
44
59
|
// Analysis holds information gathered during the analysis phase of the Go code compilation.
|
|
45
60
|
// This data is used to make decisions about how to generate TypeScript code.
|
|
46
61
|
// Analysis is read-only after being built and should not be modified during code generation.
|
|
@@ -55,25 +70,25 @@ type Analysis struct {
|
|
|
55
70
|
// Cmap stores the comment map for the file
|
|
56
71
|
Cmap ast.CommentMap
|
|
57
72
|
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
AsyncFuncs map[types.Object]bool
|
|
73
|
+
// FunctionData consolidates function-related tracking into one map
|
|
74
|
+
FunctionData map[types.Object]*FunctionInfo
|
|
61
75
|
|
|
62
|
-
//
|
|
63
|
-
|
|
76
|
+
// NodeData consolidates node-related tracking into one map
|
|
77
|
+
NodeData map[ast.Node]*NodeInfo
|
|
64
78
|
|
|
65
|
-
//
|
|
66
|
-
|
|
79
|
+
// Keep specialized maps that serve different purposes
|
|
80
|
+
// FuncLitData tracks function literal specific data since they don't have types.Object
|
|
81
|
+
FuncLitData map[*ast.FuncLit]*FunctionInfo
|
|
67
82
|
}
|
|
68
83
|
|
|
69
84
|
// NewAnalysis creates a new Analysis instance.
|
|
70
85
|
func NewAnalysis() *Analysis {
|
|
71
86
|
return &Analysis{
|
|
72
|
-
VariableUsage:
|
|
73
|
-
Imports:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
VariableUsage: make(map[types.Object]*VariableUsageInfo),
|
|
88
|
+
Imports: make(map[string]*fileImport),
|
|
89
|
+
FunctionData: make(map[types.Object]*FunctionInfo),
|
|
90
|
+
NodeData: make(map[ast.Node]*NodeInfo),
|
|
91
|
+
FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
|
|
77
92
|
}
|
|
78
93
|
}
|
|
79
94
|
|
|
@@ -82,7 +97,11 @@ func (a *Analysis) NeedsDefer(node ast.Node) bool {
|
|
|
82
97
|
if node == nil {
|
|
83
98
|
return false
|
|
84
99
|
}
|
|
85
|
-
|
|
100
|
+
nodeInfo := a.NodeData[node]
|
|
101
|
+
if nodeInfo == nil {
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
return nodeInfo.NeedsDefer
|
|
86
105
|
}
|
|
87
106
|
|
|
88
107
|
// IsInAsyncFunction returns whether the given node is inside an async function.
|
|
@@ -90,7 +109,11 @@ func (a *Analysis) IsInAsyncFunction(node ast.Node) bool {
|
|
|
90
109
|
if node == nil {
|
|
91
110
|
return false
|
|
92
111
|
}
|
|
93
|
-
|
|
112
|
+
nodeInfo := a.NodeData[node]
|
|
113
|
+
if nodeInfo == nil {
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
return nodeInfo.InAsyncContext
|
|
94
117
|
}
|
|
95
118
|
|
|
96
119
|
// IsAsyncFunc returns whether the given object represents an async function.
|
|
@@ -98,7 +121,11 @@ func (a *Analysis) IsAsyncFunc(obj types.Object) bool {
|
|
|
98
121
|
if obj == nil {
|
|
99
122
|
return false
|
|
100
123
|
}
|
|
101
|
-
|
|
124
|
+
funcInfo := a.FunctionData[obj]
|
|
125
|
+
if funcInfo == nil {
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
return funcInfo.IsAsync
|
|
102
129
|
}
|
|
103
130
|
|
|
104
131
|
// IsFuncLitAsync checks if a function literal is async based on our analysis.
|
|
@@ -106,17 +133,26 @@ func (a *Analysis) IsFuncLitAsync(funcLit *ast.FuncLit) bool {
|
|
|
106
133
|
if funcLit == nil {
|
|
107
134
|
return false
|
|
108
135
|
}
|
|
109
|
-
//
|
|
110
|
-
|
|
136
|
+
// Check function literal specific data first
|
|
137
|
+
if funcInfo := a.FuncLitData[funcLit]; funcInfo != nil {
|
|
138
|
+
return funcInfo.IsAsync
|
|
139
|
+
}
|
|
140
|
+
// Fall back to node data for backwards compatibility
|
|
141
|
+
nodeInfo := a.NodeData[funcLit]
|
|
142
|
+
if nodeInfo == nil {
|
|
143
|
+
return false
|
|
144
|
+
}
|
|
145
|
+
return nodeInfo.InAsyncContext
|
|
111
146
|
}
|
|
112
147
|
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
func (a *Analysis)
|
|
148
|
+
// NeedsVarRef returns whether the given object needs to be variable referenced.
|
|
149
|
+
// This is true when the object's address is taken (e.g., &myVar) in the analyzed code.
|
|
150
|
+
// Variables that have their address taken must be wrapped in VarRef to maintain identity.
|
|
151
|
+
func (a *Analysis) NeedsVarRef(obj types.Object) bool {
|
|
117
152
|
if obj == nil {
|
|
118
153
|
return false
|
|
119
154
|
}
|
|
155
|
+
|
|
120
156
|
usageInfo, exists := a.VariableUsage[obj]
|
|
121
157
|
if !exists {
|
|
122
158
|
return false
|
|
@@ -130,156 +166,97 @@ func (a *Analysis) NeedsBoxed(obj types.Object) bool {
|
|
|
130
166
|
return false
|
|
131
167
|
}
|
|
132
168
|
|
|
133
|
-
//
|
|
134
|
-
// This
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
// Two distinct cases determine when a variable needs .value access:
|
|
138
|
-
//
|
|
139
|
-
// 1. The variable itself is boxed (its address is taken)
|
|
140
|
-
// Example: let x = $.box(10) => x.value
|
|
169
|
+
// NeedsVarRefAccess returns whether accessing the given object requires '.value' access in TypeScript.
|
|
170
|
+
// This is more nuanced than NeedsVarRef and considers both direct variable references and
|
|
171
|
+
// pointers that may point to variable-referenced values.
|
|
141
172
|
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
|
|
173
|
+
// Examples:
|
|
174
|
+
// - Direct variable reference (NeedsVarRef = true):
|
|
175
|
+
// Example: let x = $.varRef(10) => x.value
|
|
176
|
+
// - Pointer pointing to a variable-referenced value:
|
|
177
|
+
// Example: let p: VarRef<number> | null = x => p!.value
|
|
178
|
+
// - Regular pointer (NeedsVarRef = false, but points to variable reference):
|
|
179
|
+
// Example: let q = x => q!.value (where x is VarRef)
|
|
180
|
+
func (a *Analysis) NeedsVarRefAccess(obj types.Object) bool {
|
|
149
181
|
if obj == nil {
|
|
150
182
|
return false
|
|
151
183
|
}
|
|
152
184
|
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
if a.NeedsBoxed(obj) {
|
|
185
|
+
// If the variable itself is variable referenced, it needs .value access
|
|
186
|
+
if a.NeedsVarRef(obj) {
|
|
156
187
|
return true
|
|
157
188
|
}
|
|
158
189
|
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// Check if this pointer was assigned the address of another variable
|
|
169
|
-
// (e.g., ptr = &someVar) rather than a direct literal (ptr = &Struct{})
|
|
170
|
-
if src.Type == AddressOfAssignment && src.Object != nil {
|
|
171
|
-
// Bug fix: If the source variable is boxed, the pointer needs .value to access it
|
|
172
|
-
// This distinguishes between:
|
|
173
|
-
// - ptrToVal := &val (val is boxed, so we need ptrToVal.value)
|
|
174
|
-
// - ptr := &MyStruct{} (direct literal, no boxing needed)
|
|
175
|
-
return a.NeedsBoxed(src.Object)
|
|
176
|
-
}
|
|
190
|
+
// For pointer variables, check if they point to a variable-referenced value
|
|
191
|
+
if ptrType, ok := obj.Type().(*types.Pointer); ok {
|
|
192
|
+
// Check all assignments to this pointer variable
|
|
193
|
+
for varObj, info := range a.VariableUsage {
|
|
194
|
+
if varObj == obj {
|
|
195
|
+
for _, src := range info.Sources {
|
|
196
|
+
if src.Type == AddressOfAssignment && src.Object != nil {
|
|
197
|
+
// This pointer was assigned &someVar, check if someVar is variable referenced
|
|
198
|
+
return a.NeedsVarRef(src.Object)
|
|
177
199
|
}
|
|
178
200
|
}
|
|
179
201
|
}
|
|
180
202
|
}
|
|
203
|
+
|
|
204
|
+
// Handle direct pointer initialization like: var p *int = &x
|
|
205
|
+
// Check if the pointer type's element type requires variable referencing
|
|
206
|
+
_ = ptrType.Elem()
|
|
207
|
+
// For now, conservatively return false for untracked cases
|
|
181
208
|
}
|
|
182
209
|
|
|
183
210
|
return false
|
|
184
211
|
}
|
|
185
212
|
|
|
186
|
-
//
|
|
187
|
-
//
|
|
213
|
+
// NeedsVarRefDeref determines whether a pointer dereference operation (*ptr) needs
|
|
214
|
+
// additional .value access beyond the standard !.value pattern.
|
|
188
215
|
//
|
|
189
|
-
//
|
|
216
|
+
// Standard pattern: ptr!.value (for *int, *string, etc.)
|
|
217
|
+
// Enhanced pattern: ptr.value!.value (when ptr itself is variable referenced)
|
|
190
218
|
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
// **p => p!.value!.value
|
|
219
|
+
// This function returns true when the pointer variable itself is variable referenced,
|
|
220
|
+
// meaning we need an extra .value to access the actual pointer before dereferencing.
|
|
194
221
|
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
//
|
|
222
|
+
// Examples:
|
|
223
|
+
// - ptr := &x (ptr not variable referenced): *ptr => ptr!.value
|
|
224
|
+
// - ptrPtr := &ptr (ptr is variable referenced): *ptr => ptr.value!.value
|
|
198
225
|
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
if !ok {
|
|
211
|
-
return true // Not a pointer type, default to true
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Get the underlying element type
|
|
215
|
-
elemType := ptrTypeUnwrapped.Elem()
|
|
216
|
-
if elemType == nil {
|
|
217
|
-
return true
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Check if the element is another pointer - if so, we always need .value
|
|
221
|
-
// This fixes the bug with multi-level pointer dereferencing like **p
|
|
222
|
-
if _, isPointer := elemType.(*types.Pointer); isPointer {
|
|
223
|
-
return true
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Check if the element is a struct (directly or via a named type)
|
|
227
|
-
// Bug fix: Struct pointers in TS don't need .value when dereferenced
|
|
228
|
-
// because structs are already references in JavaScript/TypeScript
|
|
229
|
-
if _, isStruct := elemType.Underlying().(*types.Struct); isStruct {
|
|
230
|
-
return false // Pointers to structs don't need .value suffix in direct dereference (*p)
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// For all other cases (primitives, pointers-to-pointers, etc.) need .value
|
|
234
|
-
// This ensures primitives and nested pointers are correctly dereferenced
|
|
235
|
-
return true
|
|
226
|
+
// Args:
|
|
227
|
+
//
|
|
228
|
+
// ptrType: The type of the pointer being dereferenced
|
|
229
|
+
//
|
|
230
|
+
// Returns:
|
|
231
|
+
//
|
|
232
|
+
// true if additional .value access is needed due to the pointer being variable referenced
|
|
233
|
+
func (a *Analysis) NeedsVarRefDeref(ptrType types.Type) bool {
|
|
234
|
+
// For now, return false - this would need more sophisticated analysis
|
|
235
|
+
// to track when pointer variables themselves are variable referenced
|
|
236
|
+
return false
|
|
236
237
|
}
|
|
237
238
|
|
|
238
|
-
//
|
|
239
|
-
//
|
|
239
|
+
// NeedsVarRefFieldAccess determines whether a pointer variable needs the .value
|
|
240
|
+
// access when performing field access through the pointer.
|
|
240
241
|
//
|
|
241
|
-
//
|
|
242
|
-
// The critical discovery was that field access through a pointer depends not on the
|
|
243
|
-
// field itself, but on whether the pointer variable is boxed:
|
|
242
|
+
// In Go, field access through pointers is automatically dereferenced:
|
|
244
243
|
//
|
|
245
|
-
//
|
|
246
|
-
// Example: let ptr = new MyStruct() => ptr.field
|
|
247
|
-
// (Common case from &MyStruct{} literals)
|
|
244
|
+
// ptr.Field // equivalent to (*ptr).Field
|
|
248
245
|
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
246
|
+
// In TypeScript, we need to determine if the pointer is:
|
|
247
|
+
// 1. A simple pointer: ptr.Field (no .value needed)
|
|
248
|
+
// 2. A variable-referenced pointer: ptr.value.Field (needs .value)
|
|
251
249
|
//
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
if !ok {
|
|
263
|
-
return false // Not a pointer type, no dereference needed for field access
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// Check if the element is a struct (directly or via a named type)
|
|
267
|
-
elemType := ptrTypeUnwrapped.Elem()
|
|
268
|
-
if elemType == nil {
|
|
269
|
-
return false // Not pointing to anything
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// For pointers to structs, check if it's a struct type first
|
|
273
|
-
_, isStruct := elemType.Underlying().(*types.Struct)
|
|
274
|
-
if !isStruct {
|
|
275
|
-
return false // Not a pointer to a struct
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// The critical decision: We'll determine if .value is needed in the WriteSelectorExpr function
|
|
279
|
-
// by checking if the pointer variable itself is boxed.
|
|
280
|
-
// This allows us to handle both:
|
|
281
|
-
// - ptr := &MyStruct{} (unboxed, direct access)
|
|
282
|
-
// - ptrToVal := &val (boxed, needs .value)
|
|
250
|
+
// Args:
|
|
251
|
+
//
|
|
252
|
+
// ptrType: The pointer type being used for field access
|
|
253
|
+
//
|
|
254
|
+
// Returns:
|
|
255
|
+
//
|
|
256
|
+
// true if .value access is needed before field access
|
|
257
|
+
func (a *Analysis) NeedsVarRefFieldAccess(ptrType types.Type) bool {
|
|
258
|
+
// This would require analysis of the specific pointer variable
|
|
259
|
+
// For now, return false as a conservative default
|
|
283
260
|
return false
|
|
284
261
|
}
|
|
285
262
|
|
|
@@ -302,6 +279,12 @@ type analysisVisitor struct {
|
|
|
302
279
|
|
|
303
280
|
// currentFuncObj tracks the object of the function declaration we're currently analyzing
|
|
304
281
|
currentFuncObj types.Object
|
|
282
|
+
|
|
283
|
+
// currentFuncDecl tracks the *ast.FuncDecl of the function we're currently analyzing.
|
|
284
|
+
currentFuncDecl *ast.FuncDecl
|
|
285
|
+
|
|
286
|
+
// currentFuncLit tracks the *ast.FuncLit of the function literal we're currently analyzing.
|
|
287
|
+
currentFuncLit *ast.FuncLit
|
|
305
288
|
}
|
|
306
289
|
|
|
307
290
|
// getOrCreateUsageInfo retrieves or creates the VariableUsageInfo for a given object.
|
|
@@ -324,8 +307,11 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
324
307
|
return nil
|
|
325
308
|
}
|
|
326
309
|
|
|
327
|
-
//
|
|
328
|
-
v.analysis.
|
|
310
|
+
// Initialize and store async state for the current node
|
|
311
|
+
if v.analysis.NodeData[node] == nil {
|
|
312
|
+
v.analysis.NodeData[node] = &NodeInfo{}
|
|
313
|
+
}
|
|
314
|
+
v.analysis.NodeData[node].InAsyncContext = v.inAsyncFunction
|
|
329
315
|
|
|
330
316
|
switch n := node.(type) {
|
|
331
317
|
case *ast.GenDecl:
|
|
@@ -393,24 +379,40 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
393
379
|
return v
|
|
394
380
|
|
|
395
381
|
case *ast.FuncDecl:
|
|
396
|
-
//
|
|
382
|
+
// Save original states to restore after visiting
|
|
383
|
+
originalInAsync := v.inAsyncFunction
|
|
384
|
+
originalFuncObj := v.currentFuncObj
|
|
385
|
+
originalFuncDecl := v.currentFuncDecl
|
|
386
|
+
originalFuncLit := v.currentFuncLit
|
|
387
|
+
originalReceiver := v.currentReceiver
|
|
388
|
+
|
|
389
|
+
// Reset for current function
|
|
397
390
|
v.currentFuncName = n.Name.Name
|
|
391
|
+
v.currentFuncDecl = n
|
|
392
|
+
v.currentFuncLit = nil
|
|
393
|
+
v.currentReceiver = nil
|
|
394
|
+
|
|
395
|
+
// Determine if this function declaration is async based on its body
|
|
398
396
|
isAsync := false
|
|
399
397
|
if n.Body != nil {
|
|
400
398
|
containsAsyncOps := v.containsAsyncOperations(n.Body)
|
|
401
399
|
if containsAsyncOps {
|
|
402
400
|
// Get the object for this function declaration
|
|
403
401
|
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
404
|
-
v.analysis.
|
|
402
|
+
v.analysis.FunctionData[obj] = &FunctionInfo{
|
|
403
|
+
IsAsync: true,
|
|
404
|
+
NamedReturns: v.getNamedReturns(n),
|
|
405
|
+
}
|
|
406
|
+
isAsync = true
|
|
405
407
|
}
|
|
406
|
-
isAsync = true
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
|
-
v.analysis.
|
|
410
|
+
if v.analysis.NodeData[n] == nil {
|
|
411
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
412
|
+
}
|
|
413
|
+
v.analysis.NodeData[n].InAsyncContext = isAsync
|
|
410
414
|
|
|
411
415
|
// Set current receiver if this is a method
|
|
412
|
-
originalReceiver := v.currentReceiver
|
|
413
|
-
v.currentReceiver = nil // Reset for current function
|
|
414
416
|
if n.Recv != nil && len(n.Recv.List) > 0 {
|
|
415
417
|
// Assuming a single receiver for simplicity for now
|
|
416
418
|
if len(n.Recv.List[0].Names) > 0 {
|
|
@@ -419,7 +421,7 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
419
421
|
if vr, ok := def.(*types.Var); ok {
|
|
420
422
|
v.currentReceiver = vr
|
|
421
423
|
// Add the receiver variable to the VariableUsage map
|
|
422
|
-
// to ensure it is properly analyzed for
|
|
424
|
+
// to ensure it is properly analyzed for varRefing
|
|
423
425
|
v.getOrCreateUsageInfo(v.currentReceiver)
|
|
424
426
|
}
|
|
425
427
|
}
|
|
@@ -427,22 +429,41 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
427
429
|
}
|
|
428
430
|
}
|
|
429
431
|
|
|
430
|
-
//
|
|
431
|
-
|
|
432
|
-
|
|
432
|
+
// Store named return variables
|
|
433
|
+
if n.Type != nil && n.Type.Results != nil {
|
|
434
|
+
var namedReturns []string
|
|
435
|
+
for _, field := range n.Type.Results.List {
|
|
436
|
+
for _, name := range field.Names {
|
|
437
|
+
namedReturns = append(namedReturns, name.Name)
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
if len(namedReturns) > 0 {
|
|
441
|
+
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
442
|
+
if v.analysis.FunctionData[obj] == nil {
|
|
443
|
+
v.analysis.FunctionData[obj] = &FunctionInfo{}
|
|
444
|
+
}
|
|
445
|
+
v.analysis.FunctionData[obj].NamedReturns = namedReturns
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
433
449
|
|
|
434
450
|
// Update visitor state for this function
|
|
435
451
|
v.inAsyncFunction = isAsync
|
|
436
452
|
v.currentFuncObj = v.pkg.TypesInfo.ObjectOf(n.Name)
|
|
437
|
-
v.analysis.
|
|
453
|
+
v.analysis.NodeData[n].InAsyncContext = isAsync // Ensure FuncDecl node itself is marked
|
|
438
454
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
v.
|
|
442
|
-
|
|
455
|
+
if n.Body != nil {
|
|
456
|
+
// Check if the body contains any defer statements
|
|
457
|
+
if v.containsDefer(n.Body) {
|
|
458
|
+
if v.analysis.NodeData[n] == nil {
|
|
459
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
460
|
+
}
|
|
461
|
+
v.analysis.NodeData[n].NeedsDefer = true
|
|
462
|
+
}
|
|
443
463
|
|
|
444
|
-
|
|
445
|
-
|
|
464
|
+
// Visit the body with updated state
|
|
465
|
+
ast.Walk(v, n.Body)
|
|
466
|
+
}
|
|
446
467
|
|
|
447
468
|
// Restore states after visiting
|
|
448
469
|
defer func() {
|
|
@@ -450,21 +471,52 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
450
471
|
v.inAsyncFunction = originalInAsync
|
|
451
472
|
v.currentReceiver = originalReceiver
|
|
452
473
|
v.currentFuncObj = originalFuncObj
|
|
474
|
+
v.currentFuncDecl = originalFuncDecl
|
|
475
|
+
v.currentFuncLit = originalFuncLit
|
|
453
476
|
}()
|
|
454
477
|
return nil // Stop traversal here, ast.Walk handled the body
|
|
455
478
|
|
|
456
479
|
case *ast.FuncLit:
|
|
480
|
+
// Save original inAsyncFunction state to restore after visiting
|
|
481
|
+
originalInAsync := v.inAsyncFunction
|
|
482
|
+
originalFuncDecl := v.currentFuncDecl
|
|
483
|
+
originalFuncLit := v.currentFuncLit
|
|
484
|
+
|
|
485
|
+
// Set current function literal
|
|
486
|
+
v.currentFuncDecl = nil
|
|
487
|
+
v.currentFuncLit = n
|
|
488
|
+
|
|
457
489
|
// Determine if this function literal is async based on its body
|
|
458
490
|
isAsync := v.containsAsyncOperations(n.Body)
|
|
459
|
-
v.analysis.
|
|
491
|
+
if v.analysis.NodeData[n] == nil {
|
|
492
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
493
|
+
}
|
|
494
|
+
v.analysis.NodeData[n].InAsyncContext = isAsync
|
|
495
|
+
|
|
496
|
+
// Store named return variables for function literal
|
|
497
|
+
if n.Type != nil && n.Type.Results != nil {
|
|
498
|
+
var namedReturns []string
|
|
499
|
+
for _, field := range n.Type.Results.List {
|
|
500
|
+
for _, name := range field.Names {
|
|
501
|
+
namedReturns = append(namedReturns, name.Name)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if len(namedReturns) > 0 {
|
|
505
|
+
v.analysis.FuncLitData[n] = &FunctionInfo{
|
|
506
|
+
IsAsync: isAsync,
|
|
507
|
+
NamedReturns: namedReturns,
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
460
511
|
|
|
461
|
-
// Save original inAsyncFunction state to restore after visiting
|
|
462
|
-
originalInAsync := v.inAsyncFunction
|
|
463
512
|
v.inAsyncFunction = isAsync
|
|
464
513
|
|
|
465
514
|
// Check if the body contains any defer statements
|
|
466
515
|
if n.Body != nil && v.containsDefer(n.Body) {
|
|
467
|
-
v.analysis.
|
|
516
|
+
if v.analysis.NodeData[n] == nil {
|
|
517
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
518
|
+
}
|
|
519
|
+
v.analysis.NodeData[n].NeedsDefer = true
|
|
468
520
|
}
|
|
469
521
|
|
|
470
522
|
// Visit the body with updated state
|
|
@@ -472,23 +524,34 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
472
524
|
|
|
473
525
|
// Restore inAsyncFunction state after visiting
|
|
474
526
|
v.inAsyncFunction = originalInAsync
|
|
527
|
+
v.currentFuncDecl = originalFuncDecl
|
|
528
|
+
v.currentFuncLit = originalFuncLit
|
|
475
529
|
return nil // Stop traversal here, ast.Walk handled the body
|
|
476
530
|
|
|
477
531
|
case *ast.BlockStmt:
|
|
532
|
+
if n == nil || len(n.List) == 0 {
|
|
533
|
+
break
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Initialize NodeData for this block
|
|
537
|
+
if v.analysis.NodeData[n] == nil {
|
|
538
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
539
|
+
}
|
|
540
|
+
|
|
478
541
|
// Check for defer statements in this block
|
|
479
542
|
if v.containsDefer(n) {
|
|
480
|
-
v.analysis.
|
|
543
|
+
v.analysis.NodeData[n].NeedsDefer = true
|
|
481
544
|
}
|
|
482
545
|
|
|
483
546
|
// Store async state for this block
|
|
484
|
-
v.analysis.
|
|
547
|
+
v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
|
|
485
548
|
|
|
486
549
|
return v
|
|
487
550
|
|
|
488
551
|
case *ast.UnaryExpr:
|
|
489
552
|
// We handle address-of (&) within AssignStmt where it's actually used.
|
|
490
553
|
// Standalone &x doesn't directly assign, but its usage in assignments
|
|
491
|
-
// or function calls determines
|
|
554
|
+
// or function calls determines varRefing. Assignments are handled below.
|
|
492
555
|
// Function calls like foo(&x) would require different tracking if needed.
|
|
493
556
|
// For now, we focus on assignments as per the request.
|
|
494
557
|
return v
|
|
@@ -500,12 +563,18 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
500
563
|
if obj := v.pkg.TypesInfo.Uses[funcIdent]; obj != nil && v.analysis.IsAsyncFunc(obj) {
|
|
501
564
|
// We're calling an async function, so mark current function as async if we're in one
|
|
502
565
|
if v.currentFuncObj != nil {
|
|
503
|
-
v.analysis.
|
|
566
|
+
v.analysis.FunctionData[v.currentFuncObj] = &FunctionInfo{
|
|
567
|
+
IsAsync: true,
|
|
568
|
+
NamedReturns: v.getNamedReturns(v.currentFuncDecl),
|
|
569
|
+
}
|
|
504
570
|
v.inAsyncFunction = true // Update visitor state
|
|
505
571
|
// Mark the FuncDecl node itself if possible (might need to store the node too)
|
|
506
|
-
for nodeAst := range v.analysis.
|
|
572
|
+
for nodeAst := range v.analysis.NodeData { // Find the node to update
|
|
507
573
|
if fd, ok := nodeAst.(*ast.FuncDecl); ok && v.pkg.TypesInfo.ObjectOf(fd.Name) == v.currentFuncObj {
|
|
508
|
-
v.analysis.
|
|
574
|
+
if v.analysis.NodeData[nodeAst] == nil {
|
|
575
|
+
v.analysis.NodeData[nodeAst] = &NodeInfo{}
|
|
576
|
+
}
|
|
577
|
+
v.analysis.NodeData[nodeAst].InAsyncContext = true
|
|
509
578
|
}
|
|
510
579
|
}
|
|
511
580
|
}
|
|
@@ -513,7 +582,10 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
513
582
|
}
|
|
514
583
|
|
|
515
584
|
// Store async state for this call expression
|
|
516
|
-
v.analysis.
|
|
585
|
+
if v.analysis.NodeData[n] == nil {
|
|
586
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
587
|
+
}
|
|
588
|
+
v.analysis.NodeData[n].InAsyncContext = v.inAsyncFunction
|
|
517
589
|
|
|
518
590
|
return v
|
|
519
591
|
|
|
@@ -594,7 +666,7 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
594
666
|
|
|
595
667
|
// 2. If RHS involved a source variable (rhsSourceObj is not nil),
|
|
596
668
|
// record that this source variable was used (its destinations).
|
|
597
|
-
// This is CRITICAL for
|
|
669
|
+
// This is CRITICAL for varRefing analysis (e.g., if &rhsSourceObj was assigned).
|
|
598
670
|
if rhsSourceObj != nil {
|
|
599
671
|
sourceUsageInfo := v.getOrCreateUsageInfo(rhsSourceObj)
|
|
600
672
|
// The 'Object' in DestinationInfo is what/where rhsSourceObj (or its address) was assigned TO.
|
|
@@ -610,14 +682,46 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
610
682
|
}
|
|
611
683
|
return v // Continue traversal
|
|
612
684
|
|
|
613
|
-
case *ast.
|
|
614
|
-
//
|
|
615
|
-
|
|
685
|
+
case *ast.ReturnStmt:
|
|
686
|
+
// Initialize NodeData for return statement
|
|
687
|
+
if v.analysis.NodeData[n] == nil {
|
|
688
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
689
|
+
}
|
|
616
690
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
691
|
+
// Record the enclosing function/literal for this return statement
|
|
692
|
+
if v.currentFuncDecl != nil {
|
|
693
|
+
v.analysis.NodeData[n].EnclosingFuncDecl = v.currentFuncDecl
|
|
694
|
+
} else if v.currentFuncLit != nil {
|
|
695
|
+
v.analysis.NodeData[n].EnclosingFuncLit = v.currentFuncLit
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Check if it's a bare return
|
|
699
|
+
if len(n.Results) == 0 {
|
|
700
|
+
if v.currentFuncDecl != nil {
|
|
701
|
+
// Check if the enclosing function declaration has named returns
|
|
702
|
+
if obj := v.pkg.TypesInfo.ObjectOf(v.currentFuncDecl.Name); obj != nil {
|
|
703
|
+
if _, ok := v.analysis.FunctionData[obj]; ok {
|
|
704
|
+
if v.analysis.NodeData[n] == nil {
|
|
705
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
706
|
+
}
|
|
707
|
+
v.analysis.NodeData[n].IsBareReturn = true
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
} else if v.currentFuncLit != nil {
|
|
711
|
+
// Check if the enclosing function literal has named returns
|
|
712
|
+
if _, ok := v.analysis.FuncLitData[v.currentFuncLit]; ok {
|
|
713
|
+
if v.analysis.NodeData[n] == nil {
|
|
714
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
715
|
+
}
|
|
716
|
+
v.analysis.NodeData[n].IsBareReturn = true
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return v // Continue traversal
|
|
620
721
|
}
|
|
722
|
+
|
|
723
|
+
// For all other nodes, continue traversal
|
|
724
|
+
return v
|
|
621
725
|
}
|
|
622
726
|
|
|
623
727
|
// containsAsyncOperations checks if a node contains any async operations like channel operations.
|
|
@@ -666,6 +770,9 @@ func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
|
|
|
666
770
|
hasDefer := false
|
|
667
771
|
|
|
668
772
|
ast.Inspect(block, func(n ast.Node) bool {
|
|
773
|
+
if n == nil {
|
|
774
|
+
return true
|
|
775
|
+
}
|
|
669
776
|
if _, ok := n.(*ast.DeferStmt); ok {
|
|
670
777
|
hasDefer = true
|
|
671
778
|
return false
|
|
@@ -677,7 +784,7 @@ func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
|
|
|
677
784
|
}
|
|
678
785
|
|
|
679
786
|
// AnalyzeFile analyzes a Go source file AST and populates the Analysis struct with information
|
|
680
|
-
// that will be used during code generation to properly handle pointers, variables that need
|
|
787
|
+
// that will be used during code generation to properly handle pointers, variables that need varRefing, etc.
|
|
681
788
|
func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap ast.CommentMap) {
|
|
682
789
|
// Store the comment map in the analysis object
|
|
683
790
|
analysis.Cmap = cmap
|
|
@@ -724,3 +831,16 @@ func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap
|
|
|
724
831
|
// Walk the AST with our visitor
|
|
725
832
|
ast.Walk(visitor, file)
|
|
726
833
|
}
|
|
834
|
+
|
|
835
|
+
// getNamedReturns retrieves the named returns for a function
|
|
836
|
+
func (v *analysisVisitor) getNamedReturns(funcDecl *ast.FuncDecl) []string {
|
|
837
|
+
var namedReturns []string
|
|
838
|
+
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
|
|
839
|
+
for _, field := range funcDecl.Type.Results.List {
|
|
840
|
+
for _, name := range field.Names {
|
|
841
|
+
namedReturns = append(namedReturns, name.Name)
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return namedReturns
|
|
846
|
+
}
|