goscript 0.0.41 → 0.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/analysis.go +260 -2
- package/compiler/assignment.go +6 -0
- package/compiler/compiler.go +98 -8
- package/compiler/decl.go +42 -20
- package/compiler/expr-call.go +37 -1
- package/compiler/expr-type.go +20 -0
- package/compiler/lit.go +4 -20
- package/compiler/spec-struct.go +3 -4
- package/compiler/spec-value.go +18 -1
- package/compiler/spec.go +15 -8
- package/compiler/stmt-range.go +1 -1
- package/compiler/stmt.go +118 -17
- package/compiler/type-assert.go +4 -4
- package/compiler/type.go +63 -5
- package/dist/gs/builtin/map.d.ts +4 -4
- package/dist/gs/builtin/map.js +6 -3
- package/dist/gs/builtin/map.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +7 -0
- package/dist/gs/builtin/slice.js +12 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +8 -70
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/fmt/fmt.d.ts +22 -21
- package/dist/gs/fmt/fmt.js +12 -12
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/internal/testlog/index.d.ts +1 -0
- package/dist/gs/internal/testlog/index.js +5 -0
- package/dist/gs/internal/testlog/index.js.map +1 -0
- package/dist/gs/io/io.d.ts +13 -13
- package/dist/gs/io/io.js +31 -21
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/maps/iter.gs.d.ts +7 -0
- package/dist/gs/maps/iter.gs.js +65 -0
- package/dist/gs/maps/iter.gs.js.map +1 -0
- package/dist/gs/maps/maps.gs.d.ts +7 -0
- package/dist/gs/maps/maps.gs.js +79 -0
- package/dist/gs/maps/maps.gs.js.map +1 -0
- package/dist/gs/path/filepath/match.d.ts +4 -3
- package/dist/gs/path/filepath/match.js +2 -2
- package/dist/gs/path/filepath/match.js.map +1 -1
- package/dist/gs/path/filepath/path.d.ts +2 -2
- package/dist/gs/path/filepath/path.js +3 -3
- package/dist/gs/path/filepath/path.js.map +1 -1
- package/dist/gs/reflect/abi.d.ts +59 -0
- package/dist/gs/reflect/abi.gs.d.ts +59 -0
- package/dist/gs/reflect/abi.gs.js +79 -0
- package/dist/gs/reflect/abi.gs.js.map +1 -0
- package/dist/gs/reflect/abi.js +79 -0
- package/dist/gs/reflect/abi.js.map +1 -0
- package/dist/gs/reflect/badlinkname.d.ts +52 -0
- package/dist/gs/reflect/badlinkname.gs.d.ts +52 -0
- package/dist/gs/reflect/badlinkname.gs.js +72 -0
- package/dist/gs/reflect/badlinkname.gs.js.map +1 -0
- package/dist/gs/reflect/badlinkname.js +72 -0
- package/dist/gs/reflect/badlinkname.js.map +1 -0
- package/dist/gs/reflect/deepequal.gs.d.ts +25 -0
- package/dist/gs/reflect/deepequal.gs.js +308 -0
- package/dist/gs/reflect/deepequal.gs.js.map +1 -0
- package/dist/gs/reflect/float32reg_generic.gs.d.ts +2 -0
- package/dist/gs/reflect/float32reg_generic.gs.js +10 -0
- package/dist/gs/reflect/float32reg_generic.gs.js.map +1 -0
- package/dist/gs/reflect/index.gs.d.ts +1 -0
- package/dist/gs/reflect/index.gs.js +3 -0
- package/dist/gs/reflect/index.gs.js.map +1 -0
- package/dist/gs/reflect/iter.gs.d.ts +3 -0
- package/dist/gs/reflect/iter.gs.js +24 -0
- package/dist/gs/reflect/iter.gs.js.map +1 -0
- package/dist/gs/reflect/makefunc.gs.d.ts +34 -0
- package/dist/gs/reflect/makefunc.gs.js +288 -0
- package/dist/gs/reflect/makefunc.gs.js.map +1 -0
- package/dist/gs/reflect/map_swiss.gs.d.ts +14 -0
- package/dist/gs/reflect/map_swiss.gs.js +70 -0
- package/dist/gs/reflect/map_swiss.gs.js.map +1 -0
- package/dist/gs/reflect/reflect.gs.d.ts +132 -0
- package/dist/gs/reflect/reflect.gs.js +437 -0
- package/dist/gs/reflect/reflect.gs.js.map +1 -0
- package/dist/gs/reflect/swapper.gs.d.ts +1 -0
- package/dist/gs/reflect/swapper.gs.js +32 -0
- package/dist/gs/reflect/swapper.gs.js.map +1 -0
- package/dist/gs/reflect/type.gs.d.ts +4 -0
- package/dist/gs/reflect/type.gs.js +21 -0
- package/dist/gs/reflect/type.gs.js.map +1 -0
- package/dist/gs/reflect/value.gs.d.ts +4 -0
- package/dist/gs/reflect/value.gs.js +12 -0
- package/dist/gs/reflect/value.gs.js.map +1 -0
- package/dist/gs/reflect/visiblefields.gs.d.ts +3 -0
- package/dist/gs/reflect/visiblefields.gs.js +123 -0
- package/dist/gs/reflect/visiblefields.gs.js.map +1 -0
- package/dist/gs/strings/reader.d.ts +1 -1
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/stringslite/index.d.ts +1 -0
- package/dist/gs/stringslite/index.js +2 -0
- package/dist/gs/stringslite/index.js.map +1 -0
- package/dist/gs/stringslite/strings.d.ts +11 -0
- package/dist/gs/stringslite/strings.js +67 -0
- package/dist/gs/stringslite/strings.js.map +1 -0
- package/dist/gs/time/time.d.ts +69 -0
- package/dist/gs/time/time.js +350 -0
- package/dist/gs/time/time.js.map +1 -1
- package/gs/builtin/map.ts +12 -8
- package/gs/builtin/slice.ts +13 -0
- package/gs/builtin/type.ts +8 -100
- package/gs/fmt/fmt.ts +33 -33
- package/gs/io/io.ts +47 -39
- package/gs/path/filepath/match.ts +4 -4
- package/gs/path/filepath/path.ts +3 -3
- package/gs/strings/reader.ts +1 -1
- package/gs/time/time.ts +403 -0
- package/package.json +1 -1
package/compiler/analysis.go
CHANGED
|
@@ -43,6 +43,14 @@ type VariableUsageInfo struct {
|
|
|
43
43
|
Destinations []AssignmentInfo
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// ShadowingInfo tracks variable shadowing in if statement initializations
|
|
47
|
+
type ShadowingInfo struct {
|
|
48
|
+
// ShadowedVariables maps shadowed variable names to their outer scope objects
|
|
49
|
+
ShadowedVariables map[string]types.Object
|
|
50
|
+
// TempVariables maps shadowed variable names to temporary variable names
|
|
51
|
+
TempVariables map[string]string
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
// FunctionTypeInfo represents Go function type information for reflection
|
|
47
55
|
type FunctionTypeInfo struct {
|
|
48
56
|
Params []types.Type // Parameter types
|
|
@@ -69,8 +77,9 @@ type NodeInfo struct {
|
|
|
69
77
|
IsBareReturn bool
|
|
70
78
|
EnclosingFuncDecl *ast.FuncDecl
|
|
71
79
|
EnclosingFuncLit *ast.FuncLit
|
|
72
|
-
IsInsideFunction bool
|
|
73
|
-
IsMethodValue bool
|
|
80
|
+
IsInsideFunction bool // true if this declaration is inside a function body
|
|
81
|
+
IsMethodValue bool // true if this SelectorExpr is a method value that needs binding
|
|
82
|
+
ShadowingInfo *ShadowingInfo // variable shadowing information for if statements
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
// Analysis holds information gathered during the analysis phase of the Go code compilation.
|
|
@@ -116,6 +125,14 @@ type PackageAnalysis struct {
|
|
|
116
125
|
// FunctionCalls maps file names to the functions they call from other files
|
|
117
126
|
// Key: filename (without .go extension), Value: map[sourceFile][]functionNames
|
|
118
127
|
FunctionCalls map[string]map[string][]string
|
|
128
|
+
|
|
129
|
+
// TypeDefs maps file names to the types defined in that file
|
|
130
|
+
// Key: filename (without .go extension), Value: list of type names
|
|
131
|
+
TypeDefs map[string][]string
|
|
132
|
+
|
|
133
|
+
// TypeCalls maps file names to the types they reference from other files
|
|
134
|
+
// Key: filename (without .go extension), Value: map[sourceFile][]typeNames
|
|
135
|
+
TypeCalls map[string]map[string][]string
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
// NewAnalysis creates a new Analysis instance.
|
|
@@ -137,6 +154,8 @@ func NewPackageAnalysis() *PackageAnalysis {
|
|
|
137
154
|
return &PackageAnalysis{
|
|
138
155
|
FunctionDefs: make(map[string][]string),
|
|
139
156
|
FunctionCalls: make(map[string]map[string][]string),
|
|
157
|
+
TypeDefs: make(map[string][]string),
|
|
158
|
+
TypeCalls: make(map[string]map[string][]string),
|
|
140
159
|
}
|
|
141
160
|
}
|
|
142
161
|
|
|
@@ -270,6 +289,30 @@ func (a *Analysis) IsMethodValue(node *ast.SelectorExpr) bool {
|
|
|
270
289
|
return nodeInfo.IsMethodValue
|
|
271
290
|
}
|
|
272
291
|
|
|
292
|
+
// HasVariableShadowing returns whether the given node has variable shadowing issues
|
|
293
|
+
func (a *Analysis) HasVariableShadowing(node ast.Node) bool {
|
|
294
|
+
if node == nil {
|
|
295
|
+
return false
|
|
296
|
+
}
|
|
297
|
+
nodeInfo := a.NodeData[node]
|
|
298
|
+
if nodeInfo == nil {
|
|
299
|
+
return false
|
|
300
|
+
}
|
|
301
|
+
return nodeInfo.ShadowingInfo != nil
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// GetShadowingInfo returns the variable shadowing information for the given node
|
|
305
|
+
func (a *Analysis) GetShadowingInfo(node ast.Node) *ShadowingInfo {
|
|
306
|
+
if node == nil {
|
|
307
|
+
return nil
|
|
308
|
+
}
|
|
309
|
+
nodeInfo := a.NodeData[node]
|
|
310
|
+
if nodeInfo == nil {
|
|
311
|
+
return nil
|
|
312
|
+
}
|
|
313
|
+
return nodeInfo.ShadowingInfo
|
|
314
|
+
}
|
|
315
|
+
|
|
273
316
|
// analysisVisitor implements ast.Visitor and is used to traverse the AST during analysis.
|
|
274
317
|
type analysisVisitor struct {
|
|
275
318
|
// analysis stores information gathered during the traversal
|
|
@@ -631,6 +674,19 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
631
674
|
return v // Continue traversal
|
|
632
675
|
|
|
633
676
|
case *ast.AssignStmt:
|
|
677
|
+
// Detect variable shadowing in any := assignment
|
|
678
|
+
if n.Tok == token.DEFINE {
|
|
679
|
+
shadowingInfo := v.detectVariableShadowing(n)
|
|
680
|
+
if shadowingInfo != nil {
|
|
681
|
+
// Store shadowing info on the assignment statement itself
|
|
682
|
+
if v.analysis.NodeData[n] == nil {
|
|
683
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
684
|
+
}
|
|
685
|
+
v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Continue with the existing assignment analysis logic
|
|
634
690
|
for i, currentLHSExpr := range n.Lhs {
|
|
635
691
|
if i >= len(n.Rhs) {
|
|
636
692
|
break // Should not happen in valid Go
|
|
@@ -779,6 +835,22 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
779
835
|
}
|
|
780
836
|
}
|
|
781
837
|
return v // Continue traversal
|
|
838
|
+
|
|
839
|
+
case *ast.IfStmt:
|
|
840
|
+
// Detect variable shadowing in if statement initializations
|
|
841
|
+
if n.Init != nil {
|
|
842
|
+
if assignStmt, ok := n.Init.(*ast.AssignStmt); ok && assignStmt.Tok == token.DEFINE {
|
|
843
|
+
shadowingInfo := v.detectVariableShadowing(assignStmt)
|
|
844
|
+
if shadowingInfo != nil {
|
|
845
|
+
// Initialize NodeData for this if statement
|
|
846
|
+
if v.analysis.NodeData[n] == nil {
|
|
847
|
+
v.analysis.NodeData[n] = &NodeInfo{}
|
|
848
|
+
}
|
|
849
|
+
v.analysis.NodeData[n].ShadowingInfo = shadowingInfo
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return v // Continue traversal
|
|
782
854
|
}
|
|
783
855
|
|
|
784
856
|
// For all other nodes, continue traversal
|
|
@@ -974,6 +1046,7 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
|
|
|
974
1046
|
baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
|
|
975
1047
|
|
|
976
1048
|
var functions []string
|
|
1049
|
+
var types []string
|
|
977
1050
|
for _, decl := range syntax.Decls {
|
|
978
1051
|
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
|
|
979
1052
|
// Only collect top-level functions (not methods)
|
|
@@ -981,11 +1054,22 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
|
|
|
981
1054
|
functions = append(functions, funcDecl.Name.Name)
|
|
982
1055
|
}
|
|
983
1056
|
}
|
|
1057
|
+
if genDecl, ok := decl.(*ast.GenDecl); ok {
|
|
1058
|
+
// Collect type declarations
|
|
1059
|
+
for _, spec := range genDecl.Specs {
|
|
1060
|
+
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
1061
|
+
types = append(types, typeSpec.Name.Name)
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
984
1065
|
}
|
|
985
1066
|
|
|
986
1067
|
if len(functions) > 0 {
|
|
987
1068
|
analysis.FunctionDefs[baseFileName] = functions
|
|
988
1069
|
}
|
|
1070
|
+
if len(types) > 0 {
|
|
1071
|
+
analysis.TypeDefs[baseFileName] = types
|
|
1072
|
+
}
|
|
989
1073
|
}
|
|
990
1074
|
|
|
991
1075
|
// Second pass: analyze function calls and determine which need imports
|
|
@@ -1049,6 +1133,71 @@ func AnalyzePackage(pkg *packages.Package) *PackageAnalysis {
|
|
|
1049
1133
|
}
|
|
1050
1134
|
}
|
|
1051
1135
|
|
|
1136
|
+
// Third pass: analyze type references and determine which need imports
|
|
1137
|
+
for i, syntax := range pkg.Syntax {
|
|
1138
|
+
fileName := pkg.CompiledGoFiles[i]
|
|
1139
|
+
baseFileName := strings.TrimSuffix(filepath.Base(fileName), ".go")
|
|
1140
|
+
|
|
1141
|
+
// Find all type references in this file
|
|
1142
|
+
typeRefsFromOtherFiles := make(map[string][]string)
|
|
1143
|
+
|
|
1144
|
+
ast.Inspect(syntax, func(n ast.Node) bool {
|
|
1145
|
+
// Look for type references in struct fields, function parameters, etc.
|
|
1146
|
+
if ident, ok := n.(*ast.Ident); ok {
|
|
1147
|
+
// Check if this identifier refers to a type
|
|
1148
|
+
if obj := pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
1149
|
+
if _, ok := obj.(*types.TypeName); ok {
|
|
1150
|
+
typeName := ident.Name
|
|
1151
|
+
|
|
1152
|
+
// Check if this type is defined in the current file
|
|
1153
|
+
currentFileTypes := analysis.TypeDefs[baseFileName]
|
|
1154
|
+
isDefinedInCurrentFile := false
|
|
1155
|
+
for _, t := range currentFileTypes {
|
|
1156
|
+
if t == typeName {
|
|
1157
|
+
isDefinedInCurrentFile = true
|
|
1158
|
+
break
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// If not defined in current file, find which file defines it
|
|
1163
|
+
if !isDefinedInCurrentFile {
|
|
1164
|
+
for sourceFile, types := range analysis.TypeDefs {
|
|
1165
|
+
if sourceFile == baseFileName {
|
|
1166
|
+
continue // Skip current file
|
|
1167
|
+
}
|
|
1168
|
+
for _, t := range types {
|
|
1169
|
+
if t == typeName {
|
|
1170
|
+
// Found the type in another file
|
|
1171
|
+
if typeRefsFromOtherFiles[sourceFile] == nil {
|
|
1172
|
+
typeRefsFromOtherFiles[sourceFile] = []string{}
|
|
1173
|
+
}
|
|
1174
|
+
// Check if already added to avoid duplicates
|
|
1175
|
+
found := false
|
|
1176
|
+
for _, existing := range typeRefsFromOtherFiles[sourceFile] {
|
|
1177
|
+
if existing == typeName {
|
|
1178
|
+
found = true
|
|
1179
|
+
break
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
if !found {
|
|
1183
|
+
typeRefsFromOtherFiles[sourceFile] = append(typeRefsFromOtherFiles[sourceFile], typeName)
|
|
1184
|
+
}
|
|
1185
|
+
break
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
return true
|
|
1194
|
+
})
|
|
1195
|
+
|
|
1196
|
+
if len(typeRefsFromOtherFiles) > 0 {
|
|
1197
|
+
analysis.TypeCalls[baseFileName] = typeRefsFromOtherFiles
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1052
1201
|
return analysis
|
|
1053
1202
|
}
|
|
1054
1203
|
|
|
@@ -1225,3 +1374,112 @@ func (v *analysisVisitor) markFunctionVariable(ident *ast.Ident, funcType *types
|
|
|
1225
1374
|
v.analysis.MarkFunctionForReflection(funcNode, funcType)
|
|
1226
1375
|
}
|
|
1227
1376
|
}
|
|
1377
|
+
|
|
1378
|
+
// detectVariableShadowing detects variable shadowing in any := assignment
|
|
1379
|
+
func (v *analysisVisitor) detectVariableShadowing(assignStmt *ast.AssignStmt) *ShadowingInfo {
|
|
1380
|
+
shadowingInfo := &ShadowingInfo{
|
|
1381
|
+
ShadowedVariables: make(map[string]types.Object),
|
|
1382
|
+
TempVariables: make(map[string]string),
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
hasShadowing := false
|
|
1386
|
+
|
|
1387
|
+
// First, collect all LHS variable names that are being declared
|
|
1388
|
+
lhsVarNames := make(map[string]*ast.Ident)
|
|
1389
|
+
for _, lhsExpr := range assignStmt.Lhs {
|
|
1390
|
+
if lhsIdent, ok := lhsExpr.(*ast.Ident); ok && lhsIdent.Name != "_" {
|
|
1391
|
+
lhsVarNames[lhsIdent.Name] = lhsIdent
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// Next, check all RHS expressions for usage of variables that are also being declared on LHS
|
|
1396
|
+
for _, rhsExpr := range assignStmt.Rhs {
|
|
1397
|
+
v.findVariableUsageInExpr(rhsExpr, lhsVarNames, shadowingInfo, &hasShadowing)
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
if hasShadowing {
|
|
1401
|
+
return shadowingInfo
|
|
1402
|
+
}
|
|
1403
|
+
return nil
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// findVariableUsageInExpr recursively searches for variable usage in an expression
|
|
1407
|
+
func (v *analysisVisitor) findVariableUsageInExpr(expr ast.Expr, lhsVarNames map[string]*ast.Ident, shadowingInfo *ShadowingInfo, hasShadowing *bool) {
|
|
1408
|
+
if expr == nil {
|
|
1409
|
+
return
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
switch e := expr.(type) {
|
|
1413
|
+
case *ast.Ident:
|
|
1414
|
+
// Check if this identifier is being shadowed
|
|
1415
|
+
if lhsIdent, exists := lhsVarNames[e.Name]; exists {
|
|
1416
|
+
// This variable is being used on RHS but also declared on LHS - this is shadowing!
|
|
1417
|
+
|
|
1418
|
+
// Get the outer scope object for this variable
|
|
1419
|
+
if outerObj := v.pkg.TypesInfo.Uses[e]; outerObj != nil {
|
|
1420
|
+
// Make sure this isn't the same object as the LHS (which would mean no shadowing)
|
|
1421
|
+
if lhsObj := v.pkg.TypesInfo.Defs[lhsIdent]; lhsObj != outerObj {
|
|
1422
|
+
shadowingInfo.ShadowedVariables[e.Name] = outerObj
|
|
1423
|
+
shadowingInfo.TempVariables[e.Name] = "_temp_" + e.Name
|
|
1424
|
+
*hasShadowing = true
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
case *ast.CallExpr:
|
|
1430
|
+
// Check function arguments
|
|
1431
|
+
for _, arg := range e.Args {
|
|
1432
|
+
v.findVariableUsageInExpr(arg, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1433
|
+
}
|
|
1434
|
+
// Check function expression itself
|
|
1435
|
+
v.findVariableUsageInExpr(e.Fun, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1436
|
+
|
|
1437
|
+
case *ast.SelectorExpr:
|
|
1438
|
+
// Check the base expression (e.g., x in x.Method())
|
|
1439
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1440
|
+
|
|
1441
|
+
case *ast.IndexExpr:
|
|
1442
|
+
// Check both the expression and index (e.g., arr[i])
|
|
1443
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1444
|
+
v.findVariableUsageInExpr(e.Index, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1445
|
+
|
|
1446
|
+
case *ast.SliceExpr:
|
|
1447
|
+
// Check the expression and slice bounds
|
|
1448
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1449
|
+
if e.Low != nil {
|
|
1450
|
+
v.findVariableUsageInExpr(e.Low, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1451
|
+
}
|
|
1452
|
+
if e.High != nil {
|
|
1453
|
+
v.findVariableUsageInExpr(e.High, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1454
|
+
}
|
|
1455
|
+
if e.Max != nil {
|
|
1456
|
+
v.findVariableUsageInExpr(e.Max, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
case *ast.UnaryExpr:
|
|
1460
|
+
// Check the operand (e.g., &x, -x, !x)
|
|
1461
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1462
|
+
|
|
1463
|
+
case *ast.BinaryExpr:
|
|
1464
|
+
// Check both operands (e.g., x + y)
|
|
1465
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1466
|
+
v.findVariableUsageInExpr(e.Y, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1467
|
+
|
|
1468
|
+
case *ast.ParenExpr:
|
|
1469
|
+
// Check the parenthesized expression
|
|
1470
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1471
|
+
|
|
1472
|
+
case *ast.TypeAssertExpr:
|
|
1473
|
+
// Check the expression being type-asserted
|
|
1474
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1475
|
+
|
|
1476
|
+
case *ast.StarExpr:
|
|
1477
|
+
// Check the expression being dereferenced
|
|
1478
|
+
v.findVariableUsageInExpr(e.X, lhsVarNames, shadowingInfo, hasShadowing)
|
|
1479
|
+
|
|
1480
|
+
// Add more expression types as needed
|
|
1481
|
+
default:
|
|
1482
|
+
// For other expression types, we might need to add specific handling
|
|
1483
|
+
// For now, we'll ignore them as they're less common in shadowing scenarios
|
|
1484
|
+
}
|
|
1485
|
+
}
|
package/compiler/assignment.go
CHANGED
|
@@ -124,6 +124,12 @@ func (c *GoToTSCompiler) writeAssignmentCore(lhs, rhs []ast.Expr, tok token.Toke
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// Add semicolon before destructuring assignment to prevent TypeScript
|
|
128
|
+
// from interpreting it as array access on the previous line
|
|
129
|
+
if tok != token.DEFINE {
|
|
130
|
+
c.tsw.WriteLiterally(";")
|
|
131
|
+
}
|
|
132
|
+
|
|
127
133
|
// Use array destructuring for multi-variable assignments
|
|
128
134
|
c.tsw.WriteLiterally("[")
|
|
129
135
|
for i, l := range lhs {
|
package/compiler/compiler.go
CHANGED
|
@@ -621,6 +621,61 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
621
621
|
}
|
|
622
622
|
}
|
|
623
623
|
|
|
624
|
+
// Generate auto-imports for types from other files in the same package
|
|
625
|
+
if typeImports := c.PackageAnalysis.TypeCalls[currentFileName]; typeImports != nil {
|
|
626
|
+
// Sort source files for consistent import order
|
|
627
|
+
var sourceFiles []string
|
|
628
|
+
for sourceFile := range typeImports {
|
|
629
|
+
sourceFiles = append(sourceFiles, sourceFile)
|
|
630
|
+
}
|
|
631
|
+
sort.Strings(sourceFiles)
|
|
632
|
+
|
|
633
|
+
for _, sourceFile := range sourceFiles {
|
|
634
|
+
typeImports := typeImports[sourceFile]
|
|
635
|
+
if len(typeImports) > 0 {
|
|
636
|
+
// Filter out protobuf types - they should be imported from .pb.js files, not .gs.js files
|
|
637
|
+
var nonProtobufTypes []string
|
|
638
|
+
for _, typeName := range typeImports {
|
|
639
|
+
// Check if this type is a protobuf type by looking for it in the package
|
|
640
|
+
isProtobuf := false
|
|
641
|
+
if typeObj := c.pkg.Types.Scope().Lookup(typeName); typeObj != nil {
|
|
642
|
+
objType := typeObj.Type()
|
|
643
|
+
if namedType, ok := objType.(*types.Named); ok {
|
|
644
|
+
obj := namedType.Obj()
|
|
645
|
+
if obj != nil && obj.Pkg() != nil {
|
|
646
|
+
// Check if the type is defined in the current package and has a corresponding .pb.go file
|
|
647
|
+
if obj.Pkg() == c.pkg.Types {
|
|
648
|
+
// Check if there's a .pb.go file in the package that exports this type
|
|
649
|
+
// For now, we'll use a simple heuristic: if the type name ends with "Msg"
|
|
650
|
+
// and there's a .pb.go file in the package, assume it's a protobuf type
|
|
651
|
+
if strings.HasSuffix(typeName, "Msg") {
|
|
652
|
+
// Check if there are any .pb.go files in this package
|
|
653
|
+
for _, fileName := range c.pkg.CompiledGoFiles {
|
|
654
|
+
if strings.HasSuffix(fileName, ".pb.go") {
|
|
655
|
+
isProtobuf = true
|
|
656
|
+
break
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if !isProtobuf {
|
|
665
|
+
nonProtobufTypes = append(nonProtobufTypes, typeName)
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if len(nonProtobufTypes) > 0 {
|
|
670
|
+
// Sort types for consistent output
|
|
671
|
+
sort.Strings(nonProtobufTypes)
|
|
672
|
+
c.codeWriter.WriteLinef("import { %s } from \"./%s.gs.js\";",
|
|
673
|
+
strings.Join(nonProtobufTypes, ", "), sourceFile)
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
624
679
|
c.codeWriter.WriteLine("") // Add a newline after imports
|
|
625
680
|
|
|
626
681
|
if err := goWriter.WriteDecls(f.Decls); err != nil {
|
|
@@ -639,6 +694,9 @@ type GoToTSCompiler struct {
|
|
|
639
694
|
pkg *packages.Package
|
|
640
695
|
|
|
641
696
|
analysis *Analysis
|
|
697
|
+
|
|
698
|
+
// shadowingContext tracks temporary variable mappings when we're in a shadowing context
|
|
699
|
+
shadowingContext map[string]string
|
|
642
700
|
}
|
|
643
701
|
|
|
644
702
|
// It initializes the compiler with a `TSCodeWriter` for output,
|
|
@@ -646,9 +704,10 @@ type GoToTSCompiler struct {
|
|
|
646
704
|
// analysis results (`Analysis`) to guide the translation process.
|
|
647
705
|
func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, analysis *Analysis) *GoToTSCompiler {
|
|
648
706
|
return &GoToTSCompiler{
|
|
649
|
-
tsw:
|
|
650
|
-
pkg:
|
|
651
|
-
analysis:
|
|
707
|
+
tsw: tsw,
|
|
708
|
+
pkg: pkg,
|
|
709
|
+
analysis: analysis,
|
|
710
|
+
shadowingContext: make(map[string]string),
|
|
652
711
|
}
|
|
653
712
|
}
|
|
654
713
|
|
|
@@ -670,6 +729,12 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
|
|
|
670
729
|
return
|
|
671
730
|
}
|
|
672
731
|
|
|
732
|
+
// Check if we're in a shadowing context and should use a temporary variable
|
|
733
|
+
if tempVarName, exists := c.shadowingContext[exp.Name]; exists {
|
|
734
|
+
c.tsw.WriteLiterally(c.sanitizeIdentifier(tempVarName))
|
|
735
|
+
return
|
|
736
|
+
}
|
|
737
|
+
|
|
673
738
|
// Use TypesInfo to find the object associated with the identifier
|
|
674
739
|
var obj types.Object
|
|
675
740
|
obj = c.pkg.TypesInfo.Uses[exp]
|
|
@@ -680,11 +745,30 @@ func (c *GoToTSCompiler) WriteIdent(exp *ast.Ident, accessVarRefedValue bool) {
|
|
|
680
745
|
// Check if this identifier refers to a constant
|
|
681
746
|
if obj != nil {
|
|
682
747
|
if constObj, isConst := obj.(*types.Const); isConst {
|
|
683
|
-
//
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
|
|
687
|
-
|
|
748
|
+
// Evaluate constants to literals in these cases:
|
|
749
|
+
// 1. Predeclared constants (like iota, true, false) - these have nil package
|
|
750
|
+
// 2. Constants from the current package that are computed expressions
|
|
751
|
+
// (like iota-based constants or mathematical expressions)
|
|
752
|
+
//
|
|
753
|
+
// For simple assignments from imported constants (const x = pkg.Y),
|
|
754
|
+
// preserve the variable name for better readability.
|
|
755
|
+
|
|
756
|
+
if constObj.Pkg() == nil {
|
|
757
|
+
// Predeclared constants - always evaluate to literals
|
|
758
|
+
c.writeConstantValue(constObj)
|
|
759
|
+
return
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if constObj.Pkg() == c.pkg.Types {
|
|
763
|
+
// This is a constant from the current package.
|
|
764
|
+
// Check if it's a simple assignment by looking at the constant value:
|
|
765
|
+
// If the constant has the same name as an identifier in the import,
|
|
766
|
+
// it's likely a simple assignment and we should preserve the name.
|
|
767
|
+
// Otherwise, evaluate to literal (for computed expressions like iota).
|
|
768
|
+
|
|
769
|
+
// For now, let's be conservative and evaluate current package constants
|
|
770
|
+
// to literals to maintain compatibility with existing behavior.
|
|
771
|
+
// This handles iota-based constants correctly.
|
|
688
772
|
c.writeConstantValue(constObj)
|
|
689
773
|
return
|
|
690
774
|
}
|
|
@@ -970,6 +1054,12 @@ func (c *Compiler) copyEmbeddedPackage(embeddedPath string, outputPath string) e
|
|
|
970
1054
|
continue
|
|
971
1055
|
}
|
|
972
1056
|
|
|
1057
|
+
// Skip .test.ts files
|
|
1058
|
+
if strings.HasSuffix(fileName, ".test.ts") {
|
|
1059
|
+
// c.le.Debugf("Skipping test file: %s", fileName)
|
|
1060
|
+
continue
|
|
1061
|
+
}
|
|
1062
|
+
|
|
973
1063
|
// Remove existing file if it exists (but preserve directories)
|
|
974
1064
|
if stat, err := os.Stat(outputEntryPath); err == nil && !stat.IsDir() {
|
|
975
1065
|
if err := os.Remove(outputEntryPath); err != nil {
|
package/compiler/decl.go
CHANGED
|
@@ -101,29 +101,13 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
101
101
|
c.WriteFuncType(decl.Type, isAsync) // Write signature (params, return type)
|
|
102
102
|
c.tsw.WriteLiterally(" ")
|
|
103
103
|
|
|
104
|
-
hasNamedReturns
|
|
105
|
-
if decl.Type.Results != nil {
|
|
106
|
-
for _, field := range decl.Type.Results.List {
|
|
107
|
-
if len(field.Names) > 0 {
|
|
108
|
-
hasNamedReturns = true
|
|
109
|
-
break
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if hasNamedReturns {
|
|
104
|
+
if c.hasNamedReturns(decl.Type.Results) {
|
|
115
105
|
c.tsw.WriteLine("{")
|
|
116
106
|
c.tsw.Indent(1)
|
|
117
107
|
|
|
118
108
|
// Declare named return variables and initialize them to their zero values
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
|
|
122
|
-
c.WriteTypeExpr(field.Type)
|
|
123
|
-
c.tsw.WriteLiterally(" = ")
|
|
124
|
-
c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
|
|
125
|
-
c.tsw.WriteLine("")
|
|
126
|
-
}
|
|
109
|
+
if err := c.writeNamedReturnDeclarations(decl.Type.Results); err != nil {
|
|
110
|
+
return fmt.Errorf("failed to write named return declarations: %w", err)
|
|
127
111
|
}
|
|
128
112
|
}
|
|
129
113
|
|
|
@@ -131,7 +115,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
131
115
|
return fmt.Errorf("failed to write function body: %w", err)
|
|
132
116
|
}
|
|
133
117
|
|
|
134
|
-
if hasNamedReturns {
|
|
118
|
+
if c.hasNamedReturns(decl.Type.Results) {
|
|
135
119
|
c.tsw.Indent(-1)
|
|
136
120
|
c.tsw.WriteLine("}")
|
|
137
121
|
}
|
|
@@ -244,6 +228,11 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
244
228
|
}
|
|
245
229
|
}
|
|
246
230
|
|
|
231
|
+
// Declare named return variables and initialize them to their zero values
|
|
232
|
+
if err := c.writeNamedReturnDeclarations(decl.Type.Results); err != nil {
|
|
233
|
+
return fmt.Errorf("failed to write named return declarations: %w", err)
|
|
234
|
+
}
|
|
235
|
+
|
|
247
236
|
// write method body without outer braces
|
|
248
237
|
for _, stmt := range decl.Body.List {
|
|
249
238
|
if err := c.WriteStmt(stmt); err != nil {
|
|
@@ -263,3 +252,36 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
263
252
|
|
|
264
253
|
return nil
|
|
265
254
|
}
|
|
255
|
+
|
|
256
|
+
// writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
|
|
257
|
+
// It declares each named return variable with its appropriate type and zero value.
|
|
258
|
+
func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
|
|
259
|
+
if results == nil {
|
|
260
|
+
return nil
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
for _, field := range results.List {
|
|
264
|
+
for _, name := range field.Names {
|
|
265
|
+
c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
|
|
266
|
+
c.WriteTypeExpr(field.Type)
|
|
267
|
+
c.tsw.WriteLiterally(" = ")
|
|
268
|
+
c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
|
|
269
|
+
c.tsw.WriteLine("")
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return nil
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// hasNamedReturns checks if a function type has any named return parameters.
|
|
276
|
+
func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
|
|
277
|
+
if results == nil {
|
|
278
|
+
return false
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
for _, field := range results.List {
|
|
282
|
+
if len(field.Names) > 0 {
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return false
|
|
287
|
+
}
|
package/compiler/expr-call.go
CHANGED
|
@@ -97,6 +97,37 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
+
|
|
101
|
+
// Handle general slice type conversions like []T(namedType) where namedType has underlying type []T
|
|
102
|
+
if arrayType.Len == nil && len(exp.Args) == 1 {
|
|
103
|
+
arg := exp.Args[0]
|
|
104
|
+
if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
105
|
+
// Check if the argument is a named type with a slice underlying type
|
|
106
|
+
if namedArgType, isNamed := argType.(*types.Named); isNamed {
|
|
107
|
+
// Check if the named type has receiver methods (is a wrapper class)
|
|
108
|
+
typeName := namedArgType.Obj().Name()
|
|
109
|
+
if c.hasReceiverMethods(typeName) {
|
|
110
|
+
// Check if the underlying type matches the target slice type
|
|
111
|
+
if sliceUnderlying, isSlice := namedArgType.Underlying().(*types.Slice); isSlice {
|
|
112
|
+
// Get the target slice type
|
|
113
|
+
targetType := c.pkg.TypesInfo.TypeOf(arrayType)
|
|
114
|
+
if targetSliceType, isTargetSlice := targetType.Underlying().(*types.Slice); isTargetSlice {
|
|
115
|
+
// Check if element types are compatible
|
|
116
|
+
if types.Identical(sliceUnderlying.Elem(), targetSliceType.Elem()) {
|
|
117
|
+
// This is a conversion from NamedType to []T where NamedType has underlying []T
|
|
118
|
+
// Use valueOf() to get the underlying slice
|
|
119
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
120
|
+
return fmt.Errorf("failed to write argument for slice type conversion: %w", err)
|
|
121
|
+
}
|
|
122
|
+
c.tsw.WriteLiterally(".valueOf()")
|
|
123
|
+
return nil // Handled named type to slice conversion
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
100
131
|
}
|
|
101
132
|
|
|
102
133
|
if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
|
|
@@ -274,7 +305,12 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
274
305
|
}
|
|
275
306
|
|
|
276
307
|
c.tsw.WriteLiterally("$.makeSlice<")
|
|
277
|
-
|
|
308
|
+
// Use AST-based type writing to preserve qualified names
|
|
309
|
+
if arrType, ok := exp.Args[0].(*ast.ArrayType); ok {
|
|
310
|
+
c.WriteTypeExpr(arrType.Elt)
|
|
311
|
+
} else {
|
|
312
|
+
c.WriteGoType(goElemType, GoTypeContextGeneral)
|
|
313
|
+
}
|
|
278
314
|
c.tsw.WriteLiterally(">(")
|
|
279
315
|
|
|
280
316
|
hasCapacity := len(exp.Args) == 3
|
package/compiler/expr-type.go
CHANGED
|
@@ -45,6 +45,26 @@ func (c *GoToTSCompiler) WriteTypeExpr(a ast.Expr) {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Handle array/slice types to preserve qualified names in element types
|
|
49
|
+
if arrayType, ok := a.(*ast.ArrayType); ok {
|
|
50
|
+
if arrayType.Len == nil {
|
|
51
|
+
// Slice type: []T -> $.Slice<T>
|
|
52
|
+
// Check if it's a []byte slice first
|
|
53
|
+
if ident, ok := arrayType.Elt.(*ast.Ident); ok && ident.Name == "byte" {
|
|
54
|
+
c.tsw.WriteLiterally("$.Bytes")
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
c.tsw.WriteLiterally("$.Slice<")
|
|
58
|
+
c.WriteTypeExpr(arrayType.Elt) // Recursively handle element type preserving qualified names
|
|
59
|
+
c.tsw.WriteLiterally(">")
|
|
60
|
+
} else {
|
|
61
|
+
// Array type: [N]T -> T[]
|
|
62
|
+
c.WriteTypeExpr(arrayType.Elt) // Recursively handle element type preserving qualified names
|
|
63
|
+
c.tsw.WriteLiterally("[]")
|
|
64
|
+
}
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
// Get type information for the expression and use WriteGoType
|
|
49
69
|
typ := c.pkg.TypesInfo.TypeOf(a)
|
|
50
70
|
c.WriteGoType(typ, GoTypeContextGeneral)
|