@cplieger/deadset-ts 8.0.0-dev.16 → 8.0.0-dev.18

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 (161) hide show
  1. package/dist/src/alias-chain.js +0 -5
  2. package/dist/src/amd.js +81 -0
  3. package/dist/src/analysis.js +82 -91
  4. package/dist/src/augmentations.js +24 -25
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +279 -43
  7. package/dist/src/commands/tools.js +5 -9
  8. package/dist/src/component-files.js +3 -2
  9. package/dist/src/conformance-record.js +1 -1
  10. package/dist/src/consumers.js +6 -9
  11. package/dist/src/contextual-uses.js +1 -17
  12. package/dist/src/contract/config.generated.js +4 -1
  13. package/dist/src/contract/contract.generated.js +2 -2
  14. package/dist/src/contract/report.generated.js +6 -2
  15. package/dist/src/dependencies.js +25 -29
  16. package/dist/src/destructuring.js +1 -19
  17. package/dist/src/element-access.js +179 -164
  18. package/dist/src/entries/build-scripts.js +2 -2
  19. package/dist/src/entries/configuration-files.js +3 -7
  20. package/dist/src/entries/conventions.js +2 -2
  21. package/dist/src/entries/html-entries.js +7 -10
  22. package/dist/src/entries/mdx-entries.js +2 -2
  23. package/dist/src/entries/static-module.js +2 -1
  24. package/dist/src/exempt.js +0 -26
  25. package/dist/src/exposure.js +400 -0
  26. package/dist/src/facts.js +4 -4
  27. package/dist/src/findings/completion.js +44 -21
  28. package/dist/src/findings/emitters.js +22 -33
  29. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  30. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  31. package/dist/src/findings/intra-function.js +3 -3
  32. package/dist/src/findings/reads-and-writes.js +71 -15
  33. package/dist/src/findings/unused-declarations.js +2 -22
  34. package/dist/src/findings/visibility-narrowing.js +14 -20
  35. package/dist/src/framework-lifecycle.js +41 -14
  36. package/dist/src/import-bindings.js +73 -28
  37. package/dist/src/index/sites.js +557 -0
  38. package/dist/src/indexed-access.js +523 -478
  39. package/dist/src/interface-satisfaction.js +1 -1
  40. package/dist/src/inventory.js +18 -0
  41. package/dist/src/keyof-comparisons.js +124 -103
  42. package/dist/src/{components.js → liveness/components.js} +1 -1
  43. package/dist/src/{graph.js → liveness/graph.js} +45 -20
  44. package/dist/src/liveness/matrix.js +99 -0
  45. package/dist/src/liveness/sweep.js +558 -0
  46. package/dist/src/load/expected-module.js +2 -2
  47. package/dist/src/load/specifiers.js +2 -4
  48. package/dist/src/load/synthesize.js +5 -0
  49. package/dist/src/load/workspace-resolution.js +2 -2
  50. package/dist/src/module-declarations.js +70 -85
  51. package/dist/src/overrides.js +134 -112
  52. package/dist/src/paths.js +46 -7
  53. package/dist/src/platform-calls.js +2 -2
  54. package/dist/src/platform-symbols.js +13 -19
  55. package/dist/src/query.js +46 -0
  56. package/dist/src/references/read-rule.js +1 -0
  57. package/dist/src/references/read-rules.js +48 -0
  58. package/dist/src/references/sink.js +237 -0
  59. package/dist/src/references/sources.js +450 -0
  60. package/dist/src/references.js +110 -1285
  61. package/dist/src/roots/configured-declarations.js +3 -0
  62. package/dist/src/roots/published-reach.js +4 -4
  63. package/dist/src/roots/root-set.js +1 -1
  64. package/dist/src/roots/unseen-loads.js +38 -10
  65. package/dist/src/serialization-contract.js +8 -3
  66. package/dist/src/stores.js +4 -2
  67. package/dist/src/symbol-keys.js +89 -83
  68. package/dist/src/tests/file-class.js +76 -0
  69. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  70. package/dist/src/{test-support.js → tests/support.js} +2 -11
  71. package/dist/src/type-parameter-reads.js +178 -139
  72. package/dist/src/unimplemented-callees.js +262 -242
  73. package/dist/src/verbs/analyze.js +3 -3
  74. package/dist/src/verbs/explain.js +75 -132
  75. package/dist/src/verbs/print-retained.js +3 -3
  76. package/jsr.json +1 -1
  77. package/package.json +2 -2
  78. package/src/alias-chain.ts +0 -7
  79. package/src/amd.ts +130 -0
  80. package/src/analysis.ts +125 -128
  81. package/src/augmentations.ts +42 -28
  82. package/src/bindings.ts +460 -0
  83. package/src/calls.ts +338 -46
  84. package/src/commands/tools.ts +5 -11
  85. package/src/component-files.ts +4 -2
  86. package/src/conformance-record.ts +1 -1
  87. package/src/consumers.ts +8 -11
  88. package/src/contextual-uses.ts +2 -22
  89. package/src/contract/config.generated.ts +4 -1
  90. package/src/contract/contract.generated.ts +2 -2
  91. package/src/contract/report.generated.ts +6 -2
  92. package/src/dependencies.ts +27 -28
  93. package/src/destructuring.ts +1 -31
  94. package/src/element-access.ts +188 -182
  95. package/src/entries/build-scripts.ts +2 -2
  96. package/src/entries/configuration-files.ts +11 -8
  97. package/src/entries/conventions.ts +2 -1
  98. package/src/entries/html-entries.ts +15 -14
  99. package/src/entries/mdx-entries.ts +2 -2
  100. package/src/entries/static-module.ts +2 -1
  101. package/src/exempt.ts +31 -46
  102. package/src/exposure.ts +492 -0
  103. package/src/facts.ts +5 -3
  104. package/src/finding.ts +1 -1
  105. package/src/findings/boundary.ts +9 -0
  106. package/src/findings/completion.ts +48 -23
  107. package/src/findings/emitter.ts +1 -1
  108. package/src/findings/emitters.ts +23 -41
  109. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  110. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  111. package/src/findings/interfaces.ts +3 -3
  112. package/src/findings/intra-function.ts +3 -3
  113. package/src/findings/reads-and-writes.ts +99 -26
  114. package/src/findings/unused-declarations.ts +4 -28
  115. package/src/findings/visibility-narrowing.ts +25 -26
  116. package/src/framework-lifecycle.ts +42 -14
  117. package/src/import-bindings.ts +98 -29
  118. package/src/index/sites.ts +781 -0
  119. package/src/indexed-access.ts +570 -527
  120. package/src/interface-satisfaction.ts +2 -2
  121. package/src/inventory.ts +19 -0
  122. package/src/keyof-comparisons.ts +131 -116
  123. package/src/{components.ts → liveness/components.ts} +3 -3
  124. package/src/{graph.ts → liveness/graph.ts} +93 -27
  125. package/src/liveness/matrix.ts +162 -0
  126. package/src/liveness/sweep.ts +855 -0
  127. package/src/load/expected-module.ts +8 -2
  128. package/src/load/specifiers.ts +8 -5
  129. package/src/load/synthesize.ts +6 -0
  130. package/src/load/workspace-resolution.ts +9 -2
  131. package/src/module-declarations.ts +74 -98
  132. package/src/overrides.ts +162 -134
  133. package/src/paths.ts +54 -8
  134. package/src/platform-calls.ts +3 -3
  135. package/src/platform-symbols.ts +11 -27
  136. package/src/query.ts +81 -0
  137. package/src/references/read-rule.ts +60 -0
  138. package/src/references/read-rules.ts +55 -0
  139. package/src/references/sink.ts +314 -0
  140. package/src/references/sources.ts +632 -0
  141. package/src/references.ts +175 -1733
  142. package/src/report.ts +1 -1
  143. package/src/roots/configured-declarations.ts +3 -0
  144. package/src/roots/published-reach.ts +9 -4
  145. package/src/roots/root-set.ts +1 -1
  146. package/src/roots/unseen-loads.ts +49 -11
  147. package/src/serialization-contract.ts +8 -3
  148. package/src/stores.ts +15 -3
  149. package/src/symbol-keys.ts +102 -102
  150. package/src/tests/file-class.ts +135 -0
  151. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  152. package/src/{test-support.ts → tests/support.ts} +9 -20
  153. package/src/type-parameter-reads.ts +187 -158
  154. package/src/unimplemented-callees.ts +297 -280
  155. package/src/verbs/analyze.ts +3 -2
  156. package/src/verbs/explain.ts +83 -158
  157. package/src/verbs/print-retained.ts +3 -3
  158. package/dist/src/matrix.js +0 -273
  159. package/dist/src/sweep.js +0 -222
  160. package/src/matrix.ts +0 -463
  161. package/src/sweep.ts +0 -361
package/src/analysis.ts CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  } from "./roots/configured-declarations.ts";
18
18
  import { decorator } from "./decorator.ts";
19
19
  import { configurationNeeds, dependenciesOf, projectNeeds } from "./dependencies.ts";
20
+ import { carriersOf, NO_CARRIERS, type Carriers } from "./exposure.ts";
20
21
  import { deprecatedDeclarations, type Deprecation } from "./deprecation.ts";
21
22
  import { moduleIdentity, type LoadedConsumer, type NotBuilt } from "./load/discover.ts";
22
23
  import type { LibraryUnfound } from "./load/classify.ts";
@@ -26,17 +27,16 @@ import { readSources, type SourceFacts } from "./entries/sources.ts";
26
27
  import { readWorkflows, type Workflows } from "./entries/workflow-steps.ts";
27
28
  import { readEdgeSides } from "./edges.ts";
28
29
  import { enumGroup } from "./enum-group.ts";
30
+ import { bindingsOf, distinctBindings, type BindingRecord } from "./bindings.ts";
29
31
  import { generatedFile, generatedFilesOf, type GeneratedFact } from "./generated-file.ts";
30
- import { inOperandNames } from "./in-operands.ts";
32
+ import { inOperandNames } from "./findings/in-operands.ts";
31
33
  import { sourceFilesOf } from "./source-files.ts";
32
34
  import {
33
35
  evidenceRecords,
34
36
  heldRecords,
35
37
  disabledClasses,
36
38
  exemptionsOf,
37
- holdingWhile,
38
39
  isUnansweredRecord,
39
- retainedIn,
40
40
  writtenRecords,
41
41
  type Detector,
42
42
  type Detectors,
@@ -49,22 +49,14 @@ import type { EmitterInput } from "./findings/emitter.ts";
49
49
  import { unmatchedRootFindings, type SelfCheckFacts } from "./findings/self-check.ts";
50
50
  import { frameworkLifecycle } from "./framework-lifecycle.ts";
51
51
  import type { Host } from "./host.ts";
52
- import { implementations, mergeImplementations } from "./implementations.ts";
52
+ import { implementations, mergeImplementations } from "./findings/implementations.ts";
53
53
  import { injectionContainer } from "./injection-container.ts";
54
54
  import { interfaceSatisfaction, settledSatisfaction } from "./interface-satisfaction.ts";
55
55
  import { intraFunctionFacts, projectParts, type ProjectParts } from "./intra-function-parts.ts";
56
56
  import { type Inventory, type InventorySymbol } from "./inventory.ts";
57
57
  import { inventory } from "./inventory/enumerate.ts";
58
58
  import { readManifest, type ManifestEntry } from "./roots/manifest.ts";
59
- import {
60
- liveFilesAt,
61
- matrixOf,
62
- referenceKey,
63
- sweepMatrix,
64
- type Configured,
65
- type Matrix,
66
- type SweepResult,
67
- } from "./matrix.ts";
59
+ import { matrixOf, referenceKey, type Configured, type Matrix } from "./liveness/matrix.ts";
68
60
  import {
69
61
  ancestors,
70
62
  dirnamePath,
@@ -78,7 +70,7 @@ import { byPosition, isComponentFile, type Position } from "./position.ts";
78
70
  import type { TypeErrorSkip, UnansweredCount } from "./report.ts";
79
71
  import { skippedUnits, type SkippedUnit } from "./type-errors.ts";
80
72
  import { reflectiveLookup } from "./reflective-lookup.ts";
81
- import { references, testFileRulesOf, type Reference, type TestFileRule } from "./references.ts";
73
+ import { references, type Reference } from "./references.ts";
82
74
  import {
83
75
  rootSet,
84
76
  unmatchedEverywhere,
@@ -88,18 +80,28 @@ import {
88
80
  } from "./roots/root-set.ts";
89
81
  import type { Scope } from "./scope.ts";
90
82
  import { decodedMembers, serializationContract, type Decoded } from "./serialization-contract.ts";
91
- import { outsideTestReferences, testFilesAt, type OutsideTest } from "./outside-tests.ts";
83
+ import { outsideTestReferences, testFilesAt, type OutsideTest } from "./tests/outside.ts";
92
84
  import type { UnseenLoad } from "./roots/unseen-loads.ts";
93
85
  import type { UnresolvedSpecifier } from "./load/specifiers.ts";
86
+ import { formedForNode } from "./load/synthesize.ts";
94
87
  import type { UnreadInput } from "./load/plan.ts";
95
88
  import { GLOBALS_RUNNER, namesTestGlobals } from "./load/test-globals.ts";
96
- import { supportReferences, testSupportFiles, type TestEvidence } from "./test-support.ts";
89
+ import {
90
+ targetTestFile,
91
+ testFilePaths,
92
+ testFileRulesOf,
93
+ withheldBy,
94
+ type TestFileRule,
95
+ } from "./tests/file-class.ts";
96
+ import { testSupportFiles, type TestEvidence } from "./tests/support.ts";
97
97
  import type { Engine, ProjectView } from "./session.ts";
98
98
  import { accessorsOf, storesOf } from "./stores.ts";
99
99
  import { isPartKind, type SuppressionRecord, type Suppressions, type Verdict } from "./suppress.ts";
100
100
  import { bindRows, readBaseline, readIgnoreFile, type Recorded } from "./suppress-file.ts";
101
101
  import { inlineDirectives, inlineSuppressions, type InlineDirective } from "./suppress-inline.ts";
102
- import type { Exemption, Mode, SweepInput } from "./sweep.ts";
102
+ import { sweep, type Sweep, type SweepPolicy } from "./liveness/sweep.ts";
103
+ import type { Exemption } from "./exempt.ts";
104
+ import type { Mode } from "./tests/file-class.ts";
103
105
  import { readTemplates, templateField } from "./template-field.ts";
104
106
  import { bytewise } from "./text/compare.ts";
105
107
  import { holdsManifest, manifestAt } from "./fs/manifest.ts";
@@ -179,8 +181,12 @@ interface ProjectRead {
179
181
  readonly skipped: readonly SkippedUnit[];
180
182
  /** The project's generated files, by path below the target root, each with what decided it. */
181
183
  readonly generated: ReadonlyMap<string, GeneratedFact>;
184
+ /** The binding identities its generated files publish (`bindings.6`, `bindings.7`). */
185
+ readonly bindings: readonly BindingRecord[];
182
186
  /** What the project's entry sources enter and name, read once. */
183
187
  readonly sources: SourceFacts;
188
+ /** Whether the run formed the program to resolve its modules as Node does (program.36). */
189
+ readonly formedForNode: boolean;
184
190
  }
185
191
 
186
192
  /** One target project the run analyzes, as its stage is handed it. */
@@ -195,6 +201,8 @@ interface ProjectsRead<Answer> {
195
201
  readonly answers: readonly Answer[];
196
202
  /** Every reference a consumer makes to a declaration of the target, each once. */
197
203
  readonly references: readonly Reference[];
204
+ /** The declarations a consumer's read rules held, each once. */
205
+ readonly consumerHeld: readonly string[];
198
206
  /** The test files below the target root no project the run reads holds, parsed. */
199
207
  readonly outsideTests: readonly OutsideTest[];
200
208
  /** The target root's workflow steps, git hooks and makefile, read once for every project. */
@@ -246,6 +254,7 @@ function readProjects<Answer>(
246
254
  workflows,
247
255
  });
248
256
  const rooted = rootSet(project, held, targetRoot, { ...options, sources, skips });
257
+ const generated = generatedFilesOf(own, sources.generated);
249
258
  return {
250
259
  configuration: planned.id,
251
260
  configFile: project.configFile,
@@ -257,8 +266,18 @@ function readProjects<Answer>(
257
266
  .ownSourceFiles()
258
267
  .filter((file) => referenceOnly(file.fileName))
259
268
  .flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
260
- generated: generatedFilesOf(own, sources.generated),
269
+ generated,
270
+ bindings: bindingsOf(
271
+ own.byPath,
272
+ generated,
273
+ new Map(
274
+ held.symbols
275
+ .filter((one) => one.kind === "file")
276
+ .map((one) => [one.position.path, one.ref]),
277
+ ),
278
+ ),
261
279
  sources,
280
+ formedForNode: formedForNode(planned),
262
281
  };
263
282
  };
264
283
  for (const file of project.ownSourceFiles()) {
@@ -267,11 +286,16 @@ function readProjects<Answer>(
267
286
  return stage(project, { configuration: planned.id, read });
268
287
  });
269
288
  const consumed = new Map<string, Reference>();
289
+ const consumerHeld = new Set<string>();
270
290
  for (const { project, consumer } of classified.consumed) {
271
291
  const side = { id: consumer.id, root: consumer.path };
272
- for (const reference of consumerReferences(project, host, targetRoot, side, config, parse)) {
292
+ const read = consumerReferences(project, host, targetRoot, side, config, parse);
293
+ for (const reference of read.references) {
273
294
  consumed.set(referenceKey(reference), reference);
274
295
  }
296
+ for (const id of read.held) {
297
+ consumerHeld.add(id);
298
+ }
275
299
  }
276
300
  const outsideTests = testFilesAt(
277
301
  host,
@@ -281,7 +305,13 @@ function readProjects<Answer>(
281
305
  ),
282
306
  parse,
283
307
  );
284
- return { answers, references: [...consumed.values()], outsideTests, workflows };
308
+ return {
309
+ answers,
310
+ references: [...consumed.values()],
311
+ consumerHeld: [...consumerHeld],
312
+ outsideTests,
313
+ workflows,
314
+ };
285
315
  });
286
316
  }
287
317
 
@@ -386,30 +416,24 @@ export function runRoots(
386
416
  };
387
417
  }
388
418
 
419
+ /** The production mode a configuration sets: a consumer's test reference is classed as `analysis.consumer_tests` says. */
420
+ export function productionMode(config: PartialConfig): Mode {
421
+ return {
422
+ production: true,
423
+ consumerTestsProduction: config.analysis.consumer_tests === "production",
424
+ };
425
+ }
426
+
389
427
  /** The run's sweep, beside the matrix it judged. */
390
428
  export interface RunSweep {
391
429
  readonly matrix: Matrix;
392
- readonly sweep: SweepResult;
393
- /** The exemption records that hold under the sweep. */
394
- readonly exempt: readonly Exemption[];
395
- /**
396
- * The exemption records that held back a declaration some configuration of the run
397
- * would otherwise judge dead, in the declarations' site order.
398
- */
399
- readonly retained: readonly Exemption[];
430
+ readonly sweep: Sweep;
400
431
  /** The derived configurations the run dropped. */
401
432
  readonly notBuilt: readonly NotBuilt[];
402
433
  /** Every question the checker could not answer, each once. */
403
434
  readonly unanswered: readonly UnansweredQuestion[];
404
- /**
405
- * Every declaration an unanswered question could have kept live, and every one only
406
- * such a declaration keeps live. The run reports none of them.
407
- */
408
- readonly heldByUnanswered: readonly string[];
409
435
  /** The lines of the units type errors skipped, on which the run reports nothing. */
410
436
  readonly skipped: readonly SkippedUnit[];
411
- /** The component files the run read, by path below the target root, each once. */
412
- readonly componentFiles: readonly string[];
413
437
  }
414
438
 
415
439
  /** One `<script` start tag of a component file the run read no block for. */
@@ -442,6 +466,8 @@ interface SweptProject<Extra> {
442
466
  readonly unanswered: readonly string[];
443
467
  /** The data members a generic declared outside the analysis fills, which it may read. */
444
468
  readonly filledOutside: readonly string[];
469
+ /** What only a bundler's retry of an import Node leaves unresolved names (program.38). */
470
+ readonly possiblyLoaded: readonly string[];
445
471
  readonly typeErrorSkips: readonly TypeErrorSkip[];
446
472
  readonly skipped: readonly SkippedUnit[];
447
473
  /** The component files the project holds of its own, by path below the target root. */
@@ -457,6 +483,9 @@ interface SweptProject<Extra> {
457
483
  interface ReadProject<Extra> {
458
484
  readonly projectRead: ProjectRead;
459
485
  readonly references: readonly Reference[];
486
+ /** The declarations its read rules held where the checker did not answer. */
487
+ readonly held: readonly string[];
488
+ readonly possiblyLoaded: readonly string[];
460
489
  readonly testFiles: readonly string[];
461
490
  readonly evidence: readonly Exemption[];
462
491
  readonly componentFiles: readonly string[];
@@ -477,6 +506,8 @@ interface ReadRun<Extra> {
477
506
  readonly projects: readonly SweptProject<Extra>[];
478
507
  /** Every declared consumer, each loaded, in the scope's order. */
479
508
  readonly consumers: readonly LoadedConsumer[];
509
+ /** Whether the scope declares a consumer, loaded or not. */
510
+ readonly declaresConsumers: boolean;
480
511
  /** The derived configurations the run dropped. */
481
512
  readonly notBuilt: readonly NotBuilt[];
482
513
  /** Every question the checker could not answer, each once. */
@@ -578,11 +609,13 @@ function readRun<Extra>(
578
609
  );
579
610
  const read = readProjects<ReadProject<Extra>>(engine, host, scope, config, (project, opened) => {
580
611
  const projectRead = opened.read();
612
+ const testFile = targetTestFile(projectRead.sources.testFiles);
581
613
  const resolved = references(project, projectRead.held, targetRoot, {
582
- testFiles: config.ts.test_files,
583
- testFileNames: projectRead.sources.testFiles,
614
+ testFile,
584
615
  directories,
616
+ formedForNode: projectRead.formedForNode,
585
617
  });
618
+ const testFiles = testFilePaths(project, targetRoot, testFile);
586
619
  const evidence = evidenceRecords(
587
620
  {
588
621
  project,
@@ -607,7 +640,9 @@ function readRun<Extra>(
607
640
  return {
608
641
  projectRead,
609
642
  references: resolved.references,
610
- testFiles: resolved.testFilePaths,
643
+ held: resolved.held,
644
+ possiblyLoaded: resolved.possiblyLoaded,
645
+ testFiles,
611
646
  evidence,
612
647
  componentFiles: components.map(pathOf),
613
648
  imports: new Map(
@@ -638,14 +673,14 @@ function readRun<Extra>(
638
673
  decoded: decodedMembers(
639
674
  project,
640
675
  projectRead.held,
641
- productionFiles(project, targetRoot, resolved.testFilePaths),
676
+ productionFiles(project, targetRoot, testFiles),
642
677
  consumers,
643
678
  ),
644
679
  extra: extra(
645
680
  project,
646
681
  projectRead,
647
682
  resolved.references,
648
- resolved.testFilePaths,
683
+ testFiles,
649
684
  resolved.unseenLoads.map((position) => ({
650
685
  configuration: projectRead.configuration,
651
686
  position,
@@ -705,7 +740,7 @@ function readRun<Extra>(
705
740
  testFiles: new Set([...one.testFiles, ...support]),
706
741
  });
707
742
  const exempt = detected.records;
708
- const made = supportReferences([...one.references, ...(satisfied?.references ?? [])], support);
743
+ const made = [...one.references, ...(satisfied?.references ?? [])];
709
744
  const held = new Set(projectRead.held.symbols.map((symbol) => symbol.position.path));
710
745
  const supportFiles = [...support].filter((path) => held.has(path)).sort(bytewise);
711
746
  const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort(bytewise);
@@ -732,13 +767,13 @@ function readRun<Extra>(
732
767
  unanswered: [
733
768
  ...projectRead.held.unanswered,
734
769
  ...projectRead.rooted.unanswered,
735
- ...made
736
- .filter((reference) => reference.resolution === "by-name")
737
- .map((reference) => reference.to),
770
+ ...made.filter((reference) => reference.guessed === true).map((reference) => reference.to),
771
+ ...one.held,
738
772
  ...exempt.filter(isUnansweredRecord).map((record) => record.id),
739
773
  ...one.decoded.unanswered,
740
774
  ],
741
775
  filledOutside: one.decoded.filled,
776
+ possiblyLoaded: one.possiblyLoaded,
742
777
  typeErrorSkips: projectRead.typeErrorSkips,
743
778
  unmappedEntries: projectRead.rooted.unmappedEntries,
744
779
  heldScripts: projectRead.rooted.heldScripts,
@@ -766,11 +801,13 @@ function readRun<Extra>(
766
801
  unansweredHeld: [
767
802
  ...new Set([
768
803
  ...answers.flatMap((one) => one.unanswered),
769
- ...consumed.filter((one) => one.resolution === "by-name").map((one) => one.to),
804
+ ...consumed.filter((one) => one.guessed === true).map((one) => one.to),
805
+ ...read.result.consumerHeld,
770
806
  ]),
771
807
  ],
772
808
  projects,
773
809
  consumers: read.plan.discovery.consumers,
810
+ declaresConsumers: scope.consumers.length > 0,
774
811
  notBuilt: read.classified.notBuilt,
775
812
  unanswered: read.unanswered,
776
813
  componentsUnread: read.plan.workspace?.componentsUnread ?? [],
@@ -785,33 +822,6 @@ function readRun<Extra>(
785
822
  };
786
823
  }
787
824
 
788
- /**
789
- * The run swept under one set of marks with the records that hold. A record a component
790
- * file's markup holds counts only while the file is live, and what it holds may be what
791
- * keeps the file live, so each file's markup joins only once a sweep without it finds the
792
- * file live, until a sweep finds no further file live.
793
- */
794
- function holdingSweep<Extra>(
795
- read: ReadRun<Extra>,
796
- input: Pick<SweepInput, "marked" | "mode">,
797
- ): { readonly swept: SweepInput; readonly result: SweepResult } {
798
- const paths = new Set(read.exempt.flatMap((record) => record.whileLive ?? []));
799
- let live = new Set<string>();
800
- for (;;) {
801
- const swept: SweepInput = {
802
- ...input,
803
- exempt: holdingWhile(read.exempt, live),
804
- unanswered: read.unansweredHeld,
805
- };
806
- const result = sweepMatrix(read.matrix, swept);
807
- const next = new Set(liveFilesAt(read.matrix, result, paths).map((one) => one.position.path));
808
- if (next.size === live.size) {
809
- return { swept, result };
810
- }
811
- live = next;
812
- }
813
- }
814
-
815
825
  /**
816
826
  * The test references exemption evidence written in a test file makes: each is a read of
817
827
  * the declaration it would retain, made by the test file at the evidence's site.
@@ -855,8 +865,8 @@ function countedWrites(
855
865
  mode: Mode,
856
866
  ): ReadonlySet<string> {
857
867
  return new Set(
858
- supportReferences(references, support).flatMap((reference) =>
859
- reference.use === "write" && !(mode.production && reference.test) ? [reference.to] : [],
868
+ references.flatMap((reference) =>
869
+ reference.use === "write" && !withheldBy(mode, reference, support) ? [reference.to] : [],
860
870
  ),
861
871
  );
862
872
  }
@@ -869,8 +879,9 @@ function writtenOnly<Extra>(read: ReadRun<Extra>, mode: Mode): ReadonlySet<strin
869
879
  const written = new Set<string>();
870
880
  const readSome = new Set<string>();
871
881
  for (const { configured } of read.projects) {
882
+ const support = new Set(configured.supportFiles ?? []);
872
883
  for (const reference of configured.references) {
873
- if (mode.production && reference.test) {
884
+ if (withheldBy(mode, reference, support)) {
874
885
  continue;
875
886
  }
876
887
  if (reference.use === "write" && reference.consumer === undefined) {
@@ -883,62 +894,27 @@ function writtenOnly<Extra>(read: ReadRun<Extra>, mode: Mode): ReadonlySet<strin
883
894
  return new Set([...written].filter((id) => !readSome.has(id)));
884
895
  }
885
896
 
886
- /**
887
- * The sweep of a run read once, under one set of marks. Where a question went unanswered,
888
- * the run is swept once more without what that question could have kept live and without
889
- * the references that stood in for its answer, and every declaration that sweep judges
890
- * dead and this one judges live, or dead by another rule, is held by the gap too, as is
891
- * every declaration that makes such a reference.
892
- */
897
+ /** The sweep of a run read once, under one set of marks. */
893
898
  function sweptOf<Extra>(
894
899
  read: ReadRun<Extra>,
895
- input: Pick<SweepInput, "marked" | "mode">,
900
+ input: Pick<SweepPolicy, "marked" | "mode">,
896
901
  ): RunSweep {
897
- const { swept, result } = holdingSweep(read, input);
898
- const exempt = swept.exempt ?? [];
899
- const held = new Set(read.unansweredHeld);
900
- if (held.size > 0) {
901
- // What a declaration reads where the checker did not answer is unknown, so a rule
902
- // over what it reads, the test of dead code among them, cannot judge it.
903
- for (const { configured } of read.projects) {
904
- for (const reference of configured.references) {
905
- if (reference.resolution === "by-name") {
906
- held.add(reference.from);
907
- }
908
- }
909
- }
910
- const dead = new Map(result.candidates.map((candidate) => [candidate.id, candidate]));
911
- const answered = matrixOf(
912
- read.projects.map(({ configured }) => ({
913
- ...configured,
914
- references: configured.references.filter((one) => one.resolution !== "by-name"),
915
- })),
916
- );
917
- const bare: SweepInput = {
918
- ...swept,
919
- exempt: exempt.filter((record) => !isUnansweredRecord(record)),
920
- unanswered: [],
921
- };
922
- for (const candidate of sweepMatrix(answered, bare).candidates) {
923
- const kept = dead.get(candidate.id);
924
- if (
925
- kept?.testOfDeadCode !== candidate.testOfDeadCode ||
926
- kept.relation !== candidate.relation
927
- ) {
928
- held.add(candidate.id);
929
- }
930
- }
931
- }
902
+ const componentFiles = [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(
903
+ bytewise,
904
+ );
932
905
  return {
933
906
  matrix: read.matrix,
934
- sweep: result,
935
- exempt,
936
- retained: retainedIn(read.matrix, swept, writtenOnly(read, input.mode)),
907
+ sweep: sweep(read.matrix, {
908
+ ...input,
909
+ exempt: read.exempt,
910
+ unanswered: read.unansweredHeld,
911
+ componentFiles,
912
+ unseenCallers: !read.declaresConsumers,
913
+ writeOnly: writtenOnly(read, input.mode),
914
+ }),
937
915
  notBuilt: read.notBuilt,
938
916
  unanswered: read.unanswered,
939
- heldByUnanswered: [...held],
940
917
  skipped: read.projects.flatMap((one) => one.skipped),
941
- componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise),
942
918
  };
943
919
  }
944
920
 
@@ -955,7 +931,7 @@ export function runSweep(
955
931
  host: Host,
956
932
  scope: Scope,
957
933
  config: ResolvedConfig,
958
- input: Pick<SweepInput, "marked" | "mode">,
934
+ input: Pick<SweepPolicy, "marked" | "mode">,
959
935
  detectors: Detectors = DETECTORS,
960
936
  ): RunSweep {
961
937
  return sweptOf(
@@ -972,12 +948,14 @@ interface EmitterExtra {
972
948
  readonly implementations: ReturnType<typeof implementations>;
973
949
  readonly heldByInclusion: readonly string[];
974
950
  readonly generated: ReadonlyMap<string, GeneratedFact>;
951
+ readonly bindings: readonly BindingRecord[];
975
952
  readonly inOperands: readonly string[];
976
953
  readonly rooted: Roots;
977
954
  readonly directives: readonly InlineDirective[];
978
955
  readonly declarationsNamed: readonly boolean[];
979
956
  readonly parts: ProjectParts;
980
957
  readonly unseenLoads: readonly UnseenLoad[];
958
+ readonly carriers: Carriers;
981
959
  }
982
960
 
983
961
  /** The project's own files below the target root that are no test file. */
@@ -1024,6 +1002,7 @@ export function readEmitterRun(
1024
1002
  implementations: implementations(project, read.held),
1025
1003
  heldByInclusion: heldByInclusion(project, targetRoot),
1026
1004
  generated: read.generated,
1005
+ bindings: read.bindings,
1027
1006
  inOperands: inOperandNames(project, targetRoot, testFiles),
1028
1007
  rooted: read.rooted,
1029
1008
  directives: inlineDirectives(project, read.held, targetRoot).filter(
@@ -1032,6 +1011,8 @@ export function readEmitterRun(
1032
1011
  declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
1033
1012
  parts: projectParts(project, read.held, references, targetRoot),
1034
1013
  unseenLoads,
1014
+ // Only a closed world reports an unnecessary export, which is all the carriers decide.
1015
+ carriers: config.consumers.complete ? carriersOf(project, read.held) : NO_CARRIERS,
1035
1016
  }),
1036
1017
  );
1037
1018
  // Whether a declaration carries the marker decides its code, so one whose answer went
@@ -1098,7 +1079,7 @@ export function emitterInputOver(
1098
1079
  scope: Scope,
1099
1080
  config: ResolvedConfig,
1100
1081
  read: ReadRun<EmitterExtra>,
1101
- input: Pick<SweepInput, "marked" | "mode">,
1082
+ input: Pick<SweepPolicy, "marked" | "mode">,
1102
1083
  selfCheck: SelfCheckFacts,
1103
1084
  ): EmitterInput {
1104
1085
  const targetRoot = scope.target.path;
@@ -1112,9 +1093,10 @@ export function emitterInputOver(
1112
1093
  stores: storesOf(
1113
1094
  projects.map((one) => ({
1114
1095
  references: one.configured.references,
1096
+ support: new Set(one.configured.supportFiles ?? []),
1115
1097
  accessors: one.extra.accessors,
1116
1098
  })),
1117
- swept.exempt,
1099
+ swept.sweep.exempt,
1118
1100
  input.mode,
1119
1101
  ),
1120
1102
  dependencies: dependenciesOf(
@@ -1143,12 +1125,24 @@ export function emitterInputOver(
1143
1125
  encapsulated: readManifest(host, targetRoot).declaresExports,
1144
1126
  edges: readEdgeSides(host, targetRoot),
1145
1127
  filledOutside: new Set(projects.flatMap((one) => one.filledOutside)),
1128
+ possiblyLoaded: new Set(projects.flatMap((one) => one.possiblyLoaded)),
1129
+ carriers: {
1130
+ named: new Map(
1131
+ [
1132
+ ...Map.groupBy(
1133
+ projects.flatMap((one) => [...one.extra.carriers.named]),
1134
+ ([id]) => id,
1135
+ ),
1136
+ ].map(([id, entries]) => [id, [...new Set(entries.flatMap(([, named]) => named))]]),
1137
+ ),
1138
+ unread: new Set(projects.flatMap((one) => [...one.extra.carriers.unread])),
1139
+ },
1146
1140
  },
1147
1141
  selfCheck,
1148
1142
  intraFunction: intraFunctionFacts(
1149
1143
  projects.map((one) => one.extra.parts),
1150
1144
  projects.flatMap((one) => one.configured.references),
1151
- new Set([...swept.exempt.map((one) => one.id), ...swept.heldByUnanswered]),
1145
+ new Set([...swept.sweep.exempt.map((one) => one.id), ...swept.sweep.held]),
1152
1146
  ),
1153
1147
  };
1154
1148
  }
@@ -1232,6 +1226,7 @@ function readAnalysis(
1232
1226
  ),
1233
1227
  unseenLoads: read.projects.flatMap((one) => one.extra.unseenLoads),
1234
1228
  generated: new Map(read.projects.flatMap((one) => [...one.extra.generated])),
1229
+ bindings: distinctBindings(read.projects.flatMap((one) => one.extra.bindings)),
1235
1230
  },
1236
1231
  configured: read.projects.map((one) => one.configured),
1237
1232
  symbols,
@@ -1340,6 +1335,8 @@ interface RunFacts {
1340
1335
  readonly unseenLoads: readonly UnseenLoad[];
1341
1336
  /** Every generated file of the run, by its path below the target root, each once. */
1342
1337
  readonly generated: ReadonlyMap<string, GeneratedFact>;
1338
+ /** Every binding record the run's generated files publish, each once. */
1339
+ readonly bindings: readonly BindingRecord[];
1343
1340
  }
1344
1341
 
1345
1342
  /** One configuration whose component files a run cannot read. */
@@ -1385,7 +1382,7 @@ export function runAnalysis(
1385
1382
  * declarations of its own the run holds for them.
1386
1383
  */
1387
1384
  export function unansweredCounts(analysis: RunAnalysis): readonly UnansweredCount[] {
1388
- const held = new Set(analysis.swept.heldByUnanswered);
1385
+ const held = new Set(analysis.swept.sweep.held);
1389
1386
  return analysis.run.projects.flatMap((project) => {
1390
1387
  const questions =
1391
1388
  analysis.run.unanswered.filter((one) => one.configFile === project.configFile).length +
@@ -14,7 +14,7 @@ import {
14
14
  type Node,
15
15
  } from "@typescript/native/ast";
16
16
  import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
17
- import { referenceKey } from "./matrix.ts";
17
+ import { referenceKey } from "./liveness/matrix.ts";
18
18
  import type { Reference } from "./references.ts";
19
19
  import type { ProjectView } from "./session.ts";
20
20
  import type { SourceFiles } from "./source-files.ts";
@@ -78,7 +78,7 @@ function holdersOf(
78
78
  * file or a `declare` holds that a `declare global` block, or the top level of a file in
79
79
  * `scripts`, declares, directly or inside a namespace there.
80
80
  */
81
- export function ambientGlobalInterfaces(
81
+ function ambientGlobalInterfaces(
82
82
  held: Inventory,
83
83
  interfaces: readonly string[],
84
84
  scripts: ReadonlySet<string>,
@@ -89,19 +89,31 @@ export function ambientGlobalInterfaces(
89
89
  : interfaces.filter((id) => held.ambient.has(id) && holders(id).global);
90
90
  }
91
91
 
92
+ /** The ambient global interfaces of a project's own files that merge into a declaration outside them. */
93
+ interface GlobalMerges {
94
+ /** The files that are no module, by identifier. */
95
+ readonly scripts: ReadonlySet<string>;
96
+ /** Each such interface, by identifier. */
97
+ readonly interfaces: ReadonlySet<string>;
98
+ /**
99
+ * Each such interface with every interface, namespace and block between it and the global
100
+ * scope: the outside declaration names it wherever a value is typed by it. Where every
101
+ * outside declaration is a package's, not the compiler's library, and declares no member,
102
+ * each member it declares as well: such an interface is a slot the package reads by type,
103
+ * as `keyof` does, in declarations the analysis does not walk.
104
+ */
105
+ readonly kept: readonly string[];
106
+ }
107
+
92
108
  /**
93
- * Each ambient global interface of the project's own files that merges into an interface
94
- * declared outside them, with every interface, namespace and block between it and the
95
- * global scope: the outside declaration names it wherever a value is typed by it. Where
96
- * every outside declaration is a package's, not the compiler's library, and declares no
97
- * member, each member it declares as well: such an interface is a slot the package reads
98
- * by type, as `keyof` does, in declarations the analysis does not walk.
109
+ * Which ambient global interfaces of the project's own files, `files`, merge into an interface
110
+ * declared outside them, and what each merge keeps.
99
111
  */
100
112
  export function mergedGlobalInterfaces<Brand>(
101
113
  project: ProjectView<Brand>,
102
114
  held: Inventory,
103
115
  files: SourceFiles,
104
- ): readonly string[] {
116
+ ): GlobalMerges {
105
117
  const scripts = new Set<string>();
106
118
  const names = new Map<string, Node>();
107
119
  for (const file of files.byName.values()) {
@@ -124,7 +136,7 @@ export function mergedGlobalInterfaces<Brand>(
124
136
  const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
125
137
  const asked = [...names].filter(([id]) => global.has(id));
126
138
  if (asked.length === 0) {
127
- return [];
139
+ return { scripts, interfaces: new Set(), kept: [] };
128
140
  }
129
141
  const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
130
142
  const holders = holdersOf(held, scripts);
@@ -135,24 +147,26 @@ export function mergedGlobalInterfaces<Brand>(
135
147
  const node = project.declarationAt(handle)?.node;
136
148
  return node !== undefined && isInterfaceDeclaration(node) && node.members.length === 0;
137
149
  };
138
- return [
139
- ...new Set(
140
- asked.flatMap(([id], at) => {
141
- const symbol = answers[at];
142
- if (typeof symbol !== "object") {
143
- return [];
144
- }
145
- const outside = symbol.declarations.filter((handle) => !files.byName.has(handle.path));
146
- if (outside.length === 0) {
147
- return [];
148
- }
149
- const members = outside.every(empty)
150
- ? held.symbols.filter((one) => one.parent === id).map((one) => one.id)
151
- : [];
152
- return [id, ...(holders?.(id).ids ?? []), ...members];
153
- }),
154
- ),
155
- ];
150
+ const interfaces = new Set<string>();
151
+ const kept = new Set<string>();
152
+ asked.forEach(([id], at) => {
153
+ const symbol = answers[at];
154
+ const outside =
155
+ typeof symbol === "object"
156
+ ? symbol.declarations.filter((handle) => !files.byName.has(handle.path))
157
+ : [];
158
+ if (outside.length === 0) {
159
+ return;
160
+ }
161
+ interfaces.add(id);
162
+ const members = outside.every(empty)
163
+ ? held.symbols.filter((one) => one.parent === id).map((one) => one.id)
164
+ : [];
165
+ for (const one of [id, ...(holders?.(id).ids ?? []), ...members]) {
166
+ kept.add(one);
167
+ }
168
+ });
169
+ return { scripts, interfaces, kept: [...kept] };
156
170
  }
157
171
 
158
172
  /**