goscript 0.1.3 → 0.1.4

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 (117) hide show
  1. package/cmd/goscript/cmd_compile.go +28 -8
  2. package/cmd/goscript/cmd_compile_test.go +105 -6
  3. package/compiler/build-flags.go +9 -10
  4. package/compiler/gotest/runner_test.go +127 -0
  5. package/compiler/lowering.go +596 -136
  6. package/compiler/lowering_bench_test.go +350 -0
  7. package/compiler/package-graph.go +61 -4
  8. package/compiler/package-graph_test.go +30 -0
  9. package/compiler/semantic-model-types.go +8 -0
  10. package/compiler/semantic-model.go +447 -22
  11. package/compiler/semantic-model_test.go +138 -0
  12. package/compiler/skeleton_test.go +948 -14
  13. package/compiler/typescript-emitter.go +19 -2
  14. package/dist/gs/builtin/builtin.d.ts +2 -2
  15. package/dist/gs/builtin/builtin.js +20 -0
  16. package/dist/gs/builtin/builtin.js.map +1 -1
  17. package/dist/gs/builtin/slice.js +5 -0
  18. package/dist/gs/builtin/slice.js.map +1 -1
  19. package/dist/gs/builtin/type.d.ts +1 -1
  20. package/dist/gs/builtin/type.js +72 -5
  21. package/dist/gs/builtin/type.js.map +1 -1
  22. package/dist/gs/compress/zlib/index.d.ts +3 -3
  23. package/dist/gs/compress/zlib/index.js +88 -26
  24. package/dist/gs/compress/zlib/index.js.map +1 -1
  25. package/dist/gs/crypto/sha1/index.js +2 -5
  26. package/dist/gs/crypto/sha1/index.js.map +1 -1
  27. package/dist/gs/crypto/sha256/index.js +2 -5
  28. package/dist/gs/crypto/sha256/index.js.map +1 -1
  29. package/dist/gs/crypto/sha512/index.js +2 -5
  30. package/dist/gs/crypto/sha512/index.js.map +1 -1
  31. package/dist/gs/embed/index.d.ts +6 -0
  32. package/dist/gs/embed/index.js +210 -5
  33. package/dist/gs/embed/index.js.map +1 -1
  34. package/dist/gs/fmt/fmt.d.ts +3 -3
  35. package/dist/gs/fmt/fmt.js +29 -16
  36. package/dist/gs/fmt/fmt.js.map +1 -1
  37. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +118 -6
  38. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +1 -1
  39. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.d.ts +45 -0
  40. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js +229 -0
  41. package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js.map +1 -0
  42. package/dist/gs/io/fs/readdir.js +5 -3
  43. package/dist/gs/io/fs/readdir.js.map +1 -1
  44. package/dist/gs/io/io.d.ts +10 -6
  45. package/dist/gs/io/io.js +87 -42
  46. package/dist/gs/io/io.js.map +1 -1
  47. package/dist/gs/math/bits/index.d.ts +26 -5
  48. package/dist/gs/math/bits/index.js +13 -24
  49. package/dist/gs/math/bits/index.js.map +1 -1
  50. package/dist/gs/net/http/index.d.ts +3 -1
  51. package/dist/gs/net/http/index.js +18 -1
  52. package/dist/gs/net/http/index.js.map +1 -1
  53. package/dist/gs/os/types_js.gs.d.ts +6 -2
  54. package/dist/gs/os/types_js.gs.js +169 -8
  55. package/dist/gs/os/types_js.gs.js.map +1 -1
  56. package/dist/gs/reflect/type.d.ts +1 -0
  57. package/dist/gs/reflect/type.js +80 -51
  58. package/dist/gs/reflect/type.js.map +1 -1
  59. package/dist/gs/strings/reader.d.ts +1 -1
  60. package/dist/gs/strings/reader.js +2 -2
  61. package/dist/gs/strings/reader.js.map +1 -1
  62. package/dist/gs/sync/sync.d.ts +2 -1
  63. package/dist/gs/sync/sync.js +37 -16
  64. package/dist/gs/sync/sync.js.map +1 -1
  65. package/dist/gs/syscall/js/index.js +9 -0
  66. package/dist/gs/syscall/js/index.js.map +1 -1
  67. package/dist/gs/testing/testing.js +8 -6
  68. package/dist/gs/testing/testing.js.map +1 -1
  69. package/gs/builtin/builtin.ts +25 -2
  70. package/gs/builtin/runtime-contract.test.ts +45 -0
  71. package/gs/builtin/slice.ts +7 -0
  72. package/gs/builtin/type.ts +85 -5
  73. package/gs/compress/zlib/index.test.ts +97 -0
  74. package/gs/compress/zlib/index.ts +117 -27
  75. package/gs/compress/zlib/meta.json +4 -1
  76. package/gs/crypto/sha1/index.test.ts +19 -2
  77. package/gs/crypto/sha1/index.ts +3 -6
  78. package/gs/crypto/sha256/index.test.ts +14 -2
  79. package/gs/crypto/sha256/index.ts +3 -6
  80. package/gs/crypto/sha512/index.test.ts +17 -2
  81. package/gs/crypto/sha512/index.ts +3 -6
  82. package/gs/embed/index.test.ts +87 -0
  83. package/gs/embed/index.ts +229 -5
  84. package/gs/fmt/fmt.test.ts +41 -3
  85. package/gs/fmt/fmt.ts +40 -17
  86. package/gs/fmt/meta.json +6 -1
  87. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +8 -1
  88. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +139 -11
  89. package/gs/github.com/go-git/go-billy/v6/osfs/index.test.ts +110 -0
  90. package/gs/github.com/go-git/go-billy/v6/osfs/index.ts +280 -0
  91. package/gs/github.com/go-git/go-billy/v6/osfs/meta.json +8 -0
  92. package/gs/io/fs/readdir.test.ts +38 -0
  93. package/gs/io/fs/readdir.ts +7 -3
  94. package/gs/io/io.test.ts +77 -6
  95. package/gs/io/io.ts +114 -52
  96. package/gs/io/meta.json +7 -1
  97. package/gs/math/bits/index.ts +52 -28
  98. package/gs/net/http/index.test.ts +16 -0
  99. package/gs/net/http/index.ts +19 -2
  100. package/gs/os/file_unix_js.test.ts +52 -0
  101. package/gs/os/meta.json +4 -0
  102. package/gs/os/readdir.test.ts +56 -0
  103. package/gs/os/types_js.gs.ts +169 -8
  104. package/gs/reflect/deepequal.test.ts +10 -1
  105. package/gs/reflect/type.ts +91 -56
  106. package/gs/reflect/typefor.test.ts +31 -1
  107. package/gs/strings/meta.json +5 -2
  108. package/gs/strings/reader.test.ts +2 -2
  109. package/gs/strings/reader.ts +2 -2
  110. package/gs/sync/meta.json +1 -0
  111. package/gs/sync/sync.test.ts +41 -1
  112. package/gs/sync/sync.ts +41 -16
  113. package/gs/syscall/js/index.test.ts +18 -0
  114. package/gs/syscall/js/index.ts +12 -0
  115. package/gs/testing/testing.test.ts +32 -3
  116. package/gs/testing/testing.ts +13 -10
  117. package/package.json +1 -1
@@ -0,0 +1,350 @@
1
+ package compiler
2
+
3
+ import (
4
+ "context"
5
+ "go/ast"
6
+ "go/types"
7
+ "os"
8
+ "path/filepath"
9
+ "testing"
10
+ )
11
+
12
+ type loweringBenchFixture struct {
13
+ model *SemanticModel
14
+ owner *LoweringOwner
15
+ semPkg *semanticPackage
16
+ file loweringBenchFile
17
+ genDecls []loweringBenchGenDecl
18
+ stmtLists []loweringBenchStmtList
19
+ }
20
+
21
+ type loweringBenchFile struct {
22
+ file *ast.File
23
+ sourcePath string
24
+ associated []*ast.FuncDecl
25
+ declFiles map[types.Object]string
26
+ outputNames map[string]string
27
+ lazyPackageVars map[types.Object]bool
28
+ }
29
+
30
+ type loweringBenchGenDecl struct {
31
+ ctx lowerFileContext
32
+ decl *ast.GenDecl
33
+ }
34
+
35
+ type loweringBenchStmtList struct {
36
+ ctx lowerFileContext
37
+ stmts []ast.Stmt
38
+ }
39
+
40
+ func BenchmarkLoweringPackage(b *testing.B) {
41
+ fixture := newLoweringBenchFixture(b)
42
+ b.ReportAllocs()
43
+ b.ResetTimer()
44
+ for i := 0; i < b.N; i++ {
45
+ if _, diagnostics := fixture.owner.lowerPackage(fixture.model, fixture.semPkg); diagnosticsHaveErrors(diagnostics) {
46
+ b.Fatal(diagnostics)
47
+ }
48
+ }
49
+ }
50
+
51
+ func BenchmarkLoweringAnalyzeLocalFileReferences(b *testing.B) {
52
+ fixture := newLoweringBenchFixture(b)
53
+ b.ReportAllocs()
54
+ b.ResetTimer()
55
+ for i := 0; i < b.N; i++ {
56
+ _ = fixture.owner.analyzeLocalFileReferences(
57
+ fixture.semPkg,
58
+ fixture.file.file,
59
+ fixture.file.sourcePath,
60
+ fixture.file.associated,
61
+ fixture.file.declFiles,
62
+ fixture.file.outputNames,
63
+ )
64
+ }
65
+ }
66
+
67
+ func BenchmarkLoweringFile(b *testing.B) {
68
+ fixture := newLoweringBenchFixture(b)
69
+ b.ReportAllocs()
70
+ b.ResetTimer()
71
+ for i := 0; i < b.N; i++ {
72
+ if _, diagnostics := fixture.owner.lowerFile(
73
+ fixture.model,
74
+ fixture.semPkg,
75
+ fixture.file.file,
76
+ fixture.file.sourcePath,
77
+ fixture.file.declFiles,
78
+ fixture.file.outputNames,
79
+ fixture.file.lazyPackageVars,
80
+ ); diagnosticsHaveErrors(diagnostics) {
81
+ b.Fatal(diagnostics)
82
+ }
83
+ }
84
+ }
85
+
86
+ func BenchmarkLoweringGenDecls(b *testing.B) {
87
+ fixture := newLoweringBenchFixture(b)
88
+ b.ReportAllocs()
89
+ b.ResetTimer()
90
+ for i := 0; i < b.N; i++ {
91
+ for _, target := range fixture.genDecls {
92
+ if _, diagnostics := fixture.owner.lowerGenDecl(target.ctx, target.decl); diagnosticsHaveErrors(diagnostics) {
93
+ b.Fatal(diagnostics)
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ func BenchmarkLoweringStmtLists(b *testing.B) {
100
+ fixture := newLoweringBenchFixture(b)
101
+ b.ReportAllocs()
102
+ b.ResetTimer()
103
+ for i := 0; i < b.N; i++ {
104
+ for _, target := range fixture.stmtLists {
105
+ if _, diagnostics := fixture.owner.lowerStmtList(target.ctx, target.stmts); diagnosticsHaveErrors(diagnostics) {
106
+ b.Fatal(diagnostics)
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ func BenchmarkLoweringPackageDeclFiles(b *testing.B) {
113
+ fixture := newLoweringBenchFixture(b)
114
+ b.ReportAllocs()
115
+ b.ResetTimer()
116
+ for i := 0; i < b.N; i++ {
117
+ _ = packageDeclFiles(fixture.semPkg)
118
+ }
119
+ }
120
+
121
+ func BenchmarkLoweringLazyPackageVars(b *testing.B) {
122
+ fixture := newLoweringBenchFixture(b)
123
+ b.ReportAllocs()
124
+ b.ResetTimer()
125
+ for i := 0; i < b.N; i++ {
126
+ _ = fixture.owner.lazyPackageVars(fixture.semPkg, fixture.file.declFiles)
127
+ }
128
+ }
129
+
130
+ func newLoweringBenchFixture(tb testing.TB) *loweringBenchFixture {
131
+ tb.Helper()
132
+ dir := tb.TempDir()
133
+ if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module example.test/loweringbench\n\ngo 1.25.3\n"), 0o644); err != nil {
134
+ tb.Fatal(err)
135
+ }
136
+ for name, source := range loweringBenchSources {
137
+ if err := os.WriteFile(filepath.Join(dir, name), []byte(source), 0o644); err != nil {
138
+ tb.Fatal(err)
139
+ }
140
+ }
141
+
142
+ service := NewCompileService()
143
+ req := &CompileRequest{
144
+ Dir: dir,
145
+ OutputPath: filepath.Join(dir, "out"),
146
+ Patterns: []string{"."},
147
+ }
148
+ graph, graphDiagnostics := service.PackageGraphOwner().Load(context.Background(), req)
149
+ if diagnosticsHaveErrors(graphDiagnostics) {
150
+ tb.Fatal(graphDiagnostics)
151
+ }
152
+ model, semanticDiagnostics := service.SemanticModelOwner().Build(context.Background(), graph)
153
+ if diagnosticsHaveErrors(semanticDiagnostics) {
154
+ tb.Fatal(semanticDiagnostics)
155
+ }
156
+ semPkg := model.packages["example.test/loweringbench"]
157
+ if semPkg == nil {
158
+ tb.Fatal("missing benchmark semantic package")
159
+ }
160
+ owner := service.LoweringOwner()
161
+ declFiles := packageDeclFiles(semPkg)
162
+ outputNames := packageOutputNames(semPkg)
163
+ lazyPackageVars := owner.lazyPackageVars(semPkg, declFiles)
164
+
165
+ var benchFile loweringBenchFile
166
+ var genDecls []loweringBenchGenDecl
167
+ var stmtLists []loweringBenchStmtList
168
+ for idx, file := range semPkg.source.Syntax {
169
+ sourcePath := sourceFilePath(semPkg, idx, file)
170
+ associated := owner.methodDeclsForFileTypes(semPkg, file)
171
+ localRefs := owner.analyzeLocalFileReferences(semPkg, file, sourcePath, associated, declFiles, outputNames)
172
+ ctx := lowerFileContext{
173
+ model: model,
174
+ semPkg: semPkg,
175
+ file: file,
176
+ importAliases: make(map[string]string),
177
+ importPaths: make(map[string]string),
178
+ importNames: make(map[string]string),
179
+ importObjects: make(map[*types.PkgName]string),
180
+ sourcePath: sourcePath,
181
+ localAliases: localRefs.aliases,
182
+ lazyPackageVars: lazyPackageVars,
183
+ tempNames: newTempNameOwner(),
184
+ topLevel: true,
185
+ }
186
+ if filepath.Base(sourcePath) == "bench.go" {
187
+ benchFile = loweringBenchFile{
188
+ file: file,
189
+ sourcePath: sourcePath,
190
+ associated: associated,
191
+ declFiles: declFiles,
192
+ outputNames: outputNames,
193
+ lazyPackageVars: lazyPackageVars,
194
+ }
195
+ }
196
+ for _, decl := range file.Decls {
197
+ switch typed := decl.(type) {
198
+ case *ast.GenDecl:
199
+ genDecls = append(genDecls, loweringBenchGenDecl{ctx: ctx, decl: typed})
200
+ case *ast.FuncDecl:
201
+ fn, _ := semPkg.source.TypesInfo.Defs[typed.Name].(*types.Func)
202
+ signature, _ := fn.Type().(*types.Signature)
203
+ bodyCtx := ctx.withSignature(signature).withAsyncFunction(owner.functionAsync(ctx, fn))
204
+ if typed.Body != nil {
205
+ stmtLists = append(stmtLists, loweringBenchStmtList{ctx: bodyCtx, stmts: typed.Body.List})
206
+ }
207
+ }
208
+ }
209
+ }
210
+ if benchFile.file == nil {
211
+ tb.Fatal("missing bench.go fixture file")
212
+ }
213
+ if len(genDecls) == 0 || len(stmtLists) == 0 {
214
+ tb.Fatalf("incomplete lowering benchmark fixture: genDecls=%d stmtLists=%d", len(genDecls), len(stmtLists))
215
+ }
216
+ return &loweringBenchFixture{
217
+ model: model,
218
+ owner: owner,
219
+ semPkg: semPkg,
220
+ file: benchFile,
221
+ genDecls: genDecls,
222
+ stmtLists: stmtLists,
223
+ }
224
+ }
225
+
226
+ var loweringBenchSources = map[string]string{
227
+ "bench.go": `package loweringbench
228
+
229
+ type Number interface {
230
+ ~int | ~int64
231
+ }
232
+
233
+ type Item[T Number] struct {
234
+ ID int
235
+ Name string
236
+ Values []T
237
+ Meta map[string]T
238
+ Next *Item[T]
239
+ }
240
+
241
+ type Processor[T Number] interface {
242
+ Process(Item[T]) (T, error)
243
+ }
244
+
245
+ type Runner[T Number] struct {
246
+ Items []Item[T]
247
+ Proc Processor[T]
248
+ }
249
+
250
+ var GlobalItems = []Item[int]{
251
+ {ID: 1, Name: "one", Values: []int{1, 2, 3}, Meta: map[string]int{"a": 1}},
252
+ {ID: 2, Name: "two", Values: []int{4, 5, 6}, Meta: map[string]int{"b": 2}},
253
+ }
254
+
255
+ var Lookup = map[string]Item[int]{
256
+ "one": GlobalItems[0],
257
+ "two": GlobalItems[1],
258
+ }
259
+
260
+ func (r *Runner[T]) Sum(seed T) (T, error) {
261
+ total := seed
262
+ for idx, item := range r.Items {
263
+ for _, value := range item.Values {
264
+ total += value
265
+ }
266
+ if r.Proc != nil {
267
+ next, err := r.Proc.Process(item)
268
+ if err != nil {
269
+ return total, err
270
+ }
271
+ total += next
272
+ }
273
+ if idx%2 == 0 {
274
+ total += T(idx)
275
+ }
276
+ }
277
+ return total, nil
278
+ }
279
+
280
+ func UseRunner(r *Runner[int]) int {
281
+ total, err := r.Sum(10)
282
+ if err != nil {
283
+ return -1
284
+ }
285
+ switch {
286
+ case total > 100:
287
+ return total / 2
288
+ case total > 20:
289
+ return total + len(r.Items)
290
+ default:
291
+ return total
292
+ }
293
+ }
294
+ `,
295
+ "flow.go": `package loweringbench
296
+
297
+ func FoldItems(items []Item[int], fn func(Item[int]) int) int {
298
+ total := 0
299
+ for _, item := range items {
300
+ total += fn(item)
301
+ }
302
+ return total
303
+ }
304
+
305
+ func ComplexFlow(items []Item[int], ch chan int) int {
306
+ total := 0
307
+ for idx := 0; idx < len(items); idx++ {
308
+ item := items[idx]
309
+ select {
310
+ case ch <- item.ID:
311
+ total += item.ID
312
+ default:
313
+ total += len(item.Values)
314
+ }
315
+ for key, value := range item.Meta {
316
+ if key == "" {
317
+ continue
318
+ }
319
+ total += value
320
+ }
321
+ }
322
+ return total
323
+ }
324
+ `,
325
+ "types.go": `package loweringbench
326
+
327
+ type NamedInt int
328
+
329
+ func (n NamedInt) Process(item Item[int]) (int, error) {
330
+ return int(n) + item.ID + len(item.Values), nil
331
+ }
332
+
333
+ type Pair[A, B any] struct {
334
+ A A
335
+ B B
336
+ }
337
+
338
+ type Registry struct {
339
+ ByName map[string]Processor[int]
340
+ Pairs []Pair[string, Item[int]]
341
+ }
342
+
343
+ func NewRegistry() *Registry {
344
+ return &Registry{
345
+ ByName: map[string]Processor[int]{"default": NamedInt(3)},
346
+ Pairs: []Pair[string, Item[int]]{{A: "one", B: GlobalItems[0]}},
347
+ }
348
+ }
349
+ `,
350
+ }
@@ -2,6 +2,7 @@ package compiler
2
2
 
3
3
  import (
4
4
  "context"
5
+ "go/ast"
5
6
  "os"
6
7
  "slices"
7
8
  "strings"
@@ -130,6 +131,15 @@ func (o *PackageGraphOwner) Load(ctx context.Context, req *CompileRequest) (*Pac
130
131
  graph.RequestedPackagePaths = append(graph.RequestedPackagePaths, path)
131
132
  }
132
133
  slices.Sort(graph.RequestedPackagePaths)
134
+ samePackageTestVariants := make(map[string]bool)
135
+ if req.Tests {
136
+ for _, pkg := range pkgs {
137
+ if pkg == nil || pkg.ForTest == "" || strings.HasSuffix(pkg.Name, "_test") {
138
+ continue
139
+ }
140
+ samePackageTestVariants[pkg.ForTest] = true
141
+ }
142
+ }
133
143
 
134
144
  var diagnostics []Diagnostic
135
145
  seen := make(map[string]bool)
@@ -137,7 +147,7 @@ func (o *PackageGraphOwner) Load(ctx context.Context, req *CompileRequest) (*Pac
137
147
  if isTestMainPackage(pkg) {
138
148
  continue
139
149
  }
140
- o.collect(graph, pkg, req.DependencyMode, requested, overrideFacts, seen)
150
+ o.collect(graph, pkg, req.DependencyMode, requested, samePackageTestVariants, overrideFacts, seen)
141
151
  diagnostics = append(diagnostics, packageDiagnostics(pkg)...)
142
152
  }
143
153
  slices.SortFunc(graph.Nodes, func(a, b *PackageGraphNode) int {
@@ -161,21 +171,33 @@ func (o *PackageGraphOwner) collect(
161
171
  pkg *packages.Package,
162
172
  mode DependencyMode,
163
173
  requested map[string]bool,
174
+ samePackageTestVariants map[string]bool,
164
175
  overrideFacts *OverrideFacts,
165
176
  seen map[string]bool,
166
177
  ) {
167
178
  if pkg == nil || seen[pkg.ID] {
168
179
  return
169
180
  }
181
+ path := packagePath(pkg)
182
+ if pkg.ForTest != "" && path != pkg.ForTest && !strings.HasSuffix(pkg.Name, "_test") && samePackageTestVariants[path] {
183
+ return
184
+ }
185
+ if pkg.ForTest == "" && samePackageTestVariants[path] {
186
+ return
187
+ }
170
188
  if pkg.ForTest != "" && !requested[pkg.ForTest] {
171
189
  if prod := pkg.Imports[pkg.ForTest]; prod != nil {
172
- o.collect(graph, prod, mode, requested, overrideFacts, seen)
190
+ o.collect(graph, prod, mode, requested, samePackageTestVariants, overrideFacts, seen)
173
191
  }
174
192
  return
175
193
  }
194
+ if graph.NodesByPackagePath[path] != nil {
195
+ return
196
+ }
176
197
  seen[pkg.ID] = true
177
198
 
178
- path := packagePath(pkg)
199
+ normalizePackageFileOrder(pkg)
200
+
179
201
  node := newPackageGraphNode(pkg, requested[path], overrideFacts)
180
202
  graph.Nodes = append(graph.Nodes, node)
181
203
  graph.NodesByPackagePath[path] = node
@@ -190,7 +212,7 @@ func (o *PackageGraphOwner) collect(
190
212
  }
191
213
  slices.Sort(imports)
192
214
  for _, importPath := range imports {
193
- o.collect(graph, pkg.Imports[importPath], mode, requested, overrideFacts, seen)
215
+ o.collect(graph, pkg.Imports[importPath], mode, requested, samePackageTestVariants, overrideFacts, seen)
194
216
  }
195
217
  }
196
218
 
@@ -223,6 +245,41 @@ func newPackageGraphNode(pkg *packages.Package, requested bool, overrideFacts *O
223
245
  }
224
246
  }
225
247
 
248
+ func normalizePackageFileOrder(pkg *packages.Package) {
249
+ if pkg == nil {
250
+ return
251
+ }
252
+ slices.Sort(pkg.GoFiles)
253
+ if len(pkg.Syntax) == len(pkg.CompiledGoFiles) {
254
+ type sourceFile struct {
255
+ name string
256
+ file *ast.File
257
+ }
258
+ files := make([]sourceFile, len(pkg.Syntax))
259
+ for idx, file := range pkg.Syntax {
260
+ files[idx] = sourceFile{name: pkg.CompiledGoFiles[idx], file: file}
261
+ }
262
+ slices.SortFunc(files, func(a, b sourceFile) int {
263
+ return strings.Compare(a.name, b.name)
264
+ })
265
+ for idx, file := range files {
266
+ pkg.CompiledGoFiles[idx] = file.name
267
+ pkg.Syntax[idx] = file.file
268
+ }
269
+ return
270
+ }
271
+ slices.Sort(pkg.CompiledGoFiles)
272
+ slices.SortFunc(pkg.Syntax, func(a, b *ast.File) int {
273
+ if pkg.Fset == nil {
274
+ return strings.Compare(a.Name.Name, b.Name.Name)
275
+ }
276
+ return strings.Compare(
277
+ pkg.Fset.Position(a.Package).Filename,
278
+ pkg.Fset.Position(b.Package).Filename,
279
+ )
280
+ })
281
+ }
282
+
226
283
  func isTestMainPackage(pkg *packages.Package) bool {
227
284
  return pkg != nil && pkg.ForTest == "" && pkg.Name == "main" && strings.HasSuffix(packagePath(pkg), ".test")
228
285
  }
@@ -176,6 +176,36 @@ func TestPackageGraphHonorsBuildFlags(t *testing.T) {
176
176
  }
177
177
  }
178
178
 
179
+ func TestPackageGraphNormalizesCompiledFileOrder(t *testing.T) {
180
+ moduleDir := writePackageGraphFixture(t, map[string]string{
181
+ "go.mod": "module example.test/order\n\ngo 1.25.3\n",
182
+ "z.go": "package order\nconst Z = 1\n",
183
+ "a.go": "package order\nconst A = 1\n",
184
+ "m.go": "package order\nconst M = 1\n",
185
+ })
186
+ graph := loadPackageGraph(t, &CompileRequest{
187
+ Patterns: []string{"."},
188
+ Dir: moduleDir,
189
+ OutputPath: filepath.Join(t.TempDir(), "out"),
190
+ DependencyMode: DependencyModeRequested,
191
+ RuntimeEmissionMode: RuntimeEmissionModeEmit,
192
+ })
193
+
194
+ pkg := graph.packagesByPath["example.test/order"]
195
+ if pkg == nil {
196
+ t.Fatalf("missing loaded package")
197
+ }
198
+ var compiled []string
199
+ var syntax []string
200
+ for idx, file := range pkg.Syntax {
201
+ compiled = append(compiled, filepath.Base(pkg.CompiledGoFiles[idx]))
202
+ syntax = append(syntax, filepath.Base(pkg.Fset.Position(file.Package).Filename))
203
+ }
204
+ if want := []string{"a.go", "m.go", "z.go"}; !slices.Equal(compiled, want) || !slices.Equal(syntax, want) {
205
+ t.Fatalf("compiled files and syntax must sort together, got compiled=%v syntax=%v", compiled, syntax)
206
+ }
207
+ }
208
+
179
209
  func TestPackageGraphAddsGoScriptBuildTag(t *testing.T) {
180
210
  moduleDir := writePackageGraphFixture(t, map[string]string{
181
211
  "go.mod": "module example.test/goscripttag\n\ngo 1.25.3\n",
@@ -14,10 +14,13 @@ type SemanticModel struct {
14
14
  functions map[*types.Func]*semanticFunction
15
15
  functionsByFullName map[string]*semanticFunction
16
16
  functionLookupMisses map[*types.Func]bool
17
+ functionFullNames map[*types.Func]string
17
18
  types map[*types.Named]*semanticType
18
19
  values map[types.Object]*semanticValue
19
20
  generatedImports map[string]map[string]bool
20
21
  interfaceImplementations []semanticInterfaceImplementation
22
+ asyncInterfaceMethods map[string]bool
23
+ asyncInterfaceMethodObjs map[*types.Func]bool
21
24
  }
22
25
 
23
26
  type semanticPackage struct {
@@ -103,6 +106,11 @@ type semanticInterfaceImplementationGraphEntry struct {
103
106
  implMethods map[string]*types.Func
104
107
  }
105
108
 
109
+ type semanticAnonymousInterfaceImplementation struct {
110
+ ifaceMethods map[string]*types.Func
111
+ implMethods map[string]*types.Func
112
+ }
113
+
106
114
  type semanticImplementationMethodSet struct {
107
115
  typ *types.Named
108
116
  receiver types.Type