goscript 0.0.83 → 0.1.0

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 (197) hide show
  1. package/README.md +13 -1
  2. package/cmd/goscript/cmd_compile.go +70 -69
  3. package/cmd/goscript/cmd_compile_test.go +79 -0
  4. package/cmd/goscript/main.go +10 -5
  5. package/compiler/compile-request.go +218 -0
  6. package/compiler/compiler.go +16 -1336
  7. package/compiler/compliance_test.go +196 -0
  8. package/compiler/config.go +6 -13
  9. package/compiler/diagnostic.go +70 -0
  10. package/compiler/index.test.ts +28 -28
  11. package/compiler/index.ts +40 -72
  12. package/compiler/lowered-program.go +132 -0
  13. package/compiler/lowering.go +3576 -0
  14. package/compiler/override-registry.go +422 -0
  15. package/compiler/override-registry_test.go +207 -0
  16. package/compiler/package-graph.go +231 -0
  17. package/compiler/package-graph_test.go +281 -0
  18. package/compiler/result.go +13 -0
  19. package/compiler/runtime-contract.go +279 -0
  20. package/compiler/runtime-contract_test.go +90 -0
  21. package/compiler/semantic-model-types.go +110 -0
  22. package/compiler/semantic-model.go +922 -0
  23. package/compiler/semantic-model_test.go +416 -0
  24. package/compiler/service.go +133 -0
  25. package/compiler/skeleton_test.go +1145 -0
  26. package/compiler/typescript-emitter.go +663 -0
  27. package/compiler/wasm/compile.go +2 -3
  28. package/compiler/wasm/compile_test.go +29 -0
  29. package/compiler/wasm_api.go +10 -159
  30. package/dist/compiler/index.d.ts +1 -3
  31. package/dist/compiler/index.js +31 -55
  32. package/dist/compiler/index.js.map +1 -1
  33. package/dist/gs/builtin/builtin.d.ts +13 -0
  34. package/dist/gs/builtin/builtin.js +27 -7
  35. package/dist/gs/builtin/builtin.js.map +1 -1
  36. package/dist/gs/builtin/channel.d.ts +3 -3
  37. package/dist/gs/builtin/channel.js.map +1 -1
  38. package/dist/gs/builtin/hostio.d.ts +86 -0
  39. package/dist/gs/builtin/hostio.js +266 -0
  40. package/dist/gs/builtin/hostio.js.map +1 -0
  41. package/dist/gs/builtin/index.d.ts +1 -0
  42. package/dist/gs/builtin/index.js +1 -0
  43. package/dist/gs/builtin/index.js.map +1 -1
  44. package/dist/gs/builtin/print.d.ts +8 -0
  45. package/dist/gs/builtin/print.js +111 -0
  46. package/dist/gs/builtin/print.js.map +1 -0
  47. package/dist/gs/builtin/slice.d.ts +1 -1
  48. package/dist/gs/builtin/slice.js.map +1 -1
  49. package/dist/gs/builtin/type.d.ts +11 -0
  50. package/dist/gs/builtin/type.js +55 -1
  51. package/dist/gs/builtin/type.js.map +1 -1
  52. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  53. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  54. package/dist/gs/bytes/reader.gs.js.map +1 -1
  55. package/dist/gs/context/context.js.map +1 -1
  56. package/dist/gs/crypto/rand/index.d.ts +5 -0
  57. package/dist/gs/crypto/rand/index.js +77 -0
  58. package/dist/gs/crypto/rand/index.js.map +1 -0
  59. package/dist/gs/encoding/json/index.d.ts +3 -0
  60. package/dist/gs/encoding/json/index.js +160 -0
  61. package/dist/gs/encoding/json/index.js.map +1 -0
  62. package/dist/gs/fmt/fmt.js +2 -22
  63. package/dist/gs/fmt/fmt.js.map +1 -1
  64. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  65. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  66. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  67. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  68. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  69. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  70. package/dist/gs/go/scanner/index.d.ts +29 -0
  71. package/dist/gs/go/scanner/index.js +120 -0
  72. package/dist/gs/go/scanner/index.js.map +1 -0
  73. package/dist/gs/go/token/index.d.ts +31 -0
  74. package/dist/gs/go/token/index.js +82 -0
  75. package/dist/gs/go/token/index.js.map +1 -0
  76. package/dist/gs/internal/abi/index.js.map +1 -1
  77. package/dist/gs/io/fs/fs.js.map +1 -1
  78. package/dist/gs/io/fs/readdir.js.map +1 -1
  79. package/dist/gs/io/fs/readfile.js.map +1 -1
  80. package/dist/gs/io/fs/stat.js.map +1 -1
  81. package/dist/gs/io/fs/sub.js.map +1 -1
  82. package/dist/gs/io/io.js.map +1 -1
  83. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  84. package/dist/gs/os/error.gs.js +2 -4
  85. package/dist/gs/os/error.gs.js.map +1 -1
  86. package/dist/gs/os/exec.gs.js.map +1 -1
  87. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  88. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  89. package/dist/gs/os/root_js.gs.js.map +1 -1
  90. package/dist/gs/os/tempfile.gs.js +66 -9
  91. package/dist/gs/os/tempfile.gs.js.map +1 -1
  92. package/dist/gs/os/types.gs.js.map +1 -1
  93. package/dist/gs/os/types_js.gs.d.ts +2 -51
  94. package/dist/gs/os/types_js.gs.js +67 -105
  95. package/dist/gs/os/types_js.gs.js.map +1 -1
  96. package/dist/gs/os/types_unix.gs.js.map +1 -1
  97. package/dist/gs/path/filepath/match.js.map +1 -1
  98. package/dist/gs/path/match.js.map +1 -1
  99. package/dist/gs/path/path.js.map +1 -1
  100. package/dist/gs/reflect/index.d.ts +2 -2
  101. package/dist/gs/reflect/index.js +1 -1
  102. package/dist/gs/reflect/index.js.map +1 -1
  103. package/dist/gs/reflect/map.js.map +1 -1
  104. package/dist/gs/reflect/type.d.ts +2 -1
  105. package/dist/gs/reflect/type.js +85 -14
  106. package/dist/gs/reflect/type.js.map +1 -1
  107. package/dist/gs/reflect/types.js.map +1 -1
  108. package/dist/gs/reflect/visiblefields.js.map +1 -1
  109. package/dist/gs/runtime/runtime.js.map +1 -1
  110. package/dist/gs/sort/sort.gs.js.map +1 -1
  111. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  112. package/dist/gs/strconv/quote.gs.js.map +1 -1
  113. package/dist/gs/strings/builder.js.map +1 -1
  114. package/dist/gs/strings/reader.js.map +1 -1
  115. package/dist/gs/strings/replace.js.map +1 -1
  116. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  117. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  118. package/dist/gs/sync/sync.d.ts +1 -0
  119. package/dist/gs/sync/sync.js +12 -0
  120. package/dist/gs/sync/sync.js.map +1 -1
  121. package/dist/gs/time/time.js.map +1 -1
  122. package/dist/gs/unicode/unicode.js.map +1 -1
  123. package/go.mod +2 -2
  124. package/gs/builtin/builtin.ts +31 -6
  125. package/gs/builtin/hostio.test.ts +246 -0
  126. package/gs/builtin/hostio.ts +413 -0
  127. package/gs/builtin/index.ts +1 -0
  128. package/gs/builtin/print.test.ts +48 -0
  129. package/gs/builtin/print.ts +154 -0
  130. package/gs/builtin/runtime-contract.test.ts +230 -0
  131. package/gs/builtin/type.ts +84 -1
  132. package/gs/crypto/rand/index.test.ts +32 -0
  133. package/gs/crypto/rand/index.ts +90 -0
  134. package/gs/crypto/rand/meta.json +5 -0
  135. package/gs/encoding/json/index.test.ts +65 -0
  136. package/gs/encoding/json/index.ts +186 -0
  137. package/gs/fmt/fmt.test.ts +41 -30
  138. package/gs/fmt/fmt.ts +2 -22
  139. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  140. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  141. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  142. package/gs/go/scanner/index.test.ts +50 -0
  143. package/gs/go/scanner/index.ts +157 -0
  144. package/gs/go/token/index.test.ts +21 -0
  145. package/gs/go/token/index.ts +120 -0
  146. package/gs/os/file_unix_js.test.ts +103 -0
  147. package/gs/os/meta.json +1 -2
  148. package/gs/os/tempfile.gs.test.ts +85 -0
  149. package/gs/os/tempfile.gs.ts +71 -11
  150. package/gs/os/types_js.gs.ts +74 -153
  151. package/gs/reflect/index.ts +1 -1
  152. package/gs/reflect/type.ts +106 -17
  153. package/gs/reflect/typefor.test.ts +75 -0
  154. package/gs/sync/sync.test.ts +24 -0
  155. package/gs/sync/sync.ts +12 -0
  156. package/package.json +13 -13
  157. package/compiler/analysis.go +0 -3475
  158. package/compiler/analysis_test.go +0 -338
  159. package/compiler/assignment.go +0 -580
  160. package/compiler/builtin_test.go +0 -92
  161. package/compiler/code-writer.go +0 -115
  162. package/compiler/compiler_test.go +0 -149
  163. package/compiler/composite-lit.go +0 -779
  164. package/compiler/config_test.go +0 -62
  165. package/compiler/constraint.go +0 -86
  166. package/compiler/decl.go +0 -801
  167. package/compiler/expr-call-async.go +0 -188
  168. package/compiler/expr-call-builtins.go +0 -208
  169. package/compiler/expr-call-helpers.go +0 -382
  170. package/compiler/expr-call-make.go +0 -318
  171. package/compiler/expr-call-type-conversion.go +0 -520
  172. package/compiler/expr-call.go +0 -413
  173. package/compiler/expr-selector.go +0 -343
  174. package/compiler/expr-star.go +0 -82
  175. package/compiler/expr-type.go +0 -442
  176. package/compiler/expr-value.go +0 -89
  177. package/compiler/expr.go +0 -773
  178. package/compiler/field.go +0 -183
  179. package/compiler/gs_dependencies_test.go +0 -298
  180. package/compiler/lit.go +0 -322
  181. package/compiler/output.go +0 -72
  182. package/compiler/primitive.go +0 -149
  183. package/compiler/protobuf.go +0 -697
  184. package/compiler/sanitize.go +0 -100
  185. package/compiler/spec-struct.go +0 -995
  186. package/compiler/spec-value.go +0 -540
  187. package/compiler/spec.go +0 -725
  188. package/compiler/stmt-assign.go +0 -664
  189. package/compiler/stmt-for.go +0 -266
  190. package/compiler/stmt-range.go +0 -475
  191. package/compiler/stmt-select.go +0 -262
  192. package/compiler/stmt-type-switch.go +0 -147
  193. package/compiler/stmt.go +0 -1308
  194. package/compiler/type-assert.go +0 -386
  195. package/compiler/type-info.go +0 -156
  196. package/compiler/type-utils.go +0 -207
  197. package/compiler/type.go +0 -892
@@ -0,0 +1,922 @@
1
+ package compiler
2
+
3
+ import (
4
+ "cmp"
5
+ "context"
6
+ "go/ast"
7
+ "go/token"
8
+ "go/types"
9
+ "slices"
10
+ "strconv"
11
+ "strings"
12
+
13
+ "golang.org/x/tools/go/packages"
14
+ )
15
+
16
+ // SemanticModelOwner owns immutable Go semantic facts used by lowering.
17
+ type SemanticModelOwner struct {
18
+ overrideOwner *OverrideRegistryOwner
19
+ }
20
+
21
+ // NewSemanticModelOwner creates the semantic model owner.
22
+ func NewSemanticModelOwner(overrideOwners ...*OverrideRegistryOwner) *SemanticModelOwner {
23
+ overrideOwner := NewOverrideRegistryOwner()
24
+ if len(overrideOwners) != 0 && overrideOwners[0] != nil {
25
+ overrideOwner = overrideOwners[0]
26
+ }
27
+ return &SemanticModelOwner{overrideOwner: overrideOwner}
28
+ }
29
+
30
+ // Build constructs semantic facts for a package graph.
31
+ func (o *SemanticModelOwner) Build(ctx context.Context, graph *PackageGraph) (*SemanticModel, []Diagnostic) {
32
+ if err := ctx.Err(); err != nil {
33
+ return nil, []Diagnostic{{
34
+ Severity: DiagnosticSeverityError,
35
+ Code: "goscript/context:canceled",
36
+ Message: err.Error(),
37
+ }}
38
+ }
39
+ if graph == nil {
40
+ return nil, []Diagnostic{{
41
+ Severity: DiagnosticSeverityError,
42
+ Code: "goscript/semantic:no-graph",
43
+ Message: "semantic model requires a loaded package graph",
44
+ }}
45
+ }
46
+
47
+ model := newSemanticModel()
48
+ var diagnostics []Diagnostic
49
+ for _, node := range graph.Nodes {
50
+ if err := ctx.Err(); err != nil {
51
+ diagnostics = append(diagnostics, Diagnostic{
52
+ Severity: DiagnosticSeverityError,
53
+ Code: "goscript/context:canceled",
54
+ Message: err.Error(),
55
+ })
56
+ break
57
+ }
58
+ if node.OverrideCandidate {
59
+ continue
60
+ }
61
+ pkg := graph.packagesByPath[node.PkgPath]
62
+ if pkg == nil {
63
+ diagnostics = append(diagnostics, Diagnostic{
64
+ Severity: DiagnosticSeverityError,
65
+ Code: "goscript/semantic:missing-package",
66
+ Message: "package graph node is missing loaded package data",
67
+ Detail: node.PkgPath,
68
+ })
69
+ continue
70
+ }
71
+ diagnostics = append(diagnostics, o.buildPackage(model, node, pkg)...)
72
+ }
73
+ if diagnosticsHaveErrors(diagnostics) {
74
+ return model, diagnostics
75
+ }
76
+
77
+ o.propagateFunctionAsync(model)
78
+ o.resolveInterfaceImplementations(model)
79
+ o.propagateFunctionAsync(model)
80
+ return model, diagnostics
81
+ }
82
+
83
+ func newSemanticModel() *SemanticModel {
84
+ return &SemanticModel{
85
+ packages: make(map[string]*semanticPackage),
86
+ addressTaken: make(map[types.Object]bool),
87
+ needsVarRef: make(map[types.Object]bool),
88
+ functions: make(map[*types.Func]*semanticFunction),
89
+ types: make(map[*types.Named]*semanticType),
90
+ values: make(map[types.Object]*semanticValue),
91
+ generatedImports: make(map[string]map[string]bool),
92
+ }
93
+ }
94
+
95
+ func (o *SemanticModelOwner) buildPackage(
96
+ model *SemanticModel,
97
+ node *PackageGraphNode,
98
+ pkg *packages.Package,
99
+ ) []Diagnostic {
100
+ semPkg := &semanticPackage{
101
+ pkgPath: node.PkgPath,
102
+ name: node.Name,
103
+ source: pkg,
104
+ generatedImports: make(map[string]map[string]bool),
105
+ }
106
+ model.packages[node.PkgPath] = semPkg
107
+
108
+ for _, file := range pkg.Syntax {
109
+ o.collectFileDeclarations(model, semPkg, pkg, file)
110
+ o.collectFileFacts(model, semPkg, pkg, file)
111
+ }
112
+ var diagnostics []Diagnostic
113
+ for _, file := range pkg.Syntax {
114
+ diagnostics = append(diagnostics, o.collectFunctionFacts(model, pkg, file)...)
115
+ }
116
+ return diagnostics
117
+ }
118
+
119
+ func (o *SemanticModelOwner) collectFileDeclarations(
120
+ model *SemanticModel,
121
+ semPkg *semanticPackage,
122
+ pkg *packages.Package,
123
+ file *ast.File,
124
+ ) {
125
+ for _, importSpec := range file.Imports {
126
+ importPath, err := strconv.Unquote(importSpec.Path.Value)
127
+ if err != nil {
128
+ importPath = importSpec.Path.Value
129
+ }
130
+ var name string
131
+ if importSpec.Name != nil {
132
+ name = importSpec.Name.Name
133
+ }
134
+ position := sourcePos(pkg, importSpec.Pos())
135
+ semPkg.imports = append(semPkg.imports, semanticImport{
136
+ path: importPath,
137
+ name: name,
138
+ file: position.file,
139
+ position: position,
140
+ })
141
+ }
142
+
143
+ for _, decl := range file.Decls {
144
+ switch typed := decl.(type) {
145
+ case *ast.GenDecl:
146
+ o.collectGenDecl(model, semPkg, pkg, typed)
147
+ case *ast.FuncDecl:
148
+ fn, _ := pkg.TypesInfo.Defs[typed.Name].(*types.Func)
149
+ if fn == nil {
150
+ continue
151
+ }
152
+ position := sourcePos(pkg, typed.Name.Pos())
153
+ o.addFunction(model, semPkg, fn, position)
154
+ semPkg.declarations = append(semPkg.declarations, semanticDeclaration{
155
+ kind: "func",
156
+ name: typed.Name.Name,
157
+ object: fn,
158
+ position: position,
159
+ })
160
+ }
161
+ }
162
+ }
163
+
164
+ func (o *SemanticModelOwner) collectGenDecl(
165
+ model *SemanticModel,
166
+ semPkg *semanticPackage,
167
+ pkg *packages.Package,
168
+ decl *ast.GenDecl,
169
+ ) {
170
+ for _, spec := range decl.Specs {
171
+ switch typed := spec.(type) {
172
+ case *ast.TypeSpec:
173
+ obj, _ := pkg.TypesInfo.Defs[typed.Name].(*types.TypeName)
174
+ if obj == nil {
175
+ continue
176
+ }
177
+ position := sourcePos(pkg, typed.Name.Pos())
178
+ o.addType(model, semPkg, obj, position, typed.Type)
179
+ semPkg.declarations = append(semPkg.declarations, semanticDeclaration{
180
+ kind: "type",
181
+ name: typed.Name.Name,
182
+ object: obj,
183
+ position: position,
184
+ })
185
+ case *ast.ValueSpec:
186
+ for _, name := range typed.Names {
187
+ obj := pkg.TypesInfo.Defs[name]
188
+ switch concrete := obj.(type) {
189
+ case *types.Var:
190
+ position := sourcePos(pkg, name.Pos())
191
+ o.addValue(model, semPkg, concrete, position, true)
192
+ semPkg.initOrder = append(semPkg.initOrder, concrete)
193
+ semPkg.declarations = append(semPkg.declarations, semanticDeclaration{
194
+ kind: "var",
195
+ name: name.Name,
196
+ object: concrete,
197
+ position: position,
198
+ })
199
+ o.recordGeneratedImports(model, semPkg, position.file, pkg.PkgPath, concrete.Type())
200
+ case *types.Const:
201
+ position := sourcePos(pkg, name.Pos())
202
+ o.addValue(model, semPkg, concrete, position, true)
203
+ semPkg.declarations = append(semPkg.declarations, semanticDeclaration{
204
+ kind: "const",
205
+ name: name.Name,
206
+ object: concrete,
207
+ position: position,
208
+ })
209
+ o.recordGeneratedImports(model, semPkg, position.file, pkg.PkgPath, concrete.Type())
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ func (o *SemanticModelOwner) collectFileFacts(
217
+ model *SemanticModel,
218
+ semPkg *semanticPackage,
219
+ pkg *packages.Package,
220
+ file *ast.File,
221
+ ) {
222
+ ast.Inspect(file, func(node ast.Node) bool {
223
+ switch typed := node.(type) {
224
+ case *ast.Ident:
225
+ o.addDefinedObject(model, semPkg, pkg, typed)
226
+ case *ast.UnaryExpr:
227
+ if typed.Op == token.AND {
228
+ o.recordAddressTaken(model, pkg, typed.X)
229
+ }
230
+ case *ast.SelectorExpr:
231
+ o.recordPointerReceiverUse(model, pkg, typed)
232
+ case *ast.TypeAssertExpr:
233
+ o.recordTypeAssertion(semPkg, pkg, typed)
234
+ case *ast.ValueSpec:
235
+ o.recordValueSpecNilFacts(semPkg, pkg, typed)
236
+ case *ast.AssignStmt:
237
+ o.recordAssignNilFacts(semPkg, pkg, typed)
238
+ }
239
+ return true
240
+ })
241
+ }
242
+
243
+ func (o *SemanticModelOwner) recordPointerReceiverUse(
244
+ model *SemanticModel,
245
+ pkg *packages.Package,
246
+ expr *ast.SelectorExpr,
247
+ ) {
248
+ selection := pkg.TypesInfo.Selections[expr]
249
+ if selection == nil || selection.Kind() != types.MethodVal {
250
+ return
251
+ }
252
+ method, _ := selection.Obj().(*types.Func)
253
+ if method == nil {
254
+ return
255
+ }
256
+ signature, _ := method.Type().(*types.Signature)
257
+ if signature == nil || signature.Recv() == nil {
258
+ return
259
+ }
260
+ if _, ok := signature.Recv().Type().(*types.Pointer); !ok {
261
+ return
262
+ }
263
+ if _, ok := types.Unalias(pkg.TypesInfo.TypeOf(expr.X)).Underlying().(*types.Pointer); ok {
264
+ return
265
+ }
266
+ obj := objectForAddress(pkg, expr.X)
267
+ if obj == nil {
268
+ return
269
+ }
270
+ model.addressTaken[obj] = true
271
+ model.needsVarRef[obj] = true
272
+ }
273
+
274
+ func (o *SemanticModelOwner) addDefinedObject(
275
+ model *SemanticModel,
276
+ semPkg *semanticPackage,
277
+ pkg *packages.Package,
278
+ ident *ast.Ident,
279
+ ) {
280
+ obj := pkg.TypesInfo.Defs[ident]
281
+ switch typed := obj.(type) {
282
+ case *types.Var:
283
+ position := sourcePos(pkg, ident.Pos())
284
+ o.addValue(model, semPkg, typed, position, false)
285
+ o.recordGeneratedImports(model, semPkg, position.file, pkg.PkgPath, typed.Type())
286
+ case *types.Const:
287
+ position := sourcePos(pkg, ident.Pos())
288
+ o.addValue(model, semPkg, typed, position, false)
289
+ o.recordGeneratedImports(model, semPkg, position.file, pkg.PkgPath, typed.Type())
290
+ case *types.TypeName:
291
+ o.addType(model, semPkg, typed, sourcePos(pkg, ident.Pos()), nil)
292
+ case *types.Func:
293
+ o.addFunction(model, semPkg, typed, sourcePos(pkg, ident.Pos()))
294
+ }
295
+ }
296
+
297
+ func (o *SemanticModelOwner) addType(
298
+ model *SemanticModel,
299
+ semPkg *semanticPackage,
300
+ obj *types.TypeName,
301
+ position sourcePosition,
302
+ typeExpr ast.Expr,
303
+ ) *semanticType {
304
+ named, _ := obj.Type().(*types.Named)
305
+ if named == nil {
306
+ return nil
307
+ }
308
+ if existing := model.types[named]; existing != nil {
309
+ return existing
310
+ }
311
+ _, isInterface := named.Underlying().(*types.Interface)
312
+ semType := &semanticType{
313
+ name: obj.Name(),
314
+ named: named,
315
+ isInterface: isInterface,
316
+ fields: semanticFields(named, typeExpr),
317
+ position: position,
318
+ }
319
+ model.types[named] = semType
320
+ semPkg.types = append(semPkg.types, semType)
321
+ if iface, ok := named.Underlying().(*types.Interface); ok {
322
+ iface.Complete()
323
+ for method := range iface.Methods() {
324
+ o.addFunction(model, semPkg, method, sourcePosition{})
325
+ }
326
+ }
327
+ return semType
328
+ }
329
+
330
+ func (o *SemanticModelOwner) addValue(
331
+ model *SemanticModel,
332
+ semPkg *semanticPackage,
333
+ obj types.Object,
334
+ position sourcePosition,
335
+ topLevel bool,
336
+ ) *semanticValue {
337
+ if obj == nil {
338
+ return nil
339
+ }
340
+ if existing := model.values[obj]; existing != nil {
341
+ if topLevel {
342
+ existing.topLevel = true
343
+ }
344
+ return existing
345
+ }
346
+ value := &semanticValue{
347
+ name: obj.Name(),
348
+ object: obj,
349
+ typ: obj.Type(),
350
+ zeroValueKind: zeroValueKind(obj.Type()),
351
+ position: position,
352
+ topLevel: topLevel,
353
+ }
354
+ model.values[obj] = value
355
+ semPkg.values = append(semPkg.values, value)
356
+ return value
357
+ }
358
+
359
+ func (o *SemanticModelOwner) addFunction(
360
+ model *SemanticModel,
361
+ semPkg *semanticPackage,
362
+ fn *types.Func,
363
+ position sourcePosition,
364
+ ) *semanticFunction {
365
+ if fn == nil {
366
+ return nil
367
+ }
368
+ if existing := model.functions[fn]; existing != nil {
369
+ return existing
370
+ }
371
+ signature, _ := fn.Type().(*types.Signature)
372
+ semFn := &semanticFunction{
373
+ name: fn.Name(),
374
+ function: fn,
375
+ signature: signature,
376
+ position: position,
377
+ calls: make(map[*types.Func]bool),
378
+ }
379
+ if signature != nil && signature.Recv() != nil {
380
+ recv := signature.Recv().Type()
381
+ if _, ok := recv.(*types.Pointer); ok {
382
+ semFn.receiverPointer = true
383
+ }
384
+ semFn.receiver = receiverNamedType(recv)
385
+ }
386
+ model.functions[fn] = semFn
387
+ semPkg.functions = append(semPkg.functions, semFn)
388
+ return semFn
389
+ }
390
+
391
+ func semanticFields(named *types.Named, typeExpr ast.Expr) []semanticField {
392
+ if named == nil {
393
+ return nil
394
+ }
395
+ structType, _ := named.Underlying().(*types.Struct)
396
+ if structType == nil {
397
+ return nil
398
+ }
399
+ docs := structFieldDocs(typeExpr)
400
+ fields := make([]semanticField, 0, structType.NumFields())
401
+ for i := range structType.NumFields() {
402
+ field := structType.Field(i)
403
+ fields = append(fields, semanticField{
404
+ name: field.Name(),
405
+ typ: field.Type(),
406
+ doc: docs[field.Name()],
407
+ tag: structType.Tag(i),
408
+ embedded: field.Embedded(),
409
+ })
410
+ }
411
+ return fields
412
+ }
413
+
414
+ func structFieldDocs(typeExpr ast.Expr) map[string]string {
415
+ structType, _ := typeExpr.(*ast.StructType)
416
+ if structType == nil || structType.Fields == nil {
417
+ return nil
418
+ }
419
+ docs := make(map[string]string)
420
+ for _, field := range structType.Fields.List {
421
+ if field.Doc == nil {
422
+ continue
423
+ }
424
+ doc := strings.TrimSpace(field.Doc.Text())
425
+ if doc == "" {
426
+ continue
427
+ }
428
+ for _, name := range field.Names {
429
+ docs[name.Name] = doc
430
+ }
431
+ }
432
+ return docs
433
+ }
434
+
435
+ func (o *SemanticModelOwner) recordAddressTaken(model *SemanticModel, pkg *packages.Package, expr ast.Expr) {
436
+ obj := objectForAddress(pkg, expr)
437
+ if obj == nil {
438
+ return
439
+ }
440
+ model.addressTaken[obj] = true
441
+ model.needsVarRef[obj] = true
442
+ }
443
+
444
+ func objectForAddress(pkg *packages.Package, expr ast.Expr) types.Object {
445
+ switch typed := expr.(type) {
446
+ case *ast.Ident:
447
+ if obj := pkg.TypesInfo.Uses[typed]; obj != nil {
448
+ return obj
449
+ }
450
+ return pkg.TypesInfo.Defs[typed]
451
+ case *ast.SelectorExpr:
452
+ if selection := pkg.TypesInfo.Selections[typed]; selection != nil {
453
+ return selection.Obj()
454
+ }
455
+ return pkg.TypesInfo.Uses[typed.Sel]
456
+ }
457
+ return nil
458
+ }
459
+
460
+ func (o *SemanticModelOwner) collectFunctionFacts(
461
+ model *SemanticModel,
462
+ pkg *packages.Package,
463
+ file *ast.File,
464
+ ) []Diagnostic {
465
+ var diagnostics []Diagnostic
466
+ for _, decl := range file.Decls {
467
+ fnDecl, ok := decl.(*ast.FuncDecl)
468
+ if !ok || fnDecl.Body == nil {
469
+ continue
470
+ }
471
+ fnObj, _ := pkg.TypesInfo.Defs[fnDecl.Name].(*types.Func)
472
+ semFn := model.functions[fnObj]
473
+ if semFn == nil {
474
+ continue
475
+ }
476
+ ast.Inspect(fnDecl.Body, func(node ast.Node) bool {
477
+ switch typed := node.(type) {
478
+ case *ast.FuncLit:
479
+ return false
480
+ case *ast.SendStmt:
481
+ markFunctionAsync(semFn, "channel-send")
482
+ case *ast.SelectStmt:
483
+ markFunctionAsync(semFn, "select")
484
+ case *ast.UnaryExpr:
485
+ if typed.Op == token.ARROW {
486
+ markFunctionAsync(semFn, "channel-receive")
487
+ }
488
+ case *ast.CallExpr:
489
+ if called := calledFunction(pkg, typed.Fun); called != nil {
490
+ semFn.calls[called] = true
491
+ }
492
+ async, err := o.isOverrideAsyncCall(pkg, typed.Fun)
493
+ if err != nil {
494
+ diagnostics = append(diagnostics, Diagnostic{
495
+ Severity: DiagnosticSeverityError,
496
+ Code: "goscript/overrides:metadata",
497
+ Message: "failed to read override metadata",
498
+ Detail: err.Error(),
499
+ })
500
+ return false
501
+ }
502
+ if async {
503
+ markFunctionAsync(semFn, "override")
504
+ }
505
+ }
506
+ return true
507
+ })
508
+ }
509
+ return diagnostics
510
+ }
511
+
512
+ func (o *SemanticModelOwner) isOverrideAsyncCall(pkg *packages.Package, expr ast.Expr) (bool, error) {
513
+ selector, ok := expr.(*ast.SelectorExpr)
514
+ if !ok {
515
+ return false, nil
516
+ }
517
+ selection := pkg.TypesInfo.Selections[selector]
518
+ if selection == nil {
519
+ return false, nil
520
+ }
521
+ method, _ := selection.Obj().(*types.Func)
522
+ if method == nil {
523
+ return false, nil
524
+ }
525
+ named := receiverNamedType(selection.Recv())
526
+ if named == nil || named.Obj() == nil || named.Obj().Pkg() == nil {
527
+ return false, nil
528
+ }
529
+ methodKey := named.Obj().Name() + "." + method.Name()
530
+ return o.overrideOwner.IsMethodAsync(named.Obj().Pkg().Path(), methodKey)
531
+ }
532
+
533
+ func calledFunction(pkg *packages.Package, expr ast.Expr) *types.Func {
534
+ switch typed := expr.(type) {
535
+ case *ast.Ident:
536
+ fn, _ := pkg.TypesInfo.Uses[typed].(*types.Func)
537
+ return fn
538
+ case *ast.SelectorExpr:
539
+ if selection := pkg.TypesInfo.Selections[typed]; selection != nil {
540
+ fn, _ := selection.Obj().(*types.Func)
541
+ return fn
542
+ }
543
+ fn, _ := pkg.TypesInfo.Uses[typed.Sel].(*types.Func)
544
+ return fn
545
+ }
546
+ return nil
547
+ }
548
+
549
+ func receiverNamedType(typ types.Type) *types.Named {
550
+ for {
551
+ pointer, ok := typ.(*types.Pointer)
552
+ if !ok {
553
+ break
554
+ }
555
+ typ = pointer.Elem()
556
+ }
557
+ named, _ := typ.(*types.Named)
558
+ return named
559
+ }
560
+
561
+ func (o *SemanticModelOwner) propagateFunctionAsync(model *SemanticModel) {
562
+ changed := true
563
+ for changed {
564
+ changed = false
565
+ for _, semFn := range model.functions {
566
+ for called := range semFn.calls {
567
+ calledFn := model.functions[called]
568
+ if calledFn != nil && calledFn.async {
569
+ if markFunctionAsync(semFn, "call:"+called.FullName()) {
570
+ changed = true
571
+ }
572
+ }
573
+ }
574
+ }
575
+ }
576
+ }
577
+
578
+ func markFunctionAsync(fn *semanticFunction, reason string) bool {
579
+ if fn == nil {
580
+ return false
581
+ }
582
+ changed := !fn.async
583
+ fn.async = true
584
+ if slices.Contains(fn.asyncReasons, reason) {
585
+ return changed
586
+ }
587
+ fn.asyncReasons = append(fn.asyncReasons, reason)
588
+ return true
589
+ }
590
+
591
+ func (o *SemanticModelOwner) resolveInterfaceImplementations(model *SemanticModel) {
592
+ var interfaces []*types.Named
593
+ var concretes []*types.Named
594
+ for named, semType := range model.types {
595
+ if semType.isInterface {
596
+ interfaces = append(interfaces, named)
597
+ continue
598
+ }
599
+ concretes = append(concretes, named)
600
+ }
601
+ sortNamedTypes(interfaces)
602
+ sortNamedTypes(concretes)
603
+
604
+ for _, ifaceNamed := range interfaces {
605
+ iface, _ := ifaceNamed.Underlying().(*types.Interface)
606
+ if iface == nil {
607
+ continue
608
+ }
609
+ iface.Complete()
610
+ for _, concrete := range concretes {
611
+ o.addInterfaceImplementation(model, concrete, ifaceNamed, iface, false)
612
+ o.addInterfaceImplementation(model, concrete, ifaceNamed, iface, true)
613
+ }
614
+ }
615
+ }
616
+
617
+ func (o *SemanticModelOwner) addInterfaceImplementation(
618
+ model *SemanticModel,
619
+ concrete *types.Named,
620
+ ifaceNamed *types.Named,
621
+ iface *types.Interface,
622
+ pointer bool,
623
+ ) {
624
+ var receiver types.Type = concrete
625
+ if pointer {
626
+ receiver = types.NewPointer(concrete)
627
+ }
628
+ if !types.Implements(receiver, iface) {
629
+ return
630
+ }
631
+
632
+ implementation := semanticInterfaceImplementation{
633
+ typ: concrete,
634
+ iface: ifaceNamed,
635
+ pointer: pointer,
636
+ asyncMethods: make(map[string]bool),
637
+ }
638
+ for ifaceMethod := range iface.Methods() {
639
+ obj, _, _ := types.LookupFieldOrMethod(receiver, true, concrete.Obj().Pkg(), ifaceMethod.Name())
640
+ implMethod, _ := obj.(*types.Func)
641
+ if implMethod == nil {
642
+ continue
643
+ }
644
+ implFn := model.functions[implMethod]
645
+ if implFn != nil && implFn.async {
646
+ implementation.asyncMethods[ifaceMethod.Name()] = true
647
+ if ifaceFn := model.functions[ifaceMethod]; ifaceFn != nil {
648
+ markFunctionAsync(ifaceFn, "interface-implementation")
649
+ }
650
+ }
651
+ }
652
+ for methodName, async := range implementation.asyncMethods {
653
+ if !async {
654
+ continue
655
+ }
656
+ obj, _, _ := types.LookupFieldOrMethod(receiver, true, concrete.Obj().Pkg(), methodName)
657
+ implMethod, _ := obj.(*types.Func)
658
+ markFunctionAsync(model.functions[implMethod], "interface-method")
659
+ }
660
+ model.interfaceImplementations = append(model.interfaceImplementations, implementation)
661
+ }
662
+
663
+ func sortNamedTypes(named []*types.Named) {
664
+ slices.SortFunc(named, func(a, b *types.Named) int {
665
+ return cmp.Compare(namedTypeKey(a), namedTypeKey(b))
666
+ })
667
+ }
668
+
669
+ func namedTypeKey(named *types.Named) string {
670
+ if named == nil || named.Obj() == nil {
671
+ return ""
672
+ }
673
+ if named.Obj().Pkg() == nil {
674
+ return named.Obj().Name()
675
+ }
676
+ return named.Obj().Pkg().Path() + "." + named.Obj().Name()
677
+ }
678
+
679
+ func (o *SemanticModelOwner) recordTypeAssertion(
680
+ semPkg *semanticPackage,
681
+ pkg *packages.Package,
682
+ expr *ast.TypeAssertExpr,
683
+ ) {
684
+ if expr.Type == nil {
685
+ return
686
+ }
687
+ semPkg.typeAssertions = append(semPkg.typeAssertions, semanticTypeAssertion{
688
+ position: sourcePos(pkg, expr.Pos()),
689
+ source: pkg.TypesInfo.TypeOf(expr.X),
690
+ target: pkg.TypesInfo.TypeOf(expr.Type),
691
+ })
692
+ }
693
+
694
+ func (o *SemanticModelOwner) recordValueSpecNilFacts(
695
+ semPkg *semanticPackage,
696
+ pkg *packages.Package,
697
+ spec *ast.ValueSpec,
698
+ ) {
699
+ for idx, value := range spec.Values {
700
+ if idx >= len(spec.Names) {
701
+ continue
702
+ }
703
+ obj := pkg.TypesInfo.Defs[spec.Names[idx]]
704
+ if obj == nil {
705
+ continue
706
+ }
707
+ o.recordNilFacts(semPkg, pkg, obj.Type(), value)
708
+ }
709
+ }
710
+
711
+ func (o *SemanticModelOwner) recordAssignNilFacts(
712
+ semPkg *semanticPackage,
713
+ pkg *packages.Package,
714
+ stmt *ast.AssignStmt,
715
+ ) {
716
+ for idx, rhs := range stmt.Rhs {
717
+ if idx >= len(stmt.Lhs) {
718
+ continue
719
+ }
720
+ targetType := pkg.TypesInfo.TypeOf(stmt.Lhs[idx])
721
+ o.recordNilFacts(semPkg, pkg, targetType, rhs)
722
+ }
723
+ }
724
+
725
+ func (o *SemanticModelOwner) recordNilFacts(
726
+ semPkg *semanticPackage,
727
+ pkg *packages.Package,
728
+ targetType types.Type,
729
+ expr ast.Expr,
730
+ ) {
731
+ position := sourcePos(pkg, expr.Pos())
732
+ if isNilIdent(expr) {
733
+ if kind := nilFactKind(targetType); kind != "" {
734
+ semPkg.nilFacts = append(semPkg.nilFacts, semanticNilFact{
735
+ position: position,
736
+ kind: kind,
737
+ typ: targetType,
738
+ })
739
+ }
740
+ return
741
+ }
742
+
743
+ exprType := pkg.TypesInfo.TypeOf(expr)
744
+ if isInterfaceType(targetType) && !isInterfaceType(exprType) && isNilableType(exprType) {
745
+ semPkg.nilFacts = append(semPkg.nilFacts, semanticNilFact{
746
+ position: position,
747
+ kind: "typed-nil-interface-risk",
748
+ typ: exprType,
749
+ })
750
+ }
751
+ }
752
+
753
+ func isNilIdent(expr ast.Expr) bool {
754
+ ident, ok := expr.(*ast.Ident)
755
+ return ok && ident.Name == "nil"
756
+ }
757
+
758
+ func nilFactKind(typ types.Type) string {
759
+ switch {
760
+ case isInterfaceType(typ):
761
+ return "nil-interface"
762
+ case isNilableType(typ):
763
+ return "typed-nil"
764
+ default:
765
+ return ""
766
+ }
767
+ }
768
+
769
+ func isInterfaceType(typ types.Type) bool {
770
+ if typ == nil {
771
+ return false
772
+ }
773
+ _, ok := types.Unalias(typ).Underlying().(*types.Interface)
774
+ return ok
775
+ }
776
+
777
+ func isNilableType(typ types.Type) bool {
778
+ if typ == nil {
779
+ return false
780
+ }
781
+ switch types.Unalias(typ).Underlying().(type) {
782
+ case *types.Pointer, *types.Slice, *types.Map, *types.Chan, *types.Signature, *types.Interface:
783
+ return true
784
+ default:
785
+ return false
786
+ }
787
+ }
788
+
789
+ func (o *SemanticModelOwner) recordGeneratedImports(
790
+ model *SemanticModel,
791
+ semPkg *semanticPackage,
792
+ file string,
793
+ currentPkg string,
794
+ typ types.Type,
795
+ ) {
796
+ if file == "" || typ == nil {
797
+ return
798
+ }
799
+ o.recordTypeImports(model, semPkg, file, currentPkg, typ, make(map[types.Type]bool))
800
+ }
801
+
802
+ func (o *SemanticModelOwner) recordTypeImports(
803
+ model *SemanticModel,
804
+ semPkg *semanticPackage,
805
+ file string,
806
+ currentPkg string,
807
+ typ types.Type,
808
+ seen map[types.Type]bool,
809
+ ) {
810
+ if typ == nil || seen[typ] {
811
+ return
812
+ }
813
+ seen[typ] = true
814
+
815
+ switch typed := types.Unalias(typ).(type) {
816
+ case *types.Named:
817
+ if obj := typed.Obj(); obj != nil && obj.Pkg() != nil && obj.Pkg().Path() != currentPkg {
818
+ addGeneratedImport(model, semPkg, file, obj.Pkg().Path())
819
+ }
820
+ if args := typed.TypeArgs(); args != nil {
821
+ for t := range args.Types() {
822
+ o.recordTypeImports(model, semPkg, file, currentPkg, t, seen)
823
+ }
824
+ }
825
+ o.recordTypeImports(model, semPkg, file, currentPkg, typed.Underlying(), seen)
826
+ case *types.Pointer:
827
+ o.recordTypeImports(model, semPkg, file, currentPkg, typed.Elem(), seen)
828
+ case *types.Slice:
829
+ o.recordTypeImports(model, semPkg, file, currentPkg, typed.Elem(), seen)
830
+ case *types.Array:
831
+ o.recordTypeImports(model, semPkg, file, currentPkg, typed.Elem(), seen)
832
+ case *types.Map:
833
+ o.recordTypeImports(model, semPkg, file, currentPkg, typed.Key(), seen)
834
+ o.recordTypeImports(model, semPkg, file, currentPkg, typed.Elem(), seen)
835
+ case *types.Chan:
836
+ o.recordTypeImports(model, semPkg, file, currentPkg, typed.Elem(), seen)
837
+ case *types.Signature:
838
+ o.recordTupleImports(model, semPkg, file, currentPkg, typed.Params(), seen)
839
+ o.recordTupleImports(model, semPkg, file, currentPkg, typed.Results(), seen)
840
+ case *types.Struct:
841
+ for field := range typed.Fields() {
842
+ o.recordTypeImports(model, semPkg, file, currentPkg, field.Type(), seen)
843
+ }
844
+ case *types.Interface:
845
+ typed.Complete()
846
+ for method := range typed.Methods() {
847
+ o.recordTypeImports(model, semPkg, file, currentPkg, method.Type(), seen)
848
+ }
849
+ for etyp := range typed.EmbeddedTypes() {
850
+ o.recordTypeImports(model, semPkg, file, currentPkg, etyp, seen)
851
+ }
852
+ }
853
+ }
854
+
855
+ func (o *SemanticModelOwner) recordTupleImports(
856
+ model *SemanticModel,
857
+ semPkg *semanticPackage,
858
+ file string,
859
+ currentPkg string,
860
+ tuple *types.Tuple,
861
+ seen map[types.Type]bool,
862
+ ) {
863
+ if tuple == nil {
864
+ return
865
+ }
866
+ for v := range tuple.Variables() {
867
+ o.recordTypeImports(model, semPkg, file, currentPkg, v.Type(), seen)
868
+ }
869
+ }
870
+
871
+ func addGeneratedImport(model *SemanticModel, semPkg *semanticPackage, file string, pkgPath string) {
872
+ if model.generatedImports[file] == nil {
873
+ model.generatedImports[file] = make(map[string]bool)
874
+ }
875
+ model.generatedImports[file][pkgPath] = true
876
+ if semPkg.generatedImports[file] == nil {
877
+ semPkg.generatedImports[file] = make(map[string]bool)
878
+ }
879
+ semPkg.generatedImports[file][pkgPath] = true
880
+ }
881
+
882
+ func zeroValueKind(typ types.Type) string {
883
+ if typ == nil {
884
+ return "unknown"
885
+ }
886
+ switch typed := types.Unalias(typ).Underlying().(type) {
887
+ case *types.Basic:
888
+ switch {
889
+ case typed.Info()&types.IsBoolean != 0:
890
+ return "false"
891
+ case typed.Info()&types.IsString != 0:
892
+ return "\"\""
893
+ case typed.Info()&types.IsNumeric != 0:
894
+ return "0"
895
+ default:
896
+ return "nil"
897
+ }
898
+ case *types.Pointer, *types.Slice, *types.Map, *types.Chan, *types.Signature, *types.Interface:
899
+ return "nil"
900
+ case *types.Array:
901
+ return "array-zero"
902
+ case *types.Struct:
903
+ return "struct-zero"
904
+ default:
905
+ return "unknown"
906
+ }
907
+ }
908
+
909
+ func sourcePos(pkg *packages.Package, pos token.Pos) sourcePosition {
910
+ if pkg == nil || pkg.Fset == nil || !pos.IsValid() {
911
+ return sourcePosition{}
912
+ }
913
+ return sourcePosFromTokenPosition(pkg.Fset.Position(pos))
914
+ }
915
+
916
+ func sourcePosFromTokenPosition(pos token.Position) sourcePosition {
917
+ return sourcePosition{
918
+ file: pos.Filename,
919
+ line: pos.Line,
920
+ column: pos.Column,
921
+ }
922
+ }