goscript 0.2.0 → 0.2.1

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 (50) hide show
  1. package/cmd/goscript-wasm/main.go +38 -6
  2. package/compiler/diagnostic.go +104 -12
  3. package/compiler/diagnostic_test.go +106 -0
  4. package/compiler/gotest/runner.go +1 -17
  5. package/compiler/gotest/runner_test.go +20 -0
  6. package/compiler/index.test.ts +23 -0
  7. package/compiler/lowered-program.go +9 -7
  8. package/compiler/lowering.go +359 -72
  9. package/compiler/lowering_bench_test.go +1 -0
  10. package/compiler/lowering_internal_test.go +18 -0
  11. package/compiler/protobuf-ts-binding.go +65 -12
  12. package/compiler/protobuf-ts-binding_test.go +230 -0
  13. package/compiler/runtime-contract.go +4 -0
  14. package/compiler/runtime-contract_test.go +2 -0
  15. package/compiler/service.go +1 -0
  16. package/compiler/skeleton_test.go +56 -2
  17. package/compiler/wasm/compile_test.go +37 -4
  18. package/compiler/wasm-api.go +57 -7
  19. package/dist/gs/builtin/hostio.js +5 -0
  20. package/dist/gs/builtin/hostio.js.map +1 -1
  21. package/dist/gs/builtin/slice.d.ts +11 -1
  22. package/dist/gs/builtin/slice.js +158 -2
  23. package/dist/gs/builtin/slice.js.map +1 -1
  24. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
  25. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +30 -5
  26. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  27. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
  28. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
  29. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
  30. package/dist/gs/internal/byteorder/index.js +2 -2
  31. package/dist/gs/internal/byteorder/index.js.map +1 -1
  32. package/dist/gs/reflect/type.js +57 -0
  33. package/dist/gs/reflect/type.js.map +1 -1
  34. package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
  35. package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
  36. package/gs/builtin/hostio.test.ts +16 -0
  37. package/gs/builtin/hostio.ts +7 -0
  38. package/gs/builtin/runtime-contract.test.ts +28 -0
  39. package/gs/builtin/slice.ts +225 -20
  40. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +162 -0
  41. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +41 -5
  42. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
  43. package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
  44. package/gs/internal/byteorder/index.test.ts +2 -2
  45. package/gs/internal/byteorder/index.ts +2 -2
  46. package/gs/reflect/type.ts +64 -0
  47. package/gs/reflect/typefor.test.ts +21 -1
  48. package/gs/sync/atomic/doc_64.gs.ts +6 -7
  49. package/gs/sync/atomic/doc_64.test.ts +43 -0
  50. package/package.json +1 -1
@@ -28,6 +28,8 @@ type LoweringOwner struct {
28
28
  type LoweringOptions struct {
29
29
  // SourceRoot is the request source root that may contain sibling protobuf TypeScript files.
30
30
  SourceRoot string
31
+ // DisplayRoot is the request root used to format source file names in diagnostics.
32
+ DisplayRoot string
31
33
  // OutputPath is the TypeScript output root used for generated relative imports.
32
34
  OutputPath string
33
35
  // ProtobufTypeScriptBinding binds .pb.go files to sibling .pb.ts files.
@@ -141,6 +143,7 @@ func (o *LoweringOwner) lowerPackage(
141
143
  lazyPackageVarsByPkg,
142
144
  runtimeMethodSets,
143
145
  protobufAdapter,
146
+ options.DisplayRoot,
144
147
  )
145
148
  diagnostics = append(diagnostics, fileDiagnostics...)
146
149
  rewriteProtobufTypeScriptBindingFile(loweredFile, binding)
@@ -160,6 +163,7 @@ func (o *LoweringOwner) lowerPackage(
160
163
  lazyPackageVarsByPkg,
161
164
  runtimeMethodSets,
162
165
  false,
166
+ options.DisplayRoot,
163
167
  )
164
168
  diagnostics = append(diagnostics, fileDiagnostics...)
165
169
  if loweredFile != nil {
@@ -195,6 +199,7 @@ func (o *LoweringOwner) lowerFile(
195
199
  lazyPackageVarsByPkg map[string]map[types.Object]bool,
196
200
  runtimeMethodSets runtimeMethodSetCache,
197
201
  protobufTypeScriptAdapter bool,
202
+ displayRoot string,
198
203
  ) (*loweredFile, []Diagnostic) {
199
204
  associatedMethods := o.methodDeclsForFileTypes(semPkg, file)
200
205
  relevantImportFiles := map[string]bool{sourcePath: true}
@@ -315,6 +320,7 @@ func (o *LoweringOwner) lowerFile(
315
320
  tempNames: newTempNameOwner(),
316
321
  topLevel: true,
317
322
  protobufTSAdapter: protobufTypeScriptAdapter,
323
+ displayRoot: displayRoot,
318
324
  }
319
325
  var diagnostics []Diagnostic
320
326
  var packageInitCalls []string
@@ -1120,6 +1126,28 @@ type lowerFileContext struct {
1120
1126
  switchBreak bool
1121
1127
  topLevel bool
1122
1128
  protobufTSAdapter bool
1129
+ displayRoot string
1130
+ }
1131
+
1132
+ func (ctx lowerFileContext) diagnosticPosition(pos token.Pos) *DiagnosticPosition {
1133
+ if ctx.semPkg == nil {
1134
+ return nil
1135
+ }
1136
+ return diagnosticPositionFromSource(sourcePos(ctx.semPkg.source, pos), ctx.displayRoot)
1137
+ }
1138
+
1139
+ func loweringUnsupportedAt(ctx lowerFileContext, node ast.Node, kind string, subject string, detail string) Diagnostic {
1140
+ diag := loweringUnsupported(kind, subject, detail)
1141
+ if node != nil {
1142
+ diag.Position = ctx.diagnosticPosition(node.Pos())
1143
+ }
1144
+ return diag
1145
+ }
1146
+
1147
+ func loweringUnsupportedPos(ctx lowerFileContext, pos token.Pos, kind string, subject string, detail string) Diagnostic {
1148
+ diag := loweringUnsupported(kind, subject, detail)
1149
+ diag.Position = ctx.diagnosticPosition(pos)
1150
+ return diag
1123
1151
  }
1124
1152
 
1125
1153
  type tempNameOwner struct {
@@ -1208,7 +1236,7 @@ func (o *LoweringOwner) lowerDecl(ctx lowerFileContext, decl ast.Decl) ([]lowere
1208
1236
  if receiver := receiverNamedTypeFromDecl(ctx, typed); receiver != nil && namedStructType(receiver) == nil {
1209
1237
  fn, diagnostics := o.lowerNamedReceiverMethodDecl(ctx, typed, receiver)
1210
1238
  if fn == nil {
1211
- return nil, []Diagnostic{loweringUnsupported("function", typed.Name.Name, "missing type information")}
1239
+ return nil, []Diagnostic{loweringUnsupportedAt(ctx, typed, "function", typed.Name.Name, "missing type information")}
1212
1240
  }
1213
1241
  return []loweredDecl{{function: fn}}, diagnostics
1214
1242
  }
@@ -1216,11 +1244,11 @@ func (o *LoweringOwner) lowerDecl(ctx lowerFileContext, decl ast.Decl) ([]lowere
1216
1244
  }
1217
1245
  fn, diagnostics := o.lowerFuncDecl(ctx, typed)
1218
1246
  if fn == nil {
1219
- return nil, []Diagnostic{loweringUnsupported("function", typed.Name.Name, "missing type information")}
1247
+ return nil, []Diagnostic{loweringUnsupportedAt(ctx, typed, "function", typed.Name.Name, "missing type information")}
1220
1248
  }
1221
1249
  return []loweredDecl{{function: fn}}, diagnostics
1222
1250
  default:
1223
- return nil, []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported declaration kind")}
1251
+ return nil, []Diagnostic{loweringUnsupportedAt(ctx, decl, "declaration", ctx.semPkg.pkgPath, "unsupported declaration kind")}
1224
1252
  }
1225
1253
  }
1226
1254
 
@@ -1268,7 +1296,7 @@ func (o *LoweringOwner) lowerGenDecl(ctx lowerFileContext, decl *ast.GenDecl) ([
1268
1296
  value = o.lowerValueForTarget(ctx, typed.Values[idx], obj.Type(), lowered)
1269
1297
  value = o.lowerTopLevelInitializerValue(ctx, typed.Values[idx], value)
1270
1298
  } else if len(embedPatterns) != 0 {
1271
- embedded, embedDiagnostics := o.lowerGoEmbedValue(ctx, obj.Type(), embedPatterns)
1299
+ embedded, embedDiagnostics := o.lowerGoEmbedValue(ctx, typed.Pos(), obj.Type(), embedPatterns)
1272
1300
  diagnostics = append(diagnostics, embedDiagnostics...)
1273
1301
  if embedded != "" {
1274
1302
  value = embedded
@@ -1359,7 +1387,7 @@ func (o *LoweringOwner) lowerGenDecl(ctx lowerFileContext, decl *ast.GenDecl) ([
1359
1387
  }
1360
1388
  }
1361
1389
  default:
1362
- diagnostics = append(diagnostics, loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported general declaration"))
1390
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, typed, "declaration", ctx.semPkg.pkgPath, "unsupported general declaration"))
1363
1391
  }
1364
1392
  }
1365
1393
  return decls, diagnostics
@@ -2144,16 +2172,17 @@ func goEmbedPatterns(groups ...*ast.CommentGroup) []string {
2144
2172
 
2145
2173
  func (o *LoweringOwner) lowerGoEmbedValue(
2146
2174
  ctx lowerFileContext,
2175
+ diagPos token.Pos,
2147
2176
  typ types.Type,
2148
2177
  patterns []string,
2149
2178
  ) (string, []Diagnostic) {
2150
2179
  if isEmbedFSType(typ) {
2151
- return o.lowerGoEmbedFSValue(ctx, patterns)
2180
+ return o.lowerGoEmbedFSValue(ctx, diagPos, patterns)
2152
2181
  }
2153
2182
  if len(patterns) != 1 {
2154
- return "", []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern list")}
2183
+ return "", []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern list")}
2155
2184
  }
2156
- cleanPattern, diagnostics := cleanGoEmbedFilePattern(ctx, patterns[0])
2185
+ cleanPattern, diagnostics := cleanGoEmbedFilePattern(ctx, diagPos, patterns[0])
2157
2186
  if len(diagnostics) != 0 {
2158
2187
  return "", diagnostics
2159
2188
  }
@@ -2167,7 +2196,7 @@ func (o *LoweringOwner) lowerGoEmbedValue(
2167
2196
  if slice, ok := types.Unalias(typ).Underlying().(*types.Slice); ok && isByteType(slice.Elem()) {
2168
2197
  return byteSliceLiteral(data), nil
2169
2198
  }
2170
- diag := loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed target type")
2199
+ diag := loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed target type")
2171
2200
  diag.Detail = "target type: " + types.TypeString(typ, func(pkg *types.Package) string {
2172
2201
  if pkg == nil {
2173
2202
  return ""
@@ -2177,18 +2206,18 @@ func (o *LoweringOwner) lowerGoEmbedValue(
2177
2206
  return "", []Diagnostic{diag}
2178
2207
  }
2179
2208
 
2180
- func (o *LoweringOwner) lowerGoEmbedFSValue(ctx lowerFileContext, patterns []string) (string, []Diagnostic) {
2209
+ func (o *LoweringOwner) lowerGoEmbedFSValue(ctx lowerFileContext, diagPos token.Pos, patterns []string) (string, []Diagnostic) {
2181
2210
  embedAlias := ctx.importPaths["embed"]
2182
2211
  if embedAlias == "" {
2183
- return "", []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed FS import")}
2212
+ return "", []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed FS import")}
2184
2213
  }
2185
2214
  if len(patterns) == 0 {
2186
- return "", []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern list")}
2215
+ return "", []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern list")}
2187
2216
  }
2188
2217
 
2189
2218
  filesByPath := make(map[string][]byte)
2190
2219
  for _, pattern := range patterns {
2191
- files, diagnostics := expandGoEmbedPattern(ctx, pattern)
2220
+ files, diagnostics := expandGoEmbedPattern(ctx, diagPos, pattern)
2192
2221
  if len(diagnostics) != 0 {
2193
2222
  return "", diagnostics
2194
2223
  }
@@ -2214,25 +2243,25 @@ type goEmbedFile struct {
2214
2243
  data []byte
2215
2244
  }
2216
2245
 
2217
- func cleanGoEmbedFilePattern(ctx lowerFileContext, pattern string) (string, []Diagnostic) {
2218
- cleanPattern, _, diagnostics := cleanGoEmbedPattern(ctx, pattern)
2246
+ func cleanGoEmbedFilePattern(ctx lowerFileContext, diagPos token.Pos, pattern string) (string, []Diagnostic) {
2247
+ cleanPattern, _, diagnostics := cleanGoEmbedPattern(ctx, diagPos, pattern)
2219
2248
  if len(diagnostics) != 0 {
2220
2249
  return "", diagnostics
2221
2250
  }
2222
2251
  if strings.Contains(cleanPattern, "*") {
2223
- return "", []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern")}
2252
+ return "", []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern")}
2224
2253
  }
2225
2254
  info, err := os.Stat(filepath.Join(filepath.Dir(ctx.sourcePath), filepath.FromSlash(cleanPattern)))
2226
2255
  if err != nil {
2227
2256
  return "", []Diagnostic{goEmbedReadDiagnostic(ctx, err)}
2228
2257
  }
2229
2258
  if info.IsDir() {
2230
- return "", []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed directory target")}
2259
+ return "", []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed directory target")}
2231
2260
  }
2232
2261
  return cleanPattern, nil
2233
2262
  }
2234
2263
 
2235
- func cleanGoEmbedPattern(ctx lowerFileContext, pattern string) (string, bool, []Diagnostic) {
2264
+ func cleanGoEmbedPattern(ctx lowerFileContext, diagPos token.Pos, pattern string) (string, bool, []Diagnostic) {
2236
2265
  pattern = strings.Trim(pattern, "`\"")
2237
2266
  all := false
2238
2267
  if strings.HasPrefix(pattern, "all:") {
@@ -2245,13 +2274,13 @@ func cleanGoEmbedPattern(ctx lowerFileContext, pattern string) (string, bool, []
2245
2274
  cleanPattern == "." ||
2246
2275
  cleanPattern == ".." ||
2247
2276
  strings.HasPrefix(cleanPattern, "../") {
2248
- return "", false, []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern")}
2277
+ return "", false, []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern")}
2249
2278
  }
2250
2279
  return cleanPattern, all, nil
2251
2280
  }
2252
2281
 
2253
- func expandGoEmbedPattern(ctx lowerFileContext, pattern string) ([]goEmbedFile, []Diagnostic) {
2254
- cleanPattern, all, diagnostics := cleanGoEmbedPattern(ctx, pattern)
2282
+ func expandGoEmbedPattern(ctx lowerFileContext, diagPos token.Pos, pattern string) ([]goEmbedFile, []Diagnostic) {
2283
+ cleanPattern, all, diagnostics := cleanGoEmbedPattern(ctx, diagPos, pattern)
2255
2284
  if len(diagnostics) != 0 {
2256
2285
  return nil, diagnostics
2257
2286
  }
@@ -2260,17 +2289,17 @@ func expandGoEmbedPattern(ctx lowerFileContext, pattern string) ([]goEmbedFile,
2260
2289
  if strings.Contains(cleanPattern, "*") {
2261
2290
  matches, err := filepath.Glob(filepath.Join(pkgDir, filepath.FromSlash(cleanPattern)))
2262
2291
  if err != nil {
2263
- return nil, []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern")}
2292
+ return nil, []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "unsupported go:embed pattern")}
2264
2293
  }
2265
2294
  if len(matches) == 0 {
2266
- return nil, []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "go:embed pattern matched no files")}
2295
+ return nil, []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "go:embed pattern matched no files")}
2267
2296
  }
2268
2297
  paths = matches
2269
2298
  }
2270
2299
 
2271
2300
  var files []goEmbedFile
2272
2301
  for _, path := range paths {
2273
- collected, diagnostics := collectGoEmbedPath(ctx, pkgDir, path, all)
2302
+ collected, diagnostics := collectGoEmbedPath(ctx, diagPos, pkgDir, path, all)
2274
2303
  if len(diagnostics) != 0 {
2275
2304
  return nil, diagnostics
2276
2305
  }
@@ -2282,7 +2311,7 @@ func expandGoEmbedPattern(ctx lowerFileContext, pattern string) ([]goEmbedFile,
2282
2311
  return files, nil
2283
2312
  }
2284
2313
 
2285
- func collectGoEmbedPath(ctx lowerFileContext, pkgDir, absPath string, all bool) ([]goEmbedFile, []Diagnostic) {
2314
+ func collectGoEmbedPath(ctx lowerFileContext, diagPos token.Pos, pkgDir, absPath string, all bool) ([]goEmbedFile, []Diagnostic) {
2286
2315
  info, err := os.Stat(absPath)
2287
2316
  if err != nil {
2288
2317
  return nil, []Diagnostic{goEmbedReadDiagnostic(ctx, err)}
@@ -2319,7 +2348,7 @@ func collectGoEmbedPath(ctx lowerFileContext, pkgDir, absPath string, all bool)
2319
2348
  return nil, []Diagnostic{goEmbedReadDiagnostic(ctx, err)}
2320
2349
  }
2321
2350
  if len(files) == 0 {
2322
- return nil, []Diagnostic{loweringUnsupported("declaration", ctx.semPkg.pkgPath, "go:embed directory matched no files")}
2351
+ return nil, []Diagnostic{loweringUnsupportedPos(ctx, diagPos, "declaration", ctx.semPkg.pkgPath, "go:embed directory matched no files")}
2323
2352
  }
2324
2353
  return files, nil
2325
2354
  }
@@ -2440,7 +2469,7 @@ func (o *LoweringOwner) lowerInterfaceType(ctx lowerFileContext, semType *semant
2440
2469
  }
2441
2470
  code = code + "\n\n" + o.runtimeOwner.QualifiedHelper(RuntimeHelperRegisterInterfaceType) +
2442
2471
  "(\n\t" + strconv.Quote(runtimeNamedTypeName(semType.named)) +
2443
- ",\n\tnull,\n\t" + o.runtimeMethodSignatures(iface) + "\n)"
2472
+ ",\n\tnull,\n\t" + o.runtimeMethodSignatures(iface) + "\n);"
2444
2473
  return loweredDecl{code: code, typeIndexExport: typeIndexExport, sideEffect: true}
2445
2474
  }
2446
2475
 
@@ -2521,12 +2550,13 @@ func (o *LoweringOwner) runtimeMethodReturns(tuple *types.Tuple, seen map[types.
2521
2550
 
2522
2551
  func (o *LoweringOwner) lowerStructType(ctx lowerFileContext, semType *semanticType) (*loweredStruct, []Diagnostic) {
2523
2552
  lowered := &loweredStruct{
2524
- exported: ctx.topLevel,
2525
- indexExported: ctx.topLevel && ast.IsExported(semType.name),
2526
- name: safeIdentifier(semType.name),
2527
- typeName: runtimeNamedTypeName(semType.named),
2528
- cloneMethod: "clone",
2529
- fields: make([]loweredStructField, 0, len(semType.fields)),
2553
+ exported: ctx.topLevel,
2554
+ indexExported: ctx.topLevel && ast.IsExported(semType.name),
2555
+ protobufPreserveJSON: ctx.protobufTSAdapter && o.protobufTypeScriptAdapterPreserveJSON(ctx, semType, make(map[*types.Named]bool)),
2556
+ name: safeIdentifier(semType.name),
2557
+ typeName: runtimeNamedTypeName(semType.named),
2558
+ cloneMethod: "clone",
2559
+ fields: make([]loweredStructField, 0, len(semType.fields)),
2530
2560
  }
2531
2561
  for idx, field := range semType.fields {
2532
2562
  structValue := isStructValueType(field.typ)
@@ -2569,7 +2599,11 @@ func (o *LoweringOwner) lowerStructType(ctx lowerFileContext, semType *semanticT
2569
2599
  var diagnostics []Diagnostic
2570
2600
  for _, methodDecl := range methodDecls {
2571
2601
  lowerDecl := methodDecl
2572
- if ctx.protobufTSAdapter && protobufTypeScriptBindingReplacesMethodName(methodDecl.Name.Name) {
2602
+ methodSourcePath := sourcePos(ctx.semPkg.source, methodDecl.Pos()).file
2603
+ if ctx.protobufTSAdapter &&
2604
+ methodSourcePath == ctx.sourcePath &&
2605
+ protobufTypeScriptBindingReplacesMethodName(methodDecl.Name.Name) &&
2606
+ !(lowered.protobufPreserveJSON && protobufTypeScriptBindingJSONMethodName(methodDecl.Name.Name)) {
2573
2607
  bodyless := *methodDecl
2574
2608
  bodyless.Body = nil
2575
2609
  lowerDecl = &bodyless
@@ -2590,6 +2624,133 @@ func (o *LoweringOwner) lowerStructType(ctx lowerFileContext, semType *semanticT
2590
2624
  return lowered, diagnostics
2591
2625
  }
2592
2626
 
2627
+ func (o *LoweringOwner) protobufTypeScriptAdapterPreserveJSON(
2628
+ ctx lowerFileContext,
2629
+ semType *semanticType,
2630
+ seen map[*types.Named]bool,
2631
+ ) bool {
2632
+ if semType == nil || semType.named == nil {
2633
+ return false
2634
+ }
2635
+ named := semType.named.Origin()
2636
+ if named == nil || seen[named] {
2637
+ return false
2638
+ }
2639
+ seen[named] = true
2640
+ for _, methodDecl := range o.methodDeclsForType(ctx, named) {
2641
+ if methodDecl == nil || !protobufTypeScriptBindingJSONMethodName(methodDecl.Name.Name) {
2642
+ continue
2643
+ }
2644
+ if sourcePos(ctx.semPkg.source, methodDecl.Pos()).file != ctx.sourcePath {
2645
+ return true
2646
+ }
2647
+ }
2648
+ for _, field := range semType.fields {
2649
+ if o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, field.typ, seen) {
2650
+ return true
2651
+ }
2652
+ }
2653
+ return false
2654
+ }
2655
+
2656
+ func (o *LoweringOwner) protobufTypeScriptAdapterTypeHasCustomJSON(
2657
+ ctx lowerFileContext,
2658
+ typ types.Type,
2659
+ seen map[*types.Named]bool,
2660
+ ) bool {
2661
+ if typ == nil {
2662
+ return false
2663
+ }
2664
+ if alias, ok := typ.(*types.Alias); ok {
2665
+ if o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, alias.Rhs(), seen) {
2666
+ return true
2667
+ }
2668
+ if args := alias.TypeArgs(); args != nil {
2669
+ for t := range args.Types() {
2670
+ if o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, t, seen) {
2671
+ return true
2672
+ }
2673
+ }
2674
+ }
2675
+ return false
2676
+ }
2677
+ if named, ok := types.Unalias(typ).(*types.Named); ok {
2678
+ if o.protobufTypeScriptAdapterNamedTypeHasCustomJSON(ctx, named) {
2679
+ return true
2680
+ }
2681
+ if obj := named.Obj(); obj != nil && obj.Pkg() != nil && obj.Pkg().Path() == ctx.semPkg.pkgPath {
2682
+ semType := ctx.model.types[named]
2683
+ if semType == nil {
2684
+ semType = ctx.model.types[named.Origin()]
2685
+ }
2686
+ if semType != nil && o.protobufTypeScriptAdapterPreserveJSON(ctx, semType, seen) {
2687
+ return true
2688
+ }
2689
+ }
2690
+ origin := named.Origin()
2691
+ if origin != nil {
2692
+ if seen[origin] {
2693
+ return false
2694
+ }
2695
+ seen[origin] = true
2696
+ }
2697
+ if args := named.TypeArgs(); args != nil {
2698
+ for t := range args.Types() {
2699
+ if o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, t, seen) {
2700
+ return true
2701
+ }
2702
+ }
2703
+ }
2704
+ if o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, named.Underlying(), seen) {
2705
+ return true
2706
+ }
2707
+ }
2708
+ switch typed := types.Unalias(typ).Underlying().(type) {
2709
+ case *types.Pointer:
2710
+ return o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, typed.Elem(), seen)
2711
+ case *types.Slice:
2712
+ return o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, typed.Elem(), seen)
2713
+ case *types.Array:
2714
+ return o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, typed.Elem(), seen)
2715
+ case *types.Map:
2716
+ return o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, typed.Key(), seen) ||
2717
+ o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, typed.Elem(), seen)
2718
+ case *types.Struct:
2719
+ for field := range typed.Fields() {
2720
+ if o.protobufTypeScriptAdapterTypeHasCustomJSON(ctx, field.Type(), seen) {
2721
+ return true
2722
+ }
2723
+ }
2724
+ }
2725
+ return false
2726
+ }
2727
+
2728
+ func (o *LoweringOwner) protobufTypeScriptAdapterNamedTypeHasCustomJSON(
2729
+ ctx lowerFileContext,
2730
+ named *types.Named,
2731
+ ) bool {
2732
+ if named == nil {
2733
+ return false
2734
+ }
2735
+ methodSet := types.NewMethodSet(types.NewPointer(named))
2736
+ for _, name := range []string{"MarshalJSON", "MarshalProtoJSON", "UnmarshalJSON", "UnmarshalProtoJSON"} {
2737
+ selection := methodSet.Lookup(nil, name)
2738
+ if selection == nil {
2739
+ continue
2740
+ }
2741
+ method, ok := selection.Obj().(*types.Func)
2742
+ if !ok {
2743
+ continue
2744
+ }
2745
+ sourcePath := sourcePos(ctx.semPkg.source, method.Pos()).file
2746
+ if sourcePath == "" || strings.HasSuffix(sourcePath, ".pb.go") {
2747
+ continue
2748
+ }
2749
+ return true
2750
+ }
2751
+ return false
2752
+ }
2753
+
2593
2754
  func (o *LoweringOwner) lowerEmbeddedMethodForwarders(
2594
2755
  ctx lowerFileContext,
2595
2756
  field semanticField,
@@ -2627,6 +2788,7 @@ func (o *LoweringOwner) lowerEmbeddedMethodForwarders(
2627
2788
  targetType := o.tsEmbeddedForwarderTargetType(ctx, field.typ)
2628
2789
  lowered := loweredFunction{
2629
2790
  async: async,
2791
+ sourcePath: ctx.sourcePath,
2630
2792
  name: methodMemberName(method.Name()),
2631
2793
  runtimeName: method.Name(),
2632
2794
  runtimeSignature: o.runtimeMethodSignature(method, make(map[types.Type]bool)),
@@ -2770,6 +2932,7 @@ func (o *LoweringOwner) lowerNamedReceiverMethodDecl(
2770
2932
  exported: ctx.topLevel,
2771
2933
  indexExported: ctx.topLevel && (ast.IsExported(receiver.Obj().Name()) || ast.IsExported(decl.Name.Name)),
2772
2934
  async: async,
2935
+ sourcePath: sourcePos(ctx.semPkg.source, decl.Pos()).file,
2773
2936
  name: methodFunctionName(receiver, decl.Name.Name),
2774
2937
  result: asyncResultType(result, async),
2775
2938
  deferState: deferState,
@@ -2844,6 +3007,7 @@ func (o *LoweringOwner) lowerFuncDecl(ctx lowerFileContext, decl *ast.FuncDecl)
2844
3007
  indexExported: ctx.topLevel && !blankName && !initFunc && (ast.IsExported(decl.Name.Name) || decl.Name.Name == "main"),
2845
3008
  init: initFunc,
2846
3009
  async: async,
3010
+ sourcePath: sourcePos(ctx.semPkg.source, decl.Pos()).file,
2847
3011
  name: name,
2848
3012
  runtimeName: runtimeName,
2849
3013
  result: asyncResultType(result, async),
@@ -3329,9 +3493,9 @@ func (o *LoweringOwner) lowerStmtInto(ctx lowerFileContext, stmt ast.Stmt, out [
3329
3493
  if ctx.gotoLabels[label] {
3330
3494
  return append(out, loweredStmt{text: "continue " + label}), nil
3331
3495
  }
3332
- return out, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported goto branch to "+label)}
3496
+ return out, []Diagnostic{loweringUnsupportedAt(ctx, typed, "statement", ctx.semPkg.pkgPath, "unsupported goto branch to "+label)}
3333
3497
  default:
3334
- return out, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported labeled branch")}
3498
+ return out, []Diagnostic{loweringUnsupportedAt(ctx, typed, "statement", ctx.semPkg.pkgPath, "unsupported labeled branch")}
3335
3499
  }
3336
3500
  }
3337
3501
  switch typed.Tok {
@@ -3352,12 +3516,12 @@ func (o *LoweringOwner) lowerStmtInto(ctx lowerFileContext, stmt ast.Stmt, out [
3352
3516
  case token.FALLTHROUGH:
3353
3517
  return append(out, loweredStmt{text: "fallthrough"}), nil
3354
3518
  default:
3355
- return out, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported branch")}
3519
+ return out, []Diagnostic{loweringUnsupportedAt(ctx, typed, "statement", ctx.semPkg.pkgPath, "unsupported branch")}
3356
3520
  }
3357
3521
  case *ast.EmptyStmt:
3358
3522
  return out, nil
3359
3523
  default:
3360
- return out, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported statement kind")}
3524
+ return out, []Diagnostic{loweringUnsupportedAt(ctx, typed, "statement", ctx.semPkg.pkgPath, "unsupported statement kind")}
3361
3525
  }
3362
3526
  }
3363
3527
 
@@ -3388,7 +3552,7 @@ func (o *LoweringOwner) lowerElse(ctx lowerFileContext, stmt ast.Stmt) ([]lowere
3388
3552
  case *ast.IfStmt:
3389
3553
  return o.lowerStmt(ctx, typed)
3390
3554
  default:
3391
- return nil, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported else statement")}
3555
+ return nil, []Diagnostic{loweringUnsupportedAt(ctx, typed, "statement", ctx.semPkg.pkgPath, "unsupported else statement")}
3392
3556
  }
3393
3557
  }
3394
3558
 
@@ -5617,7 +5781,7 @@ func (o *LoweringOwner) lowerRangeStmt(ctx lowerFileContext, stmt *ast.RangeStmt
5617
5781
  if isFunctionType(rangeType) {
5618
5782
  signature := rangeFunctionSignature(rangeType)
5619
5783
  if signature == nil {
5620
- return loweredStmt{}, append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported function range signature"))
5784
+ return loweredStmt{}, append(diagnostics, loweringUnsupportedAt(ctx, stmt, "statement", ctx.semPkg.pkgPath, "unsupported function range signature"))
5621
5785
  }
5622
5786
  lowered, funcDiagnostics := o.lowerRangeFuncStmt(ctx, stmt, rangeValue, signature)
5623
5787
  diagnostics = append(diagnostics, funcDiagnostics...)
@@ -5697,7 +5861,7 @@ func (o *LoweringOwner) lowerRangeFuncStmt(
5697
5861
  ) (loweredStmt, []Diagnostic) {
5698
5862
  yieldSignature, ok := types.Unalias(signature.Params().At(0).Type()).Underlying().(*types.Signature)
5699
5863
  if !ok {
5700
- return loweredStmt{}, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported function range yield signature")}
5864
+ return loweredStmt{}, []Diagnostic{loweringUnsupportedAt(ctx, stmt, "statement", ctx.semPkg.pkgPath, "unsupported function range yield signature")}
5701
5865
  }
5702
5866
  keyName := rangeKeyName(stmt.Key)
5703
5867
  valueName := rangeKeyName(stmt.Value)
@@ -5780,7 +5944,7 @@ func (o *LoweringOwner) lowerSelectStmt(ctx lowerFileContext, stmt *ast.SelectSt
5780
5944
  for _, raw := range stmt.Body.List {
5781
5945
  clause, ok := raw.(*ast.CommClause)
5782
5946
  if !ok {
5783
- diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported select clause"))
5947
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, raw, "statement", ctx.semPkg.pkgPath, "unsupported select clause"))
5784
5948
  continue
5785
5949
  }
5786
5950
  switch comm := clause.Comm.(type) {
@@ -5833,7 +5997,7 @@ func (o *LoweringOwner) lowerSelectStmt(ctx lowerFileContext, stmt *ast.SelectSt
5833
5997
  })
5834
5998
  caseID++
5835
5999
  default:
5836
- diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported select communication"))
6000
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, comm, "statement", ctx.semPkg.pkgPath, "unsupported select communication"))
5837
6001
  }
5838
6002
  }
5839
6003
  lowered.returns = selectCasesReturn(lowered.cases)
@@ -5937,7 +6101,7 @@ func (o *LoweringOwner) lowerSelectReceiveComm(
5937
6101
  }
5938
6102
  receive, ok := receiveExpr.(*ast.UnaryExpr)
5939
6103
  if !ok || receive.Op != token.ARROW {
5940
- return "null", nil, []Diagnostic{loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported select receive")}
6104
+ return "null", nil, []Diagnostic{loweringUnsupportedAt(ctx, receiveExpr, "statement", ctx.semPkg.pkgPath, "unsupported select receive")}
5941
6105
  }
5942
6106
  channel, diagnostics := o.lowerExpr(ctx, receive.X)
5943
6107
  if assign == nil {
@@ -5995,7 +6159,7 @@ func (o *LoweringOwner) lowerSwitchStmt(ctx lowerFileContext, stmt *ast.SwitchSt
5995
6159
  for _, raw := range stmt.Body.List {
5996
6160
  clause, ok := raw.(*ast.CaseClause)
5997
6161
  if !ok {
5998
- diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported switch clause"))
6162
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, raw, "statement", ctx.semPkg.pkgPath, "unsupported switch clause"))
5999
6163
  continue
6000
6164
  }
6001
6165
  bodyStmts := clause.Body
@@ -6090,7 +6254,7 @@ func (o *LoweringOwner) lowerTypeSwitchStmt(ctx lowerFileContext, stmt *ast.Type
6090
6254
  valueExpr, varName, varRef, assignDiagnostics := o.lowerTypeSwitchAssign(ctx, stmt.Assign)
6091
6255
  diagnostics = append(diagnostics, assignDiagnostics...)
6092
6256
  if valueExpr == "" {
6093
- return lowered, append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported type switch assignment"))
6257
+ return lowered, append(diagnostics, loweringUnsupportedAt(ctx, stmt.Assign, "statement", ctx.semPkg.pkgPath, "unsupported type switch assignment"))
6094
6258
  }
6095
6259
 
6096
6260
  switchIR := &loweredTypeSwitch{
@@ -6101,7 +6265,7 @@ func (o *LoweringOwner) lowerTypeSwitchStmt(ctx lowerFileContext, stmt *ast.Type
6101
6265
  for _, clauseStmt := range stmt.Body.List {
6102
6266
  clause, ok := clauseStmt.(*ast.CaseClause)
6103
6267
  if !ok {
6104
- diagnostics = append(diagnostics, loweringUnsupported("statement", ctx.semPkg.pkgPath, "unsupported type switch clause"))
6268
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, clauseStmt, "statement", ctx.semPkg.pkgPath, "unsupported type switch clause"))
6105
6269
  continue
6106
6270
  }
6107
6271
  body, bodyDiagnostics := o.lowerStmtList(ctx.withoutRangeBreak().withSwitchBreak(), clause.Body)
@@ -6537,13 +6701,21 @@ func (o *LoweringOwner) lowerExpr(ctx lowerFileContext, expr ast.Expr) (string,
6537
6701
  return lowerPrefixUnaryExpr(typed.Op, value), diagnostics
6538
6702
  }
6539
6703
  if typed.Op == token.XOR {
6540
- if bits, ok := unsignedIntegerBits(ctx.semPkg.source.TypesInfo.TypeOf(typed)); ok && bits <= 32 {
6541
- return o.runtimeOwner.QualifiedHelper(RuntimeHelperUint) +
6542
- "(~" + value + ", " + strconv.Itoa(bits) + ")", diagnostics
6704
+ if bits, ok := unsignedIntegerBits(ctx.semPkg.source.TypesInfo.TypeOf(typed)); ok {
6705
+ if bits <= 32 {
6706
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperUint) +
6707
+ "(~" + value + ", " + strconv.Itoa(bits) + ")", diagnostics
6708
+ }
6709
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperUint64Xor) +
6710
+ "(" + value + ", -1n)", diagnostics
6711
+ }
6712
+ if bits, ok := signedIntegerBits(ctx.semPkg.source.TypesInfo.TypeOf(typed)); ok && bits > 32 {
6713
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperInt64Xor) +
6714
+ "(" + value + ", -1n)", diagnostics
6543
6715
  }
6544
6716
  return "~" + value, diagnostics
6545
6717
  }
6546
- return value, append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported unary operator"))
6718
+ return value, append(diagnostics, loweringUnsupportedAt(ctx, typed, "expression", ctx.semPkg.pkgPath, "unsupported unary operator"))
6547
6719
  case *ast.StarExpr:
6548
6720
  return o.lowerPointerValueExpr(ctx, typed.X)
6549
6721
  case *ast.ParenExpr:
@@ -6567,7 +6739,7 @@ func (o *LoweringOwner) lowerExpr(ctx lowerFileContext, expr ast.Expr) (string,
6567
6739
  case *ast.IndexListExpr:
6568
6740
  return o.lowerExpr(ctx, typed.X)
6569
6741
  default:
6570
- return "undefined", []Diagnostic{loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported expression kind")}
6742
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, typed, "expression", ctx.semPkg.pkgPath, "unsupported expression kind")}
6571
6743
  }
6572
6744
  }
6573
6745
 
@@ -6933,7 +7105,7 @@ func (o *LoweringOwner) lowerCallExpr(ctx lowerFileContext, expr *ast.CallExpr)
6933
7105
  return o.runtimeOwner.QualifiedHelper(RuntimeHelperRecover) + "(" + strings.Join(args, ", ") + ")", diagnostics
6934
7106
  case "close":
6935
7107
  if len(args) != 1 {
6936
- return "undefined", append(diagnostics, loweringUnsupported("call", ctx.semPkg.pkgPath, "close requires one argument"))
7108
+ return "undefined", append(diagnostics, loweringUnsupportedAt(ctx, expr, "call", ctx.semPkg.pkgPath, "close requires one argument"))
6937
7109
  }
6938
7110
  return args[0] + "!.close()", diagnostics
6939
7111
  }
@@ -7030,9 +7202,9 @@ func (o *LoweringOwner) lowerCallExpr(ctx lowerFileContext, expr *ast.CallExpr)
7030
7202
  call := o.lowerCallableExpr(ctx, expr.Fun, callee) + "(" + strings.Join(args, ", ") + ")"
7031
7203
  return o.awaitCallIfNeeded(ctx, expr.Fun, call), append(diagnostics, calleeDiagnostics...)
7032
7204
  }
7033
- return "undefined", append(diagnostics, loweringUnsupported("call", ctx.semPkg.pkgPath, fmt.Sprintf("unsupported call target %T", expr.Fun)))
7205
+ return "undefined", append(diagnostics, loweringUnsupportedAt(ctx, expr.Fun, "call", ctx.semPkg.pkgPath, fmt.Sprintf("unsupported call target %T", expr.Fun)))
7034
7206
  }
7035
- return "undefined", append(diagnostics, loweringUnsupported("call", ctx.semPkg.pkgPath, fmt.Sprintf("unsupported call target %T", expr.Fun)))
7207
+ return "undefined", append(diagnostics, loweringUnsupportedAt(ctx, expr.Fun, "call", ctx.semPkg.pkgPath, fmt.Sprintf("unsupported call target %T", expr.Fun)))
7036
7208
  }
7037
7209
 
7038
7210
  func (o *LoweringOwner) lowerCallableExpr(ctx lowerFileContext, expr ast.Expr, callee string) string {
@@ -7321,11 +7493,11 @@ func unsafePackageFunction(ctx lowerFileContext, expr ast.Expr, name string) boo
7321
7493
 
7322
7494
  func (o *LoweringOwner) lowerMakeExpr(ctx lowerFileContext, expr *ast.CallExpr) (string, []Diagnostic) {
7323
7495
  if len(expr.Args) < 1 {
7324
- return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "make requires a type argument")}
7496
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, expr, "call", ctx.semPkg.pkgPath, "make requires a type argument")}
7325
7497
  }
7326
7498
  targetType := typeFromExpr(ctx, expr.Args[0])
7327
7499
  if targetType == nil {
7328
- return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "make requires a type expression")}
7500
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, expr.Args[0], "call", ctx.semPkg.pkgPath, "make requires a type expression")}
7329
7501
  }
7330
7502
  switch typed := types.Unalias(targetType).Underlying().(type) {
7331
7503
  case *types.Slice:
@@ -7378,13 +7550,13 @@ func (o *LoweringOwner) lowerMakeExpr(ctx lowerFileContext, expr *ast.CallExpr)
7378
7550
  "<" + o.tsTypeFor(ctx, typed.Elem()) + ">(" + capacity + ", " +
7379
7551
  o.lowerZeroValueExprFor(ctx, typed.Elem()) + ", " + strconv.Quote(channelDirectionString(typed.Dir())) + ")", diagnostics
7380
7552
  default:
7381
- return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "unsupported make type")}
7553
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, expr.Args[0], "call", ctx.semPkg.pkgPath, "unsupported make type")}
7382
7554
  }
7383
7555
  }
7384
7556
 
7385
7557
  func (o *LoweringOwner) lowerNewExpr(ctx lowerFileContext, expr *ast.CallExpr) (string, []Diagnostic) {
7386
7558
  if len(expr.Args) != 1 {
7387
- return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "new requires one type argument")}
7559
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, expr, "call", ctx.semPkg.pkgPath, "new requires one type argument")}
7388
7560
  }
7389
7561
  typ := typeFromExpr(ctx, expr.Args[0])
7390
7562
  if named := namedStructType(typ); named != nil {
@@ -7400,7 +7572,7 @@ func (o *LoweringOwner) lowerConversionExpr(
7400
7572
  targetType types.Type,
7401
7573
  ) (string, []Diagnostic) {
7402
7574
  if len(expr.Args) != 1 {
7403
- return "undefined", []Diagnostic{loweringUnsupported("call", ctx.semPkg.pkgPath, "unsupported conversion arity")}
7575
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, expr, "call", ctx.semPkg.pkgPath, "unsupported conversion arity")}
7404
7576
  }
7405
7577
  value, diagnostics := o.lowerExpr(ctx, expr.Args[0])
7406
7578
  sourceType := ctx.semPkg.source.TypesInfo.TypeOf(expr.Args[0])
@@ -8371,7 +8543,7 @@ func (o *LoweringOwner) lowerAddressExpr(ctx lowerFileContext, expr ast.Expr) (s
8371
8543
  case *ast.IndexExpr:
8372
8544
  return o.lowerIndexAddressExpr(ctx, typed)
8373
8545
  default:
8374
- return "undefined", []Diagnostic{loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported address expression")}
8546
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, typed, "expression", ctx.semPkg.pkgPath, "unsupported address expression")}
8375
8547
  }
8376
8548
  }
8377
8549
 
@@ -8430,7 +8602,7 @@ func (o *LoweringOwner) lowerIndexAddressExpr(ctx lowerFileContext, expr *ast.In
8430
8602
  diagnostics := append(targetDiagnostics, indexDiagnostics...)
8431
8603
  targetType := ctx.semPkg.source.TypesInfo.TypeOf(expr.X)
8432
8604
  if isStringType(targetType) || isMapType(targetType) {
8433
- return "undefined", append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported address expression"))
8605
+ return "undefined", append(diagnostics, loweringUnsupportedAt(ctx, expr, "expression", ctx.semPkg.pkgPath, "unsupported address expression"))
8434
8606
  }
8435
8607
  return o.runtimeOwner.QualifiedHelper(RuntimeHelperIndexRef) + "(" + o.lowerIndexTarget(ctx, target, targetType) + ", " + index + ")", diagnostics
8436
8608
  }
@@ -8458,7 +8630,7 @@ func (o *LoweringOwner) lowerUnsafePointerIntegerExpr(
8458
8630
  if !ok || len(call.Args) != 1 || !isUnsafePointerType(typeFromExpr(ctx, call.Fun)) {
8459
8631
  return "", nil, false
8460
8632
  }
8461
- return o.lowerIndexAddressIntegerExpr(ctx, call.Args[0])
8633
+ return o.lowerIndexByteAddressIntegerExpr(ctx, call.Args[0])
8462
8634
  }
8463
8635
 
8464
8636
  func (o *LoweringOwner) lowerIndexAddressIntegerExpr(
@@ -8484,6 +8656,30 @@ func (o *LoweringOwner) lowerIndexAddressIntegerExpr(
8484
8656
  "(" + o.lowerIndexTarget(ctx, target, targetType) + ", " + index + ")", diagnostics, true
8485
8657
  }
8486
8658
 
8659
+ func (o *LoweringOwner) lowerIndexByteAddressIntegerExpr(
8660
+ ctx lowerFileContext,
8661
+ expr ast.Expr,
8662
+ ) (string, []Diagnostic, bool) {
8663
+ address, ok := unwrapParenExpr(expr).(*ast.UnaryExpr)
8664
+ if !ok || address.Op != token.AND {
8665
+ return "", nil, false
8666
+ }
8667
+ indexExpr, ok := unwrapParenExpr(address.X).(*ast.IndexExpr)
8668
+ if !ok {
8669
+ return "", nil, false
8670
+ }
8671
+ target, targetDiagnostics := o.lowerExpr(ctx, indexExpr.X)
8672
+ index, indexDiagnostics := o.lowerExpr(ctx, indexExpr.Index)
8673
+ diagnostics := append(targetDiagnostics, indexDiagnostics...)
8674
+ targetType := ctx.semPkg.source.TypesInfo.TypeOf(indexExpr.X)
8675
+ if isStringType(targetType) || isMapType(targetType) {
8676
+ return "", diagnostics, false
8677
+ }
8678
+ elementSize := goScriptElementByteSize(ctx, indexElementType(targetType))
8679
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperIndexByteAddress) +
8680
+ "(" + o.lowerIndexTarget(ctx, target, targetType) + ", " + index + ", " + strconv.FormatInt(elementSize, 10) + ")", diagnostics, true
8681
+ }
8682
+
8487
8683
  func (o *LoweringOwner) lowerPointerValueExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
8488
8684
  if value, diagnostics, ok := o.lowerUnsafeStringPointerValue(ctx, expr); ok {
8489
8685
  return value, diagnostics
@@ -8491,6 +8687,12 @@ func (o *LoweringOwner) lowerPointerValueExpr(ctx lowerFileContext, expr ast.Exp
8491
8687
  if value, diagnostics, ok := o.lowerUnsafeStringByteSlicePointerValue(ctx, expr); ok {
8492
8688
  return value, diagnostics
8493
8689
  }
8690
+ if ref, diagnostics, ok := o.lowerUnsafeArrayPointerRefExpr(ctx, expr); ok {
8691
+ return ref + ".value", diagnostics
8692
+ }
8693
+ if ref, diagnostics, ok := o.lowerUnsafePointerRefExpr(ctx, expr); ok {
8694
+ return ref + ".value", diagnostics
8695
+ }
8494
8696
  base, diagnostics := o.lowerExpr(ctx, expr)
8495
8697
  typeArg := ""
8496
8698
  if pointer, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(expr)).Underlying().(*types.Pointer); ok {
@@ -8622,8 +8824,13 @@ func (o *LoweringOwner) lowerUnsafeArrayPointerConversion(
8622
8824
  return "", nil, false
8623
8825
  }
8624
8826
  ref, diagnostics := o.lowerAddressExpr(ctx, index)
8827
+ sourceElementSize := goScriptElementByteSize(ctx, indexElementType(ctx.semPkg.source.TypesInfo.TypeOf(index.X)))
8828
+ targetElementSize := goScriptElementByteSize(ctx, array.Elem())
8625
8829
  helper := o.runtimeOwner.QualifiedHelper(RuntimeHelperArrayPointerFromIndexRef) +
8626
- "<" + o.tsTypeFor(ctx, array.Elem()) + ">(" + ref + ", " + strconv.FormatInt(array.Len(), 10) + ")"
8830
+ "<" + o.tsTypeFor(ctx, array.Elem()) + ">(" + ref + ", " +
8831
+ strconv.FormatInt(array.Len(), 10) + ", " +
8832
+ strconv.FormatInt(sourceElementSize, 10) + ", " +
8833
+ strconv.FormatInt(targetElementSize, 10) + ")"
8627
8834
  return "(" + helper + " as unknown as " + o.tsTypeFor(ctx, targetType) + ")", diagnostics, true
8628
8835
  }
8629
8836
 
@@ -8717,6 +8924,12 @@ func sameLoweredSourceExpr(ctx lowerFileContext, left ast.Expr, right ast.Expr)
8717
8924
  }
8718
8925
 
8719
8926
  func (o *LoweringOwner) lowerPointerStorageExpr(ctx lowerFileContext, expr ast.Expr) (string, []Diagnostic) {
8927
+ if ref, diagnostics, ok := o.lowerUnsafeArrayPointerRefExpr(ctx, expr); ok {
8928
+ return ref + ".value", diagnostics
8929
+ }
8930
+ if ref, diagnostics, ok := o.lowerUnsafePointerRefExpr(ctx, expr); ok {
8931
+ return ref + ".value", diagnostics
8932
+ }
8720
8933
  if ref, diagnostics, ok := o.lowerUnsafePointerStorageExpr(ctx, expr); ok {
8721
8934
  return ref, diagnostics
8722
8935
  }
@@ -8724,6 +8937,42 @@ func (o *LoweringOwner) lowerPointerStorageExpr(ctx lowerFileContext, expr ast.E
8724
8937
  return base + "!.value", diagnostics
8725
8938
  }
8726
8939
 
8940
+ func (o *LoweringOwner) lowerUnsafeArrayPointerRefExpr(
8941
+ ctx lowerFileContext,
8942
+ expr ast.Expr,
8943
+ ) (string, []Diagnostic, bool) {
8944
+ call, ok := unwrapParenExpr(expr).(*ast.CallExpr)
8945
+ if !ok || len(call.Args) != 1 {
8946
+ return "", nil, false
8947
+ }
8948
+ targetType := typeFromExpr(ctx, call.Fun)
8949
+ if targetType == nil {
8950
+ return "", nil, false
8951
+ }
8952
+ return o.lowerUnsafeArrayPointerConversion(ctx, targetType, call.Args[0])
8953
+ }
8954
+
8955
+ func (o *LoweringOwner) lowerUnsafePointerRefExpr(
8956
+ ctx lowerFileContext,
8957
+ expr ast.Expr,
8958
+ ) (string, []Diagnostic, bool) {
8959
+ call, ok := unwrapParenExpr(expr).(*ast.CallExpr)
8960
+ if !ok || len(call.Args) != 1 {
8961
+ return "", nil, false
8962
+ }
8963
+ targetType := typeFromExpr(ctx, call.Fun)
8964
+ if targetType == nil {
8965
+ return "", nil, false
8966
+ }
8967
+ pointer, _ := types.Unalias(targetType).Underlying().(*types.Pointer)
8968
+ if pointer == nil || !isUnsafePointerType(ctx.semPkg.source.TypesInfo.TypeOf(call.Args[0])) {
8969
+ return "", nil, false
8970
+ }
8971
+ value, diagnostics := o.lowerExpr(ctx, call.Args[0])
8972
+ return o.runtimeOwner.QualifiedHelper(RuntimeHelperUnsafePointerRef) +
8973
+ "<" + o.tsTypeFor(ctx, pointer.Elem()) + ">(" + value + ")", diagnostics, true
8974
+ }
8975
+
8727
8976
  func (o *LoweringOwner) lowerUnsafePointerStorageExpr(
8728
8977
  ctx lowerFileContext,
8729
8978
  expr ast.Expr,
@@ -8893,15 +9142,11 @@ func (o *LoweringOwner) lowerCompositeLit(
8893
9142
  if mapType, ok := types.Unalias(ctx.semPkg.source.TypesInfo.TypeOf(lit)).Underlying().(*types.Map); ok {
8894
9143
  return o.lowerMapCompositeLit(ctx, lit, mapType)
8895
9144
  }
8896
- position := sourcePos(ctx.semPkg.source, lit.Pos())
8897
9145
  detail := "unsupported composite literal"
8898
- if position.file != "" {
8899
- detail += " at " + filepath.Base(position.file) + ":" + strconv.Itoa(position.line)
8900
- }
8901
9146
  if typ := ctx.semPkg.source.TypesInfo.TypeOf(lit); typ != nil {
8902
9147
  detail += " of type " + typ.String()
8903
9148
  }
8904
- return "undefined", []Diagnostic{loweringUnsupported("expression", ctx.semPkg.pkgPath, detail)}
9149
+ return "undefined", []Diagnostic{loweringUnsupportedAt(ctx, lit, "expression", ctx.semPkg.pkgPath, detail)}
8905
9150
  }
8906
9151
 
8907
9152
  func (o *LoweringOwner) lowerStructCompositeLit(
@@ -8936,7 +9181,7 @@ func (o *LoweringOwner) lowerStructCompositeLit(
8936
9181
  fieldType = field.Type()
8937
9182
  }
8938
9183
  if fieldName == "" {
8939
- diagnostics = append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported struct literal field"))
9184
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, elt, "expression", ctx.semPkg.pkgPath, "unsupported struct literal field"))
8940
9185
  continue
8941
9186
  }
8942
9187
  value, valueDiagnostics := o.lowerExpr(ctx, valueExpr)
@@ -9047,7 +9292,7 @@ func (o *LoweringOwner) lowerAnonymousStructCompositeLit(
9047
9292
  fieldType = field.Type()
9048
9293
  }
9049
9294
  if fieldName == "" {
9050
- diagnostics = append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported anonymous struct literal field"))
9295
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, elt, "expression", ctx.semPkg.pkgPath, "unsupported anonymous struct literal field"))
9051
9296
  continue
9052
9297
  }
9053
9298
  value, valueDiagnostics := o.lowerExpr(ctx, valueExpr)
@@ -9156,7 +9401,7 @@ func (o *LoweringOwner) lowerMapCompositeLit(
9156
9401
  for _, elt := range lit.Elts {
9157
9402
  keyed, ok := elt.(*ast.KeyValueExpr)
9158
9403
  if !ok {
9159
- diagnostics = append(diagnostics, loweringUnsupported("expression", ctx.semPkg.pkgPath, "unsupported map literal entry"))
9404
+ diagnostics = append(diagnostics, loweringUnsupportedAt(ctx, elt, "expression", ctx.semPkg.pkgPath, "unsupported map literal entry"))
9160
9405
  continue
9161
9406
  }
9162
9407
  key, keyDiagnostics := o.lowerExpr(ctx, keyed.Key)
@@ -10398,6 +10643,42 @@ func isMapType(typ types.Type) bool {
10398
10643
  return ok
10399
10644
  }
10400
10645
 
10646
+ func indexElementType(typ types.Type) types.Type {
10647
+ if pointer, ok := types.Unalias(typ).Underlying().(*types.Pointer); ok {
10648
+ typ = pointer.Elem()
10649
+ }
10650
+ switch typed := types.Unalias(typ).Underlying().(type) {
10651
+ case *types.Slice:
10652
+ return typed.Elem()
10653
+ case *types.Array:
10654
+ return typed.Elem()
10655
+ default:
10656
+ return nil
10657
+ }
10658
+ }
10659
+
10660
+ func goScriptElementByteSize(ctx lowerFileContext, typ types.Type) int64 {
10661
+ if typ == nil {
10662
+ return 1
10663
+ }
10664
+ if sizes := ctx.semPkg.source.TypesSizes; sizes != nil {
10665
+ if size := sizes.Sizeof(typ); size > 0 {
10666
+ return size
10667
+ }
10668
+ }
10669
+ if bits, ok := integerBits(typ); ok && bits > 0 {
10670
+ return int64((bits + 7) / 8)
10671
+ }
10672
+ if isFloatType(typ) {
10673
+ basic, _ := types.Unalias(typ).Underlying().(*types.Basic)
10674
+ if basic != nil && basic.Kind() == types.Float32 {
10675
+ return 4
10676
+ }
10677
+ return 8
10678
+ }
10679
+ return 1
10680
+ }
10681
+
10401
10682
  func isChannelType(typ types.Type) bool {
10402
10683
  if typ == nil {
10403
10684
  return false
@@ -10471,6 +10752,9 @@ func isFloatType(typ types.Type) bool {
10471
10752
  }
10472
10753
 
10473
10754
  func unsignedIntegerBits(typ types.Type) (int, bool) {
10755
+ if typ == nil {
10756
+ return 0, false
10757
+ }
10474
10758
  basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
10475
10759
  if !ok || basic.Info()&types.IsUnsigned == 0 {
10476
10760
  return 0, false
@@ -10488,6 +10772,9 @@ func unsignedIntegerBits(typ types.Type) (int, bool) {
10488
10772
  }
10489
10773
 
10490
10774
  func signedIntegerBits(typ types.Type) (int, bool) {
10775
+ if typ == nil {
10776
+ return 0, false
10777
+ }
10491
10778
  basic, ok := types.Unalias(typ).Underlying().(*types.Basic)
10492
10779
  if !ok || basic.Info()&types.IsInteger == 0 || basic.Info()&types.IsUnsigned != 0 {
10493
10780
  return 0, false