goscript 0.0.58 → 0.0.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -33
- package/compiler/analysis.go +184 -43
- package/compiler/assignment.go +163 -217
- package/compiler/compiler.go +35 -31
- package/compiler/composite-lit.go +233 -196
- package/compiler/constraint.go +88 -0
- package/compiler/decl.go +82 -24
- package/compiler/expr-call-async.go +20 -34
- package/compiler/expr-call-builtins.go +19 -0
- package/compiler/expr-call-helpers.go +0 -28
- package/compiler/expr-call-make.go +93 -343
- package/compiler/expr-call-type-conversion.go +221 -249
- package/compiler/expr-call.go +70 -69
- package/compiler/expr-selector.go +21 -24
- package/compiler/expr.go +3 -60
- package/compiler/protobuf.go +180 -36
- package/compiler/spec-value.go +132 -24
- package/compiler/spec.go +14 -55
- package/compiler/stmt-assign.go +338 -356
- package/compiler/stmt-range.go +4 -24
- package/compiler/stmt.go +92 -203
- package/compiler/type-utils.go +185 -0
- package/compiler/type.go +26 -80
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +3 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +8 -2
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/fmt/fmt.js +113 -16
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -1
- package/dist/gs/runtime/runtime.js +1 -1
- package/dist/gs/slices/slices.d.ts +23 -0
- package/dist/gs/slices/slices.js +61 -0
- package/dist/gs/slices/slices.js.map +1 -1
- package/go.mod +10 -10
- package/go.sum +22 -14
- package/gs/builtin/slice.ts +5 -2
- package/gs/builtin/type.ts +13 -6
- package/gs/fmt/fmt.test.ts +176 -0
- package/gs/fmt/fmt.ts +109 -18
- package/gs/runtime/runtime.ts +1 -1
- package/gs/slices/slices.ts +68 -0
- package/package.json +3 -3
package/compiler/stmt-range.go
CHANGED
|
@@ -55,7 +55,7 @@ func (c *GoToTSCompiler) WriteStmtRange(exp *ast.RangeStmt) error {
|
|
|
55
55
|
|
|
56
56
|
// Handle basic types (string, integer)
|
|
57
57
|
if basic, ok := underlying.(*types.Basic); ok {
|
|
58
|
-
if
|
|
58
|
+
if c.isStringType(iterType) {
|
|
59
59
|
return c.writeStringRange(exp)
|
|
60
60
|
} else if basic.Info()&types.IsInteger != 0 {
|
|
61
61
|
return c.writeIntegerRange(exp)
|
|
@@ -96,28 +96,6 @@ func (c *GoToTSCompiler) WriteStmtRange(exp *ast.RangeStmt) error {
|
|
|
96
96
|
|
|
97
97
|
// Helper functions
|
|
98
98
|
|
|
99
|
-
func (c *GoToTSCompiler) isMapType(iterType, underlying types.Type) bool {
|
|
100
|
-
if _, ok := underlying.(*types.Map); ok {
|
|
101
|
-
return true
|
|
102
|
-
}
|
|
103
|
-
if typeParam, isTypeParam := iterType.(*types.TypeParam); isTypeParam {
|
|
104
|
-
constraint := typeParam.Constraint()
|
|
105
|
-
if constraint != nil {
|
|
106
|
-
constraintUnderlying := constraint.Underlying()
|
|
107
|
-
if iface, isInterface := constraintUnderlying.(*types.Interface); isInterface {
|
|
108
|
-
return hasMapConstraint(iface)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return false
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
func (c *GoToTSCompiler) isArrayOrSlice(underlying types.Type) bool {
|
|
116
|
-
_, isSlice := underlying.(*types.Slice)
|
|
117
|
-
_, isArray := underlying.(*types.Array)
|
|
118
|
-
return isArray || isSlice
|
|
119
|
-
}
|
|
120
|
-
|
|
121
99
|
func (c *GoToTSCompiler) isIteratorSignature(sig *types.Signature) bool {
|
|
122
100
|
params := sig.Params()
|
|
123
101
|
if params.Len() != 1 {
|
|
@@ -195,7 +173,9 @@ func (c *GoToTSCompiler) writeStringRange(exp *ast.RangeStmt) error {
|
|
|
195
173
|
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
196
174
|
c.tsw.WriteLiterally("const ")
|
|
197
175
|
c.WriteIdent(ident, false)
|
|
198
|
-
c.tsw.WriteLiterally(" = _runes[
|
|
176
|
+
c.tsw.WriteLiterally(" = _runes[")
|
|
177
|
+
c.tsw.WriteLiterally(indexVarName)
|
|
178
|
+
c.tsw.WriteLiterally("]")
|
|
199
179
|
c.tsw.WriteLine("")
|
|
200
180
|
}
|
|
201
181
|
}
|
package/compiler/stmt.go
CHANGED
|
@@ -139,7 +139,7 @@ func (c *GoToTSCompiler) WriteStmtDecl(stmt *ast.DeclStmt) error {
|
|
|
139
139
|
return fmt.Errorf("failed to write type spec in declaration statement: %w", err)
|
|
140
140
|
}
|
|
141
141
|
} else {
|
|
142
|
-
|
|
142
|
+
return fmt.Errorf("unhandled spec in DeclStmt: %T", spec)
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
} else {
|
|
@@ -213,7 +213,7 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
|
213
213
|
case *ast.Ident:
|
|
214
214
|
// Handle named functions: go namedFunc(args)
|
|
215
215
|
// Get the object for this function
|
|
216
|
-
obj := c.
|
|
216
|
+
obj := c.objectOfIdent(fun)
|
|
217
217
|
if obj == nil {
|
|
218
218
|
return errors.Errorf("could not find object for function: %s", fun.Name)
|
|
219
219
|
}
|
|
@@ -255,7 +255,7 @@ func (c *GoToTSCompiler) WriteStmtGo(exp *ast.GoStmt) error {
|
|
|
255
255
|
case *ast.SelectorExpr:
|
|
256
256
|
// Handle selector expressions: go x.Method(args)
|
|
257
257
|
// Get the object for the selected method
|
|
258
|
-
obj := c.
|
|
258
|
+
obj := c.objectOfIdent(fun.Sel)
|
|
259
259
|
if obj == nil {
|
|
260
260
|
return errors.Errorf("could not find object for selected method: %s", fun.Sel.Name)
|
|
261
261
|
}
|
|
@@ -447,64 +447,22 @@ func (c *GoToTSCompiler) WriteStmtSend(exp *ast.SendStmt) error {
|
|
|
447
447
|
func (c *GoToTSCompiler) WriteStmtIf(exp *ast.IfStmt) error {
|
|
448
448
|
// Handle optional initialization statement
|
|
449
449
|
if exp.Init != nil {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
for varName, tempVarName := range shadowingInfo.TempVariables {
|
|
456
|
-
c.tsw.WriteLiterally("const ")
|
|
457
|
-
c.tsw.WriteLiterally(tempVarName)
|
|
458
|
-
c.tsw.WriteLiterally(" = ")
|
|
459
|
-
|
|
460
|
-
// Check if this is a built-in function and handle it directly
|
|
461
|
-
if c.isBuiltinFunction(varName) {
|
|
462
|
-
c.tsw.WriteLiterally("$.")
|
|
463
|
-
c.tsw.WriteLiterally(varName)
|
|
464
|
-
} else {
|
|
465
|
-
// Get the original object for this shadowed variable
|
|
466
|
-
if originalObj, exists := shadowingInfo.ShadowedVariables[varName]; exists {
|
|
467
|
-
// Create an identifier with the original name and use WriteValueExpr to properly resolve it
|
|
468
|
-
originalIdent := &ast.Ident{Name: varName}
|
|
469
|
-
// Set the identifier in the Uses map so WriteValueExpr can find the object
|
|
470
|
-
c.pkg.TypesInfo.Uses[originalIdent] = originalObj
|
|
471
|
-
c.WriteValueExpr(originalIdent)
|
|
472
|
-
} else {
|
|
473
|
-
// Fallback to literal name if no object found (shouldn't happen in normal cases)
|
|
474
|
-
c.tsw.WriteLiterally(varName)
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
c.tsw.WriteLine("")
|
|
478
|
-
}
|
|
479
|
-
}
|
|
450
|
+
shadowingInfo := c.analysis.GetShadowingInfo(exp)
|
|
451
|
+
|
|
452
|
+
// Write temp variables if shadowing detected
|
|
453
|
+
if shadowingInfo != nil {
|
|
454
|
+
c.writeShadowingTempVars(shadowingInfo)
|
|
480
455
|
}
|
|
481
456
|
|
|
482
457
|
c.tsw.WriteLine("{")
|
|
483
458
|
c.tsw.Indent(1)
|
|
484
459
|
|
|
485
|
-
//
|
|
486
|
-
if
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
// Handle the initialization with shadowing support
|
|
490
|
-
if assignStmt, ok := exp.Init.(*ast.AssignStmt); ok {
|
|
491
|
-
if err := c.writeShadowedAssignmentWithoutTempVars(assignStmt, shadowingInfo); err != nil {
|
|
492
|
-
return fmt.Errorf("failed to write shadowed assignment in if init: %w", err)
|
|
493
|
-
}
|
|
494
|
-
} else {
|
|
495
|
-
// Non-assignment initialization statement
|
|
496
|
-
if err := c.WriteStmt(exp.Init); err != nil {
|
|
497
|
-
return fmt.Errorf("failed to write if initialization statement: %w", err)
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
} else {
|
|
501
|
-
// No shadowing info, write normally
|
|
502
|
-
if err := c.WriteStmt(exp.Init); err != nil {
|
|
503
|
-
return fmt.Errorf("failed to write if initialization statement: %w", err)
|
|
504
|
-
}
|
|
460
|
+
// Write initialization with shadowing support if needed
|
|
461
|
+
if assignStmt, ok := exp.Init.(*ast.AssignStmt); ok && shadowingInfo != nil {
|
|
462
|
+
if err := c.writeAssignmentWithShadowing(assignStmt, shadowingInfo); err != nil {
|
|
463
|
+
return fmt.Errorf("failed to write shadowed assignment in if init: %w", err)
|
|
505
464
|
}
|
|
506
465
|
} else {
|
|
507
|
-
// No variable shadowing, write initialization normally
|
|
508
466
|
if err := c.WriteStmt(exp.Init); err != nil {
|
|
509
467
|
return fmt.Errorf("failed to write if initialization statement: %w", err)
|
|
510
468
|
}
|
|
@@ -675,6 +633,9 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
|
|
|
675
633
|
hasAsyncDefer := false
|
|
676
634
|
for _, stmt := range exp.List {
|
|
677
635
|
if deferStmt, ok := stmt.(*ast.DeferStmt); ok {
|
|
636
|
+
if deferStmt.Call == nil || deferStmt.Call.Fun == nil {
|
|
637
|
+
continue
|
|
638
|
+
}
|
|
678
639
|
if funcLit, ok := deferStmt.Call.Fun.(*ast.FuncLit); ok {
|
|
679
640
|
if c.analysis.IsFuncLitAsync(funcLit) {
|
|
680
641
|
hasAsyncDefer = true
|
|
@@ -682,7 +643,7 @@ func (c *GoToTSCompiler) WriteStmtBlock(exp *ast.BlockStmt, suppressNewline bool
|
|
|
682
643
|
}
|
|
683
644
|
} else {
|
|
684
645
|
// Check if the deferred call is to an async function
|
|
685
|
-
if c.
|
|
646
|
+
if c.isCallExprAsync(deferStmt.Call) {
|
|
686
647
|
hasAsyncDefer = true
|
|
687
648
|
break
|
|
688
649
|
}
|
|
@@ -854,7 +815,7 @@ func (c *GoToTSCompiler) WriteStmtSwitch(exp *ast.SwitchStmt) error {
|
|
|
854
815
|
return fmt.Errorf("failed to write case clause in switch statement: %w", err)
|
|
855
816
|
}
|
|
856
817
|
} else {
|
|
857
|
-
|
|
818
|
+
return fmt.Errorf("unhandled statement in switch body: %T", stmt)
|
|
858
819
|
}
|
|
859
820
|
}
|
|
860
821
|
|
|
@@ -885,7 +846,7 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
|
|
|
885
846
|
isAsyncDeferred = c.analysis.IsFuncLitAsync(funcLit)
|
|
886
847
|
} else {
|
|
887
848
|
// Check if the deferred call is to an async function
|
|
888
|
-
isAsyncDeferred = c.
|
|
849
|
+
isAsyncDeferred = c.isCallExprAsync(exp.Call)
|
|
889
850
|
}
|
|
890
851
|
|
|
891
852
|
// Set async prefix based on pre-computed async status
|
|
@@ -923,35 +884,6 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
|
|
|
923
884
|
return nil
|
|
924
885
|
}
|
|
925
886
|
|
|
926
|
-
// isCallAsyncInDefer determines if a call expression in a defer statement is async
|
|
927
|
-
func (c *GoToTSCompiler) isCallAsyncInDefer(callExpr *ast.CallExpr) bool {
|
|
928
|
-
switch fun := callExpr.Fun.(type) {
|
|
929
|
-
case *ast.Ident:
|
|
930
|
-
// Direct function call (e.g., defer myFunc())
|
|
931
|
-
if obj := c.pkg.TypesInfo.Uses[fun]; obj != nil {
|
|
932
|
-
return c.analysis.IsAsyncFunc(obj)
|
|
933
|
-
}
|
|
934
|
-
case *ast.SelectorExpr:
|
|
935
|
-
// Method call (e.g., defer handle.Release()) or package function call
|
|
936
|
-
if selection := c.pkg.TypesInfo.Selections[fun]; selection != nil {
|
|
937
|
-
// Method call on an object
|
|
938
|
-
if methodObj := selection.Obj(); methodObj != nil {
|
|
939
|
-
return c.analysis.IsAsyncFunc(methodObj)
|
|
940
|
-
}
|
|
941
|
-
} else if ident, ok := fun.X.(*ast.Ident); ok {
|
|
942
|
-
// Package-level function call (e.g., defer time.Sleep())
|
|
943
|
-
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
944
|
-
if pkgName, isPkg := obj.(*types.PkgName); isPkg {
|
|
945
|
-
methodName := fun.Sel.Name
|
|
946
|
-
pkgPath := pkgName.Imported().Path()
|
|
947
|
-
return c.analysis.IsMethodAsync(pkgPath, "", methodName)
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
return false
|
|
953
|
-
}
|
|
954
|
-
|
|
955
887
|
// WriteStmtLabeled handles labeled statements (ast.LabeledStmt), such as "label: statement".
|
|
956
888
|
// In TypeScript, labels cannot be used with variable declarations, so we need to handle this case specially.
|
|
957
889
|
func (c *GoToTSCompiler) WriteStmtLabeled(stmt *ast.LabeledStmt) error {
|
|
@@ -992,10 +924,10 @@ func (c *GoToTSCompiler) WriteStmtLabeled(stmt *ast.LabeledStmt) error {
|
|
|
992
924
|
return nil
|
|
993
925
|
}
|
|
994
926
|
|
|
995
|
-
//
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
927
|
+
// ============ Variable Shadowing Support ============
|
|
928
|
+
|
|
929
|
+
// writeShadowingTempVars creates temporary variables for shadowed variables
|
|
930
|
+
func (c *GoToTSCompiler) writeShadowingTempVars(shadowingInfo *ShadowingInfo) {
|
|
999
931
|
for varName, tempVarName := range shadowingInfo.TempVariables {
|
|
1000
932
|
c.tsw.WriteLiterally("const ")
|
|
1001
933
|
c.tsw.WriteLiterally(tempVarName)
|
|
@@ -1020,94 +952,20 @@ func (c *GoToTSCompiler) writeShadowedAssignment(stmt *ast.AssignStmt, shadowing
|
|
|
1020
952
|
}
|
|
1021
953
|
c.tsw.WriteLine("")
|
|
1022
954
|
}
|
|
1023
|
-
|
|
1024
|
-
// Now write the LHS variables (these are new declarations)
|
|
1025
|
-
for i, lhsExpr := range stmt.Lhs {
|
|
1026
|
-
if i > 0 {
|
|
1027
|
-
c.tsw.WriteLiterally(", ")
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
if ident, ok := lhsExpr.(*ast.Ident); ok {
|
|
1031
|
-
if ident.Name == "_" {
|
|
1032
|
-
c.tsw.WriteLiterally("_")
|
|
1033
|
-
} else {
|
|
1034
|
-
c.tsw.WriteLiterally("let ")
|
|
1035
|
-
c.WriteIdent(ident, false) // Don't use temp variable for LHS
|
|
1036
|
-
}
|
|
1037
|
-
} else {
|
|
1038
|
-
// For non-identifier LHS (shouldn't happen in := assignments), write normally
|
|
1039
|
-
if err := c.WriteValueExpr(lhsExpr); err != nil {
|
|
1040
|
-
return err
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
c.tsw.WriteLiterally(" = ")
|
|
1046
|
-
|
|
1047
|
-
// Write RHS expressions - but we need to replace shadowed variables with temporary variables
|
|
1048
|
-
for i, rhsExpr := range stmt.Rhs {
|
|
1049
|
-
if i > 0 {
|
|
1050
|
-
c.tsw.WriteLiterally(", ")
|
|
1051
|
-
}
|
|
1052
|
-
if err := c.writeShadowedRHSExpression(rhsExpr, shadowingInfo); err != nil {
|
|
1053
|
-
return err
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
c.tsw.WriteLine("")
|
|
1058
|
-
return nil
|
|
1059
955
|
}
|
|
1060
956
|
|
|
1061
|
-
//
|
|
1062
|
-
//
|
|
1063
|
-
func (c *GoToTSCompiler)
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1069
|
-
valueExpr := stmt.Lhs[0]
|
|
1070
|
-
okExpr := stmt.Lhs[1]
|
|
1071
|
-
valueIdent, valueIsIdent := valueExpr.(*ast.Ident)
|
|
1072
|
-
okIdent, okIsIdent := okExpr.(*ast.Ident)
|
|
1073
|
-
if valueIsIdent && okIsIdent {
|
|
1074
|
-
valueName := valueIdent.Name
|
|
1075
|
-
okName := okIdent.Name
|
|
1076
|
-
valueIsBlank := valueName == "_"
|
|
1077
|
-
okIsBlank := okName == "_"
|
|
1078
|
-
if valueIsBlank && okIsBlank {
|
|
1079
|
-
// Both blank, evaluate RHS for side effects
|
|
1080
|
-
if err := c.writeShadowedRHSExpression(typeAssert.X, shadowingInfo); err != nil {
|
|
1081
|
-
return err
|
|
1082
|
-
}
|
|
1083
|
-
c.tsw.WriteLine("")
|
|
1084
|
-
return nil
|
|
1085
|
-
}
|
|
1086
|
-
c.tsw.WriteLiterally("let { ")
|
|
1087
|
-
var parts []string
|
|
1088
|
-
if !valueIsBlank {
|
|
1089
|
-
parts = append(parts, "value: "+valueName)
|
|
1090
|
-
}
|
|
1091
|
-
if !okIsBlank {
|
|
1092
|
-
parts = append(parts, "ok: "+okName)
|
|
1093
|
-
}
|
|
1094
|
-
c.tsw.WriteLiterally(strings.Join(parts, ", "))
|
|
1095
|
-
c.tsw.WriteLiterally(" } = $.typeAssert<")
|
|
1096
|
-
c.WriteTypeExpr(typeAssert.Type)
|
|
1097
|
-
c.tsw.WriteLiterally(">(")
|
|
1098
|
-
if err := c.writeShadowedRHSExpression(typeAssert.X, shadowingInfo); err != nil {
|
|
1099
|
-
return err
|
|
1100
|
-
}
|
|
1101
|
-
c.tsw.WriteLiterally(", ")
|
|
1102
|
-
c.writeTypeDescription(typeAssert.Type)
|
|
1103
|
-
c.tsw.WriteLiterally(")")
|
|
1104
|
-
c.tsw.WriteLine("")
|
|
1105
|
-
return nil
|
|
1106
|
-
}
|
|
957
|
+
// writeAssignmentWithShadowing writes an assignment statement that has variable shadowing.
|
|
958
|
+
// Handles both regular assignments and type assertions, with optional temp variable creation.
|
|
959
|
+
func (c *GoToTSCompiler) writeAssignmentWithShadowing(stmt *ast.AssignStmt, shadowingInfo *ShadowingInfo) error {
|
|
960
|
+
// Check for type assertion special case
|
|
961
|
+
if len(stmt.Rhs) == 1 && len(stmt.Lhs) == 2 {
|
|
962
|
+
if typeAssert, ok := stmt.Rhs[0].(*ast.TypeAssertExpr); ok {
|
|
963
|
+
return c.writeTypeAssertWithShadowing(stmt, typeAssert, shadowingInfo)
|
|
1107
964
|
}
|
|
1108
965
|
}
|
|
1109
966
|
|
|
1110
|
-
|
|
967
|
+
// Regular assignment: write LHS declarations
|
|
968
|
+
firstDecl := true
|
|
1111
969
|
for i, lhsExpr := range stmt.Lhs {
|
|
1112
970
|
if i > 0 {
|
|
1113
971
|
c.tsw.WriteLiterally(", ")
|
|
@@ -1128,12 +986,14 @@ func (c *GoToTSCompiler) writeShadowedAssignmentWithoutTempVars(stmt *ast.Assign
|
|
|
1128
986
|
}
|
|
1129
987
|
}
|
|
1130
988
|
}
|
|
989
|
+
|
|
990
|
+
// Write RHS with shadowed variable substitution
|
|
1131
991
|
c.tsw.WriteLiterally(" = ")
|
|
1132
992
|
for i, rhsExpr := range stmt.Rhs {
|
|
1133
993
|
if i > 0 {
|
|
1134
994
|
c.tsw.WriteLiterally(", ")
|
|
1135
995
|
}
|
|
1136
|
-
if err := c.
|
|
996
|
+
if err := c.substituteExprForShadowing(rhsExpr, shadowingInfo); err != nil {
|
|
1137
997
|
return err
|
|
1138
998
|
}
|
|
1139
999
|
}
|
|
@@ -1141,8 +1001,54 @@ func (c *GoToTSCompiler) writeShadowedAssignmentWithoutTempVars(stmt *ast.Assign
|
|
|
1141
1001
|
return nil
|
|
1142
1002
|
}
|
|
1143
1003
|
|
|
1144
|
-
//
|
|
1145
|
-
func (c *GoToTSCompiler)
|
|
1004
|
+
// writeTypeAssertWithShadowing writes a type assertion assignment (v, ok := x.(T)) with shadowing support
|
|
1005
|
+
func (c *GoToTSCompiler) writeTypeAssertWithShadowing(stmt *ast.AssignStmt, typeAssert *ast.TypeAssertExpr, shadowingInfo *ShadowingInfo) error {
|
|
1006
|
+
valueIdent, valueIsIdent := stmt.Lhs[0].(*ast.Ident)
|
|
1007
|
+
okIdent, okIsIdent := stmt.Lhs[1].(*ast.Ident)
|
|
1008
|
+
|
|
1009
|
+
if !valueIsIdent || !okIsIdent {
|
|
1010
|
+
return fmt.Errorf("type assertion LHS must be identifiers")
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
valueName := valueIdent.Name
|
|
1014
|
+
okName := okIdent.Name
|
|
1015
|
+
valueIsBlank := valueName == "_"
|
|
1016
|
+
okIsBlank := okName == "_"
|
|
1017
|
+
|
|
1018
|
+
if valueIsBlank && okIsBlank {
|
|
1019
|
+
// Both blank, evaluate RHS for side effects only
|
|
1020
|
+
if err := c.substituteExprForShadowing(typeAssert.X, shadowingInfo); err != nil {
|
|
1021
|
+
return err
|
|
1022
|
+
}
|
|
1023
|
+
c.tsw.WriteLine("")
|
|
1024
|
+
return nil
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// Destructure into value and ok
|
|
1028
|
+
c.tsw.WriteLiterally("let { ")
|
|
1029
|
+
var parts []string
|
|
1030
|
+
if !valueIsBlank {
|
|
1031
|
+
parts = append(parts, "value: "+valueName)
|
|
1032
|
+
}
|
|
1033
|
+
if !okIsBlank {
|
|
1034
|
+
parts = append(parts, "ok: "+okName)
|
|
1035
|
+
}
|
|
1036
|
+
c.tsw.WriteLiterally(strings.Join(parts, ", "))
|
|
1037
|
+
c.tsw.WriteLiterally(" } = $.typeAssert<")
|
|
1038
|
+
c.WriteTypeExpr(typeAssert.Type)
|
|
1039
|
+
c.tsw.WriteLiterally(">(")
|
|
1040
|
+
if err := c.substituteExprForShadowing(typeAssert.X, shadowingInfo); err != nil {
|
|
1041
|
+
return err
|
|
1042
|
+
}
|
|
1043
|
+
c.tsw.WriteLiterally(", ")
|
|
1044
|
+
c.writeTypeDescription(typeAssert.Type)
|
|
1045
|
+
c.tsw.WriteLiterally(")")
|
|
1046
|
+
c.tsw.WriteLine("")
|
|
1047
|
+
return nil
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// substituteExprForShadowing writes an expression, replacing shadowed variables with temporary variables
|
|
1051
|
+
func (c *GoToTSCompiler) substituteExprForShadowing(expr ast.Expr, shadowingInfo *ShadowingInfo) error {
|
|
1146
1052
|
switch e := expr.(type) {
|
|
1147
1053
|
case *ast.Ident:
|
|
1148
1054
|
// Check if this identifier is a shadowed variable
|
|
@@ -1157,7 +1063,7 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
|
|
|
1157
1063
|
|
|
1158
1064
|
case *ast.CallExpr:
|
|
1159
1065
|
// Handle function calls - replace identifiers in arguments with temp variables
|
|
1160
|
-
if err := c.
|
|
1066
|
+
if err := c.substituteExprForShadowing(e.Fun, shadowingInfo); err != nil {
|
|
1161
1067
|
return err
|
|
1162
1068
|
}
|
|
1163
1069
|
|
|
@@ -1169,7 +1075,7 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
|
|
|
1169
1075
|
if i > 0 {
|
|
1170
1076
|
c.tsw.WriteLiterally(", ")
|
|
1171
1077
|
}
|
|
1172
|
-
if err := c.
|
|
1078
|
+
if err := c.substituteExprForShadowing(arg, shadowingInfo); err != nil {
|
|
1173
1079
|
return err
|
|
1174
1080
|
}
|
|
1175
1081
|
}
|
|
@@ -1178,7 +1084,7 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
|
|
|
1178
1084
|
|
|
1179
1085
|
case *ast.SelectorExpr:
|
|
1180
1086
|
// Handle selector expressions (e.g., obj.Method)
|
|
1181
|
-
if err := c.
|
|
1087
|
+
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1182
1088
|
return err
|
|
1183
1089
|
}
|
|
1184
1090
|
c.tsw.WriteLiterally(".")
|
|
@@ -1187,11 +1093,11 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
|
|
|
1187
1093
|
|
|
1188
1094
|
case *ast.IndexExpr:
|
|
1189
1095
|
// Handle index expressions (e.g., arr[i])
|
|
1190
|
-
if err := c.
|
|
1096
|
+
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1191
1097
|
return err
|
|
1192
1098
|
}
|
|
1193
1099
|
c.tsw.WriteLiterally("[")
|
|
1194
|
-
if err := c.
|
|
1100
|
+
if err := c.substituteExprForShadowing(e.Index, shadowingInfo); err != nil {
|
|
1195
1101
|
return err
|
|
1196
1102
|
}
|
|
1197
1103
|
c.tsw.WriteLiterally("]")
|
|
@@ -1200,22 +1106,22 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
|
|
|
1200
1106
|
case *ast.UnaryExpr:
|
|
1201
1107
|
// Handle unary expressions (e.g., &x, -x)
|
|
1202
1108
|
c.tsw.WriteLiterally(e.Op.String())
|
|
1203
|
-
return c.
|
|
1109
|
+
return c.substituteExprForShadowing(e.X, shadowingInfo)
|
|
1204
1110
|
|
|
1205
1111
|
case *ast.BinaryExpr:
|
|
1206
1112
|
// Handle binary expressions (e.g., x + y)
|
|
1207
|
-
if err := c.
|
|
1113
|
+
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1208
1114
|
return err
|
|
1209
1115
|
}
|
|
1210
1116
|
c.tsw.WriteLiterally(" ")
|
|
1211
1117
|
c.tsw.WriteLiterally(e.Op.String())
|
|
1212
1118
|
c.tsw.WriteLiterally(" ")
|
|
1213
|
-
return c.
|
|
1119
|
+
return c.substituteExprForShadowing(e.Y, shadowingInfo)
|
|
1214
1120
|
|
|
1215
1121
|
case *ast.ParenExpr:
|
|
1216
1122
|
// Handle parenthesized expressions
|
|
1217
1123
|
c.tsw.WriteLiterally("(")
|
|
1218
|
-
if err := c.
|
|
1124
|
+
if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
|
|
1219
1125
|
return err
|
|
1220
1126
|
}
|
|
1221
1127
|
c.tsw.WriteLiterally(")")
|
|
@@ -1229,22 +1135,5 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
|
|
|
1229
1135
|
|
|
1230
1136
|
// isBuiltinFunction checks if the given name is a Go built-in function
|
|
1231
1137
|
func (c *GoToTSCompiler) isBuiltinFunction(name string) bool {
|
|
1232
|
-
|
|
1233
|
-
"len": true,
|
|
1234
|
-
"cap": true,
|
|
1235
|
-
"make": true,
|
|
1236
|
-
"new": true,
|
|
1237
|
-
"append": true,
|
|
1238
|
-
"copy": true,
|
|
1239
|
-
"delete": true,
|
|
1240
|
-
"complex": true,
|
|
1241
|
-
"real": true,
|
|
1242
|
-
"imag": true,
|
|
1243
|
-
"close": true,
|
|
1244
|
-
"panic": true,
|
|
1245
|
-
"recover": true,
|
|
1246
|
-
"print": true,
|
|
1247
|
-
"println": true,
|
|
1248
|
-
}
|
|
1249
|
-
return builtins[name]
|
|
1138
|
+
return builtinFunctions[name]
|
|
1250
1139
|
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/types"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// isByteSliceType checks if a type is []byte (slice of uint8)
|
|
9
|
+
func (c *GoToTSCompiler) isByteSliceType(t types.Type) bool {
|
|
10
|
+
if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
|
|
11
|
+
if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Uint8 {
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// isRuneSliceType checks if a type is []rune (slice of int32)
|
|
19
|
+
func (c *GoToTSCompiler) isRuneSliceType(t types.Type) bool {
|
|
20
|
+
if sliceType, isSlice := t.Underlying().(*types.Slice); isSlice {
|
|
21
|
+
if basicElem, isBasic := sliceType.Elem().(*types.Basic); isBasic && basicElem.Kind() == types.Int32 {
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// isStringType checks if a type is string
|
|
29
|
+
func (c *GoToTSCompiler) isStringType(t types.Type) bool {
|
|
30
|
+
if basic, isBasic := t.Underlying().(*types.Basic); isBasic {
|
|
31
|
+
return basic.Kind() == types.String || basic.Kind() == types.UntypedString
|
|
32
|
+
}
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// isPointerType checks if a type expression represents a pointer type
|
|
37
|
+
func (c *GoToTSCompiler) isPointerType(expr ast.Expr) bool {
|
|
38
|
+
_, isPointer := expr.(*ast.StarExpr)
|
|
39
|
+
return isPointer
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// isProtobufType checks if a given type is a protobuf type by examining its methods
|
|
43
|
+
// and, when available, by verifying it implements the protobuf-go-lite Message interface.
|
|
44
|
+
func (c *GoToTSCompiler) isProtobufType(typ types.Type) bool {
|
|
45
|
+
// Normalize to a named type if possible
|
|
46
|
+
var named *types.Named
|
|
47
|
+
switch t := typ.(type) {
|
|
48
|
+
case *types.Named:
|
|
49
|
+
named = t
|
|
50
|
+
case *types.Pointer:
|
|
51
|
+
if n, ok := t.Elem().(*types.Named); ok {
|
|
52
|
+
named = n
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if named == nil {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Prefer interface-based detection when the protobuf-go-lite package is loaded
|
|
60
|
+
if iface := c.getProtobufMessageInterface(); iface != nil {
|
|
61
|
+
if types.Implements(named, iface) || types.Implements(types.NewPointer(named), iface) {
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Fallback: method-set detection for common protobuf-go-lite methods
|
|
67
|
+
// Check both value and pointer method sets
|
|
68
|
+
if c.typeHasMethods(named, "MarshalVT", "UnmarshalVT") || c.typeHasMethods(types.NewPointer(named), "MarshalVT", "UnmarshalVT") {
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// isNamedNumericType checks if a given type is a named type with an underlying numeric type.
|
|
76
|
+
func (c *GoToTSCompiler) isNamedNumericType(t types.Type) bool {
|
|
77
|
+
finalType, wasNamed := c.getFinalUnderlyingType(t)
|
|
78
|
+
if !wasNamed {
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if basicType, isBasic := finalType.(*types.Basic); isBasic {
|
|
83
|
+
info := basicType.Info()
|
|
84
|
+
return (info&types.IsInteger) != 0 || (info&types.IsFloat) != 0
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// isWrapperType checks if a type should be treated as a wrapper type (type alias with basic underlying type).
|
|
91
|
+
// Wrapper types are rendered as TypeScript type aliases rather than classes with constructors.
|
|
92
|
+
// Examples: os.FileMode (uint32), MyString (string), etc.
|
|
93
|
+
func (c *GoToTSCompiler) isWrapperType(t types.Type) bool {
|
|
94
|
+
// Check analysis cache first (for types with methods in analyzed packages)
|
|
95
|
+
if c.analysis.IsNamedBasicType(t) {
|
|
96
|
+
return true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// For external package types, check if it's a named type with a basic underlying type
|
|
100
|
+
if namedType, ok := t.(*types.Named); ok {
|
|
101
|
+
if _, ok := namedType.Underlying().(*types.Basic); ok {
|
|
102
|
+
return true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Also check for type aliases with basic underlying types
|
|
107
|
+
if aliasType, ok := t.(*types.Alias); ok {
|
|
108
|
+
if _, ok := aliasType.Underlying().(*types.Basic); ok {
|
|
109
|
+
return true
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// isStructValueType checks if a type is a named struct type
|
|
117
|
+
func (c *GoToTSCompiler) isStructValueType(fieldType types.Type) bool {
|
|
118
|
+
if named, ok := fieldType.(*types.Named); ok {
|
|
119
|
+
if _, isStruct := named.Underlying().(*types.Struct); isStruct {
|
|
120
|
+
return true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// isImportedBasicType checks if a type is an imported named type with a basic underlying type
|
|
127
|
+
func (c *GoToTSCompiler) isImportedBasicType(fieldType types.Type) bool {
|
|
128
|
+
// Handle named types
|
|
129
|
+
if named, isNamed := fieldType.(*types.Named); isNamed {
|
|
130
|
+
obj := named.Obj()
|
|
131
|
+
if obj == nil || obj.Pkg() == nil || obj.Pkg() == c.pkg.Types {
|
|
132
|
+
return false // Not imported or is local
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
underlying := named.Underlying()
|
|
136
|
+
if underlying == nil {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_, isBasic := underlying.(*types.Basic)
|
|
141
|
+
return isBasic
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Handle type aliases (like os.FileMode = fs.FileMode)
|
|
145
|
+
if alias, isAlias := fieldType.(*types.Alias); isAlias {
|
|
146
|
+
obj := alias.Obj()
|
|
147
|
+
if obj == nil || obj.Pkg() == nil || obj.Pkg() == c.pkg.Types {
|
|
148
|
+
return false // Not imported or is local
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
underlying := alias.Underlying()
|
|
152
|
+
if underlying == nil {
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
_, isBasic := underlying.(*types.Basic)
|
|
157
|
+
return isBasic
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return false
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// isMapType checks if a type is a map type (including type parameters constrained to maps)
|
|
164
|
+
func (c *GoToTSCompiler) isMapType(iterType, underlying types.Type) bool {
|
|
165
|
+
if _, ok := underlying.(*types.Map); ok {
|
|
166
|
+
return true
|
|
167
|
+
}
|
|
168
|
+
if typeParam, isTypeParam := iterType.(*types.TypeParam); isTypeParam {
|
|
169
|
+
constraint := typeParam.Constraint()
|
|
170
|
+
if constraint != nil {
|
|
171
|
+
constraintUnderlying := constraint.Underlying()
|
|
172
|
+
if iface, isInterface := constraintUnderlying.(*types.Interface); isInterface {
|
|
173
|
+
return hasMapConstraint(iface)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// isArrayOrSlice checks if a type is an array or slice type
|
|
181
|
+
func (c *GoToTSCompiler) isArrayOrSlice(underlying types.Type) bool {
|
|
182
|
+
_, isSlice := underlying.(*types.Slice)
|
|
183
|
+
_, isArray := underlying.(*types.Array)
|
|
184
|
+
return isArray || isSlice
|
|
185
|
+
}
|