goscript 0.0.84 → 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 (188) 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 +23 -0
  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 +15 -1
  39. package/dist/gs/builtin/hostio.js +134 -49
  40. package/dist/gs/builtin/hostio.js.map +1 -1
  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/slice.d.ts +1 -1
  45. package/dist/gs/builtin/slice.js.map +1 -1
  46. package/dist/gs/builtin/type.d.ts +11 -0
  47. package/dist/gs/builtin/type.js +55 -1
  48. package/dist/gs/builtin/type.js.map +1 -1
  49. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  50. package/dist/gs/bytes/bytes.gs.js.map +1 -1
  51. package/dist/gs/bytes/reader.gs.js.map +1 -1
  52. package/dist/gs/context/context.js.map +1 -1
  53. package/dist/gs/crypto/rand/index.d.ts +5 -0
  54. package/dist/gs/crypto/rand/index.js +77 -0
  55. package/dist/gs/crypto/rand/index.js.map +1 -0
  56. package/dist/gs/encoding/json/index.d.ts +3 -0
  57. package/dist/gs/encoding/json/index.js +160 -0
  58. package/dist/gs/encoding/json/index.js.map +1 -0
  59. package/dist/gs/fmt/fmt.js.map +1 -1
  60. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
  61. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
  62. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  63. package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
  64. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  65. package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
  66. package/dist/gs/go/scanner/index.d.ts +29 -0
  67. package/dist/gs/go/scanner/index.js +120 -0
  68. package/dist/gs/go/scanner/index.js.map +1 -0
  69. package/dist/gs/go/token/index.d.ts +31 -0
  70. package/dist/gs/go/token/index.js +82 -0
  71. package/dist/gs/go/token/index.js.map +1 -0
  72. package/dist/gs/internal/abi/index.js.map +1 -1
  73. package/dist/gs/io/fs/fs.js.map +1 -1
  74. package/dist/gs/io/fs/readdir.js.map +1 -1
  75. package/dist/gs/io/fs/readfile.js.map +1 -1
  76. package/dist/gs/io/fs/stat.js.map +1 -1
  77. package/dist/gs/io/fs/sub.js.map +1 -1
  78. package/dist/gs/io/io.js.map +1 -1
  79. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  80. package/dist/gs/os/error.gs.js +2 -4
  81. package/dist/gs/os/error.gs.js.map +1 -1
  82. package/dist/gs/os/exec.gs.js.map +1 -1
  83. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  84. package/dist/gs/os/rawconn_js.gs.js.map +1 -1
  85. package/dist/gs/os/root_js.gs.js.map +1 -1
  86. package/dist/gs/os/tempfile.gs.js +66 -9
  87. package/dist/gs/os/tempfile.gs.js.map +1 -1
  88. package/dist/gs/os/types.gs.js.map +1 -1
  89. package/dist/gs/os/types_js.gs.js +9 -9
  90. package/dist/gs/os/types_js.gs.js.map +1 -1
  91. package/dist/gs/os/types_unix.gs.js.map +1 -1
  92. package/dist/gs/path/filepath/match.js.map +1 -1
  93. package/dist/gs/path/match.js.map +1 -1
  94. package/dist/gs/path/path.js.map +1 -1
  95. package/dist/gs/reflect/index.d.ts +2 -2
  96. package/dist/gs/reflect/index.js +1 -1
  97. package/dist/gs/reflect/index.js.map +1 -1
  98. package/dist/gs/reflect/map.js.map +1 -1
  99. package/dist/gs/reflect/type.d.ts +2 -1
  100. package/dist/gs/reflect/type.js +85 -14
  101. package/dist/gs/reflect/type.js.map +1 -1
  102. package/dist/gs/reflect/types.js.map +1 -1
  103. package/dist/gs/reflect/visiblefields.js.map +1 -1
  104. package/dist/gs/runtime/runtime.js.map +1 -1
  105. package/dist/gs/sort/sort.gs.js.map +1 -1
  106. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  107. package/dist/gs/strconv/quote.gs.js.map +1 -1
  108. package/dist/gs/strings/builder.js.map +1 -1
  109. package/dist/gs/strings/reader.js.map +1 -1
  110. package/dist/gs/strings/replace.js.map +1 -1
  111. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  112. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  113. package/dist/gs/sync/sync.d.ts +1 -0
  114. package/dist/gs/sync/sync.js +12 -0
  115. package/dist/gs/sync/sync.js.map +1 -1
  116. package/dist/gs/time/time.js.map +1 -1
  117. package/dist/gs/unicode/unicode.js.map +1 -1
  118. package/go.mod +2 -2
  119. package/gs/builtin/builtin.ts +27 -0
  120. package/gs/builtin/hostio.test.ts +177 -0
  121. package/gs/builtin/hostio.ts +171 -56
  122. package/gs/builtin/index.ts +1 -0
  123. package/gs/builtin/runtime-contract.test.ts +230 -0
  124. package/gs/builtin/type.ts +84 -1
  125. package/gs/crypto/rand/index.test.ts +32 -0
  126. package/gs/crypto/rand/index.ts +90 -0
  127. package/gs/crypto/rand/meta.json +5 -0
  128. package/gs/encoding/json/index.test.ts +65 -0
  129. package/gs/encoding/json/index.ts +186 -0
  130. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
  131. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
  132. package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
  133. package/gs/go/scanner/index.test.ts +50 -0
  134. package/gs/go/scanner/index.ts +157 -0
  135. package/gs/go/token/index.test.ts +21 -0
  136. package/gs/go/token/index.ts +120 -0
  137. package/gs/os/file_unix_js.test.ts +50 -0
  138. package/gs/os/meta.json +1 -2
  139. package/gs/os/tempfile.gs.test.ts +85 -0
  140. package/gs/os/tempfile.gs.ts +71 -11
  141. package/gs/os/types_js.gs.ts +9 -9
  142. package/gs/reflect/index.ts +1 -1
  143. package/gs/reflect/type.ts +106 -17
  144. package/gs/reflect/typefor.test.ts +75 -0
  145. package/gs/sync/sync.test.ts +24 -0
  146. package/gs/sync/sync.ts +12 -0
  147. package/package.json +13 -13
  148. package/compiler/analysis.go +0 -3475
  149. package/compiler/analysis_test.go +0 -338
  150. package/compiler/assignment.go +0 -580
  151. package/compiler/builtin_test.go +0 -92
  152. package/compiler/code-writer.go +0 -115
  153. package/compiler/compiler_test.go +0 -149
  154. package/compiler/composite-lit.go +0 -779
  155. package/compiler/config_test.go +0 -62
  156. package/compiler/constraint.go +0 -86
  157. package/compiler/decl.go +0 -801
  158. package/compiler/expr-call-async.go +0 -188
  159. package/compiler/expr-call-builtins.go +0 -208
  160. package/compiler/expr-call-helpers.go +0 -382
  161. package/compiler/expr-call-make.go +0 -318
  162. package/compiler/expr-call-type-conversion.go +0 -520
  163. package/compiler/expr-call.go +0 -413
  164. package/compiler/expr-selector.go +0 -343
  165. package/compiler/expr-star.go +0 -82
  166. package/compiler/expr-type.go +0 -442
  167. package/compiler/expr-value.go +0 -89
  168. package/compiler/expr.go +0 -773
  169. package/compiler/field.go +0 -183
  170. package/compiler/gs_dependencies_test.go +0 -298
  171. package/compiler/lit.go +0 -322
  172. package/compiler/output.go +0 -72
  173. package/compiler/primitive.go +0 -149
  174. package/compiler/protobuf.go +0 -697
  175. package/compiler/sanitize.go +0 -100
  176. package/compiler/spec-struct.go +0 -995
  177. package/compiler/spec-value.go +0 -540
  178. package/compiler/spec.go +0 -725
  179. package/compiler/stmt-assign.go +0 -664
  180. package/compiler/stmt-for.go +0 -266
  181. package/compiler/stmt-range.go +0 -475
  182. package/compiler/stmt-select.go +0 -262
  183. package/compiler/stmt-type-switch.go +0 -147
  184. package/compiler/stmt.go +0 -1308
  185. package/compiler/type-assert.go +0 -386
  186. package/compiler/type-info.go +0 -156
  187. package/compiler/type-utils.go +0 -207
  188. 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
+ }