goscript 0.0.57 → 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 +418 -7
  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 +99 -172
  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,11 +633,20 @@ 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
681
642
  break
682
643
  }
644
+ } else {
645
+ // Check if the deferred call is to an async function
646
+ if c.isCallExprAsync(deferStmt.Call) {
647
+ hasAsyncDefer = true
648
+ break
649
+ }
683
650
  }
684
651
  }
685
652
  }
@@ -848,7 +815,7 @@ func (c *GoToTSCompiler) WriteStmtSwitch(exp *ast.SwitchStmt) error {
848
815
  return fmt.Errorf("failed to write case clause in switch statement: %w", err)
849
816
  }
850
817
  } else {
851
- c.tsw.WriteCommentLinef("unhandled statement in switch body: %T", stmt)
818
+ return fmt.Errorf("unhandled statement in switch body: %T", stmt)
852
819
  }
853
820
  }
854
821
 
@@ -877,6 +844,9 @@ func (c *GoToTSCompiler) WriteStmtDefer(exp *ast.DeferStmt) error {
877
844
  isAsyncDeferred := false
878
845
  if funcLit, ok := exp.Call.Fun.(*ast.FuncLit); ok {
879
846
  isAsyncDeferred = c.analysis.IsFuncLitAsync(funcLit)
847
+ } else {
848
+ // Check if the deferred call is to an async function
849
+ isAsyncDeferred = c.isCallExprAsync(exp.Call)
880
850
  }
881
851
 
882
852
  // Set async prefix based on pre-computed async status
@@ -954,10 +924,10 @@ func (c *GoToTSCompiler) WriteStmtLabeled(stmt *ast.LabeledStmt) error {
954
924
  return nil
955
925
  }
956
926
 
957
- // writeShadowedAssignment writes an assignment statement that has variable shadowing,
958
- // using pre-computed identifier mappings from analysis instead of dynamic context.
959
- func (c *GoToTSCompiler) writeShadowedAssignment(stmt *ast.AssignStmt, shadowingInfo *ShadowingInfo) error {
960
- // 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) {
961
931
  for varName, tempVarName := range shadowingInfo.TempVariables {
962
932
  c.tsw.WriteLiterally("const ")
963
933
  c.tsw.WriteLiterally(tempVarName)
@@ -982,94 +952,20 @@ func (c *GoToTSCompiler) writeShadowedAssignment(stmt *ast.AssignStmt, shadowing
982
952
  }
983
953
  c.tsw.WriteLine("")
984
954
  }
985
-
986
- // Now write the LHS variables (these are new declarations)
987
- for i, lhsExpr := range stmt.Lhs {
988
- if i > 0 {
989
- c.tsw.WriteLiterally(", ")
990
- }
991
-
992
- if ident, ok := lhsExpr.(*ast.Ident); ok {
993
- if ident.Name == "_" {
994
- c.tsw.WriteLiterally("_")
995
- } else {
996
- c.tsw.WriteLiterally("let ")
997
- c.WriteIdent(ident, false) // Don't use temp variable for LHS
998
- }
999
- } else {
1000
- // For non-identifier LHS (shouldn't happen in := assignments), write normally
1001
- if err := c.WriteValueExpr(lhsExpr); err != nil {
1002
- return err
1003
- }
1004
- }
1005
- }
1006
-
1007
- c.tsw.WriteLiterally(" = ")
1008
-
1009
- // Write RHS expressions - but we need to replace shadowed variables with temporary variables
1010
- for i, rhsExpr := range stmt.Rhs {
1011
- if i > 0 {
1012
- c.tsw.WriteLiterally(", ")
1013
- }
1014
- if err := c.writeShadowedRHSExpression(rhsExpr, shadowingInfo); err != nil {
1015
- return err
1016
- }
1017
- }
1018
-
1019
- c.tsw.WriteLine("")
1020
- return nil
1021
955
  }
1022
956
 
1023
- // writeShadowedAssignmentWithoutTempVars writes an assignment statement that has variable shadowing,
1024
- // but assumes temporary variables have already been created outside this scope.
1025
- func (c *GoToTSCompiler) writeShadowedAssignmentWithoutTempVars(stmt *ast.AssignStmt, shadowingInfo *ShadowingInfo) error {
1026
- if len(stmt.Rhs) == 1 {
1027
- if typeAssert, isTypeAssert := stmt.Rhs[0].(*ast.TypeAssertExpr); isTypeAssert {
1028
- if len(stmt.Lhs) != 2 {
1029
- return fmt.Errorf("type assertion assignment requires 2 LHS, got %d", len(stmt.Lhs))
1030
- }
1031
- valueExpr := stmt.Lhs[0]
1032
- okExpr := stmt.Lhs[1]
1033
- valueIdent, valueIsIdent := valueExpr.(*ast.Ident)
1034
- okIdent, okIsIdent := okExpr.(*ast.Ident)
1035
- if valueIsIdent && okIsIdent {
1036
- valueName := valueIdent.Name
1037
- okName := okIdent.Name
1038
- valueIsBlank := valueName == "_"
1039
- okIsBlank := okName == "_"
1040
- if valueIsBlank && okIsBlank {
1041
- // Both blank, evaluate RHS for side effects
1042
- if err := c.writeShadowedRHSExpression(typeAssert.X, shadowingInfo); err != nil {
1043
- return err
1044
- }
1045
- c.tsw.WriteLine("")
1046
- return nil
1047
- }
1048
- c.tsw.WriteLiterally("let { ")
1049
- var parts []string
1050
- if !valueIsBlank {
1051
- parts = append(parts, "value: "+valueName)
1052
- }
1053
- if !okIsBlank {
1054
- parts = append(parts, "ok: "+okName)
1055
- }
1056
- c.tsw.WriteLiterally(strings.Join(parts, ", "))
1057
- c.tsw.WriteLiterally(" } = $.typeAssert<")
1058
- c.WriteTypeExpr(typeAssert.Type)
1059
- c.tsw.WriteLiterally(">(")
1060
- if err := c.writeShadowedRHSExpression(typeAssert.X, shadowingInfo); err != nil {
1061
- return err
1062
- }
1063
- c.tsw.WriteLiterally(", ")
1064
- c.writeTypeDescription(typeAssert.Type)
1065
- c.tsw.WriteLiterally(")")
1066
- c.tsw.WriteLine("")
1067
- return nil
1068
- }
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)
1069
964
  }
1070
965
  }
1071
966
 
1072
- var firstDecl = true
967
+ // Regular assignment: write LHS declarations
968
+ firstDecl := true
1073
969
  for i, lhsExpr := range stmt.Lhs {
1074
970
  if i > 0 {
1075
971
  c.tsw.WriteLiterally(", ")
@@ -1090,21 +986,69 @@ func (c *GoToTSCompiler) writeShadowedAssignmentWithoutTempVars(stmt *ast.Assign
1090
986
  }
1091
987
  }
1092
988
  }
989
+
990
+ // Write RHS with shadowed variable substitution
1093
991
  c.tsw.WriteLiterally(" = ")
1094
992
  for i, rhsExpr := range stmt.Rhs {
1095
993
  if i > 0 {
1096
994
  c.tsw.WriteLiterally(", ")
1097
995
  }
1098
- if err := c.writeShadowedRHSExpression(rhsExpr, shadowingInfo); err != nil {
996
+ if err := c.substituteExprForShadowing(rhsExpr, shadowingInfo); err != nil {
997
+ return err
998
+ }
999
+ }
1000
+ c.tsw.WriteLine("")
1001
+ return nil
1002
+ }
1003
+
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 {
1099
1021
  return err
1100
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)
1101
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(")")
1102
1046
  c.tsw.WriteLine("")
1103
1047
  return nil
1104
1048
  }
1105
1049
 
1106
- // writeShadowedRHSExpression writes a RHS expression, replacing shadowed variables with temporary variables
1107
- func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo *ShadowingInfo) error {
1050
+ // substituteExprForShadowing writes an expression, replacing shadowed variables with temporary variables
1051
+ func (c *GoToTSCompiler) substituteExprForShadowing(expr ast.Expr, shadowingInfo *ShadowingInfo) error {
1108
1052
  switch e := expr.(type) {
1109
1053
  case *ast.Ident:
1110
1054
  // Check if this identifier is a shadowed variable
@@ -1119,7 +1063,7 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
1119
1063
 
1120
1064
  case *ast.CallExpr:
1121
1065
  // Handle function calls - replace identifiers in arguments with temp variables
1122
- if err := c.writeShadowedRHSExpression(e.Fun, shadowingInfo); err != nil {
1066
+ if err := c.substituteExprForShadowing(e.Fun, shadowingInfo); err != nil {
1123
1067
  return err
1124
1068
  }
1125
1069
 
@@ -1131,7 +1075,7 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
1131
1075
  if i > 0 {
1132
1076
  c.tsw.WriteLiterally(", ")
1133
1077
  }
1134
- if err := c.writeShadowedRHSExpression(arg, shadowingInfo); err != nil {
1078
+ if err := c.substituteExprForShadowing(arg, shadowingInfo); err != nil {
1135
1079
  return err
1136
1080
  }
1137
1081
  }
@@ -1140,7 +1084,7 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
1140
1084
 
1141
1085
  case *ast.SelectorExpr:
1142
1086
  // Handle selector expressions (e.g., obj.Method)
1143
- if err := c.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
1087
+ if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
1144
1088
  return err
1145
1089
  }
1146
1090
  c.tsw.WriteLiterally(".")
@@ -1149,11 +1093,11 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
1149
1093
 
1150
1094
  case *ast.IndexExpr:
1151
1095
  // Handle index expressions (e.g., arr[i])
1152
- if err := c.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
1096
+ if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
1153
1097
  return err
1154
1098
  }
1155
1099
  c.tsw.WriteLiterally("[")
1156
- if err := c.writeShadowedRHSExpression(e.Index, shadowingInfo); err != nil {
1100
+ if err := c.substituteExprForShadowing(e.Index, shadowingInfo); err != nil {
1157
1101
  return err
1158
1102
  }
1159
1103
  c.tsw.WriteLiterally("]")
@@ -1162,22 +1106,22 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
1162
1106
  case *ast.UnaryExpr:
1163
1107
  // Handle unary expressions (e.g., &x, -x)
1164
1108
  c.tsw.WriteLiterally(e.Op.String())
1165
- return c.writeShadowedRHSExpression(e.X, shadowingInfo)
1109
+ return c.substituteExprForShadowing(e.X, shadowingInfo)
1166
1110
 
1167
1111
  case *ast.BinaryExpr:
1168
1112
  // Handle binary expressions (e.g., x + y)
1169
- if err := c.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
1113
+ if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
1170
1114
  return err
1171
1115
  }
1172
1116
  c.tsw.WriteLiterally(" ")
1173
1117
  c.tsw.WriteLiterally(e.Op.String())
1174
1118
  c.tsw.WriteLiterally(" ")
1175
- return c.writeShadowedRHSExpression(e.Y, shadowingInfo)
1119
+ return c.substituteExprForShadowing(e.Y, shadowingInfo)
1176
1120
 
1177
1121
  case *ast.ParenExpr:
1178
1122
  // Handle parenthesized expressions
1179
1123
  c.tsw.WriteLiterally("(")
1180
- if err := c.writeShadowedRHSExpression(e.X, shadowingInfo); err != nil {
1124
+ if err := c.substituteExprForShadowing(e.X, shadowingInfo); err != nil {
1181
1125
  return err
1182
1126
  }
1183
1127
  c.tsw.WriteLiterally(")")
@@ -1191,22 +1135,5 @@ func (c *GoToTSCompiler) writeShadowedRHSExpression(expr ast.Expr, shadowingInfo
1191
1135
 
1192
1136
  // isBuiltinFunction checks if the given name is a Go built-in function
1193
1137
  func (c *GoToTSCompiler) isBuiltinFunction(name string) bool {
1194
- builtins := map[string]bool{
1195
- "len": true,
1196
- "cap": true,
1197
- "make": true,
1198
- "new": true,
1199
- "append": true,
1200
- "copy": true,
1201
- "delete": true,
1202
- "complex": true,
1203
- "real": true,
1204
- "imag": true,
1205
- "close": true,
1206
- "panic": true,
1207
- "recover": true,
1208
- "print": true,
1209
- "println": true,
1210
- }
1211
- return builtins[name]
1138
+ return builtinFunctions[name]
1212
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
+ }