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.
Files changed (66) hide show
  1. package/README.md +1 -1
  2. package/cmd/goscript/cmd_compile.go +3 -3
  3. package/compiler/analysis.go +302 -182
  4. package/compiler/analysis_test.go +220 -0
  5. package/compiler/assignment.go +42 -43
  6. package/compiler/builtin_test.go +102 -0
  7. package/compiler/compiler.go +117 -29
  8. package/compiler/compiler_test.go +36 -8
  9. package/compiler/composite-lit.go +133 -53
  10. package/compiler/config.go +7 -3
  11. package/compiler/config_test.go +6 -33
  12. package/compiler/decl.go +36 -0
  13. package/compiler/expr-call.go +116 -60
  14. package/compiler/expr-selector.go +88 -43
  15. package/compiler/expr-star.go +57 -65
  16. package/compiler/expr-type.go +132 -5
  17. package/compiler/expr-value.go +8 -38
  18. package/compiler/expr.go +326 -30
  19. package/compiler/field.go +3 -3
  20. package/compiler/lit.go +34 -2
  21. package/compiler/primitive.go +19 -12
  22. package/compiler/spec-struct.go +140 -9
  23. package/compiler/spec-value.go +119 -41
  24. package/compiler/spec.go +21 -6
  25. package/compiler/stmt-assign.go +65 -3
  26. package/compiler/stmt-for.go +11 -0
  27. package/compiler/stmt-range.go +119 -11
  28. package/compiler/stmt-select.go +211 -0
  29. package/compiler/stmt-type-switch.go +147 -0
  30. package/compiler/stmt.go +175 -238
  31. package/compiler/type-assert.go +125 -379
  32. package/compiler/type.go +216 -129
  33. package/dist/gs/builtin/builtin.js +37 -0
  34. package/dist/gs/builtin/builtin.js.map +1 -0
  35. package/dist/gs/builtin/channel.js +471 -0
  36. package/dist/gs/builtin/channel.js.map +1 -0
  37. package/dist/gs/builtin/defer.js +54 -0
  38. package/dist/gs/builtin/defer.js.map +1 -0
  39. package/dist/gs/builtin/io.js +15 -0
  40. package/dist/gs/builtin/io.js.map +1 -0
  41. package/dist/gs/builtin/map.js +44 -0
  42. package/dist/gs/builtin/map.js.map +1 -0
  43. package/dist/gs/builtin/slice.js +799 -0
  44. package/dist/gs/builtin/slice.js.map +1 -0
  45. package/dist/gs/builtin/type.js +745 -0
  46. package/dist/gs/builtin/type.js.map +1 -0
  47. package/dist/gs/builtin/varRef.js +14 -0
  48. package/dist/gs/builtin/varRef.js.map +1 -0
  49. package/dist/gs/context/context.js +55 -0
  50. package/dist/gs/context/context.js.map +1 -0
  51. package/dist/gs/context/index.js +2 -0
  52. package/dist/gs/context/index.js.map +1 -0
  53. package/dist/gs/runtime/index.js +2 -0
  54. package/dist/gs/runtime/index.js.map +1 -0
  55. package/dist/gs/runtime/runtime.js +158 -0
  56. package/dist/gs/runtime/runtime.js.map +1 -0
  57. package/dist/gs/time/index.js +2 -0
  58. package/dist/gs/time/index.js.map +1 -0
  59. package/dist/gs/time/time.js +115 -0
  60. package/dist/gs/time/time.js.map +1 -0
  61. package/package.json +7 -6
  62. package/builtin/builtin.go +0 -11
  63. package/builtin/builtin.ts +0 -2379
  64. package/dist/builtin/builtin.d.ts +0 -513
  65. package/dist/builtin/builtin.js +0 -1686
  66. package/dist/builtin/builtin.js.map +0 -1
@@ -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
- // AsyncFuncs tracks which functions are async using the function's types.Object
59
- // as the key to avoid false-positive matches with functions having the same name
60
- AsyncFuncs map[types.Object]bool
73
+ // FunctionData consolidates function-related tracking into one map
74
+ FunctionData map[types.Object]*FunctionInfo
61
75
 
62
- // NeedsDeferMap tracks nodes that need defer handling
63
- NeedsDeferMap map[ast.Node]bool
76
+ // NodeData consolidates node-related tracking into one map
77
+ NodeData map[ast.Node]*NodeInfo
64
78
 
65
- // IsInAsyncFunctionMap tracks nodes that are inside async functions
66
- IsInAsyncFunctionMap map[ast.Node]bool
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: make(map[types.Object]*VariableUsageInfo),
73
- Imports: make(map[string]*fileImport),
74
- AsyncFuncs: make(map[types.Object]bool),
75
- NeedsDeferMap: make(map[ast.Node]bool),
76
- IsInAsyncFunctionMap: make(map[ast.Node]bool),
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
- return a.NeedsDeferMap[node]
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
- return a.IsInAsyncFunctionMap[node]
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
- return a.AsyncFuncs[obj]
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
- // Function literals are marked during analysis if they contain async operations
110
- return a.IsInAsyncFunctionMap[funcLit]
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
- // NeedsBoxed returns whether the given object needs to be boxed.
114
- // According to the new logic, a variable needs boxing if its address is taken
115
- // and assigned to another variable (i.e., it appears as a destination with AddressOfAssignment).
116
- func (a *Analysis) NeedsBoxed(obj types.Object) bool {
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
- // NeedsBoxedAccess returns whether accessing the given object requires '.value' access in TypeScript.
134
- // This function is critical for correctly handling pointer dereferencing by determining when
135
- // a variable is boxed and needs .value to access its content.
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
- // 2. For pointer variables: it points to a boxed struct variable rather than a direct struct literal
143
- // Example: let ptrToVal = val (where val is boxed) => ptrToVal.value
144
- // vs. let ptr = new MyStruct() => ptr (no .value needed)
145
- //
146
- // This distinction is crucial for avoiding over-dereferencing or under-dereferencing,
147
- // which was the root cause of several bugs in our pointer handling.
148
- func (a *Analysis) NeedsBoxedAccess(obj types.Object) bool {
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
- // First, check if the variable itself is boxed - this always requires .value
154
- // A variable is boxed if its address is taken elsewhere in the code
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
- // Check if this is a pointer variable pointing to a boxed struct value
160
- objType := obj.Type()
161
- if ptrType, isPointer := objType.Underlying().(*types.Pointer); isPointer {
162
- // Check if it's a pointer to a struct
163
- if elemType := ptrType.Elem(); elemType != nil {
164
- if _, isStructType := elemType.Underlying().(*types.Struct); isStructType {
165
- // For struct pointers, check if it points to a boxed struct variable
166
- if usageInfo, exists := a.VariableUsage[obj]; exists {
167
- for _, src := range usageInfo.Sources {
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
- // NeedsBoxedDeref determines whether a pointer dereference operation (*ptr) needs
187
- // the .value suffix in TypeScript when used in a direct dereference expression.
213
+ // NeedsVarRefDeref determines whether a pointer dereference operation (*ptr) needs
214
+ // additional .value access beyond the standard !.value pattern.
188
215
  //
189
- // Critical distinction (source of bugs):
216
+ // Standard pattern: ptr!.value (for *int, *string, etc.)
217
+ // Enhanced pattern: ptr.value!.value (when ptr itself is variable referenced)
190
218
  //
191
- // 1. For primitive types and pointers-to-primitive: Need .value
192
- // *p => p!.value
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
- // 2. For pointers to structs: No .value needed because structs are references
196
- // *p => p!
197
- // Where p is a pointer to a struct
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
- // This distinction is essential because in TypeScript:
200
- // - Primitives are stored inside $.Box with a .value property
201
- // - Structs are reference types, so dereferencing just removes the null possibility
202
- func (a *Analysis) NeedsBoxedDeref(ptrType types.Type) bool {
203
- // If we don't have a valid pointer type, default to true (safer)
204
- if ptrType == nil {
205
- return true
206
- }
207
-
208
- // Unwrap the pointer to get the element type
209
- ptrTypeUnwrapped, ok := ptrType.(*types.Pointer)
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
- // NeedsBoxedFieldAccess determines whether a pointer variable needs the .value
239
- // suffix when accessing fields (e.g., ptr.field).
239
+ // NeedsVarRefFieldAccess determines whether a pointer variable needs the .value
240
+ // access when performing field access through the pointer.
240
241
  //
241
- // Bug fix: This function was a major source of issues with struct field access.
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
- // 1. For normal struct pointers (unboxed): No .value needed
246
- // Example: let ptr = new MyStruct() => ptr.field
247
- // (Common case from &MyStruct{} literals)
244
+ // ptr.Field // equivalent to (*ptr).Field
248
245
  //
249
- // 2. For boxed struct pointers: Need .value to access the pointed-to struct
250
- // Example: let ptrToVal = val (where val is boxed) => ptrToVal.value.field
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
- // We ultimately delegated this decision to WriteSelectorExpr which examines
253
- // the actual variable to determine if it's boxed, rather than just the type.
254
- func (a *Analysis) NeedsBoxedFieldAccess(ptrType types.Type) bool {
255
- // If we don't have a valid pointer type, default to false
256
- if ptrType == nil {
257
- return false
258
- }
259
-
260
- // Unwrap the pointer to get the element type
261
- ptrTypeUnwrapped, ok := ptrType.(*types.Pointer)
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
- // Store async state for the current node
328
- v.analysis.IsInAsyncFunctionMap[node] = v.inAsyncFunction
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
- // Determine if this function declaration is async based on its body
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.AsyncFuncs[obj] = true
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.IsInAsyncFunctionMap[n] = isAsync
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 boxing
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
- // Save original states to restore after visiting
431
- originalInAsync := v.inAsyncFunction
432
- originalFuncObj := v.currentFuncObj
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.IsInAsyncFunctionMap[n] = isAsync // Ensure FuncDecl node itself is marked
453
+ v.analysis.NodeData[n].InAsyncContext = isAsync // Ensure FuncDecl node itself is marked
438
454
 
439
- // Check if the body contains any defer statements
440
- if n.Body != nil && v.containsDefer(n.Body) {
441
- v.analysis.NeedsDeferMap[n.Body] = true
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
- // Visit the body with updated state
445
- ast.Walk(v, n.Body)
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.IsInAsyncFunctionMap[n] = isAsync
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.NeedsDeferMap[n.Body] = true
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.NeedsDeferMap[n] = true
543
+ v.analysis.NodeData[n].NeedsDefer = true
481
544
  }
482
545
 
483
546
  // Store async state for this block
484
- v.analysis.IsInAsyncFunctionMap[n] = v.inAsyncFunction
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 boxing. Assignments are handled below.
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.AsyncFuncs[v.currentFuncObj] = true
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.IsInAsyncFunctionMap { // Find the node to update
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.IsInAsyncFunctionMap[nodeAst] = true
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.IsInAsyncFunctionMap[n] = v.inAsyncFunction
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 boxing analysis (e.g., if &rhsSourceObj was assigned).
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.CompositeLit:
614
- // No need to track private field access in composite literals since all fields are public
615
- return v
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
- default:
618
- // For all other nodes, continue traversal
619
- return v
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 boxing, etc.
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
+ }