goscript 0.0.44 → 0.0.47
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 +198 -504
- package/compiler/analysis.go +98 -0
- package/compiler/compiler.go +20 -70
- package/compiler/decl.go +63 -40
- package/compiler/expr-call-async.go +101 -0
- package/compiler/expr-call-builtins.go +133 -0
- package/compiler/expr-call-helpers.go +138 -0
- package/compiler/expr-call-make.go +568 -0
- package/compiler/expr-call-type-conversion.go +424 -0
- package/compiler/expr-call.go +59 -1305
- package/compiler/expr.go +126 -4
- package/compiler/spec-struct.go +50 -30
- package/compiler/spec.go +69 -112
- package/compiler/type.go +51 -0
- package/dist/gs/builtin/builtin.d.ts +3 -1
- package/dist/gs/builtin/builtin.js +6 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/errors.js +1 -1
- package/dist/gs/builtin/slice.js +2 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/io/fs/fs.d.ts +12 -1
- package/dist/gs/io/fs/fs.js +106 -30
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +1 -3
- package/dist/gs/os/types_js.gs.js +2 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +2 -2
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/filepath/path.d.ts +1 -1
- package/dist/gs/path/filepath/path.js +1 -1
- package/dist/gs/path/filepath/path.js.map +1 -1
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/time/time.js +11 -3
- package/dist/gs/time/time.js.map +1 -1
- package/gs/builtin/builtin.ts +8 -3
- package/gs/builtin/errors.ts +2 -2
- package/gs/builtin/slice.ts +24 -8
- package/gs/fmt/fmt.ts +12 -3
- package/gs/fmt/index.ts +1 -1
- package/gs/io/fs/fs.ts +100 -31
- package/gs/io/fs/godoc.txt +370 -17
- package/gs/os/types_js.gs.ts +2 -2
- package/gs/os/types_unix.gs.ts +2 -2
- package/gs/path/filepath/path.test.ts +2 -2
- package/gs/path/filepath/path.ts +3 -10
- package/gs/reflect/value.ts +10 -2
- package/gs/time/time.ts +52 -17
- package/package.json +1 -1
package/compiler/analysis.go
CHANGED
|
@@ -61,6 +61,7 @@ type FunctionTypeInfo struct {
|
|
|
61
61
|
// FunctionInfo consolidates function-related tracking data.
|
|
62
62
|
type FunctionInfo struct {
|
|
63
63
|
IsAsync bool
|
|
64
|
+
ReceiverUsed bool
|
|
64
65
|
NamedReturns []string
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -195,6 +196,17 @@ func (a *Analysis) IsAsyncFunc(obj types.Object) bool {
|
|
|
195
196
|
return funcInfo.IsAsync
|
|
196
197
|
}
|
|
197
198
|
|
|
199
|
+
func (a *Analysis) IsReceiverUsed(obj types.Object) bool {
|
|
200
|
+
if obj == nil {
|
|
201
|
+
return false
|
|
202
|
+
}
|
|
203
|
+
funcInfo := a.FunctionData[obj]
|
|
204
|
+
if funcInfo == nil {
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
return funcInfo.ReceiverUsed
|
|
208
|
+
}
|
|
209
|
+
|
|
198
210
|
// IsFuncLitAsync checks if a function literal is async based on our analysis.
|
|
199
211
|
func (a *Analysis) IsFuncLitAsync(funcLit *ast.FuncLit) bool {
|
|
200
212
|
if funcLit == nil {
|
|
@@ -488,6 +500,24 @@ func (v *analysisVisitor) Visit(node ast.Node) ast.Visitor {
|
|
|
488
500
|
// Add the receiver variable to the VariableUsage map
|
|
489
501
|
// to ensure it is properly analyzed for varRefing
|
|
490
502
|
v.getOrCreateUsageInfo(v.currentReceiver)
|
|
503
|
+
|
|
504
|
+
// Check if receiver is used in method body
|
|
505
|
+
receiverUsed := false
|
|
506
|
+
if n.Body != nil {
|
|
507
|
+
if v.isInterfaceMethod(n) {
|
|
508
|
+
receiverUsed = true
|
|
509
|
+
} else {
|
|
510
|
+
receiverUsed = v.containsReceiverUsage(n.Body, vr)
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Update function data with receiver usage info
|
|
515
|
+
if obj := v.pkg.TypesInfo.ObjectOf(n.Name); obj != nil {
|
|
516
|
+
if v.analysis.FunctionData[obj] == nil {
|
|
517
|
+
v.analysis.FunctionData[obj] = &FunctionInfo{}
|
|
518
|
+
}
|
|
519
|
+
v.analysis.FunctionData[obj].ReceiverUsed = receiverUsed
|
|
520
|
+
}
|
|
491
521
|
}
|
|
492
522
|
}
|
|
493
523
|
}
|
|
@@ -945,6 +975,74 @@ func (v *analysisVisitor) containsDefer(block *ast.BlockStmt) bool {
|
|
|
945
975
|
return hasDefer
|
|
946
976
|
}
|
|
947
977
|
|
|
978
|
+
// containsReceiverUsage checks if a method body contains any references to the receiver variable.
|
|
979
|
+
func (v *analysisVisitor) containsReceiverUsage(node ast.Node, receiver *types.Var) bool {
|
|
980
|
+
if receiver == nil {
|
|
981
|
+
return false
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
var hasReceiverUsage bool
|
|
985
|
+
|
|
986
|
+
ast.Inspect(node, func(n ast.Node) bool {
|
|
987
|
+
if n == nil {
|
|
988
|
+
return true
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
switch expr := n.(type) {
|
|
992
|
+
case *ast.Ident:
|
|
993
|
+
// Check if this identifier refers to the receiver variable
|
|
994
|
+
if obj := v.pkg.TypesInfo.Uses[expr]; obj != nil && obj == receiver {
|
|
995
|
+
hasReceiverUsage = true
|
|
996
|
+
return false
|
|
997
|
+
}
|
|
998
|
+
case *ast.SelectorExpr:
|
|
999
|
+
// Check if selector expression uses the receiver (e.g., m.Field, m.Method())
|
|
1000
|
+
if ident, ok := expr.X.(*ast.Ident); ok {
|
|
1001
|
+
if obj := v.pkg.TypesInfo.Uses[ident]; obj != nil && obj == receiver {
|
|
1002
|
+
hasReceiverUsage = true
|
|
1003
|
+
return false
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
return true
|
|
1009
|
+
})
|
|
1010
|
+
|
|
1011
|
+
return hasReceiverUsage
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
func (v *analysisVisitor) isInterfaceMethod(decl *ast.FuncDecl) bool {
|
|
1015
|
+
if decl.Recv == nil {
|
|
1016
|
+
return false
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// Get the method name
|
|
1020
|
+
methodName := decl.Name.Name
|
|
1021
|
+
|
|
1022
|
+
// Get the receiver variable
|
|
1023
|
+
var receiver *types.Var
|
|
1024
|
+
if len(decl.Recv.List) > 0 && len(decl.Recv.List[0].Names) > 0 {
|
|
1025
|
+
if ident := decl.Recv.List[0].Names[0]; ident != nil && ident.Name != "_" {
|
|
1026
|
+
if def := v.pkg.TypesInfo.Defs[ident]; def != nil {
|
|
1027
|
+
if vr, ok := def.(*types.Var); ok {
|
|
1028
|
+
receiver = vr
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
return v.couldImplementInterfaceMethod(methodName, receiver)
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
func (v *analysisVisitor) couldImplementInterfaceMethod(methodName string, receiver *types.Var) bool {
|
|
1038
|
+
// Check if method is exported (interface methods must be exported)
|
|
1039
|
+
if !ast.IsExported(methodName) {
|
|
1040
|
+
return false
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
return false
|
|
1044
|
+
}
|
|
1045
|
+
|
|
948
1046
|
// AnalyzeFile analyzes a Go source file AST and populates the Analysis struct with information
|
|
949
1047
|
// that will be used during code generation to properly handle pointers, variables that need varRefing, etc.
|
|
950
1048
|
func AnalyzeFile(file *ast.File, pkg *packages.Package, analysis *Analysis, cmap ast.CommentMap) {
|
package/compiler/compiler.go
CHANGED
|
@@ -374,68 +374,6 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
374
374
|
}
|
|
375
375
|
defer indexFile.Close() //nolint:errcheck
|
|
376
376
|
|
|
377
|
-
// Collect exported symbols from all files in the package
|
|
378
|
-
var exportedSymbols []string
|
|
379
|
-
|
|
380
|
-
// Iterate through all syntax files to find exported symbols
|
|
381
|
-
for i, syntax := range c.pkg.Syntax {
|
|
382
|
-
fileName := c.pkg.CompiledGoFiles[i]
|
|
383
|
-
baseFileName := filepath.Base(fileName)
|
|
384
|
-
gsFileName := strings.TrimSuffix(baseFileName, ".go") + ".gs"
|
|
385
|
-
|
|
386
|
-
// Only include this file if it was compiled (in our compiledFiles list)
|
|
387
|
-
fileWasCompiled := false
|
|
388
|
-
for _, compiledFile := range compiledFiles {
|
|
389
|
-
if compiledFile == gsFileName {
|
|
390
|
-
fileWasCompiled = true
|
|
391
|
-
break
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
if !fileWasCompiled {
|
|
395
|
-
continue
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Analyze declarations in this file to find exported symbols
|
|
399
|
-
for _, decl := range syntax.Decls {
|
|
400
|
-
switch d := decl.(type) {
|
|
401
|
-
case *ast.FuncDecl:
|
|
402
|
-
// Only include top-level functions (not methods)
|
|
403
|
-
if d.Recv == nil && d.Name.IsExported() {
|
|
404
|
-
exportedSymbols = append(exportedSymbols, d.Name.Name)
|
|
405
|
-
}
|
|
406
|
-
case *ast.GenDecl:
|
|
407
|
-
for _, spec := range d.Specs {
|
|
408
|
-
switch s := spec.(type) {
|
|
409
|
-
case *ast.TypeSpec:
|
|
410
|
-
if s.Name.IsExported() {
|
|
411
|
-
exportedSymbols = append(exportedSymbols, s.Name.Name)
|
|
412
|
-
}
|
|
413
|
-
case *ast.ValueSpec:
|
|
414
|
-
for _, name := range s.Names {
|
|
415
|
-
if name.IsExported() {
|
|
416
|
-
exportedSymbols = append(exportedSymbols, name.Name)
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Remove duplicates and sort
|
|
426
|
-
symbolMap := make(map[string]bool)
|
|
427
|
-
for _, symbol := range exportedSymbols {
|
|
428
|
-
symbolMap[symbol] = true
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
var uniqueSymbols []string
|
|
432
|
-
for symbol := range symbolMap {
|
|
433
|
-
uniqueSymbols = append(uniqueSymbols, symbol)
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Sort for consistent output
|
|
437
|
-
sort.Strings(uniqueSymbols)
|
|
438
|
-
|
|
439
377
|
// Write selective re-exports for each compiled file
|
|
440
378
|
for _, fileName := range compiledFiles {
|
|
441
379
|
// Check if this is a protobuf file
|
|
@@ -452,7 +390,8 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
452
390
|
}
|
|
453
391
|
|
|
454
392
|
// Find which symbols this file exports
|
|
455
|
-
var
|
|
393
|
+
var valueSymbols []string
|
|
394
|
+
var typeSymbols []string
|
|
456
395
|
|
|
457
396
|
// Find the corresponding syntax file
|
|
458
397
|
for i, syntax := range c.pkg.Syntax {
|
|
@@ -469,19 +408,21 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
469
408
|
switch d := decl.(type) {
|
|
470
409
|
case *ast.FuncDecl:
|
|
471
410
|
if d.Recv == nil && d.Name.IsExported() {
|
|
472
|
-
|
|
411
|
+
valueSymbols = append(valueSymbols, d.Name.Name)
|
|
473
412
|
}
|
|
474
413
|
case *ast.GenDecl:
|
|
475
414
|
for _, spec := range d.Specs {
|
|
476
415
|
switch s := spec.(type) {
|
|
477
416
|
case *ast.TypeSpec:
|
|
478
417
|
if s.Name.IsExported() {
|
|
479
|
-
|
|
418
|
+
// All type declarations (interfaces, structs, type definitions, type aliases)
|
|
419
|
+
// become TypeScript types and must be exported with "export type"
|
|
420
|
+
typeSymbols = append(typeSymbols, s.Name.Name)
|
|
480
421
|
}
|
|
481
422
|
case *ast.ValueSpec:
|
|
482
423
|
for _, name := range s.Names {
|
|
483
424
|
if name.IsExported() {
|
|
484
|
-
|
|
425
|
+
valueSymbols = append(valueSymbols, name.Name)
|
|
485
426
|
}
|
|
486
427
|
}
|
|
487
428
|
}
|
|
@@ -491,11 +432,20 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
491
432
|
break
|
|
492
433
|
}
|
|
493
434
|
|
|
494
|
-
// Write
|
|
495
|
-
if len(
|
|
496
|
-
sort.Strings(
|
|
435
|
+
// Write exports if this file has exported symbols
|
|
436
|
+
if len(valueSymbols) > 0 {
|
|
437
|
+
sort.Strings(valueSymbols)
|
|
497
438
|
exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
|
|
498
|
-
strings.Join(
|
|
439
|
+
strings.Join(valueSymbols, ", "), fileName)
|
|
440
|
+
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
441
|
+
return err
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if len(typeSymbols) > 0 {
|
|
446
|
+
sort.Strings(typeSymbols)
|
|
447
|
+
exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
|
|
448
|
+
strings.Join(typeSymbols, ", "), fileName)
|
|
499
449
|
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
500
450
|
return err
|
|
501
451
|
}
|
package/compiler/decl.go
CHANGED
|
@@ -142,6 +142,49 @@ func (c *GoToTSCompiler) WriteFuncDeclAsFunction(decl *ast.FuncDecl) error {
|
|
|
142
142
|
//
|
|
143
143
|
// This function assumes it is called only for `FuncDecl` nodes that are methods.
|
|
144
144
|
func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
145
|
+
_, err := c.writeMethodSignature(decl)
|
|
146
|
+
if err != nil {
|
|
147
|
+
return err
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return c.writeMethodBodyWithReceiverBinding(decl, "this")
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// writeNamedReturnDeclarations generates TypeScript variable declarations for named return parameters.
|
|
154
|
+
// It declares each named return variable with its appropriate type and zero value.
|
|
155
|
+
func (c *GoToTSCompiler) writeNamedReturnDeclarations(results *ast.FieldList) error {
|
|
156
|
+
if results == nil {
|
|
157
|
+
return nil
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for _, field := range results.List {
|
|
161
|
+
for _, name := range field.Names {
|
|
162
|
+
c.tsw.WriteLiterallyf("let %s: ", c.sanitizeIdentifier(name.Name))
|
|
163
|
+
c.WriteTypeExpr(field.Type)
|
|
164
|
+
c.tsw.WriteLiterally(" = ")
|
|
165
|
+
c.WriteZeroValueForType(c.pkg.TypesInfo.TypeOf(field.Type))
|
|
166
|
+
c.tsw.WriteLine("")
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return nil
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// hasNamedReturns checks if a function type has any named return parameters.
|
|
173
|
+
func (c *GoToTSCompiler) hasNamedReturns(results *ast.FieldList) bool {
|
|
174
|
+
if results == nil {
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for _, field := range results.List {
|
|
179
|
+
if len(field.Names) > 0 {
|
|
180
|
+
return true
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return false
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// writeMethodSignature writes the TypeScript method signature including async, public modifiers, name, parameters, and return type
|
|
187
|
+
func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error) {
|
|
145
188
|
if decl.Doc != nil {
|
|
146
189
|
c.WriteDoc(decl.Doc)
|
|
147
190
|
}
|
|
@@ -162,11 +205,10 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
162
205
|
|
|
163
206
|
// Keep original Go casing for method names
|
|
164
207
|
if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
|
|
165
|
-
return err
|
|
208
|
+
return isAsync, err
|
|
166
209
|
}
|
|
167
210
|
|
|
168
211
|
// Write signature (parameters and return type)
|
|
169
|
-
// We adapt the logic from WriteFuncType here, but without the 'function' keyword
|
|
170
212
|
funcType := decl.Type
|
|
171
213
|
c.tsw.WriteLiterally("(")
|
|
172
214
|
if funcType.Params != nil {
|
|
@@ -208,23 +250,37 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
208
250
|
}
|
|
209
251
|
|
|
210
252
|
c.tsw.WriteLiterally(" ")
|
|
253
|
+
return isAsync, nil
|
|
254
|
+
}
|
|
211
255
|
|
|
212
|
-
|
|
256
|
+
// writeMethodBodyWithReceiverBinding writes the method body with optional receiver binding
|
|
257
|
+
// receiverTarget should be "this" for struct methods or "this._value" for named type methods
|
|
258
|
+
func (c *GoToTSCompiler) writeMethodBodyWithReceiverBinding(decl *ast.FuncDecl, receiverTarget string) error {
|
|
259
|
+
// Bind receiver name conditionally
|
|
213
260
|
if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
|
|
214
261
|
recvName := recvField.Names[0].Name
|
|
215
262
|
if recvName != "_" {
|
|
263
|
+
// Check if receiver is actually used
|
|
264
|
+
var needsReceiverBinding bool
|
|
265
|
+
if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
|
|
266
|
+
needsReceiverBinding = c.analysis.IsReceiverUsed(obj)
|
|
267
|
+
}
|
|
268
|
+
|
|
216
269
|
c.tsw.WriteLine("{")
|
|
217
270
|
c.tsw.Indent(1)
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
271
|
+
|
|
272
|
+
if needsReceiverBinding {
|
|
273
|
+
// Sanitize the receiver name to avoid conflicts with TypeScript reserved words
|
|
274
|
+
sanitizedRecvName := c.sanitizeIdentifier(recvName)
|
|
275
|
+
c.tsw.WriteLinef("const %s = %s", sanitizedRecvName, receiverTarget)
|
|
276
|
+
}
|
|
221
277
|
|
|
222
278
|
// Add using statement if needed
|
|
223
279
|
if c.analysis.NeedsDefer(decl.Body) {
|
|
224
280
|
if c.analysis.IsInAsyncFunction(decl) {
|
|
225
281
|
c.tsw.WriteLine("await using __defer = new $.AsyncDisposableStack();")
|
|
226
282
|
} else {
|
|
227
|
-
c.tsw.WriteLine("using
|
|
283
|
+
c.tsw.WriteLine("using __defer = new $.DisposableStack();")
|
|
228
284
|
}
|
|
229
285
|
}
|
|
230
286
|
|
|
@@ -252,36 +308,3 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
252
308
|
|
|
253
309
|
return nil
|
|
254
310
|
}
|
|
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
|
-
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"go/ast"
|
|
5
|
+
"go/types"
|
|
6
|
+
"strings"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// writeAsyncCall writes the await prefix for async function calls
|
|
10
|
+
func (c *GoToTSCompiler) writeAsyncCall(exp *ast.CallExpr, funIdent *ast.Ident) bool {
|
|
11
|
+
if funIdent == nil {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Check if this is an async function call
|
|
16
|
+
if obj := c.pkg.TypesInfo.Uses[funIdent]; obj != nil && c.analysis.IsAsyncFunc(obj) {
|
|
17
|
+
c.tsw.WriteLiterally("await ")
|
|
18
|
+
return true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// writeAsyncMethodCall writes the await prefix for async method calls
|
|
25
|
+
func (c *GoToTSCompiler) writeAsyncMethodCall(exp *ast.CallExpr) bool {
|
|
26
|
+
selExpr, ok := exp.Fun.(*ast.SelectorExpr)
|
|
27
|
+
if !ok {
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check if this is a method call on a variable (e.g., mu.Lock())
|
|
32
|
+
ident, ok := selExpr.X.(*ast.Ident)
|
|
33
|
+
if !ok {
|
|
34
|
+
return false
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Get the type of the receiver
|
|
38
|
+
obj := c.pkg.TypesInfo.Uses[ident]
|
|
39
|
+
if obj == nil {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
varObj, ok := obj.(*types.Var)
|
|
44
|
+
if !ok {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Get the type name and package
|
|
49
|
+
namedType, ok := varObj.Type().(*types.Named)
|
|
50
|
+
if !ok {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
typeName := namedType.Obj().Name()
|
|
55
|
+
methodName := selExpr.Sel.Name
|
|
56
|
+
|
|
57
|
+
// Check if the type is from an imported package
|
|
58
|
+
typePkg := namedType.Obj().Pkg()
|
|
59
|
+
if typePkg == nil || typePkg == c.pkg.Types {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Use the actual package name from the type information
|
|
64
|
+
pkgName := typePkg.Name()
|
|
65
|
+
|
|
66
|
+
// Check if this method is async based on metadata
|
|
67
|
+
if c.analysis.IsMethodAsync(pkgName, typeName, methodName) {
|
|
68
|
+
c.tsw.WriteLiterally("await ")
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// addNonNullAssertion adds ! for function calls that might return null
|
|
76
|
+
func (c *GoToTSCompiler) addNonNullAssertion(expFun ast.Expr) {
|
|
77
|
+
if funType := c.pkg.TypesInfo.TypeOf(expFun); funType != nil {
|
|
78
|
+
if _, ok := funType.Underlying().(*types.Signature); ok {
|
|
79
|
+
// Check if this is a function parameter identifier that needs not-null assertion
|
|
80
|
+
if ident, isIdent := expFun.(*ast.Ident); isIdent {
|
|
81
|
+
// Check if this identifier is a function parameter
|
|
82
|
+
if obj := c.pkg.TypesInfo.Uses[ident]; obj != nil {
|
|
83
|
+
if _, isVar := obj.(*types.Var); isVar {
|
|
84
|
+
// This is a variable (including function parameters)
|
|
85
|
+
// Function parameters that are function types need ! assertion
|
|
86
|
+
c.tsw.WriteLiterally("!")
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} else if _, isNamed := funType.(*types.Named); isNamed {
|
|
90
|
+
c.tsw.WriteLiterally("!")
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// Check if the function type is nullable (e.g., func(...) | null)
|
|
94
|
+
// This handles cases where a function call returns a nullable function
|
|
95
|
+
funTypeStr := funType.String()
|
|
96
|
+
if strings.Contains(funTypeStr, "| null") || strings.Contains(funTypeStr, "null |") {
|
|
97
|
+
c.tsw.WriteLiterally("!")
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/token"
|
|
7
|
+
|
|
8
|
+
"github.com/pkg/errors"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// writeBuiltinFunction handles built-in Go functions
|
|
12
|
+
func (c *GoToTSCompiler) writeBuiltinFunction(exp *ast.CallExpr, funName string) (handled bool, err error) {
|
|
13
|
+
switch funName {
|
|
14
|
+
case "panic":
|
|
15
|
+
c.tsw.WriteLiterally("$.panic")
|
|
16
|
+
return true, nil
|
|
17
|
+
case "println":
|
|
18
|
+
c.tsw.WriteLiterally("console.log")
|
|
19
|
+
return true, nil
|
|
20
|
+
case "len":
|
|
21
|
+
if len(exp.Args) != 1 {
|
|
22
|
+
return true, errors.Errorf("unhandled len call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
23
|
+
}
|
|
24
|
+
c.tsw.WriteLiterally("$.len")
|
|
25
|
+
return true, nil
|
|
26
|
+
case "cap":
|
|
27
|
+
if len(exp.Args) != 1 {
|
|
28
|
+
return true, errors.Errorf("unhandled cap call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
29
|
+
}
|
|
30
|
+
c.tsw.WriteLiterally("$.cap")
|
|
31
|
+
return true, nil
|
|
32
|
+
case "new":
|
|
33
|
+
if len(exp.Args) != 1 {
|
|
34
|
+
return true, errors.Errorf("unhandled new call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
35
|
+
}
|
|
36
|
+
c.tsw.WriteLiterally("new ")
|
|
37
|
+
c.WriteTypeExpr(exp.Args[0]) // This should write the TypeScript type T_ts
|
|
38
|
+
c.tsw.WriteLiterally("()")
|
|
39
|
+
return true, nil
|
|
40
|
+
case "delete":
|
|
41
|
+
if len(exp.Args) != 2 {
|
|
42
|
+
return true, errors.Errorf("unhandled delete call with incorrect number of arguments: %d != 2", len(exp.Args))
|
|
43
|
+
}
|
|
44
|
+
c.tsw.WriteLiterally("$.deleteMapEntry")
|
|
45
|
+
return true, nil
|
|
46
|
+
case "copy":
|
|
47
|
+
if len(exp.Args) != 2 {
|
|
48
|
+
return true, errors.Errorf("unhandled copy call with incorrect number of arguments: %d != 2", len(exp.Args))
|
|
49
|
+
}
|
|
50
|
+
c.tsw.WriteLiterally("$.copy")
|
|
51
|
+
return true, nil
|
|
52
|
+
case "recover":
|
|
53
|
+
if len(exp.Args) != 0 {
|
|
54
|
+
return true, errors.Errorf("unhandled recover call with incorrect number of arguments: %d != 0", len(exp.Args))
|
|
55
|
+
}
|
|
56
|
+
c.tsw.WriteLiterally("$.recover")
|
|
57
|
+
return true, nil
|
|
58
|
+
case "make":
|
|
59
|
+
return true, c.WriteCallExprMake(exp)
|
|
60
|
+
case "string":
|
|
61
|
+
return true, c.writeStringConversion(exp)
|
|
62
|
+
case "close":
|
|
63
|
+
if len(exp.Args) != 1 {
|
|
64
|
+
return true, errors.New("unhandled close call with incorrect number of arguments")
|
|
65
|
+
}
|
|
66
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
67
|
+
return true, fmt.Errorf("failed to write channel in close call: %w", err)
|
|
68
|
+
}
|
|
69
|
+
c.tsw.WriteLiterally(".close()")
|
|
70
|
+
return true, nil
|
|
71
|
+
case "append":
|
|
72
|
+
return true, c.writeAppendCall(exp)
|
|
73
|
+
case "byte":
|
|
74
|
+
if len(exp.Args) != 1 {
|
|
75
|
+
return true, errors.Errorf("unhandled byte call with incorrect number of arguments: %d != 1", len(exp.Args))
|
|
76
|
+
}
|
|
77
|
+
c.tsw.WriteLiterally("$.byte(")
|
|
78
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
79
|
+
return true, fmt.Errorf("failed to write argument for byte() conversion: %w", err)
|
|
80
|
+
}
|
|
81
|
+
c.tsw.WriteLiterally(")")
|
|
82
|
+
return true, nil
|
|
83
|
+
case "int":
|
|
84
|
+
return true, c.writeIntConversion(exp)
|
|
85
|
+
default:
|
|
86
|
+
return false, nil
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// writeAppendCall handles append() function calls
|
|
91
|
+
func (c *GoToTSCompiler) writeAppendCall(exp *ast.CallExpr) error {
|
|
92
|
+
if len(exp.Args) < 1 {
|
|
93
|
+
return errors.New("unhandled append call with incorrect number of arguments")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
c.tsw.WriteLiterally("$.append(")
|
|
97
|
+
// The first argument is the slice
|
|
98
|
+
if err := c.WriteValueExpr(exp.Args[0]); err != nil {
|
|
99
|
+
return fmt.Errorf("failed to write slice in append call: %w", err)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The remaining arguments are the elements to append
|
|
103
|
+
for i, arg := range exp.Args[1:] {
|
|
104
|
+
if i > 0 || len(exp.Args) > 1 {
|
|
105
|
+
c.tsw.WriteLiterally(", ")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Special case: append([]byte, string...) should convert string to bytes
|
|
109
|
+
if exp.Ellipsis != token.NoPos && i == 0 { // This is the first element after slice and has ellipsis
|
|
110
|
+
// Check if the slice is []byte and the argument is a string
|
|
111
|
+
sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
|
|
112
|
+
argType := c.pkg.TypesInfo.TypeOf(arg)
|
|
113
|
+
|
|
114
|
+
if sliceType != nil && argType != nil {
|
|
115
|
+
if c.isByteSliceType(sliceType) && c.isStringType(argType) {
|
|
116
|
+
// Convert string to bytes: append([]byte, string...) -> $.append(slice, ...$.stringToBytes(string))
|
|
117
|
+
c.tsw.WriteLiterally("...$.stringToBytes(")
|
|
118
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
119
|
+
return fmt.Errorf("failed to write string argument in append call: %w", err)
|
|
120
|
+
}
|
|
121
|
+
c.tsw.WriteLiterally(")")
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
128
|
+
return fmt.Errorf("failed to write argument %d in append call: %w", i+1, err)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
c.tsw.WriteLiterally(")")
|
|
132
|
+
return nil
|
|
133
|
+
}
|