@soundscript/soundscript 0.1.15 → 0.1.17

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 (47) hide show
  1. package/decode.d.ts +2 -2
  2. package/decode.js.map +1 -1
  3. package/json.js +1 -1
  4. package/numerics.js +4 -4
  5. package/package.json +6 -6
  6. package/project-transform/src/checker/rules/flow.js +16 -3
  7. package/project-transform/src/checker/rules/flow.ts +20 -3
  8. package/project-transform/src/checker/rules/flow_invalidation.js +18 -21
  9. package/project-transform/src/checker/rules/flow_invalidation.ts +25 -20
  10. package/project-transform/src/checker/rules/relations.js +157 -3
  11. package/project-transform/src/checker/rules/relations.ts +207 -1
  12. package/project-transform/src/checker/rules/unsound_syntax.js +0 -3
  13. package/project-transform/src/checker/rules/unsound_syntax.ts +0 -4
  14. package/project-transform/src/cli.js +1 -1
  15. package/project-transform/src/cli.ts +1 -1
  16. package/project-transform/src/compiler/compile_project.js +75 -9
  17. package/project-transform/src/compiler/compile_project.ts +121 -7
  18. package/project-transform/src/compiler/ir.ts +19 -1
  19. package/project-transform/src/compiler/lower.js +10335 -1477
  20. package/project-transform/src/compiler/lower.ts +16826 -4074
  21. package/project-transform/src/compiler/toolchain.js +36 -4
  22. package/project-transform/src/compiler/toolchain.ts +36 -4
  23. package/project-transform/src/compiler/wasm_js_host_runtime.js +134 -0
  24. package/project-transform/src/compiler/wasm_js_host_runtime.ts +146 -0
  25. package/project-transform/src/compiler/wat_arrays.js +4 -1
  26. package/project-transform/src/compiler/wat_arrays.ts +5 -1
  27. package/project-transform/src/compiler/wat_emitter.js +1497 -311
  28. package/project-transform/src/compiler/wat_emitter.ts +2971 -1017
  29. package/project-transform/src/compiler/wat_tagged.js +5 -0
  30. package/project-transform/src/compiler/wat_tagged.ts +5 -0
  31. package/project-transform/src/compiler_generator_runner.js +2139 -19
  32. package/project-transform/src/compiler_generator_runner.ts +2143 -20
  33. package/project-transform/src/compiler_promise_runner.js +4615 -636
  34. package/project-transform/src/compiler_promise_runner.ts +4703 -659
  35. package/project-transform/src/compiler_test_helpers.js +0 -579
  36. package/project-transform/src/compiler_test_helpers.ts +0 -648
  37. package/project-transform/src/frontend/macro_expander.js +4 -6
  38. package/project-transform/src/frontend/macro_expander.ts +4 -6
  39. package/project-transform/src/frontend/macro_operand_semantics.js +124 -1
  40. package/project-transform/src/frontend/macro_operand_semantics.ts +230 -6
  41. package/project-transform/src/frontend/macro_resolver.js +2 -2
  42. package/project-transform/src/frontend/macro_resolver.ts +2 -1
  43. package/project-transform/src/frontend/project_macro_support.js +29 -5
  44. package/project-transform/src/frontend/project_macro_support.ts +46 -10
  45. package/project-transform/src/stdlib/decode.d.ts +2 -2
  46. package/project-transform/src/stdlib/decode.ts +2 -2
  47. package/soundscript/decode.sts +2 -2
@@ -99,6 +99,8 @@ interface TargetNewtypeIdentitySet {
99
99
  mode: 'intersection' | 'single' | 'union';
100
100
  }
101
101
 
102
+ type CanonicalResultClassFamily = 'option' | 'result';
103
+
102
104
  type NominalIdentityLike = { symbol: ts.Symbol; typeArguments: readonly ts.Type[] };
103
105
  type NominalIdentitySet<TIdentity extends NominalIdentityLike> = {
104
106
  identities: readonly TIdentity[];
@@ -2513,7 +2515,8 @@ function isInstalledSoundStdlibSourceFile(sourceFile: ts.SourceFile): boolean {
2513
2515
  return normalizedFileName.includes('/node_modules/@soundscript/soundscript/') &&
2514
2516
  (
2515
2517
  normalizedFileName.endsWith('.d.ts') ||
2516
- normalizedFileName.endsWith('.sts')
2518
+ normalizedFileName.endsWith('.sts') ||
2519
+ normalizedFileName.endsWith('.sts.ts')
2517
2520
  );
2518
2521
  }
2519
2522
 
@@ -5609,10 +5612,24 @@ function classifyCurrentTypeNodeNominalClassRelation(
5609
5612
  sourceExpression?: ts.Expression,
5610
5613
  sourceTypeNode?: ts.TypeNode,
5611
5614
  ): RelationMismatch | undefined {
5615
+ if (
5616
+ sharesCanonicalResultClassFamily(context, sourceType, targetType) ||
5617
+ sharesGenericAliasRelationFamily(context, sourceType, targetType) ||
5618
+ sharesExactGenericClassIdentityFamilies(context, sourceType, targetType) ||
5619
+ sharesEquivalentTargetClassIdentitySets(context, sourceType, targetType)
5620
+ ) {
5621
+ return undefined;
5622
+ }
5623
+
5612
5624
  const targetIdentitySet = getDeclaredClassIdentitySetFromTypeNode(context, targetTypeNode);
5613
5625
  if (!targetIdentitySet) {
5614
5626
  return undefined;
5615
5627
  }
5628
+ const sourceFamily = getCanonicalResultClassFamilyForType(context, sourceType);
5629
+ const targetFamily = getCanonicalResultClassFamilyForTargetIdentitySet(targetIdentitySet);
5630
+ if (sourceFamily !== undefined && sourceFamily === targetFamily) {
5631
+ return undefined;
5632
+ }
5616
5633
 
5617
5634
  const sourceIdentitySet = getDeclaredClassIdentitySetFromTypeNode(context, sourceTypeNode) ??
5618
5635
  getDeclaredClassIdentitySetFromExpression(context, sourceExpression);
@@ -6019,6 +6036,12 @@ function classifySourceTypeAgainstTargetClassIdentitySet(
6019
6036
  targetType: ts.Type,
6020
6037
  targetIdentitySet: TargetClassIdentitySet,
6021
6038
  ): RelationMismatch | undefined {
6039
+ const sourceFamily = getCanonicalResultClassFamilyForType(context, sourceType);
6040
+ const targetFamily = getCanonicalResultClassFamilyForTargetIdentitySet(targetIdentitySet);
6041
+ if (sourceFamily !== undefined && sourceFamily === targetFamily) {
6042
+ return undefined;
6043
+ }
6044
+
6022
6045
  if (targetIdentitySet.mode === 'union') {
6023
6046
  const normalizedSourceType = getSafeNonNullableRelationType(context, sourceType);
6024
6047
  if ((normalizedSourceType.flags & ts.TypeFlags.Never) !== 0) {
@@ -6098,6 +6121,7 @@ function classifyUnsoundNominalClassRelation(
6098
6121
  targetType: ts.Type,
6099
6122
  ): RelationMismatch | undefined {
6100
6123
  if (
6124
+ sharesCanonicalResultClassFamily(context, sourceType, targetType) ||
6101
6125
  sharesGenericAliasRelationFamily(context, sourceType, targetType) ||
6102
6126
  sharesExactGenericClassIdentityFamilies(context, sourceType, targetType) ||
6103
6127
  sharesEquivalentTargetClassIdentitySets(context, sourceType, targetType)
@@ -6194,6 +6218,7 @@ function classifyUnsoundGenericClassInstanceRelation(
6194
6218
  targetType: ts.Type,
6195
6219
  ): RelationMismatch | undefined {
6196
6220
  if (
6221
+ sharesCanonicalResultClassFamily(context, sourceType, targetType) ||
6197
6222
  sharesGenericAliasRelationFamily(context, sourceType, targetType) ||
6198
6223
  sharesExactGenericClassIdentityFamilies(context, sourceType, targetType) ||
6199
6224
  sharesEquivalentTargetClassIdentitySets(context, sourceType, targetType)
@@ -6312,6 +6337,187 @@ function sharesEquivalentTargetClassIdentitySets(
6312
6337
  );
6313
6338
  }
6314
6339
 
6340
+ function isTrustedResultStdlibSourceFileName(fileName: string): boolean {
6341
+ return /(?:^|[\\/])(?:index|result)(?:\.sts)?(?:\.d)?\.ts$/.test(fileName);
6342
+ }
6343
+
6344
+ function getCanonicalResultClassFamilyForName(
6345
+ name: string,
6346
+ ): CanonicalResultClassFamily | undefined {
6347
+ switch (name) {
6348
+ case 'Err':
6349
+ case 'Ok':
6350
+ case 'Result':
6351
+ return 'result';
6352
+ case 'None':
6353
+ case 'Option':
6354
+ case 'Some':
6355
+ return 'option';
6356
+ default:
6357
+ return undefined;
6358
+ }
6359
+ }
6360
+
6361
+ function getCanonicalResultClassFamilyForGenericRelationInfo(
6362
+ info: GenericRelationTypeInfo,
6363
+ ): CanonicalResultClassFamily | undefined {
6364
+ const declarations = info.symbol.getDeclarations() ?? [];
6365
+ if (!declarations.some((declaration) =>
6366
+ isTrustedSoundLibSourceFile(declaration.getSourceFile()) &&
6367
+ isTrustedResultStdlibSourceFileName(declaration.getSourceFile().fileName)
6368
+ )) {
6369
+ return undefined;
6370
+ }
6371
+
6372
+ return getCanonicalResultClassFamilyForName(info.name);
6373
+ }
6374
+
6375
+ function getCanonicalResultClassFamilyForIdentity(
6376
+ identity: GenericClassIdentity,
6377
+ ): CanonicalResultClassFamily | undefined {
6378
+ const declarations = identity.symbol.getDeclarations() ?? [];
6379
+ if (!declarations.some((declaration) =>
6380
+ isTrustedSoundLibSourceFile(declaration.getSourceFile()) &&
6381
+ isTrustedResultStdlibSourceFileName(declaration.getSourceFile().fileName)
6382
+ )) {
6383
+ return undefined;
6384
+ }
6385
+
6386
+ return getCanonicalResultClassFamilyForName(identity.symbol.getName());
6387
+ }
6388
+
6389
+ function getCanonicalResultClassFamilyForTargetIdentitySet(
6390
+ identitySet: TargetClassIdentitySet,
6391
+ ): CanonicalResultClassFamily | undefined {
6392
+ let family: CanonicalResultClassFamily | undefined;
6393
+ for (const identity of identitySet.identities) {
6394
+ const identityFamily = getCanonicalResultClassFamilyForIdentity(identity);
6395
+ if (!identityFamily) {
6396
+ return undefined;
6397
+ }
6398
+ if (family && family !== identityFamily) {
6399
+ return undefined;
6400
+ }
6401
+ family = identityFamily;
6402
+ }
6403
+
6404
+ return family;
6405
+ }
6406
+
6407
+ function getCanonicalResultClassFamilyForType(
6408
+ context: AnalysisContext,
6409
+ type: ts.Type,
6410
+ visitedTypeIds: Set<number> = new Set(),
6411
+ ): CanonicalResultClassFamily | undefined {
6412
+ const normalizedType = getSafeNonNullableRelationType(context, type);
6413
+ const rawTypeId = (normalizedType as ts.Type & { id?: number }).id;
6414
+ let visitedTypeId: number | undefined;
6415
+ if (typeof rawTypeId === 'number') {
6416
+ if (visitedTypeIds.has(rawTypeId)) {
6417
+ return undefined;
6418
+ }
6419
+ visitedTypeIds.add(rawTypeId);
6420
+ visitedTypeId = rawTypeId;
6421
+ }
6422
+
6423
+ try {
6424
+ if ((normalizedType.flags & ts.TypeFlags.Union) !== 0) {
6425
+ let family: CanonicalResultClassFamily | undefined;
6426
+ let sawConstituent = false;
6427
+ for (const constituentType of (normalizedType as ts.UnionType).types) {
6428
+ const normalizedConstituentType = getSafeNonNullableRelationType(context, constituentType);
6429
+ if ((normalizedConstituentType.flags & ts.TypeFlags.Never) !== 0) {
6430
+ continue;
6431
+ }
6432
+ const constituentFamily = getCanonicalResultClassFamilyForType(
6433
+ context,
6434
+ normalizedConstituentType,
6435
+ visitedTypeIds,
6436
+ );
6437
+ if (!constituentFamily) {
6438
+ return undefined;
6439
+ }
6440
+ if (family && family !== constituentFamily) {
6441
+ return undefined;
6442
+ }
6443
+ sawConstituent = true;
6444
+ family = constituentFamily;
6445
+ }
6446
+
6447
+ return sawConstituent ? family : undefined;
6448
+ }
6449
+
6450
+ if ((normalizedType.flags & ts.TypeFlags.Intersection) !== 0) {
6451
+ let family: CanonicalResultClassFamily | undefined;
6452
+ for (const constituentType of (normalizedType as ts.IntersectionType).types) {
6453
+ const constituentFamily = getCanonicalResultClassFamilyForType(
6454
+ context,
6455
+ constituentType,
6456
+ visitedTypeIds,
6457
+ );
6458
+ if (!constituentFamily) {
6459
+ continue;
6460
+ }
6461
+ if (family && family !== constituentFamily) {
6462
+ return undefined;
6463
+ }
6464
+ family = constituentFamily;
6465
+ }
6466
+
6467
+ return family;
6468
+ }
6469
+
6470
+ const relationInfo = getGenericRelationTypeInfo(context, normalizedType);
6471
+ const relationFamily = relationInfo
6472
+ ? getCanonicalResultClassFamilyForGenericRelationInfo(relationInfo)
6473
+ : undefined;
6474
+ if (relationFamily) {
6475
+ return relationFamily;
6476
+ }
6477
+
6478
+ const directIdentity = getDirectClassIdentity(context, normalizedType);
6479
+ const directFamily = directIdentity
6480
+ ? getCanonicalResultClassFamilyForIdentity(directIdentity)
6481
+ : undefined;
6482
+ if (directFamily) {
6483
+ return directFamily;
6484
+ }
6485
+
6486
+ const identities = collectGenericClassIdentities(context, normalizedType);
6487
+ if (identities.length === 0) {
6488
+ return undefined;
6489
+ }
6490
+
6491
+ let family: CanonicalResultClassFamily | undefined;
6492
+ for (const identity of identities) {
6493
+ const identityFamily = getCanonicalResultClassFamilyForIdentity(identity);
6494
+ if (!identityFamily) {
6495
+ return undefined;
6496
+ }
6497
+ if (family && family !== identityFamily) {
6498
+ return undefined;
6499
+ }
6500
+ family = identityFamily;
6501
+ }
6502
+
6503
+ return family;
6504
+ } finally {
6505
+ if (visitedTypeId !== undefined) {
6506
+ visitedTypeIds.delete(visitedTypeId);
6507
+ }
6508
+ }
6509
+ }
6510
+
6511
+ function sharesCanonicalResultClassFamily(
6512
+ context: AnalysisContext,
6513
+ sourceType: ts.Type,
6514
+ targetType: ts.Type,
6515
+ ): boolean {
6516
+ const sourceFamily = getCanonicalResultClassFamilyForType(context, sourceType);
6517
+ return sourceFamily !== undefined &&
6518
+ sourceFamily === getCanonicalResultClassFamilyForType(context, targetType);
6519
+ }
6520
+
6315
6521
  function getGenericParameterNames(
6316
6522
  symbol: ts.Symbol,
6317
6523
  arity: number,
@@ -1583,9 +1583,6 @@ function getUnsupportedFeatureDiagnostic(context, node) {
1583
1583
  return unsupportedFeature(node.expression, 'prototypeMutation');
1584
1584
  }
1585
1585
  }
1586
- if (ts.isForInStatement(node)) {
1587
- return unsupportedFeature(node, 'forIn');
1588
- }
1589
1586
  if (ts.isLabeledStatement(node)) {
1590
1587
  return unsupportedFeature(node.label, 'labeledStatement');
1591
1588
  }
@@ -2381,10 +2381,6 @@ function getUnsupportedFeatureDiagnostic(
2381
2381
  }
2382
2382
  }
2383
2383
 
2384
- if (ts.isForInStatement(node)) {
2385
- return unsupportedFeature(node, 'forIn');
2386
- }
2387
-
2388
2384
  if (ts.isLabeledStatement(node)) {
2389
2385
  return unsupportedFeature(node.label, 'labeledStatement');
2390
2386
  }
@@ -11,7 +11,7 @@ import { createTempDirectory, fileExistsSync, makeDirectory, readStdinText, remo
11
11
  import { materializeRuntimeGraph, } from './runtime/materialize.js';
12
12
  import { runProgram } from './run_program.js';
13
13
  import { projectEditorFile } from './editor_projection.js';
14
- export const VERSION = '0.1.15';
14
+ export const VERSION = '0.1.17';
15
15
  const FINDINGS_EXIT_CODE = 1;
16
16
  const CLI_FAILURE_EXIT_CODE = 2;
17
17
  function createCliDiagnostic(code, message, filePath, details) {
@@ -59,7 +59,7 @@ import {
59
59
  import { runProgram, type RunProgramOptions, type RunProgramResult } from './run_program.ts';
60
60
  import { projectEditorFile } from './editor_projection.ts';
61
61
 
62
- export const VERSION = '0.1.15';
62
+ export const VERSION = '0.1.17';
63
63
  const FINDINGS_EXIT_CODE = 1;
64
64
  const CLI_FAILURE_EXIT_CODE = 2;
65
65
 
@@ -2,14 +2,14 @@ import ts from 'typescript';
2
2
  import { dirname, relative } from '../platform/path.js';
3
3
  import { createAnnotationLookup } from '../annotation_syntax.js';
4
4
  import { createSoundStdlibCompilerHost } from '../bundled/sound_stdlib.js';
5
- import { formatDiagnostics, getNodeDiagnosticRange, hasErrorDiagnostics, toMergedDiagnostic, } from '../checker/diagnostics.js';
5
+ import { formatDiagnostics, getNodeDiagnosticRange, hasErrorDiagnostics, remapDiagnosticFilePaths, toMergedDiagnostic, } from '../checker/diagnostics.js';
6
6
  import { COMPILER_DIAGNOSTIC_CODES, COMPILER_DIAGNOSTIC_MESSAGES, } from '../checker/engine/diagnostic_codes.js';
7
7
  import { createAnalysisContext } from '../checker/engine/context.js';
8
8
  import { runSoundAnalysis } from '../checker/rules/index.js';
9
9
  import { runUniversalPolicyAnalysis } from '../checker/rules/universal.js';
10
10
  import { collectSoundscriptRootNames, getConfigFileParsingDiagnostics, loadConfig, } from '../config.js';
11
11
  import { createBuiltinExpandedProgram } from '../frontend/builtin_macro_support.js';
12
- import { getLineAndCharacterOfPosition, mapProgramEnclosingRangeToSource, toSourceFileName, } from '../frontend/project_frontend.js';
12
+ import { getLineAndCharacterOfPosition, getPositionOfLineAndCharacter, mapProgramEnclosingRangeToSource, toSourceFileName, } from '../frontend/project_frontend.js';
13
13
  import { CompilerUnsupportedError } from './errors.js';
14
14
  import { lowerProgramToCompilerIR, validateHonestHeapBoundarySurfaces } from './lower.js';
15
15
  import { CompilerToolchainError, packageCompilerOutput, } from './toolchain.js';
@@ -57,6 +57,72 @@ function createCompilerDiagnosticForNode(node, overrides) {
57
57
  ...getNodeDiagnosticRange(node),
58
58
  };
59
59
  }
60
+ function remapDiagnostics(diagnostics) {
61
+ return diagnostics.map((diagnostic) => remapDiagnosticFilePaths(diagnostic, toSourceFileName));
62
+ }
63
+ function hasRelatedInformation(diagnostic) {
64
+ return 'relatedInformation' in diagnostic;
65
+ }
66
+ function remapPreparedDiagnosticRange(diagnostic, preparedFile, diagnosticPreparedFiles) {
67
+ const remappedFilePath = diagnostic.filePath
68
+ ? toSourceFileName(diagnostic.filePath)
69
+ : diagnostic.filePath;
70
+ if (!preparedFile ||
71
+ !diagnostic.filePath ||
72
+ diagnostic.line === undefined ||
73
+ diagnostic.column === undefined) {
74
+ const remappedDiagnostic = {
75
+ ...diagnostic,
76
+ filePath: remappedFilePath,
77
+ };
78
+ if (hasRelatedInformation(diagnostic)) {
79
+ remappedDiagnostic.relatedInformation = diagnostic.relatedInformation?.map((relatedInformation) => remapPreparedDiagnosticRange(relatedInformation, relatedInformation.filePath
80
+ ? diagnosticPreparedFiles.get(toSourceFileName(relatedInformation.filePath))
81
+ : undefined, diagnosticPreparedFiles));
82
+ }
83
+ return remappedDiagnostic;
84
+ }
85
+ const programStart = getPositionOfLineAndCharacter(preparedFile.rewrittenText, diagnostic.line - 1, diagnostic.column - 1);
86
+ const programEnd = diagnostic.endLine !== undefined && diagnostic.endColumn !== undefined
87
+ ? getPositionOfLineAndCharacter(preparedFile.rewrittenText, diagnostic.endLine - 1, diagnostic.endColumn - 1)
88
+ : programStart;
89
+ const mappedRange = mapProgramEnclosingRangeToSource(preparedFile, programStart, programEnd);
90
+ const mappedStart = getLineAndCharacterOfPosition(preparedFile.originalText, mappedRange.start);
91
+ const mappedEnd = getLineAndCharacterOfPosition(preparedFile.originalText, mappedRange.end);
92
+ const remappedDiagnostic = {
93
+ ...diagnostic,
94
+ filePath: remappedFilePath,
95
+ line: mappedStart.line + 1,
96
+ column: mappedStart.character + 1,
97
+ endLine: mappedEnd.line + 1,
98
+ endColumn: mappedEnd.character + 1,
99
+ };
100
+ if (hasRelatedInformation(diagnostic)) {
101
+ remappedDiagnostic.relatedInformation = diagnostic.relatedInformation?.map((relatedInformation) => {
102
+ const relatedPreparedFile = relatedInformation.filePath
103
+ ? diagnosticPreparedFiles.get(toSourceFileName(relatedInformation.filePath))
104
+ : undefined;
105
+ return remapPreparedDiagnosticRange(relatedInformation, relatedPreparedFile, diagnosticPreparedFiles);
106
+ });
107
+ }
108
+ return remappedDiagnostic;
109
+ }
110
+ function remapSoundDiagnostics(diagnostics, diagnosticPreparedFiles) {
111
+ return diagnostics.map((diagnostic) => {
112
+ const preparedFile = diagnostic.filePath
113
+ ? diagnosticPreparedFiles.get(toSourceFileName(diagnostic.filePath))
114
+ : undefined;
115
+ return remapPreparedDiagnosticRange(diagnostic, preparedFile, diagnosticPreparedFiles);
116
+ });
117
+ }
118
+ function remapCompilerDiagnostics(diagnostics, diagnosticPreparedFiles) {
119
+ return diagnostics.map((diagnostic) => {
120
+ const preparedFile = diagnostic.filePath
121
+ ? diagnosticPreparedFiles.get(toSourceFileName(diagnostic.filePath))
122
+ : undefined;
123
+ return remapPreparedDiagnosticRange(diagnostic, preparedFile, diagnosticPreparedFiles);
124
+ });
125
+ }
60
126
  function createProgram(options) {
61
127
  const loadedConfig = loadConfig(options.projectPath, { target: options.target });
62
128
  const soundscriptRootNames = collectSoundscriptRootNames(options.projectPath, loadedConfig);
@@ -78,7 +144,7 @@ function createProgram(options) {
78
144
  analysisPreparedProgram: expandedProgram.analysisPreparedProgram,
79
145
  diagnosticPreparedFiles: expandedProgram.diagnosticPreparedFiles,
80
146
  dispose: () => expandedProgram.dispose(),
81
- frontendDiagnostics: expandedProgram.frontendDiagnostics(),
147
+ frontendDiagnostics: remapDiagnostics(expandedProgram.frontendDiagnostics()),
82
148
  program: expandedProgram.program,
83
149
  runtime: loadedConfig.runtime,
84
150
  tsDiagnosticPrograms: expandedProgram.tsDiagnosticPrograms,
@@ -193,7 +259,7 @@ function collectDiagnostics(analysisPreparedProgram, diagnosticPreparedFiles, pr
193
259
  workingDirectory,
194
260
  includeSourceFile: (sourceFile) => !sourceFile.isDeclarationFile,
195
261
  });
196
- const universalDiagnostics = runUniversalPolicyAnalysis(analysisContext);
262
+ const universalDiagnostics = remapSoundDiagnostics(runUniversalPolicyAnalysis(analysisContext), diagnosticPreparedFiles);
197
263
  if (hasErrorDiagnostics(tsDiagnostics)) {
198
264
  return [...frontendDiagnostics, ...tsDiagnostics, ...universalDiagnostics];
199
265
  }
@@ -201,7 +267,7 @@ function collectDiagnostics(analysisPreparedProgram, diagnosticPreparedFiles, pr
201
267
  ...frontendDiagnostics,
202
268
  ...tsDiagnostics,
203
269
  ...universalDiagnostics,
204
- ...runSoundAnalysis(analysisContext),
270
+ ...remapSoundDiagnostics(runSoundAnalysis(analysisContext), diagnosticPreparedFiles),
205
271
  ];
206
272
  }
207
273
  function findUnsupportedValueClass(program) {
@@ -242,13 +308,13 @@ export function compileProject(options) {
242
308
  }
243
309
  const unsupportedValueClass = findUnsupportedValueClass(program);
244
310
  if (unsupportedValueClass) {
245
- const compilerDiagnostics = [
311
+ const compilerDiagnostics = remapCompilerDiagnostics([
246
312
  createCompilerDiagnosticForNode(unsupportedValueClass, {
247
313
  code: COMPILER_DIAGNOSTIC_CODES.valueClassesRequireJsEmit,
248
314
  message: COMPILER_DIAGNOSTIC_MESSAGES.valueClassesRequireJsEmit,
249
315
  hint: 'Use `soundscript build`, `soundscript node`, or another JS emit path for `#[value]`, or remove the annotation before compiling to Wasm.',
250
316
  }),
251
- ];
317
+ ], diagnosticPreparedFiles);
252
318
  return {
253
319
  diagnostics: compilerDiagnostics,
254
320
  output: formatDiagnostics(compilerDiagnostics, options.workingDirectory),
@@ -286,11 +352,11 @@ export function compileProject(options) {
286
352
  hint: error.diagnosticHint,
287
353
  notes: error.diagnosticNotes,
288
354
  };
289
- const compilerDiagnostics = [
355
+ const compilerDiagnostics = remapCompilerDiagnostics([
290
356
  error.node
291
357
  ? createCompilerDiagnosticForNode(error.node, diagnosticOverrides)
292
358
  : createCompilerDiagnostic(options.projectPath, diagnosticOverrides),
293
- ];
359
+ ], diagnosticPreparedFiles);
294
360
  return {
295
361
  diagnostics: compilerDiagnostics,
296
362
  output: formatDiagnostics(compilerDiagnostics, options.workingDirectory),
@@ -10,6 +10,7 @@ import {
10
10
  getNodeDiagnosticRange,
11
11
  hasErrorDiagnostics,
12
12
  type MergedDiagnostic,
13
+ remapDiagnosticFilePaths,
13
14
  toMergedDiagnostic,
14
15
  } from '../checker/diagnostics.ts';
15
16
  import {
@@ -29,6 +30,7 @@ import {
29
30
  import { createBuiltinExpandedProgram } from '../frontend/builtin_macro_support.ts';
30
31
  import {
31
32
  getLineAndCharacterOfPosition,
33
+ getPositionOfLineAndCharacter,
32
34
  mapProgramEnclosingRangeToSource,
33
35
  type PreparedSourceFile,
34
36
  toSourceFileName,
@@ -118,6 +120,115 @@ function createCompilerDiagnosticForNode(
118
120
  };
119
121
  }
120
122
 
123
+ function remapDiagnostics<T extends MergedDiagnostic>(diagnostics: readonly T[]): T[] {
124
+ return diagnostics.map((diagnostic) => remapDiagnosticFilePaths(diagnostic, toSourceFileName));
125
+ }
126
+
127
+ function hasRelatedInformation(
128
+ diagnostic: MergedDiagnostic | DiagnosticRelatedInformation,
129
+ ): diagnostic is MergedDiagnostic {
130
+ return 'relatedInformation' in diagnostic;
131
+ }
132
+
133
+ function remapPreparedDiagnosticRange<T extends MergedDiagnostic | DiagnosticRelatedInformation>(
134
+ diagnostic: T,
135
+ preparedFile: PreparedSourceFile | undefined,
136
+ diagnosticPreparedFiles: ReadonlyMap<string, PreparedSourceFile>,
137
+ ): T {
138
+ const remappedFilePath = diagnostic.filePath
139
+ ? toSourceFileName(diagnostic.filePath)
140
+ : diagnostic.filePath;
141
+
142
+ if (
143
+ !preparedFile ||
144
+ !diagnostic.filePath ||
145
+ diagnostic.line === undefined ||
146
+ diagnostic.column === undefined
147
+ ) {
148
+ const remappedDiagnostic = {
149
+ ...diagnostic,
150
+ filePath: remappedFilePath,
151
+ } as T;
152
+ if (hasRelatedInformation(diagnostic)) {
153
+ (remappedDiagnostic as MergedDiagnostic).relatedInformation = diagnostic.relatedInformation?.map(
154
+ (relatedInformation) =>
155
+ remapPreparedDiagnosticRange(
156
+ relatedInformation,
157
+ relatedInformation.filePath
158
+ ? diagnosticPreparedFiles.get(toSourceFileName(relatedInformation.filePath))
159
+ : undefined,
160
+ diagnosticPreparedFiles,
161
+ ),
162
+ );
163
+ }
164
+ return remappedDiagnostic;
165
+ }
166
+
167
+ const programStart = getPositionOfLineAndCharacter(
168
+ preparedFile.rewrittenText,
169
+ diagnostic.line - 1,
170
+ diagnostic.column - 1,
171
+ );
172
+ const programEnd = diagnostic.endLine !== undefined && diagnostic.endColumn !== undefined
173
+ ? getPositionOfLineAndCharacter(
174
+ preparedFile.rewrittenText,
175
+ diagnostic.endLine - 1,
176
+ diagnostic.endColumn - 1,
177
+ )
178
+ : programStart;
179
+ const mappedRange = mapProgramEnclosingRangeToSource(preparedFile, programStart, programEnd);
180
+ const mappedStart = getLineAndCharacterOfPosition(preparedFile.originalText, mappedRange.start);
181
+ const mappedEnd = getLineAndCharacterOfPosition(preparedFile.originalText, mappedRange.end);
182
+
183
+ const remappedDiagnostic = {
184
+ ...diagnostic,
185
+ filePath: remappedFilePath,
186
+ line: mappedStart.line + 1,
187
+ column: mappedStart.character + 1,
188
+ endLine: mappedEnd.line + 1,
189
+ endColumn: mappedEnd.character + 1,
190
+ } as T;
191
+ if (hasRelatedInformation(diagnostic)) {
192
+ (remappedDiagnostic as MergedDiagnostic).relatedInformation = diagnostic.relatedInformation?.map(
193
+ (relatedInformation) => {
194
+ const relatedPreparedFile = relatedInformation.filePath
195
+ ? diagnosticPreparedFiles.get(toSourceFileName(relatedInformation.filePath))
196
+ : undefined;
197
+ return remapPreparedDiagnosticRange(
198
+ relatedInformation,
199
+ relatedPreparedFile,
200
+ diagnosticPreparedFiles,
201
+ );
202
+ },
203
+ );
204
+ }
205
+ return remappedDiagnostic;
206
+ }
207
+
208
+ function remapSoundDiagnostics<T extends MergedDiagnostic>(
209
+ diagnostics: readonly T[],
210
+ diagnosticPreparedFiles: ReadonlyMap<string, PreparedSourceFile>,
211
+ ): T[] {
212
+ return diagnostics.map((diagnostic) => {
213
+ const preparedFile = diagnostic.filePath
214
+ ? diagnosticPreparedFiles.get(toSourceFileName(diagnostic.filePath))
215
+ : undefined;
216
+ return remapPreparedDiagnosticRange(diagnostic, preparedFile, diagnosticPreparedFiles);
217
+ });
218
+ }
219
+
220
+ function remapCompilerDiagnostics<T extends MergedDiagnostic>(
221
+ diagnostics: readonly T[],
222
+ diagnosticPreparedFiles: ReadonlyMap<string, PreparedSourceFile>,
223
+ ): T[] {
224
+ return diagnostics.map((diagnostic) => {
225
+ const preparedFile = diagnostic.filePath
226
+ ? diagnosticPreparedFiles.get(toSourceFileName(diagnostic.filePath))
227
+ : undefined;
228
+ return remapPreparedDiagnosticRange(diagnostic, preparedFile, diagnosticPreparedFiles);
229
+ });
230
+ }
231
+
121
232
  function createProgram(options: CompileProjectOptions): {
122
233
  analysisPreparedProgram: {
123
234
  toProgramFileName(fileName: string): string;
@@ -159,7 +270,7 @@ function createProgram(options: CompileProjectOptions): {
159
270
  analysisPreparedProgram: expandedProgram.analysisPreparedProgram,
160
271
  diagnosticPreparedFiles: expandedProgram.diagnosticPreparedFiles,
161
272
  dispose: () => expandedProgram.dispose(),
162
- frontendDiagnostics: expandedProgram.frontendDiagnostics(),
273
+ frontendDiagnostics: remapDiagnostics(expandedProgram.frontendDiagnostics()),
163
274
  program: expandedProgram.program,
164
275
  runtime: loadedConfig.runtime,
165
276
  tsDiagnosticPrograms: expandedProgram.tsDiagnosticPrograms,
@@ -356,7 +467,10 @@ function collectDiagnostics(
356
467
  workingDirectory,
357
468
  includeSourceFile: (sourceFile) => !sourceFile.isDeclarationFile,
358
469
  });
359
- const universalDiagnostics = runUniversalPolicyAnalysis(analysisContext);
470
+ const universalDiagnostics = remapSoundDiagnostics(
471
+ runUniversalPolicyAnalysis(analysisContext),
472
+ diagnosticPreparedFiles,
473
+ );
360
474
  if (hasErrorDiagnostics(tsDiagnostics)) {
361
475
  return [...frontendDiagnostics, ...tsDiagnostics, ...universalDiagnostics];
362
476
  }
@@ -365,7 +479,7 @@ function collectDiagnostics(
365
479
  ...frontendDiagnostics,
366
480
  ...tsDiagnostics,
367
481
  ...universalDiagnostics,
368
- ...runSoundAnalysis(analysisContext),
482
+ ...remapSoundDiagnostics(runSoundAnalysis(analysisContext), diagnosticPreparedFiles),
369
483
  ];
370
484
  }
371
485
 
@@ -430,14 +544,14 @@ export function compileProject(options: CompileProjectOptions): CompileProjectRe
430
544
 
431
545
  const unsupportedValueClass = findUnsupportedValueClass(program);
432
546
  if (unsupportedValueClass) {
433
- const compilerDiagnostics = [
547
+ const compilerDiagnostics = remapCompilerDiagnostics([
434
548
  createCompilerDiagnosticForNode(unsupportedValueClass, {
435
549
  code: COMPILER_DIAGNOSTIC_CODES.valueClassesRequireJsEmit,
436
550
  message: COMPILER_DIAGNOSTIC_MESSAGES.valueClassesRequireJsEmit,
437
551
  hint:
438
552
  'Use `soundscript build`, `soundscript node`, or another JS emit path for `#[value]`, or remove the annotation before compiling to Wasm.',
439
553
  }),
440
- ];
554
+ ], diagnosticPreparedFiles);
441
555
  return {
442
556
  diagnostics: compilerDiagnostics,
443
557
  output: formatDiagnostics(compilerDiagnostics, options.workingDirectory),
@@ -475,11 +589,11 @@ export function compileProject(options: CompileProjectOptions): CompileProjectRe
475
589
  hint: error.diagnosticHint,
476
590
  notes: error.diagnosticNotes,
477
591
  };
478
- const compilerDiagnostics = [
592
+ const compilerDiagnostics = remapCompilerDiagnostics([
479
593
  error.node
480
594
  ? createCompilerDiagnosticForNode(error.node, diagnosticOverrides)
481
595
  : createCompilerDiagnostic(options.projectPath, diagnosticOverrides),
482
- ];
596
+ ], diagnosticPreparedFiles);
483
597
  return {
484
598
  diagnostics: compilerDiagnostics,
485
599
  output: formatDiagnostics(compilerDiagnostics, options.workingDirectory),