goscript 0.0.61 → 0.0.63

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 (92) hide show
  1. package/README.md +62 -46
  2. package/compiler/analysis.go +621 -19
  3. package/compiler/analysis_test.go +3 -3
  4. package/compiler/assignment.go +100 -0
  5. package/compiler/builtin_test.go +1 -1
  6. package/compiler/compiler.go +76 -16
  7. package/compiler/compiler_test.go +9 -9
  8. package/compiler/composite-lit.go +29 -8
  9. package/compiler/decl.go +20 -11
  10. package/compiler/expr-call-async.go +26 -1
  11. package/compiler/expr-call-builtins.go +60 -4
  12. package/compiler/expr-call-type-conversion.go +37 -5
  13. package/compiler/expr-call.go +26 -6
  14. package/compiler/expr-selector.go +35 -2
  15. package/compiler/expr-type.go +12 -2
  16. package/compiler/expr.go +61 -0
  17. package/compiler/index.test.ts +3 -1
  18. package/compiler/lit.go +13 -4
  19. package/compiler/spec-struct.go +30 -8
  20. package/compiler/spec-value.go +2 -2
  21. package/compiler/spec.go +23 -4
  22. package/compiler/stmt-assign.go +124 -0
  23. package/compiler/stmt-range.go +2 -2
  24. package/compiler/stmt.go +160 -14
  25. package/compiler/type-info.go +3 -5
  26. package/compiler/type-utils.go +40 -1
  27. package/compiler/type.go +52 -14
  28. package/dist/gs/builtin/builtin.d.ts +8 -1
  29. package/dist/gs/builtin/builtin.js +26 -1
  30. package/dist/gs/builtin/builtin.js.map +1 -1
  31. package/dist/gs/builtin/errors.d.ts +1 -0
  32. package/dist/gs/builtin/errors.js +8 -0
  33. package/dist/gs/builtin/errors.js.map +1 -1
  34. package/dist/gs/builtin/slice.d.ts +5 -4
  35. package/dist/gs/builtin/slice.js +88 -51
  36. package/dist/gs/builtin/slice.js.map +1 -1
  37. package/dist/gs/builtin/type.d.ts +23 -2
  38. package/dist/gs/builtin/type.js +125 -0
  39. package/dist/gs/builtin/type.js.map +1 -1
  40. package/dist/gs/builtin/varRef.d.ts +3 -0
  41. package/dist/gs/builtin/varRef.js +6 -1
  42. package/dist/gs/builtin/varRef.js.map +1 -1
  43. package/dist/gs/bytes/reader.gs.d.ts +1 -1
  44. package/dist/gs/bytes/reader.gs.js +1 -1
  45. package/dist/gs/bytes/reader.gs.js.map +1 -1
  46. package/dist/gs/reflect/index.d.ts +2 -2
  47. package/dist/gs/reflect/index.js +1 -1
  48. package/dist/gs/reflect/index.js.map +1 -1
  49. package/dist/gs/reflect/map.d.ts +3 -2
  50. package/dist/gs/reflect/map.js +37 -3
  51. package/dist/gs/reflect/map.js.map +1 -1
  52. package/dist/gs/reflect/type.d.ts +53 -12
  53. package/dist/gs/reflect/type.js +906 -31
  54. package/dist/gs/reflect/type.js.map +1 -1
  55. package/dist/gs/reflect/types.d.ts +11 -12
  56. package/dist/gs/reflect/types.js +26 -15
  57. package/dist/gs/reflect/types.js.map +1 -1
  58. package/dist/gs/reflect/value.d.ts +4 -4
  59. package/dist/gs/reflect/value.js +8 -2
  60. package/dist/gs/reflect/value.js.map +1 -1
  61. package/dist/gs/slices/slices.d.ts +21 -0
  62. package/dist/gs/slices/slices.js +48 -0
  63. package/dist/gs/slices/slices.js.map +1 -1
  64. package/dist/gs/strconv/atoi.gs.js +20 -2
  65. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  66. package/dist/gs/sync/atomic/type.gs.d.ts +3 -3
  67. package/dist/gs/sync/atomic/type.gs.js +13 -7
  68. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  69. package/dist/gs/unicode/utf8/utf8.d.ts +2 -2
  70. package/dist/gs/unicode/utf8/utf8.js +10 -6
  71. package/dist/gs/unicode/utf8/utf8.js.map +1 -1
  72. package/go.mod +6 -6
  73. package/go.sum +12 -8
  74. package/gs/builtin/builtin.ts +27 -2
  75. package/gs/builtin/errors.ts +12 -0
  76. package/gs/builtin/slice.ts +126 -55
  77. package/gs/builtin/type.ts +159 -2
  78. package/gs/builtin/varRef.ts +8 -2
  79. package/gs/bytes/reader.gs.ts +2 -2
  80. package/gs/math/hypot.gs.test.ts +3 -1
  81. package/gs/math/pow10.gs.test.ts +5 -4
  82. package/gs/reflect/index.ts +3 -2
  83. package/gs/reflect/map.test.ts +7 -6
  84. package/gs/reflect/map.ts +49 -7
  85. package/gs/reflect/type.ts +1150 -57
  86. package/gs/reflect/types.ts +34 -21
  87. package/gs/reflect/value.ts +12 -6
  88. package/gs/slices/slices.ts +55 -0
  89. package/gs/strconv/atoi.gs.ts +18 -2
  90. package/gs/sync/atomic/type.gs.ts +15 -10
  91. package/gs/unicode/utf8/utf8.ts +12 -8
  92. package/package.json +23 -14
@@ -52,6 +52,10 @@ type ShadowingInfo struct {
52
52
  ShadowedVariables map[string]types.Object
53
53
  // TempVariables maps shadowed variable names to temporary variable names
54
54
  TempVariables map[string]string
55
+ // TypeShadowedVars maps variable names that shadow type names to their renamed identifier
56
+ // This happens when a variable name matches a type name used in its initialization
57
+ // e.g., field := field{...} where the variable 'field' shadows the type 'field'
58
+ TypeShadowedVars map[string]string
55
59
  }
56
60
 
57
61
  // FunctionTypeInfo represents Go function type information for reflection
@@ -141,6 +145,11 @@ type Analysis struct {
141
145
  // FunctionAssignments tracks which function literals are assigned to which variables
142
146
  FunctionAssignments map[types.Object]ast.Node
143
147
 
148
+ // AsyncReturningVars tracks variables whose function type returns async values
149
+ // This happens when a variable is assigned from a higher-order function (like sync.OnceValue)
150
+ // that receives an async function literal as an argument
151
+ AsyncReturningVars map[types.Object]bool
152
+
144
153
  // NamedBasicTypes tracks types that should be implemented as type aliases with standalone functions
145
154
  // This includes named types with basic underlying types (like uint32, string) that have methods
146
155
  NamedBasicTypes map[types.Type]bool
@@ -156,6 +165,16 @@ type Analysis struct {
156
165
  // MethodAsyncStatus stores the async status of all methods analyzed
157
166
  // This is computed once during analysis and reused during code generation
158
167
  MethodAsyncStatus map[MethodKey]bool
168
+
169
+ // ReferencedTypesPerFile tracks which named types are referenced in each file.
170
+ // This is used to filter synthetic imports to only include packages needed
171
+ // by types actually used in each specific file, not all types in the package.
172
+ // Key: file path, Value: set of named types referenced in that file
173
+ ReferencedTypesPerFile map[string]map[*types.Named]bool
174
+
175
+ // SyntheticImportsPerFile stores synthetic imports needed per file.
176
+ // Key: file path, Value: map of package name to import info
177
+ SyntheticImportsPerFile map[string]map[string]*fileImport
159
178
  }
160
179
 
161
180
  // PackageAnalysis holds cross-file analysis data for a package
@@ -175,6 +194,14 @@ type PackageAnalysis struct {
175
194
  // TypeCalls maps file names to the types they reference from other files
176
195
  // Key: filename (without .go extension), Value: map[sourceFile][]typeNames
177
196
  TypeCalls map[string]map[string][]string
197
+
198
+ // VariableDefs maps file names to the package-level variables defined in that file
199
+ // Key: filename (without .go extension), Value: list of variable names
200
+ VariableDefs map[string][]string
201
+
202
+ // VariableCalls maps file names to the package-level variables they reference from other files
203
+ // Key: filename (without .go extension), Value: map[sourceFile][]variableNames
204
+ VariableCalls map[string]map[string][]string
178
205
  }
179
206
 
180
207
  // NewAnalysis creates a new Analysis instance.
@@ -184,18 +211,20 @@ func NewAnalysis(allPackages map[string]*packages.Package) *Analysis {
184
211
  }
185
212
 
186
213
  return &Analysis{
187
- VariableUsage: make(map[types.Object]*VariableUsageInfo),
188
- Imports: make(map[string]*fileImport),
189
- FunctionData: make(map[types.Object]*FunctionInfo),
190
- NodeData: make(map[ast.Node]*NodeInfo),
191
- FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
192
- ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
193
- FunctionAssignments: make(map[types.Object]ast.Node),
194
- // PackageMetadata removed - using MethodAsyncStatus only
214
+ VariableUsage: make(map[types.Object]*VariableUsageInfo),
215
+ Imports: make(map[string]*fileImport),
216
+ FunctionData: make(map[types.Object]*FunctionInfo),
217
+ NodeData: make(map[ast.Node]*NodeInfo),
218
+ FuncLitData: make(map[*ast.FuncLit]*FunctionInfo),
219
+ ReflectedFunctions: make(map[ast.Node]*ReflectedFunctionInfo),
220
+ FunctionAssignments: make(map[types.Object]ast.Node),
221
+ AsyncReturningVars: make(map[types.Object]bool),
195
222
  NamedBasicTypes: make(map[types.Type]bool),
196
223
  AllPackages: allPackages,
197
- InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
198
- MethodAsyncStatus: make(map[MethodKey]bool),
224
+ InterfaceImplementations: make(map[InterfaceMethodKey][]ImplementationInfo),
225
+ MethodAsyncStatus: make(map[MethodKey]bool),
226
+ ReferencedTypesPerFile: make(map[string]map[*types.Named]bool),
227
+ SyntheticImportsPerFile: make(map[string]map[string]*fileImport),
199
228
  }
200
229
  }
201
230
 
@@ -206,6 +235,8 @@ func NewPackageAnalysis() *PackageAnalysis {
206
235
  FunctionCalls: make(map[string]map[string][]string),
207
236
  TypeDefs: make(map[string][]string),
208
237
  TypeCalls: make(map[string]map[string][]string),
238
+ VariableDefs: make(map[string][]string),
239
+ VariableCalls: make(map[string]map[string][]string),
209
240
  }
210
241
  }
211
242
 
@@ -313,6 +344,15 @@ func (a *Analysis) IsAsyncFunc(obj types.Object) bool {
313
344
  return false
314
345
  }
315
346
 
347
+ // IsAsyncReturningVar returns whether the given variable holds a function that returns async values.
348
+ // This is true when the variable is assigned from a higher-order function that receives an async function literal.
349
+ func (a *Analysis) IsAsyncReturningVar(obj types.Object) bool {
350
+ if obj == nil {
351
+ return false
352
+ }
353
+ return a.AsyncReturningVars[obj]
354
+ }
355
+
316
356
  func (a *Analysis) IsReceiverUsed(obj types.Object) bool {
317
357
  if obj == nil {
318
358
  return false
@@ -459,6 +499,10 @@ type analysisVisitor struct {
459
499
 
460
500
  // currentFuncLit tracks the *ast.FuncLit of the function literal we're currently analyzing.
461
501
  currentFuncLit *ast.FuncLit
502
+
503
+ // currentFilePath tracks the file path of the file we're currently analyzing.
504
+ // This is used to track which types are referenced in each file.
505
+ currentFilePath string
462
506
  }
463
507
 
464
508
  // getOrCreateUsageInfo retrieves or creates the VariableUsageInfo for a given object.
@@ -491,6 +535,18 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
491
535
  if n.Tok == token.VAR {
492
536
  for _, spec := range n.Specs {
493
537
  if valueSpec, ok := spec.(*ast.ValueSpec); ok {
538
+ // Track type references from variable declarations for synthetic import filtering
539
+ if valueSpec.Type != nil {
540
+ if t := v.pkg.TypesInfo.TypeOf(valueSpec.Type); t != nil {
541
+ v.trackTypeReference(t)
542
+ }
543
+ }
544
+ for _, name := range valueSpec.Names {
545
+ if obj := v.pkg.TypesInfo.ObjectOf(name); obj != nil {
546
+ v.trackTypeReference(obj.Type())
547
+ }
548
+ }
549
+
494
550
  // Process each declared variable (LHS)
495
551
  for i, lhsIdent := range valueSpec.Names {
496
552
  if lhsIdent.Name == "_" {
@@ -780,9 +836,30 @@ func (v *analysisVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor {
780
836
  // Check for implicit address-taking in method calls with pointer receivers
781
837
  v.checkImplicitAddressTaking(n)
782
838
 
839
+ // Check for address-of expressions in function arguments
840
+ v.checkAddressOfInArguments(n)
841
+
783
842
  return v
784
843
  }
785
844
 
845
+ // checkAddressOfInArguments detects when &variable is passed as a function argument.
846
+ // Example: json.Unmarshal(data, &person) where person needs to be marked as NeedsVarRef
847
+ func (v *analysisVisitor) checkAddressOfInArguments(callExpr *ast.CallExpr) {
848
+ for _, arg := range callExpr.Args {
849
+ if unaryExpr, ok := arg.(*ast.UnaryExpr); ok && unaryExpr.Op == token.AND {
850
+ if ident, ok := unaryExpr.X.(*ast.Ident); ok {
851
+ if obj := v.pkg.TypesInfo.ObjectOf(ident); obj != nil {
852
+ usageInfo := v.getOrCreateUsageInfo(obj)
853
+ usageInfo.Destinations = append(usageInfo.Destinations, AssignmentInfo{
854
+ Object: nil,
855
+ Type: AddressOfAssignment,
856
+ })
857
+ }
858
+ }
859
+ }
860
+ }
861
+ }
862
+
786
863
  // checkImplicitAddressTaking detects when a method call with a pointer receiver
787
864
  // is called on a non-pointer variable, which requires implicit address-taking.
788
865
  // Example: var s MySlice; s.Add(10) where Add has receiver *MySlice
@@ -909,9 +986,63 @@ func (v *analysisVisitor) visitAssignStmt(n *ast.AssignStmt) ast.Visitor {
909
986
  }
910
987
  }
911
988
 
989
+ // NOTE: Async-returning variable tracking (trackAsyncReturningVar) is done in a separate pass
990
+ // after function literals are analyzed for async status. See trackAsyncReturningVarsAllFiles.
991
+
912
992
  return v
913
993
  }
914
994
 
995
+ // trackAsyncReturningVar tracks variables that are assigned from higher-order function calls
996
+ // where one of the arguments is an async function literal.
997
+ // Pattern: x := higherOrderFunc(asyncFuncLit)
998
+ // This is needed because when sync.OnceValue(asyncFunc) is called, the result is a function
999
+ // that returns a Promise, and callers of x() need to await the result.
1000
+ func (v *analysisVisitor) trackAsyncReturningVar(lhs ast.Expr, rhs ast.Expr) {
1001
+ // LHS must be an identifier
1002
+ lhsIdent, ok := lhs.(*ast.Ident)
1003
+ if !ok {
1004
+ return
1005
+ }
1006
+
1007
+ // RHS must be a call expression
1008
+ callExpr, ok := rhs.(*ast.CallExpr)
1009
+ if !ok {
1010
+ return
1011
+ }
1012
+
1013
+ // The result type of the call must be a function type
1014
+ rhsType := v.pkg.TypesInfo.TypeOf(rhs)
1015
+ if rhsType == nil {
1016
+ return
1017
+ }
1018
+ _, isFunc := rhsType.Underlying().(*types.Signature)
1019
+ if !isFunc {
1020
+ return
1021
+ }
1022
+
1023
+ // Check if any argument is an async function literal
1024
+ // Use containsAsyncOperationsComplete to check the function body directly
1025
+ // rather than relying on the InAsyncContext flag which may not be set yet
1026
+ hasAsyncArg := false
1027
+ for _, arg := range callExpr.Args {
1028
+ if funcLit, ok := arg.(*ast.FuncLit); ok {
1029
+ if funcLit.Body != nil && v.containsAsyncOperationsComplete(funcLit.Body, v.pkg) {
1030
+ hasAsyncArg = true
1031
+ break
1032
+ }
1033
+ }
1034
+ }
1035
+
1036
+ if !hasAsyncArg {
1037
+ return
1038
+ }
1039
+
1040
+ // Mark the LHS variable as returning async values
1041
+ if obj := v.pkg.TypesInfo.ObjectOf(lhsIdent); obj != nil {
1042
+ v.analysis.AsyncReturningVars[obj] = true
1043
+ }
1044
+ }
1045
+
915
1046
  // visitReturnStmt handles return statement analysis
916
1047
  func (v *analysisVisitor) visitReturnStmt(n *ast.ReturnStmt) ast.Visitor {
917
1048
  nodeInfo := v.analysis.ensureNodeData(n)
@@ -940,12 +1071,28 @@ func (v *analysisVisitor) visitDeclStmt(n *ast.DeclStmt) ast.Visitor {
940
1071
  // Check if we're inside a function (either FuncDecl or FuncLit)
941
1072
  isInsideFunction := v.currentFuncDecl != nil || v.currentFuncLit != nil
942
1073
 
943
- if isInsideFunction {
944
- // Mark all specs in this declaration as being inside a function
945
- for _, spec := range genDecl.Specs {
1074
+ for _, spec := range genDecl.Specs {
1075
+ if isInsideFunction {
1076
+ // Mark all specs in this declaration as being inside a function
946
1077
  nodeInfo := v.analysis.ensureNodeData(spec)
947
1078
  nodeInfo.IsInsideFunction = true
948
1079
  }
1080
+
1081
+ // Track type references from variable declarations (e.g., var w MyWriter)
1082
+ if valueSpec, ok := spec.(*ast.ValueSpec); ok {
1083
+ // Track explicit type if present
1084
+ if valueSpec.Type != nil {
1085
+ if t := v.pkg.TypesInfo.TypeOf(valueSpec.Type); t != nil {
1086
+ v.trackTypeReference(t)
1087
+ }
1088
+ }
1089
+ // Also track types inferred from values
1090
+ for _, name := range valueSpec.Names {
1091
+ if obj := v.pkg.TypesInfo.ObjectOf(name); obj != nil {
1092
+ v.trackTypeReference(obj.Type())
1093
+ }
1094
+ }
1095
+ }
949
1096
  }
950
1097
  }
951
1098
  return v
@@ -974,6 +1121,9 @@ func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) as
974
1121
  return v
975
1122
  }
976
1123
 
1124
+ // Track the asserted type for synthetic import filtering
1125
+ v.trackTypeReference(assertedType)
1126
+
977
1127
  // Check if the asserted type is an interface
978
1128
  interfaceType, isInterface := assertedType.Underlying().(*types.Interface)
979
1129
  if !isInterface {
@@ -1011,10 +1161,36 @@ func (v *analysisVisitor) visitTypeAssertExpr(typeAssert *ast.TypeAssertExpr) as
1011
1161
  return v
1012
1162
  }
1013
1163
 
1164
+ // trackTypeReference records that a named type is referenced in the current file.
1165
+ // This is used to filter synthetic imports to only include packages actually needed.
1166
+ func (v *analysisVisitor) trackTypeReference(t types.Type) {
1167
+ if t == nil || v.currentFilePath == "" {
1168
+ return
1169
+ }
1170
+ // Unwrap pointers
1171
+ if ptr, ok := t.(*types.Pointer); ok {
1172
+ t = ptr.Elem()
1173
+ }
1174
+ // Track named types per file
1175
+ if named, ok := t.(*types.Named); ok {
1176
+ if v.analysis.ReferencedTypesPerFile[v.currentFilePath] == nil {
1177
+ v.analysis.ReferencedTypesPerFile[v.currentFilePath] = make(map[*types.Named]bool)
1178
+ }
1179
+ v.analysis.ReferencedTypesPerFile[v.currentFilePath][named] = true
1180
+ }
1181
+ }
1182
+
1014
1183
  // visitCompositeLit analyzes composite literals for address-of expressions
1015
1184
  // This is important for detecting cases like: arr := []interface{}{value1, &value2}
1016
1185
  // where value2 needs to be marked as NeedsVarRef due to the &value2 usage
1017
1186
  func (v *analysisVisitor) visitCompositeLit(compLit *ast.CompositeLit) ast.Visitor {
1187
+ // Track the type of this composite literal for synthetic import filtering
1188
+ if compLit.Type != nil {
1189
+ if t := v.pkg.TypesInfo.TypeOf(compLit.Type); t != nil {
1190
+ v.trackTypeReference(t)
1191
+ }
1192
+ }
1193
+
1018
1194
  // Analyze each element of the composite literal
1019
1195
  for _, elt := range compLit.Elts {
1020
1196
  // Handle both direct elements and key-value pairs
@@ -1174,7 +1350,11 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
1174
1350
  }
1175
1351
 
1176
1352
  // First pass: analyze all declarations and statements across all files
1177
- for _, file := range pkg.Syntax {
1353
+ for i, file := range pkg.Syntax {
1354
+ // Set the current file path for per-file type tracking
1355
+ if i < len(pkg.CompiledGoFiles) {
1356
+ visitor.currentFilePath = pkg.CompiledGoFiles[i]
1357
+ }
1178
1358
  ast.Walk(visitor, file)
1179
1359
  }
1180
1360
 
@@ -1207,9 +1387,153 @@ func AnalyzePackageFiles(pkg *packages.Package, allPackages map[string]*packages
1207
1387
  // Interface implementation async status is now updated on-demand in IsInterfaceMethodAsync
1208
1388
  visitor.analyzeAllMethodsAsync()
1209
1389
 
1390
+ // Fourth pass: collect imports needed by promoted methods from embedded structs
1391
+ analysis.addImportsForPromotedMethods(pkg)
1392
+
1210
1393
  return analysis
1211
1394
  }
1212
1395
 
1396
+ // addImportsForPromotedMethods scans struct types that are actually referenced in each file
1397
+ // and adds imports for any packages referenced by the promoted methods' parameter/return types.
1398
+ // This generates per-file synthetic imports to avoid adding unused imports.
1399
+ func (a *Analysis) addImportsForPromotedMethods(pkg *packages.Package) {
1400
+ // Process each file's referenced types separately
1401
+ for filePath, referencedTypes := range a.ReferencedTypesPerFile {
1402
+ // Collect package imports needed for this specific file
1403
+ packagesToAdd := make(map[string]*types.Package)
1404
+
1405
+ // Only process types that are actually referenced in this file
1406
+ // and are defined in the current package
1407
+ for namedType := range referencedTypes {
1408
+ // Skip types from other packages - we only need to process types defined in this package
1409
+ if namedType.Obj().Pkg() != pkg.Types {
1410
+ continue
1411
+ }
1412
+
1413
+ // Check if it's a struct
1414
+ structType, ok := namedType.Underlying().(*types.Struct)
1415
+ if !ok {
1416
+ continue
1417
+ }
1418
+
1419
+ // Look for embedded fields
1420
+ for i := 0; i < structType.NumFields(); i++ {
1421
+ field := structType.Field(i)
1422
+ if !field.Embedded() {
1423
+ continue
1424
+ }
1425
+
1426
+ // Get the type of the embedded field
1427
+ embeddedType := field.Type()
1428
+
1429
+ // Handle pointer to embedded type
1430
+ if ptr, ok := embeddedType.(*types.Pointer); ok {
1431
+ embeddedType = ptr.Elem()
1432
+ }
1433
+
1434
+ // Use method set to get all promoted methods including pointer receiver methods
1435
+ // This matches Go's behavior where embedding T promotes both T and *T methods
1436
+ methodSetType := embeddedType
1437
+ if _, isPtr := embeddedType.(*types.Pointer); !isPtr {
1438
+ if _, isInterface := embeddedType.Underlying().(*types.Interface); !isInterface {
1439
+ methodSetType = types.NewPointer(embeddedType)
1440
+ }
1441
+ }
1442
+ embeddedMethodSet := types.NewMethodSet(methodSetType)
1443
+
1444
+ // Scan all methods in the method set
1445
+ for j := 0; j < embeddedMethodSet.Len(); j++ {
1446
+ selection := embeddedMethodSet.At(j)
1447
+ method := selection.Obj()
1448
+ sig, ok := method.Type().(*types.Signature)
1449
+ if !ok {
1450
+ continue
1451
+ }
1452
+
1453
+ // Scan parameters
1454
+ if sig.Params() != nil {
1455
+ for k := 0; k < sig.Params().Len(); k++ {
1456
+ param := sig.Params().At(k)
1457
+ a.collectPackageFromType(param.Type(), pkg.Types, packagesToAdd)
1458
+ }
1459
+ }
1460
+
1461
+ // Scan results
1462
+ if sig.Results() != nil {
1463
+ for k := 0; k < sig.Results().Len(); k++ {
1464
+ result := sig.Results().At(k)
1465
+ a.collectPackageFromType(result.Type(), pkg.Types, packagesToAdd)
1466
+ }
1467
+ }
1468
+ }
1469
+ }
1470
+ }
1471
+
1472
+ // Store the synthetic imports for this file
1473
+ if len(packagesToAdd) > 0 {
1474
+ fileImports := make(map[string]*fileImport)
1475
+ for pkgName, pkgObj := range packagesToAdd {
1476
+ tsImportPath := "@goscript/" + pkgObj.Path()
1477
+ fileImports[pkgName] = &fileImport{
1478
+ importPath: tsImportPath,
1479
+ importVars: make(map[string]struct{}),
1480
+ }
1481
+ }
1482
+ a.SyntheticImportsPerFile[filePath] = fileImports
1483
+ }
1484
+ }
1485
+ }
1486
+
1487
+ // collectPackageFromType recursively collects packages referenced by a type.
1488
+ func (a *Analysis) collectPackageFromType(t types.Type, currentPkg *types.Package, packagesToAdd map[string]*types.Package) {
1489
+ switch typ := t.(type) {
1490
+ case *types.Named:
1491
+ pkg := typ.Obj().Pkg()
1492
+ if pkg != nil && pkg != currentPkg {
1493
+ packagesToAdd[pkg.Name()] = pkg
1494
+ }
1495
+ // Check type arguments for generics
1496
+ if typ.TypeArgs() != nil {
1497
+ for i := 0; i < typ.TypeArgs().Len(); i++ {
1498
+ a.collectPackageFromType(typ.TypeArgs().At(i), currentPkg, packagesToAdd)
1499
+ }
1500
+ }
1501
+ case *types.Interface:
1502
+ // For interfaces, we need to check embedded interfaces and method signatures
1503
+ for i := 0; i < typ.NumEmbeddeds(); i++ {
1504
+ a.collectPackageFromType(typ.EmbeddedType(i), currentPkg, packagesToAdd)
1505
+ }
1506
+ for i := 0; i < typ.NumExplicitMethods(); i++ {
1507
+ method := typ.ExplicitMethod(i)
1508
+ a.collectPackageFromType(method.Type(), currentPkg, packagesToAdd)
1509
+ }
1510
+ case *types.Pointer:
1511
+ a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1512
+ case *types.Slice:
1513
+ a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1514
+ case *types.Array:
1515
+ a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1516
+ case *types.Map:
1517
+ a.collectPackageFromType(typ.Key(), currentPkg, packagesToAdd)
1518
+ a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1519
+ case *types.Chan:
1520
+ a.collectPackageFromType(typ.Elem(), currentPkg, packagesToAdd)
1521
+ case *types.Signature:
1522
+ // Collect from parameters
1523
+ if typ.Params() != nil {
1524
+ for i := 0; i < typ.Params().Len(); i++ {
1525
+ a.collectPackageFromType(typ.Params().At(i).Type(), currentPkg, packagesToAdd)
1526
+ }
1527
+ }
1528
+ // Collect from results
1529
+ if typ.Results() != nil {
1530
+ for i := 0; i < typ.Results().Len(); i++ {
1531
+ a.collectPackageFromType(typ.Results().At(i).Type(), currentPkg, packagesToAdd)
1532
+ }
1533
+ }
1534
+ }
1535
+ }
1536
+
1213
1537
  // AnalyzePackageImports performs package-level analysis to collect function definitions
1214
1538
  // and calls across all files in the package for auto-import generation
1215
1539
  func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
@@ -1221,19 +1545,50 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1221
1545
  baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1222
1546
 
1223
1547
  var functions []string
1224
- var types []string
1548
+ var typeNames []string
1549
+ var variables []string
1225
1550
  for _, decl := range syntax.Decls {
1226
1551
  if funcDecl, ok := decl.(*ast.FuncDecl); ok {
1227
- // Only collect top-level functions (not methods)
1552
+ // Collect top-level functions (not methods)
1228
1553
  if funcDecl.Recv == nil {
1229
1554
  functions = append(functions, funcDecl.Name.Name)
1555
+ } else {
1556
+ // Check if this is a method on a wrapper type (named basic type)
1557
+ // If so, it will be compiled as TypeName_MethodName function
1558
+ if len(funcDecl.Recv.List) > 0 {
1559
+ recvType := funcDecl.Recv.List[0].Type
1560
+ // Handle pointer receiver (*Type)
1561
+ if starExpr, ok := recvType.(*ast.StarExpr); ok {
1562
+ recvType = starExpr.X
1563
+ }
1564
+ if recvIdent, ok := recvType.(*ast.Ident); ok {
1565
+ // Check if this receiver type is a wrapper type
1566
+ if obj := pkg.TypesInfo.Uses[recvIdent]; obj != nil {
1567
+ if typeName, ok := obj.(*types.TypeName); ok {
1568
+ if namedType, ok := typeName.Type().(*types.Named); ok {
1569
+ if _, ok := namedType.Underlying().(*types.Basic); ok {
1570
+ // This is a method on a wrapper type
1571
+ funcName := recvIdent.Name + "_" + funcDecl.Name.Name
1572
+ functions = append(functions, funcName)
1573
+ }
1574
+ }
1575
+ }
1576
+ }
1577
+ }
1578
+ }
1230
1579
  }
1231
1580
  }
1232
1581
  if genDecl, ok := decl.(*ast.GenDecl); ok {
1233
1582
  // Collect type declarations
1234
1583
  for _, spec := range genDecl.Specs {
1235
1584
  if typeSpec, ok := spec.(*ast.TypeSpec); ok {
1236
- types = append(types, typeSpec.Name.Name)
1585
+ typeNames = append(typeNames, typeSpec.Name.Name)
1586
+ }
1587
+ // Collect variable/constant declarations
1588
+ if valueSpec, ok := spec.(*ast.ValueSpec); ok {
1589
+ for _, name := range valueSpec.Names {
1590
+ variables = append(variables, name.Name)
1591
+ }
1237
1592
  }
1238
1593
  }
1239
1594
  }
@@ -1242,8 +1597,11 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1242
1597
  if len(functions) > 0 {
1243
1598
  analysis.FunctionDefs[baseFileName] = functions
1244
1599
  }
1245
- if len(types) > 0 {
1246
- analysis.TypeDefs[baseFileName] = types
1600
+ if len(typeNames) > 0 {
1601
+ analysis.TypeDefs[baseFileName] = typeNames
1602
+ }
1603
+ if len(variables) > 0 {
1604
+ analysis.VariableDefs[baseFileName] = variables
1247
1605
  }
1248
1606
  }
1249
1607
 
@@ -1343,6 +1701,172 @@ func AnalyzePackageImports(pkg *packages.Package) *PackageAnalysis {
1343
1701
  }
1344
1702
  }
1345
1703
 
1704
+ // Fourth pass: analyze variable references and determine which need imports
1705
+ for i, syntax := range pkg.Syntax {
1706
+ fileName := pkg.CompiledGoFiles[i]
1707
+ baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1708
+
1709
+ // Find all variable references in this file
1710
+ varRefsFromOtherFiles := make(map[string][]string)
1711
+
1712
+ ast.Inspect(syntax, func(n ast.Node) bool {
1713
+ // Look for identifier references
1714
+ if ident, ok := n.(*ast.Ident); ok {
1715
+ // Check if this identifier refers to a package-level variable
1716
+ if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
1717
+ if varObj, ok := obj.(*types.Var); ok {
1718
+ // Only track package-level variables (not function parameters or local vars)
1719
+ if varObj.Parent() == pkg.Types.Scope() {
1720
+ varName := ident.Name
1721
+
1722
+ // Check if this variable is defined in the current file
1723
+ currentFileVars := analysis.VariableDefs[baseFileName]
1724
+ isDefinedInCurrentFile := slices.Contains(currentFileVars, varName)
1725
+
1726
+ // If not defined in current file, find which file defines it
1727
+ if !isDefinedInCurrentFile {
1728
+ for sourceFile, vars := range analysis.VariableDefs {
1729
+ if sourceFile == baseFileName {
1730
+ continue // Skip current file
1731
+ }
1732
+ if slices.Contains(vars, varName) {
1733
+ // Found the variable in another file
1734
+ if varRefsFromOtherFiles[sourceFile] == nil {
1735
+ varRefsFromOtherFiles[sourceFile] = []string{}
1736
+ }
1737
+ // Check if already added to avoid duplicates
1738
+ found := slices.Contains(varRefsFromOtherFiles[sourceFile], varName)
1739
+ if !found {
1740
+ varRefsFromOtherFiles[sourceFile] = append(varRefsFromOtherFiles[sourceFile], varName)
1741
+ }
1742
+ }
1743
+ }
1744
+ }
1745
+ }
1746
+ }
1747
+ // Also check for constants
1748
+ if constObj, ok := obj.(*types.Const); ok {
1749
+ // Only track package-level constants
1750
+ if constObj.Parent() == pkg.Types.Scope() {
1751
+ constName := ident.Name
1752
+
1753
+ // Check if this constant is defined in the current file
1754
+ currentFileVars := analysis.VariableDefs[baseFileName]
1755
+ isDefinedInCurrentFile := slices.Contains(currentFileVars, constName)
1756
+
1757
+ // If not defined in current file, find which file defines it
1758
+ if !isDefinedInCurrentFile {
1759
+ for sourceFile, vars := range analysis.VariableDefs {
1760
+ if sourceFile == baseFileName {
1761
+ continue // Skip current file
1762
+ }
1763
+ if slices.Contains(vars, constName) {
1764
+ // Found the constant in another file
1765
+ if varRefsFromOtherFiles[sourceFile] == nil {
1766
+ varRefsFromOtherFiles[sourceFile] = []string{}
1767
+ }
1768
+ // Check if already added to avoid duplicates
1769
+ found := slices.Contains(varRefsFromOtherFiles[sourceFile], constName)
1770
+ if !found {
1771
+ varRefsFromOtherFiles[sourceFile] = append(varRefsFromOtherFiles[sourceFile], constName)
1772
+ }
1773
+ }
1774
+ }
1775
+ }
1776
+ }
1777
+ }
1778
+ }
1779
+ }
1780
+ return true
1781
+ })
1782
+
1783
+ if len(varRefsFromOtherFiles) > 0 {
1784
+ analysis.VariableCalls[baseFileName] = varRefsFromOtherFiles
1785
+ }
1786
+ }
1787
+
1788
+ // Fifth pass: analyze method calls on wrapper types (named basic types with methods)
1789
+ // These generate TypeName_MethodName function calls that need to be imported
1790
+ for i, syntax := range pkg.Syntax {
1791
+ fileName := pkg.CompiledGoFiles[i]
1792
+ baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
1793
+
1794
+ // Find all method calls on wrapper types in this file
1795
+ ast.Inspect(syntax, func(n ast.Node) bool {
1796
+ callExpr, ok := n.(*ast.CallExpr)
1797
+ if !ok {
1798
+ return true
1799
+ }
1800
+
1801
+ // Check if this is a method call (selector expression)
1802
+ selectorExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
1803
+ if !ok {
1804
+ return true
1805
+ }
1806
+
1807
+ // Get the type of the receiver
1808
+ receiverType := pkg.TypesInfo.TypeOf(selectorExpr.X)
1809
+ if receiverType == nil {
1810
+ return true
1811
+ }
1812
+
1813
+ // Check if this is a wrapper type (named type with basic underlying type and methods)
1814
+ namedType, ok := receiverType.(*types.Named)
1815
+ if !ok {
1816
+ return true
1817
+ }
1818
+
1819
+ // Check if it has a basic underlying type
1820
+ if _, ok := namedType.Underlying().(*types.Basic); !ok {
1821
+ return true
1822
+ }
1823
+
1824
+ // Check if this type is defined in the same package
1825
+ obj := namedType.Obj()
1826
+ if obj == nil || obj.Pkg() == nil || obj.Pkg() != pkg.Types {
1827
+ return true // Not from this package
1828
+ }
1829
+
1830
+ // Check if this type has the method being called
1831
+ methodName := selectorExpr.Sel.Name
1832
+ found := false
1833
+ for j := 0; j < namedType.NumMethods(); j++ {
1834
+ if namedType.Method(j).Name() == methodName {
1835
+ found = true
1836
+ break
1837
+ }
1838
+ }
1839
+ if !found {
1840
+ return true
1841
+ }
1842
+
1843
+ // Generate the function name: TypeName_MethodName
1844
+ funcName := obj.Name() + "_" + methodName
1845
+
1846
+ // Find which file defines this function
1847
+ for sourceFile, funcs := range analysis.FunctionDefs {
1848
+ if sourceFile == baseFileName {
1849
+ continue // Skip current file
1850
+ }
1851
+ if slices.Contains(funcs, funcName) {
1852
+ // Found the function in another file
1853
+ if analysis.FunctionCalls[baseFileName] == nil {
1854
+ analysis.FunctionCalls[baseFileName] = make(map[string][]string)
1855
+ }
1856
+ if analysis.FunctionCalls[baseFileName][sourceFile] == nil {
1857
+ analysis.FunctionCalls[baseFileName][sourceFile] = []string{}
1858
+ }
1859
+ // Check if already added to avoid duplicates
1860
+ if !slices.Contains(analysis.FunctionCalls[baseFileName][sourceFile], funcName) {
1861
+ analysis.FunctionCalls[baseFileName][sourceFile] = append(analysis.FunctionCalls[baseFileName][sourceFile], funcName)
1862
+ }
1863
+ }
1864
+ }
1865
+
1866
+ return true
1867
+ })
1868
+ }
1869
+
1346
1870
  return analysis
1347
1871
  }
1348
1872
 
@@ -1546,6 +2070,7 @@ func (v *analysisVisitor) detectVariableShadowing(assignStmt *ast.AssignStmt) *S
1546
2070
  shadowingInfo := &ShadowingInfo{
1547
2071
  ShadowedVariables: make(map[string]types.Object),
1548
2072
  TempVariables: make(map[string]string),
2073
+ TypeShadowedVars: make(map[string]string),
1549
2074
  }
1550
2075
 
1551
2076
  hasShadowing := false
@@ -1563,12 +2088,60 @@ func (v *analysisVisitor) detectVariableShadowing(assignStmt *ast.AssignStmt) *S
1563
2088
  v.findVariableUsageInExpr(rhsExpr, lhsVarNames, shadowingInfo, &hasShadowing)
1564
2089
  }
1565
2090
 
2091
+ // Check for type shadowing: variable name matches a type name used in its initialization
2092
+ // e.g., field := field{...} where the variable 'field' shadows the type 'field'
2093
+ if assignStmt.Tok == token.DEFINE {
2094
+ for i, lhsExpr := range assignStmt.Lhs {
2095
+ if i < len(assignStmt.Rhs) {
2096
+ if lhsIdent, ok := lhsExpr.(*ast.Ident); ok && lhsIdent.Name != "_" {
2097
+ if typeName := v.findTypeShadowing(lhsIdent.Name, assignStmt.Rhs[i]); typeName != "" {
2098
+ shadowingInfo.TypeShadowedVars[lhsIdent.Name] = lhsIdent.Name + "_"
2099
+ hasShadowing = true
2100
+ }
2101
+ }
2102
+ }
2103
+ }
2104
+ }
2105
+
1566
2106
  if hasShadowing {
1567
2107
  return shadowingInfo
1568
2108
  }
1569
2109
  return nil
1570
2110
  }
1571
2111
 
2112
+ // findTypeShadowing checks if the given variable name matches a type name used in the RHS expression.
2113
+ // Returns the type name if shadowing is detected, empty string otherwise.
2114
+ func (v *analysisVisitor) findTypeShadowing(varName string, rhsExpr ast.Expr) string {
2115
+ // Handle address-of expressions: field := &field{...}
2116
+ if unary, ok := rhsExpr.(*ast.UnaryExpr); ok && unary.Op == token.AND {
2117
+ rhsExpr = unary.X
2118
+ }
2119
+
2120
+ // Check if RHS is a composite literal with a type name matching varName
2121
+ compLit, ok := rhsExpr.(*ast.CompositeLit)
2122
+ if !ok {
2123
+ return ""
2124
+ }
2125
+
2126
+ // Get the type name from the composite literal
2127
+ var typeName string
2128
+ switch t := compLit.Type.(type) {
2129
+ case *ast.Ident:
2130
+ typeName = t.Name
2131
+ case *ast.SelectorExpr:
2132
+ // pkg.Type - just use the type name part
2133
+ typeName = t.Sel.Name
2134
+ default:
2135
+ return ""
2136
+ }
2137
+
2138
+ // Check if variable name matches type name
2139
+ if typeName == varName {
2140
+ return typeName
2141
+ }
2142
+ return ""
2143
+ }
2144
+
1572
2145
  // findVariableUsageInExpr recursively searches for variable usage in an expression
1573
2146
  func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map[string]*ast.Ident, shadowingInfo *ShadowingInfo, hasShadowing *bool) {
1574
2147
  if expr == nil {
@@ -2173,8 +2746,17 @@ func (v *analysisVisitor) analyzeAllMethodsAsync() {
2173
2746
  v.analyzeMethodAsyncTopological(methodKey, methodCalls[methodKey])
2174
2747
  }
2175
2748
 
2749
+ // Track async-returning variables BEFORE analyzing function literals
2750
+ // This detects variables assigned from higher-order functions with async function literal args
2751
+ // e.g., indirect := sync.OnceValue(asyncFunc)
2752
+ // This must happen first so that function literals containing calls to these variables
2753
+ // will be correctly identified as async.
2754
+ v.trackAsyncReturningVarsAllFiles()
2755
+
2176
2756
  // Finally, analyze function literals in the current package only
2177
2757
  // (external packages' function literals are not accessible)
2758
+ // This must run AFTER trackAsyncReturningVarsAllFiles so that function literals
2759
+ // containing calls to async-returning variables are correctly marked as async.
2178
2760
  v.analyzeFunctionLiteralsAsync(v.pkg)
2179
2761
  }
2180
2762
 
@@ -2190,6 +2772,21 @@ func (v *analysisVisitor) analyzeFunctionLiteralsAsync(pkg *packages.Package) {
2190
2772
  }
2191
2773
  }
2192
2774
 
2775
+ // trackAsyncReturningVarsAllFiles scans all files for assignment statements
2776
+ // and marks variables that are assigned from higher-order functions with async function literal args
2777
+ func (v *analysisVisitor) trackAsyncReturningVarsAllFiles() {
2778
+ for _, file := range v.pkg.Syntax {
2779
+ ast.Inspect(file, func(n ast.Node) bool {
2780
+ if assignStmt, ok := n.(*ast.AssignStmt); ok {
2781
+ if len(assignStmt.Lhs) == 1 && len(assignStmt.Rhs) == 1 {
2782
+ v.trackAsyncReturningVar(assignStmt.Lhs[0], assignStmt.Rhs[0])
2783
+ }
2784
+ }
2785
+ return true
2786
+ })
2787
+ }
2788
+ }
2789
+
2193
2790
  // analyzeFunctionLiteralAsync determines if a function literal is async and stores the result
2194
2791
  func (v *analysisVisitor) analyzeFunctionLiteralAsync(funcLit *ast.FuncLit, pkg *packages.Package) {
2195
2792
  // Check if already analyzed
@@ -2641,6 +3238,11 @@ func (v *analysisVisitor) isCallAsync(callExpr *ast.CallExpr, pkg *packages.Pack
2641
3238
  result := v.isFunctionAsync(funcObj, pkg)
2642
3239
  return result
2643
3240
  }
3241
+ // Check if this is a variable that returns async values
3242
+ // (e.g., indirect := sync.OnceValue(asyncFunc))
3243
+ if v.analysis.IsAsyncReturningVar(obj) {
3244
+ return true
3245
+ }
2644
3246
  }
2645
3247
 
2646
3248
  case *ast.SelectorExpr: