goscript 0.0.58 → 0.0.59

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 (44) hide show
  1. package/README.md +40 -33
  2. package/compiler/analysis.go +115 -19
  3. package/compiler/assignment.go +163 -217
  4. package/compiler/compiler.go +35 -31
  5. package/compiler/composite-lit.go +233 -196
  6. package/compiler/constraint.go +88 -0
  7. package/compiler/decl.go +82 -24
  8. package/compiler/expr-call-async.go +20 -34
  9. package/compiler/expr-call-builtins.go +19 -0
  10. package/compiler/expr-call-helpers.go +0 -28
  11. package/compiler/expr-call-make.go +93 -343
  12. package/compiler/expr-call-type-conversion.go +221 -249
  13. package/compiler/expr-call.go +70 -69
  14. package/compiler/expr-selector.go +21 -24
  15. package/compiler/expr.go +3 -60
  16. package/compiler/protobuf.go +180 -36
  17. package/compiler/spec-value.go +132 -24
  18. package/compiler/spec.go +14 -55
  19. package/compiler/stmt-assign.go +338 -356
  20. package/compiler/stmt-range.go +4 -24
  21. package/compiler/stmt.go +92 -203
  22. package/compiler/type-utils.go +185 -0
  23. package/compiler/type.go +26 -80
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +3 -0
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/builtin/type.js +8 -2
  28. package/dist/gs/builtin/type.js.map +1 -1
  29. package/dist/gs/fmt/fmt.js +113 -16
  30. package/dist/gs/fmt/fmt.js.map +1 -1
  31. package/dist/gs/runtime/runtime.d.ts +1 -1
  32. package/dist/gs/runtime/runtime.js +1 -1
  33. package/dist/gs/slices/slices.d.ts +23 -0
  34. package/dist/gs/slices/slices.js +61 -0
  35. package/dist/gs/slices/slices.js.map +1 -1
  36. package/go.mod +8 -8
  37. package/go.sum +14 -14
  38. package/gs/builtin/slice.ts +5 -2
  39. package/gs/builtin/type.ts +13 -6
  40. package/gs/fmt/fmt.test.ts +176 -0
  41. package/gs/fmt/fmt.ts +109 -18
  42. package/gs/runtime/runtime.ts +1 -1
  43. package/gs/slices/slices.ts +68 -0
  44. package/package.json +3 -3
@@ -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 basic.Info()&types.IsString != 0 {
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[i]") // TODO: should be indexVarName?
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
- c.tsw.WriteCommentLinef("unhandled spec in DeclStmt: %T", spec)
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.pkg.TypesInfo.Uses[fun]
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.pkg.TypesInfo.Uses[fun.Sel]
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
- // Check for variable shadowing in the initialization first
451
- if c.analysis.HasVariableShadowing(exp) {
452
- shadowingInfo := c.analysis.GetShadowingInfo(exp)
453
- if shadowingInfo != nil {
454
- // Handle variable shadowing by creating temporary variables
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
- // Handle the initialization with or without shadowing support
486
- if c.analysis.HasVariableShadowing(exp) {
487
- shadowingInfo := c.analysis.GetShadowingInfo(exp)
488
- if shadowingInfo != nil {
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.isCallAsyncInDefer(deferStmt.Call) {
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
- c.tsw.WriteCommentLinef("unhandled statement in switch body: %T", stmt)
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.isCallAsyncInDefer(exp.Call)
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
- // writeShadowedAssignment writes an assignment statement that has variable shadowing,
996
- // using pre-computed identifier mappings from analysis instead of dynamic context.
997
- func (c *GoToTSCompiler) writeShadowedAssignment(stmt *ast.AssignStmt, shadowingInfo *ShadowingInfo) error {
998
- // First, create temporary variables for the shadowed variables
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
- // writeShadowedAssignmentWithoutTempVars writes an assignment statement that has variable shadowing,
1062
- // but assumes temporary variables have already been created outside this scope.
1063
- func (c *GoToTSCompiler) writeShadowedAssignmentWithoutTempVars(stmt *ast.AssignStmt, shadowingInfo *ShadowingInfo) error {
1064
- if len(stmt.Rhs) == 1 {
1065
- if typeAssert, isTypeAssert := stmt.Rhs[0].(*ast.TypeAssertExpr); isTypeAssert {
1066
- if len(stmt.Lhs) != 2 {
1067
- return fmt.Errorf("type assertion assignment requires 2 LHS, got %d", len(stmt.Lhs))
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
- var firstDecl = true
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.writeShadowedRHSExpression(rhsExpr, shadowingInfo); err != nil {
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
- // writeShadowedRHSExpression writes a RHS expression, replacing shadowed variables with temporary variables
1145
- func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo *ShadowingInfo) error {
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.writeShadowedRHSExpression(e.Fun, shadowingInfo); err != nil {
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.writeShadowedRHSExpression(arg, shadowingInfo); err != nil {
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.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
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.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
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.writeShadowedRHSExpression(e.Index, shadowingInfo); err != nil {
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.writeShadowedRHSExpression(e.X, shadowingInfo)
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.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
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.writeShadowedRHSExpression(e.Y, shadowingInfo)
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.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
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
- builtins := map[string]bool{
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
+ }