goscript 0.0.49 → 0.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/analysis.go +1003 -557
- package/compiler/analysis_test.go +12 -15
- package/compiler/compiler.go +69 -37
- package/compiler/decl.go +22 -0
- package/compiler/expr-call-async.go +46 -52
- package/compiler/expr-call-type-conversion.go +6 -10
- package/compiler/expr-call.go +58 -27
- package/compiler/spec-value.go +2 -2
- package/compiler/spec.go +84 -40
- package/compiler/stmt-assign.go +7 -4
- package/compiler/stmt.go +63 -5
- package/compiler/type.go +13 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/context/context.d.ts +16 -18
- package/dist/gs/context/context.js +23 -13
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/fmt/fmt.js +3 -1
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/reflect/type.js +5 -8
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/time/time.d.ts +2 -1
- package/dist/gs/time/time.js +29 -19
- package/dist/gs/time/time.js.map +1 -1
- package/gs/builtin/builtin.ts +1 -7
- package/gs/context/context.ts +63 -45
- package/gs/fmt/fmt.ts +5 -1
- package/gs/reflect/type.ts +5 -10
- package/gs/time/time.ts +35 -21
- package/package.json +2 -2
- package/gs/TODO.md +0 -129
|
@@ -160,6 +160,7 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
|
|
|
160
160
|
Defs: make(map[*ast.Ident]types.Object),
|
|
161
161
|
Uses: make(map[*ast.Ident]types.Object),
|
|
162
162
|
},
|
|
163
|
+
Fset: fset,
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
// Type check the package
|
|
@@ -170,10 +171,8 @@ func parseAndAnalyze(t *testing.T, code string) (*Analysis, map[string]types.Obj
|
|
|
170
171
|
}
|
|
171
172
|
pkg.Types = typePkg
|
|
172
173
|
|
|
173
|
-
// Run analysis
|
|
174
|
-
analysis :=
|
|
175
|
-
cmap := ast.NewCommentMap(fset, file, file.Comments)
|
|
176
|
-
AnalyzeFile(file, pkg, analysis, cmap)
|
|
174
|
+
// Run package-level analysis
|
|
175
|
+
analysis := AnalyzePackageFiles(pkg, nil)
|
|
177
176
|
|
|
178
177
|
// Collect variable objects
|
|
179
178
|
objects := make(map[string]types.Object)
|
|
@@ -269,18 +268,16 @@ func TestWrapperTypeDetection(t *testing.T) {
|
|
|
269
268
|
t.Fatalf("Package has errors: %v", pkg.Errors[0])
|
|
270
269
|
}
|
|
271
270
|
|
|
272
|
-
// Run analysis
|
|
271
|
+
// Run package-level analysis
|
|
273
272
|
if len(pkg.Syntax) == 0 {
|
|
274
273
|
t.Fatal("No syntax files found")
|
|
275
274
|
}
|
|
276
275
|
|
|
277
|
-
analysis :=
|
|
278
|
-
cmap := ast.NewCommentMap(pkg.Fset, pkg.Syntax[0], pkg.Syntax[0].Comments)
|
|
279
|
-
AnalyzeFile(pkg.Syntax[0], pkg, analysis, cmap)
|
|
276
|
+
analysis := AnalyzePackageFiles(pkg, nil)
|
|
280
277
|
|
|
281
|
-
// Verify the
|
|
282
|
-
if analysis.
|
|
283
|
-
t.Error("
|
|
278
|
+
// Verify the NamedBasicTypes map was initialized
|
|
279
|
+
if analysis.NamedBasicTypes == nil {
|
|
280
|
+
t.Error("NamedBasicTypes map was not initialized")
|
|
284
281
|
}
|
|
285
282
|
|
|
286
283
|
// Test some type lookups to verify wrapper type detection works
|
|
@@ -290,7 +287,7 @@ func TestWrapperTypeDetection(t *testing.T) {
|
|
|
290
287
|
// Check if MyMode is detected as a wrapper type
|
|
291
288
|
if obj := scope.Lookup("MyMode"); obj != nil {
|
|
292
289
|
if typeName, ok := obj.(*types.TypeName); ok {
|
|
293
|
-
isWrapper := analysis.
|
|
290
|
+
isWrapper := analysis.IsNamedBasicType(typeName.Type())
|
|
294
291
|
if !isWrapper {
|
|
295
292
|
t.Errorf("MyMode should be detected as wrapper type, got %v", isWrapper)
|
|
296
293
|
}
|
|
@@ -301,7 +298,7 @@ func TestWrapperTypeDetection(t *testing.T) {
|
|
|
301
298
|
// Test that regular struct types are not detected as wrapper types
|
|
302
299
|
if obj := scope.Lookup("MyDir"); obj != nil {
|
|
303
300
|
if typeName, ok := obj.(*types.TypeName); ok {
|
|
304
|
-
isWrapper := analysis.
|
|
301
|
+
isWrapper := analysis.IsNamedBasicType(typeName.Type())
|
|
305
302
|
if isWrapper {
|
|
306
303
|
t.Errorf("MyDir should not be detected as wrapper type, got %v", isWrapper)
|
|
307
304
|
}
|
|
@@ -309,13 +306,13 @@ func TestWrapperTypeDetection(t *testing.T) {
|
|
|
309
306
|
}
|
|
310
307
|
}
|
|
311
308
|
|
|
312
|
-
t.Logf("Analysis completed successfully with %d
|
|
309
|
+
t.Logf("Analysis completed successfully with %d named basic types tracked", len(analysis.NamedBasicTypes))
|
|
313
310
|
}
|
|
314
311
|
|
|
315
312
|
// TestDiscoverGsPackages verifies that the discoverEmbeddedGsPackages function
|
|
316
313
|
// can find packages in the embedded gs/ directory
|
|
317
314
|
func TestDiscoverGsPackages(t *testing.T) {
|
|
318
|
-
analysis := NewAnalysis()
|
|
315
|
+
analysis := NewAnalysis(nil)
|
|
319
316
|
|
|
320
317
|
// Test package discovery using the embedded filesystem
|
|
321
318
|
packages := analysis.discoverEmbeddedGsPackages()
|
package/compiler/compiler.go
CHANGED
|
@@ -72,7 +72,11 @@ func NewCompiler(conf *Config, le *logrus.Entry, opts *packages.Config) (*Compil
|
|
|
72
72
|
packages.NeedTypesInfo |
|
|
73
73
|
packages.NeedTypesSizes
|
|
74
74
|
|
|
75
|
-
return &Compiler{
|
|
75
|
+
return &Compiler{
|
|
76
|
+
config: *conf,
|
|
77
|
+
le: le,
|
|
78
|
+
opts: *opts,
|
|
79
|
+
}, nil
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
// CompilationResult contains information about what was compiled
|
|
@@ -141,7 +145,6 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
141
145
|
// Check if this package has a handwritten equivalent
|
|
142
146
|
if hasHandwrittenEquivalent(pkg.PkgPath) {
|
|
143
147
|
// Add this package but don't visit its dependencies
|
|
144
|
-
// c.le.Debugf("Skipping dependencies of handwritten package: %s", pkg.PkgPath)
|
|
145
148
|
return
|
|
146
149
|
}
|
|
147
150
|
|
|
@@ -149,13 +152,11 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
149
152
|
for _, imp := range pkg.Imports {
|
|
150
153
|
// Skip protobuf-go-lite packages and their dependencies
|
|
151
154
|
if isProtobufGoLitePackage(imp.PkgPath) {
|
|
152
|
-
// c.le.Debugf("Skipping protobuf-go-lite package: %s", imp.PkgPath)
|
|
153
155
|
continue
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
// Skip packages that are only used by .pb.go files
|
|
157
159
|
if isPackageOnlyUsedByProtobufFiles(pkg, imp.PkgPath) {
|
|
158
|
-
// c.le.Debugf("Skipping package only used by .pb.go files: %s", imp.PkgPath)
|
|
159
160
|
continue
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -168,28 +169,39 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
168
169
|
visit(pkg)
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
//
|
|
172
|
-
|
|
172
|
+
// Now we have collected all dependencies, but they only have minimal information.
|
|
173
|
+
// We need to reload them with complete type information for compilation.
|
|
174
|
+
// Collect all package paths from the dependency graph
|
|
175
|
+
var pkgPaths []string
|
|
176
|
+
pkgPathSet := make(map[string]bool) // Use set to avoid duplicates
|
|
173
177
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if pkg.PkgPath != "" {
|
|
179
|
-
pkgPaths = append(pkgPaths, pkg.PkgPath)
|
|
180
|
-
}
|
|
178
|
+
for _, pkg := range allPkgs {
|
|
179
|
+
if pkg.PkgPath != "" && !pkgPathSet[pkg.PkgPath] {
|
|
180
|
+
pkgPaths = append(pkgPaths, pkg.PkgPath)
|
|
181
|
+
pkgPathSet[pkg.PkgPath] = true
|
|
181
182
|
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Reload all collected packages with complete type information
|
|
186
|
+
if len(pkgPaths) > 0 {
|
|
187
|
+
c.le.Debugf("Reloading %d packages with complete type information", len(pkgPaths))
|
|
182
188
|
|
|
183
|
-
// Reload all packages with full mode
|
|
184
|
-
// TODO: Can we get rid of this? This would be very slow!
|
|
185
189
|
fullOpts := c.opts
|
|
186
190
|
fullOpts.Context = ctx
|
|
191
|
+
// Use LoadAllSyntax to get complete type information, syntax trees, and type checking
|
|
187
192
|
fullOpts.Mode = packages.LoadAllSyntax
|
|
188
|
-
|
|
193
|
+
|
|
194
|
+
reloadedPkgs, err := packages.Load(&fullOpts, pkgPaths...)
|
|
189
195
|
if err != nil {
|
|
190
|
-
return fmt.Errorf("failed to reload packages with
|
|
196
|
+
return nil, fmt.Errorf("failed to reload packages with complete type information: %w", err)
|
|
191
197
|
}
|
|
192
|
-
|
|
198
|
+
|
|
199
|
+
// Replace the minimal packages with the fully loaded ones
|
|
200
|
+
pkgs = reloadedPkgs
|
|
201
|
+
} else {
|
|
202
|
+
// No packages to reload, use the original set
|
|
203
|
+
pkgs = allPkgs
|
|
204
|
+
}
|
|
193
205
|
}
|
|
194
206
|
|
|
195
207
|
// If DisableEmitBuiltin is false, we need to copy the builtin package to the output directory
|
|
@@ -206,6 +218,12 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
206
218
|
// Track which gs packages have been processed to avoid duplicates
|
|
207
219
|
processedGsPackages := make(map[string]bool)
|
|
208
220
|
|
|
221
|
+
// Create a map of all loaded packages for dependency analysis
|
|
222
|
+
allPackages := make(map[string]*packages.Package)
|
|
223
|
+
for _, pkg := range pkgs {
|
|
224
|
+
allPackages[pkg.PkgPath] = pkg
|
|
225
|
+
}
|
|
226
|
+
|
|
209
227
|
// Compile all packages
|
|
210
228
|
for _, pkg := range pkgs {
|
|
211
229
|
// Check if the package has a handwritten equivalent
|
|
@@ -237,7 +255,7 @@ func (c *Compiler) CompilePackages(ctx context.Context, patterns ...string) (*Co
|
|
|
237
255
|
continue
|
|
238
256
|
}
|
|
239
257
|
|
|
240
|
-
pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg)
|
|
258
|
+
pkgCompiler, err := NewPackageCompiler(c.le, &c.config, pkg, allPackages)
|
|
241
259
|
if err != nil {
|
|
242
260
|
return nil, fmt.Errorf("failed to create package compiler for %s: %w", pkg.PkgPath, err)
|
|
243
261
|
}
|
|
@@ -262,6 +280,7 @@ type PackageCompiler struct {
|
|
|
262
280
|
compilerConf *Config
|
|
263
281
|
outputPath string
|
|
264
282
|
pkg *packages.Package
|
|
283
|
+
allPackages map[string]*packages.Package
|
|
265
284
|
}
|
|
266
285
|
|
|
267
286
|
// NewPackageCompiler creates a new `PackageCompiler` for a given Go package.
|
|
@@ -272,12 +291,14 @@ func NewPackageCompiler(
|
|
|
272
291
|
le *logrus.Entry,
|
|
273
292
|
compilerConf *Config,
|
|
274
293
|
pkg *packages.Package,
|
|
294
|
+
allPackages map[string]*packages.Package,
|
|
275
295
|
) (*PackageCompiler, error) {
|
|
276
296
|
res := &PackageCompiler{
|
|
277
297
|
le: le,
|
|
278
298
|
pkg: pkg,
|
|
279
299
|
compilerConf: compilerConf,
|
|
280
300
|
outputPath: ComputeModulePath(compilerConf.OutputPath, pkg.PkgPath),
|
|
301
|
+
allPackages: allPackages,
|
|
281
302
|
}
|
|
282
303
|
|
|
283
304
|
return res, nil
|
|
@@ -304,7 +325,10 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
|
|
|
304
325
|
}
|
|
305
326
|
|
|
306
327
|
// Perform package-level analysis for auto-imports
|
|
307
|
-
packageAnalysis :=
|
|
328
|
+
packageAnalysis := AnalyzePackageImports(c.pkg)
|
|
329
|
+
|
|
330
|
+
// Perform comprehensive package-level analysis for code generation
|
|
331
|
+
analysis := AnalyzePackageFiles(c.pkg, c.allPackages)
|
|
308
332
|
|
|
309
333
|
// Track all compiled files for later generating the index.ts
|
|
310
334
|
compiledFiles := make([]string, 0, len(c.pkg.CompiledGoFiles))
|
|
@@ -342,7 +366,7 @@ func (c *PackageCompiler) Compile(ctx context.Context) error {
|
|
|
342
366
|
|
|
343
367
|
// log just the filename
|
|
344
368
|
c.le.Debugf("GS: %s", filepath.Base(fileName))
|
|
345
|
-
if err := c.CompileFile(ctx, fileName, f, packageAnalysis); err != nil {
|
|
369
|
+
if err := c.CompileFile(ctx, fileName, f, analysis, packageAnalysis); err != nil {
|
|
346
370
|
return err
|
|
347
371
|
}
|
|
348
372
|
|
|
@@ -392,6 +416,7 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
392
416
|
// Find which symbols this file exports
|
|
393
417
|
var valueSymbols []string
|
|
394
418
|
var typeSymbols []string
|
|
419
|
+
var structSymbols []string // New: Track structs separately
|
|
395
420
|
|
|
396
421
|
// Find the corresponding syntax file
|
|
397
422
|
for i, syntax := range c.pkg.Syntax {
|
|
@@ -415,9 +440,15 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
415
440
|
switch s := spec.(type) {
|
|
416
441
|
case *ast.TypeSpec:
|
|
417
442
|
if s.Name.IsExported() {
|
|
418
|
-
//
|
|
419
|
-
|
|
420
|
-
|
|
443
|
+
// Check if this is a struct type
|
|
444
|
+
if _, isStruct := s.Type.(*ast.StructType); isStruct {
|
|
445
|
+
// Structs become TypeScript classes and need both type and value exports
|
|
446
|
+
structSymbols = append(structSymbols, s.Name.Name)
|
|
447
|
+
} else {
|
|
448
|
+
// Other type declarations (interfaces, type definitions, type aliases)
|
|
449
|
+
// become TypeScript types and must be exported with "export type"
|
|
450
|
+
typeSymbols = append(typeSymbols, s.Name.Name)
|
|
451
|
+
}
|
|
421
452
|
}
|
|
422
453
|
case *ast.ValueSpec:
|
|
423
454
|
for _, name := range s.Names {
|
|
@@ -442,6 +473,17 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
442
473
|
}
|
|
443
474
|
}
|
|
444
475
|
|
|
476
|
+
// Write struct exports (both as types and values)
|
|
477
|
+
if len(structSymbols) > 0 {
|
|
478
|
+
sort.Strings(structSymbols)
|
|
479
|
+
// Export classes as values (which makes them available as both types and values in TypeScript)
|
|
480
|
+
exportLine := fmt.Sprintf("export { %s } from \"./%s.js\"\n",
|
|
481
|
+
strings.Join(structSymbols, ", "), fileName)
|
|
482
|
+
if _, err := indexFile.WriteString(exportLine); err != nil {
|
|
483
|
+
return err
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
445
487
|
if len(typeSymbols) > 0 {
|
|
446
488
|
sort.Strings(typeSymbols)
|
|
447
489
|
exportLine := fmt.Sprintf("export type { %s } from \"./%s.js\"\n",
|
|
@@ -456,21 +498,11 @@ func (c *PackageCompiler) generateIndexFile(compiledFiles []string) error {
|
|
|
456
498
|
}
|
|
457
499
|
|
|
458
500
|
// CompileFile handles the compilation of a single Go source file to TypeScript.
|
|
459
|
-
// It
|
|
460
|
-
//
|
|
461
|
-
// about varRefing, async functions, defer statements).
|
|
501
|
+
// It uses the pre-computed package-level analysis for accurate TypeScript generation
|
|
502
|
+
// (e.g., about varRefing, async functions, defer statements, receiver usage across files).
|
|
462
503
|
// Then, it creates a `FileCompiler` instance for the file and invokes its
|
|
463
504
|
// `Compile` method to generate the TypeScript code.
|
|
464
|
-
func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, packageAnalysis *PackageAnalysis) error {
|
|
465
|
-
// Create a new analysis instance for per-file data
|
|
466
|
-
analysis := NewAnalysis()
|
|
467
|
-
|
|
468
|
-
// Create comment map for the file
|
|
469
|
-
cmap := ast.NewCommentMap(p.pkg.Fset, syntax, syntax.Comments)
|
|
470
|
-
|
|
471
|
-
// Analyze the file before compiling
|
|
472
|
-
AnalyzeFile(syntax, p.pkg, analysis, cmap)
|
|
473
|
-
|
|
505
|
+
func (p *PackageCompiler) CompileFile(ctx context.Context, name string, syntax *ast.File, analysis *Analysis, packageAnalysis *PackageAnalysis) error {
|
|
474
506
|
fileCompiler, err := NewFileCompiler(p.compilerConf, p.pkg, syntax, name, analysis, packageAnalysis)
|
|
475
507
|
if err != nil {
|
|
476
508
|
return err
|
package/compiler/decl.go
CHANGED
|
@@ -3,6 +3,7 @@ package compiler
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
|
+
"go/types"
|
|
6
7
|
)
|
|
7
8
|
|
|
8
9
|
// WriteDecls iterates through a slice of Go top-level declarations (`ast.Decl`)
|
|
@@ -193,6 +194,27 @@ func (c *GoToTSCompiler) writeMethodSignature(decl *ast.FuncDecl) (bool, error)
|
|
|
193
194
|
var isAsync bool
|
|
194
195
|
if obj := c.pkg.TypesInfo.Defs[decl.Name]; obj != nil {
|
|
195
196
|
isAsync = c.analysis.IsAsyncFunc(obj)
|
|
197
|
+
|
|
198
|
+
// Check if this method must be async due to interface constraints
|
|
199
|
+
if !isAsync && decl.Recv != nil && len(decl.Recv.List) > 0 {
|
|
200
|
+
// Get the receiver type
|
|
201
|
+
receiverType := decl.Recv.List[0].Type
|
|
202
|
+
if starExpr, ok := receiverType.(*ast.StarExpr); ok {
|
|
203
|
+
receiverType = starExpr.X
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if ident, ok := receiverType.(*ast.Ident); ok {
|
|
207
|
+
// Get the named type for this receiver
|
|
208
|
+
if receiverObj := c.pkg.TypesInfo.Uses[ident]; receiverObj != nil {
|
|
209
|
+
if namedType, ok := receiverObj.Type().(*types.Named); ok {
|
|
210
|
+
// Check if this method must be async due to interface constraints
|
|
211
|
+
if c.analysis.MustBeAsyncDueToInterface(namedType, decl.Name.Name) {
|
|
212
|
+
isAsync = true
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
196
218
|
}
|
|
197
219
|
|
|
198
220
|
// Methods are typically public in the TS output
|
|
@@ -6,70 +6,64 @@ import (
|
|
|
6
6
|
"strings"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
// writeAsyncCallIfNeeded writes the await prefix for async function or method calls
|
|
10
|
+
// Returns true if await was written, false otherwise
|
|
11
|
+
func (c *GoToTSCompiler) writeAsyncCallIfNeeded(exp *ast.CallExpr) bool {
|
|
12
|
+
switch fun := exp.Fun.(type) {
|
|
13
|
+
case *ast.Ident:
|
|
14
|
+
// Function call (e.g., func())
|
|
15
|
+
if obj := c.pkg.TypesInfo.Uses[fun]; obj != nil && c.analysis.IsAsyncFunc(obj) {
|
|
16
|
+
c.tsw.WriteLiterally("await ")
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
12
19
|
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
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
case *ast.SelectorExpr:
|
|
22
|
+
// Method call (e.g., obj.method())
|
|
23
|
+
ident, ok := fun.X.(*ast.Ident)
|
|
24
|
+
if !ok {
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
23
27
|
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
28
|
+
// Get the type of the receiver
|
|
29
|
+
obj := c.pkg.TypesInfo.Uses[ident]
|
|
30
|
+
if obj == nil {
|
|
31
|
+
return false
|
|
32
|
+
}
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
34
|
+
varObj, ok := obj.(*types.Var)
|
|
35
|
+
if !ok {
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
// Get the type name and package
|
|
40
|
+
namedType, ok := varObj.Type().(*types.Named)
|
|
41
|
+
if !ok {
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
45
|
+
typeName := namedType.Obj().Name()
|
|
46
|
+
methodName := fun.Sel.Name
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
// Check if the type is from an imported package
|
|
49
|
+
typePkg := namedType.Obj().Pkg()
|
|
50
|
+
if typePkg == nil || typePkg == c.pkg.Types {
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Use the full package path from the type information (not just the package name)
|
|
55
|
+
pkgPath := typePkg.Path()
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
// Check if this method is async based on metadata
|
|
58
|
+
if c.analysis.IsMethodAsync(pkgPath, typeName, methodName) {
|
|
59
|
+
c.tsw.WriteLiterally("await ")
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
60
62
|
return false
|
|
61
|
-
}
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
64
|
+
default:
|
|
65
|
+
return false
|
|
70
66
|
}
|
|
71
|
-
|
|
72
|
-
return false
|
|
73
67
|
}
|
|
74
68
|
|
|
75
69
|
// addNonNullAssertion adds ! for function calls that might return null
|
|
@@ -81,7 +81,7 @@ func (c *GoToTSCompiler) writeArrayTypeConversion(exp *ast.CallExpr) (handled bo
|
|
|
81
81
|
// Check if the argument is a named type with a slice underlying type
|
|
82
82
|
if namedArgType, isNamed := argType.(*types.Named); isNamed {
|
|
83
83
|
// Check if the named type has receiver methods (is a wrapper type)
|
|
84
|
-
if c.analysis.
|
|
84
|
+
if c.analysis.IsNamedBasicType(namedArgType) {
|
|
85
85
|
// Check if the underlying type matches the target slice type
|
|
86
86
|
if sliceUnderlying, isSlice := namedArgType.Underlying().(*types.Slice); isSlice {
|
|
87
87
|
// Get the target slice type
|
|
@@ -216,7 +216,7 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
|
|
|
216
216
|
if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
217
217
|
if namedArgType, isNamed := argType.(*types.Named); isNamed {
|
|
218
218
|
// Check if the argument type is a wrapper type
|
|
219
|
-
if c.analysis.
|
|
219
|
+
if c.analysis.IsNamedBasicType(namedArgType) {
|
|
220
220
|
// Check if we're converting to the underlying type
|
|
221
221
|
targetType := typeName.Type()
|
|
222
222
|
underlyingType := namedArgType.Underlying()
|
|
@@ -247,7 +247,7 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
|
|
|
247
247
|
return true, nil
|
|
248
248
|
} else {
|
|
249
249
|
// Check if this is a wrapper type
|
|
250
|
-
isWrapperType := c.analysis.
|
|
250
|
+
isWrapperType := c.analysis.IsNamedBasicType(typeName.Type())
|
|
251
251
|
if isWrapperType {
|
|
252
252
|
// For wrapper types, use type casting instead of constructor calls
|
|
253
253
|
c.tsw.WriteLiterally("(")
|
|
@@ -288,8 +288,6 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
|
|
|
288
288
|
c.tsw.WriteLiterally(funIdent.String())
|
|
289
289
|
c.tsw.WriteLiterally("(")
|
|
290
290
|
|
|
291
|
-
fmt.Printf("DEBUG: Type conversion constructor for %s\n", funIdent.String())
|
|
292
|
-
|
|
293
291
|
// Use auto-wrapping for the constructor argument
|
|
294
292
|
// The constructor parameter type is the underlying type of the named type
|
|
295
293
|
// For MyMode (which is type MyMode os.FileMode), the constructor expects os.FileMode
|
|
@@ -299,8 +297,6 @@ func (c *GoToTSCompiler) writeTypeConversion(exp *ast.CallExpr, funIdent *ast.Id
|
|
|
299
297
|
constructorParamType = namedType.Underlying()
|
|
300
298
|
}
|
|
301
299
|
|
|
302
|
-
fmt.Printf("DEBUG: Constructor param type: %v\n", constructorParamType)
|
|
303
|
-
|
|
304
300
|
if err := c.writeAutoWrappedArgument(exp.Args[0], constructorParamType); err != nil {
|
|
305
301
|
return true, fmt.Errorf("failed to write argument for type constructor: %w", err)
|
|
306
302
|
}
|
|
@@ -376,7 +372,7 @@ func (c *GoToTSCompiler) writeIntConversion(exp *ast.CallExpr) error {
|
|
|
376
372
|
if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
377
373
|
if namedArgType, isNamed := argType.(*types.Named); isNamed {
|
|
378
374
|
// Check if the argument type is a wrapper type
|
|
379
|
-
if c.analysis.
|
|
375
|
+
if c.analysis.IsNamedBasicType(namedArgType) {
|
|
380
376
|
// Check if we're converting to int (the underlying type)
|
|
381
377
|
if types.Identical(types.Typ[types.Int], namedArgType.Underlying()) {
|
|
382
378
|
// This is a conversion from a wrapper type to int
|
|
@@ -413,7 +409,7 @@ func (c *GoToTSCompiler) writeQualifiedTypeConversion(exp *ast.CallExpr, selecto
|
|
|
413
409
|
if argType := c.pkg.TypesInfo.TypeOf(arg); argType != nil {
|
|
414
410
|
if namedArgType, isNamed := argType.(*types.Named); isNamed {
|
|
415
411
|
// Check if the argument type is a wrapper type
|
|
416
|
-
if c.analysis.
|
|
412
|
+
if c.analysis.IsNamedBasicType(namedArgType) {
|
|
417
413
|
// Check if we're converting to the underlying type
|
|
418
414
|
targetType := typeName.Type()
|
|
419
415
|
underlyingType := namedArgType.Underlying()
|
|
@@ -444,7 +440,7 @@ func (c *GoToTSCompiler) writeQualifiedTypeConversion(exp *ast.CallExpr, selecto
|
|
|
444
440
|
return true, nil
|
|
445
441
|
} else {
|
|
446
442
|
// Check if this is a wrapper type
|
|
447
|
-
isWrapperType := c.analysis.
|
|
443
|
+
isWrapperType := c.analysis.IsNamedBasicType(typeName.Type())
|
|
448
444
|
if isWrapperType {
|
|
449
445
|
// For wrapper types, use type casting instead of constructor calls
|
|
450
446
|
c.tsw.WriteLiterally("(")
|
package/compiler/expr-call.go
CHANGED
|
@@ -74,7 +74,7 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Check if this is an async function call
|
|
77
|
-
|
|
77
|
+
c.writeAsyncCallIfNeeded(exp)
|
|
78
78
|
|
|
79
79
|
// Not a special built-in, treat as a regular function call
|
|
80
80
|
if err := c.WriteValueExpr(expFun); err != nil {
|
|
@@ -99,7 +99,7 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
99
99
|
|
|
100
100
|
// Handle non-identifier function expressions (method calls, function literals, etc.)
|
|
101
101
|
// Check if this is an async method call (e.g., mu.Lock())
|
|
102
|
-
|
|
102
|
+
c.writeAsyncCallIfNeeded(exp)
|
|
103
103
|
|
|
104
104
|
// If expFun is a function literal, it needs to be wrapped in parentheses for IIFE syntax
|
|
105
105
|
if _, isFuncLit := expFun.(*ast.FuncLit); isFuncLit {
|
|
@@ -169,7 +169,7 @@ func (c *GoToTSCompiler) writeCallArguments(exp *ast.CallExpr) error {
|
|
|
169
169
|
func (c *GoToTSCompiler) writeArgumentWithTypeHandling(arg ast.Expr, funcSig *types.Signature, argIndex int) error {
|
|
170
170
|
if funcSig != nil && argIndex < funcSig.Params().Len() {
|
|
171
171
|
paramType := funcSig.Params().At(argIndex).Type()
|
|
172
|
-
isWrapper := c.analysis.
|
|
172
|
+
isWrapper := c.analysis.IsNamedBasicType(paramType)
|
|
173
173
|
|
|
174
174
|
if isWrapper {
|
|
175
175
|
// For wrapper types (now type aliases), no auto-wrapping is needed
|
|
@@ -234,7 +234,7 @@ func (c *GoToTSCompiler) getImportAlias(pkgPath string) string {
|
|
|
234
234
|
func (c *GoToTSCompiler) writeAutoWrappedArgument(arg ast.Expr, expectedType types.Type) error {
|
|
235
235
|
// For wrapper types (now type aliases), no auto-wrapping is needed
|
|
236
236
|
// Just use type casting if the types don't match exactly
|
|
237
|
-
if c.analysis.
|
|
237
|
+
if c.analysis.IsNamedBasicType(expectedType) {
|
|
238
238
|
argType := c.pkg.TypesInfo.TypeOf(arg)
|
|
239
239
|
|
|
240
240
|
// Only add type casting if needed
|
|
@@ -271,39 +271,70 @@ func (c *GoToTSCompiler) writeWrapperTypeMethodCall(exp *ast.CallExpr, selectorE
|
|
|
271
271
|
return false, nil
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
// Check if this is a wrapper type
|
|
275
|
-
|
|
274
|
+
// Check if this is a wrapper type using the analysis
|
|
275
|
+
isWrapperType := c.analysis.IsNamedBasicType(baseType)
|
|
276
|
+
|
|
277
|
+
// Special handling for type aliases to basic types that might have wrapper functions
|
|
278
|
+
// Even if IsNamedBasicType returns false, we should check for known wrapper type patterns
|
|
279
|
+
var typeName string
|
|
280
|
+
if !isWrapperType {
|
|
281
|
+
// Check if this is a type alias to a basic type that might have wrapper methods
|
|
282
|
+
if aliasType, ok := baseType.(*types.Alias); ok {
|
|
283
|
+
if aliasType.Obj() != nil && aliasType.Obj().Pkg() != nil {
|
|
284
|
+
// Check if the underlying type is a basic type
|
|
285
|
+
underlying := aliasType.Underlying()
|
|
286
|
+
if _, isBasic := underlying.(*types.Basic); isBasic {
|
|
287
|
+
// This is a type alias to a basic type - treat it as a potential wrapper type
|
|
288
|
+
isWrapperType = true
|
|
289
|
+
if aliasType.Obj().Pkg() != c.pkg.Types {
|
|
290
|
+
// Imported type alias like os.FileMode
|
|
291
|
+
if importAlias := c.getImportAlias(aliasType.Obj().Pkg().Path()); importAlias != "" {
|
|
292
|
+
typeName = importAlias + "." + aliasType.Obj().Name()
|
|
293
|
+
} else {
|
|
294
|
+
typeName = aliasType.Obj().Name()
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
// Local type alias
|
|
298
|
+
typeName = aliasType.Obj().Name()
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if !isWrapperType {
|
|
276
306
|
return false, nil
|
|
277
307
|
}
|
|
278
308
|
|
|
279
|
-
// Get the type name for the function call
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
309
|
+
// Get the type name for the function call if not already set
|
|
310
|
+
if typeName == "" {
|
|
311
|
+
if namedType, ok := baseType.(*types.Named); ok {
|
|
312
|
+
if obj := namedType.Obj(); obj != nil {
|
|
313
|
+
if obj.Pkg() != nil && obj.Pkg() != c.pkg.Types {
|
|
314
|
+
// Imported type like os.FileMode
|
|
315
|
+
if importAlias := c.getImportAlias(obj.Pkg().Path()); importAlias != "" {
|
|
316
|
+
typeName = importAlias + "." + obj.Name()
|
|
317
|
+
} else {
|
|
318
|
+
typeName = obj.Name()
|
|
319
|
+
}
|
|
287
320
|
} else {
|
|
321
|
+
// Local type
|
|
288
322
|
typeName = obj.Name()
|
|
289
323
|
}
|
|
290
|
-
} else {
|
|
291
|
-
// Local type
|
|
292
|
-
typeName = obj.Name()
|
|
293
324
|
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
325
|
+
} else if aliasType, ok := baseType.(*types.Alias); ok {
|
|
326
|
+
if obj := aliasType.Obj(); obj != nil {
|
|
327
|
+
if obj.Pkg() != nil && obj.Pkg() != c.pkg.Types {
|
|
328
|
+
// Imported type alias
|
|
329
|
+
if importAlias := c.getImportAlias(obj.Pkg().Path()); importAlias != "" {
|
|
330
|
+
typeName = importAlias + "." + obj.Name()
|
|
331
|
+
} else {
|
|
332
|
+
typeName = obj.Name()
|
|
333
|
+
}
|
|
301
334
|
} else {
|
|
335
|
+
// Local type alias
|
|
302
336
|
typeName = obj.Name()
|
|
303
337
|
}
|
|
304
|
-
} else {
|
|
305
|
-
// Local type alias
|
|
306
|
-
typeName = obj.Name()
|
|
307
338
|
}
|
|
308
339
|
}
|
|
309
340
|
}
|
package/compiler/spec-value.go
CHANGED
|
@@ -237,7 +237,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
237
237
|
// Check if this is a named type with methods and the initializer is a basic value
|
|
238
238
|
if namedType, isNamed := goType.(*types.Named); isNamed {
|
|
239
239
|
// Check if this is a wrapper type first
|
|
240
|
-
isWrapperType := c.analysis.
|
|
240
|
+
isWrapperType := c.analysis.IsNamedBasicType(namedType)
|
|
241
241
|
if isWrapperType {
|
|
242
242
|
// For wrapper types, no constructor wrapping needed
|
|
243
243
|
if shouldApplyClone(c.pkg, initializerExpr) {
|
|
@@ -334,7 +334,7 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
334
334
|
// No initializer, use the zero value directly
|
|
335
335
|
// Check if this is a wrapper type first
|
|
336
336
|
if namedType, isNamed := goType.(*types.Named); isNamed {
|
|
337
|
-
isWrapperType := c.analysis.
|
|
337
|
+
isWrapperType := c.analysis.IsNamedBasicType(namedType)
|
|
338
338
|
if isWrapperType {
|
|
339
339
|
// For wrapper types, just use zero value directly
|
|
340
340
|
c.WriteZeroValueForType(goType)
|