@cplieger/deadset-ts 7.0.0-dev.2 → 7.0.0-dev.4

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 (137) hide show
  1. package/README.md +1 -1
  2. package/dist/src/alias-chain.js +2 -2
  3. package/dist/src/analysis.js +89 -44
  4. package/dist/src/augmentations.js +14 -12
  5. package/dist/src/calls.js +2 -2
  6. package/dist/src/class-members.js +1 -1
  7. package/dist/src/components.js +62 -20
  8. package/dist/src/configuration-files.js +6 -6
  9. package/dist/src/configured-declarations.js +2 -2
  10. package/dist/src/conformance-record.js +1 -1
  11. package/dist/src/contextual-uses.js +71 -14
  12. package/dist/src/convention-rows.js +6 -2
  13. package/dist/src/conventions.js +4 -2
  14. package/dist/src/dependencies.js +81 -27
  15. package/dist/src/deprecation.js +1 -1
  16. package/dist/src/destructuring.js +2 -2
  17. package/dist/src/discover.js +1 -1
  18. package/dist/src/element-access.js +341 -0
  19. package/dist/src/entry-points.js +2 -2
  20. package/dist/src/enum-group.js +2 -2
  21. package/dist/src/erased-stores.js +317 -0
  22. package/dist/src/findings/completion.js +46 -15
  23. package/dist/src/findings/dependencies-and-module-machinery.js +1 -1
  24. package/dist/src/findings/emitters.js +1 -1
  25. package/dist/src/findings/self-check.js +17 -4
  26. package/dist/src/findings-pass.js +6 -5
  27. package/dist/src/framework-lifecycle.js +1 -1
  28. package/dist/src/html-entries.js +1 -1
  29. package/dist/src/implementations.js +2 -2
  30. package/dist/src/in-operands.js +30 -0
  31. package/dist/src/indexed-access.js +264 -125
  32. package/dist/src/injection-container.js +1 -1
  33. package/dist/src/interface-satisfaction.js +2 -2
  34. package/dist/src/intra-function-parts.js +26 -12
  35. package/dist/src/inventory.js +26 -1
  36. package/dist/src/keyof-comparisons.js +170 -0
  37. package/dist/src/kinds.js +17 -7
  38. package/dist/src/manifest.js +6 -16
  39. package/dist/src/matrix.js +20 -2
  40. package/dist/src/member-names.js +1 -1
  41. package/dist/src/module-declarations.js +1 -1
  42. package/dist/src/namespace-objects.js +1 -1
  43. package/dist/src/outside-tests.js +1 -1
  44. package/dist/src/overrides.js +2 -2
  45. package/dist/src/query.js +4 -1
  46. package/dist/src/references.js +13 -2
  47. package/dist/src/reflective-lookup.js +2 -2
  48. package/dist/src/report.js +1 -1
  49. package/dist/src/reporters/sarif.js +4 -0
  50. package/dist/src/roots.js +1 -1
  51. package/dist/src/serialization-contract.js +45 -16
  52. package/dist/src/session.js +15 -8
  53. package/dist/src/signatures.js +1 -1
  54. package/dist/src/stores.js +1 -1
  55. package/dist/src/suppress-file.js +58 -6
  56. package/dist/src/suppress-inline.js +14 -5
  57. package/dist/src/suppress.js +54 -24
  58. package/dist/src/symbol-keys.js +1 -1
  59. package/dist/src/type-errors.js +1 -1
  60. package/dist/src/type-query-alias.js +1 -1
  61. package/dist/src/unimplemented-callees.js +312 -0
  62. package/dist/src/value-flow.js +1 -1
  63. package/dist/src/value-reach.js +8 -8
  64. package/dist/src/verbs/analyze.js +11 -9
  65. package/dist/src/workspace-programs.js +2 -2
  66. package/jsr.json +1 -1
  67. package/package.json +3 -3
  68. package/src/alias-chain.ts +2 -2
  69. package/src/analysis.ts +135 -52
  70. package/src/augmentations.ts +14 -11
  71. package/src/calls.ts +2 -2
  72. package/src/class-members.ts +2 -2
  73. package/src/component-layer.ts +1 -1
  74. package/src/components.ts +103 -21
  75. package/src/configuration-files.ts +6 -5
  76. package/src/configured-declarations.ts +2 -6
  77. package/src/conformance-record.ts +1 -1
  78. package/src/contextual-uses.ts +82 -18
  79. package/src/convention-rows.ts +6 -2
  80. package/src/conventions.ts +11 -2
  81. package/src/decorator.ts +1 -1
  82. package/src/dependencies.ts +108 -31
  83. package/src/deprecation.ts +1 -1
  84. package/src/destructuring.ts +2 -2
  85. package/src/discover.ts +2 -2
  86. package/src/element-access.ts +446 -0
  87. package/src/entry-points.ts +2 -2
  88. package/src/enum-group.ts +2 -2
  89. package/src/erased-stores.ts +413 -0
  90. package/src/file-facts.ts +3 -1
  91. package/src/findings/completion.ts +54 -20
  92. package/src/findings/dependencies-and-module-machinery.ts +1 -1
  93. package/src/findings/emitters.ts +1 -1
  94. package/src/findings/self-check.ts +18 -4
  95. package/src/findings-pass.ts +12 -5
  96. package/src/framework-lifecycle.ts +2 -2
  97. package/src/html-entries.ts +1 -1
  98. package/src/implementations.ts +2 -2
  99. package/src/in-operands.ts +45 -0
  100. package/src/indexed-access.ts +304 -146
  101. package/src/injection-container.ts +1 -1
  102. package/src/interface-satisfaction.ts +2 -2
  103. package/src/intra-function-parts.ts +42 -17
  104. package/src/inventory.ts +34 -2
  105. package/src/keyof-comparisons.ts +211 -0
  106. package/src/kinds.ts +20 -7
  107. package/src/manifest.ts +6 -21
  108. package/src/matrix.ts +34 -2
  109. package/src/member-names.ts +1 -1
  110. package/src/module-declarations.ts +1 -1
  111. package/src/namespace-objects.ts +1 -1
  112. package/src/outside-tests.ts +1 -1
  113. package/src/overrides.ts +2 -2
  114. package/src/position.ts +1 -1
  115. package/src/query.ts +12 -2
  116. package/src/references.ts +25 -3
  117. package/src/reflective-lookup.ts +2 -2
  118. package/src/report.ts +3 -1
  119. package/src/reporters/sarif.ts +4 -0
  120. package/src/roots.ts +2 -2
  121. package/src/serialization-contract.ts +55 -16
  122. package/src/session.ts +39 -32
  123. package/src/signatures.ts +1 -1
  124. package/src/source-files.ts +1 -1
  125. package/src/stores.ts +1 -1
  126. package/src/suppress-file.ts +68 -5
  127. package/src/suppress-inline.ts +15 -5
  128. package/src/suppress.ts +63 -28
  129. package/src/symbol-keys.ts +2 -2
  130. package/src/type-errors.ts +2 -2
  131. package/src/type-query-alias.ts +1 -1
  132. package/src/unimplemented-callees.ts +411 -0
  133. package/src/value-flow.ts +1 -1
  134. package/src/value-reach.ts +12 -8
  135. package/src/verbs/analyze.ts +15 -9
  136. package/src/workflow-steps.ts +1 -1
  137. package/src/workspace-programs.ts +4 -4
package/src/matrix.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * and intersected, with the dead components computed once over every configuration.
5
5
  */
6
6
 
7
- import { componentsOf, type Component } from "./components.ts";
7
+ import { componentsOf, type Component, type Positional } from "./components.ts";
8
8
  import { graphOf, OUTSIDE, type Graph } from "./graph.ts";
9
9
  import type { InventorySymbol } from "./inventory.ts";
10
10
  import { byPosition } from "./position.ts";
@@ -36,6 +36,8 @@ export interface Configured {
36
36
  * finding.
37
37
  */
38
38
  readonly referenceOnly?: readonly string[] | undefined;
39
+ /** Each declaration's dependents, by id, as the configuration's inventory states them. */
40
+ readonly dependents?: ReadonlyMap<string, readonly string[]> | undefined;
39
41
  }
40
42
 
41
43
  /** The run's configurations, indexed for the sweep. */
@@ -56,6 +58,8 @@ export interface Matrix {
56
58
  * whose project holds it, in the run's order.
57
59
  */
58
60
  readonly heldIn: readonly (readonly string[])[];
61
+ /** Each declaration's dependents, by id, as the first configuration holding it states them. */
62
+ readonly dependents: ReadonlyMap<string, readonly string[]>;
59
63
  }
60
64
 
61
65
  /** One declaration dead in every configuration that holds it. */
@@ -128,7 +132,13 @@ export function matrixOf(per: readonly Configured[]): Matrix {
128
132
  const references: Reference[] = [];
129
133
  const firstSeen = new Map<string, number>();
130
134
  const judged = new Set<string>();
135
+ const dependents = new Map<string, readonly string[]>();
131
136
  per.forEach((one, index) => {
137
+ for (const [id, after] of one.dependents ?? []) {
138
+ if (!dependents.has(id)) {
139
+ dependents.set(id, after);
140
+ }
141
+ }
132
142
  const referenceOnly = new Set(one.referenceOnly);
133
143
  for (const symbol of one.symbols) {
134
144
  const held = holders.get(symbol.id);
@@ -168,6 +178,7 @@ export function matrixOf(per: readonly Configured[]): Matrix {
168
178
  [...new Set(per.flatMap((one) => one.supportFiles ?? []))],
169
179
  ),
170
180
  heldIn: symbols.map((symbol) => holders.get(symbol.id) ?? []),
181
+ dependents,
171
182
  };
172
183
  }
173
184
 
@@ -257,14 +268,16 @@ export function sweepMatrix(matrix: Matrix, input: SweepInput): SweepResult {
257
268
  });
258
269
  });
259
270
 
271
+ const liveUnder = liveAnywhere(union.symbols, per);
260
272
  return {
261
- liveUnder: liveAnywhere(union.symbols, per),
273
+ liveUnder,
262
274
  candidates,
263
275
  components: componentsOf(
264
276
  union,
265
277
  dead.map((one, at) => one && heldBy[at] === undefined),
266
278
  testOfDeadCode,
267
279
  unreferencedTest,
280
+ positionsOf(union, matrix.dependents, liveUnder),
268
281
  ),
269
282
  };
270
283
  }
@@ -286,6 +299,25 @@ export function liveFilesAt(
286
299
  );
287
300
  }
288
301
 
302
+ /**
303
+ * Each declaration's dependents by its place in the union, and per place whether the
304
+ * declaration is live under reachability in some configuration.
305
+ */
306
+ function positionsOf(
307
+ union: Graph,
308
+ dependents: ReadonlyMap<string, readonly string[]>,
309
+ liveUnder: ReadonlyMap<string, readonly Relation[]>,
310
+ ): Positional {
311
+ return {
312
+ dependents: union.symbols.map((symbol) =>
313
+ (dependents.get(symbol.id) ?? []).map((id) => union.at(id)).filter((at) => at !== OUTSIDE),
314
+ ),
315
+ live: union.symbols.map(
316
+ (symbol) => liveUnder.get(symbol.id)?.includes("reachability") === true,
317
+ ),
318
+ };
319
+ }
320
+
289
321
  /** The relations in the order a declaration's entry lists them. */
290
322
  const RELATIONS: readonly Relation[] = ["reference-counting", "reachability"];
291
323
 
@@ -12,7 +12,7 @@ import {
12
12
  isStringLiteral,
13
13
  type Node,
14
14
  type SourceFile,
15
- } from "@typescript/native/unstable/ast";
15
+ } from "@typescript/native/ast";
16
16
  import { nodeKey, type Inventory, type InventorySymbol, type SymbolKind } from "./inventory.ts";
17
17
 
18
18
  /** The kinds of declaration a member name can name. */
@@ -22,7 +22,7 @@ import {
22
22
  SyntaxKind,
23
23
  type Node,
24
24
  type SourceFile,
25
- } from "@typescript/native/unstable/ast";
25
+ } from "@typescript/native/ast";
26
26
  import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
27
27
  import { renderPosition } from "./position.ts";
28
28
  import { UNANSWERED } from "./query.ts";
@@ -31,7 +31,7 @@ import {
31
31
  type Identifier,
32
32
  type Node,
33
33
  type SourceFile,
34
- } from "@typescript/native/unstable/ast";
34
+ } from "@typescript/native/ast";
35
35
 
36
36
  /** What one use of a namespace object reads beyond the names the checker resolves. */
37
37
  type NamespaceUse =
@@ -18,7 +18,7 @@ import {
18
18
  SyntaxKind,
19
19
  type Node,
20
20
  type SourceFile,
21
- } from "@typescript/native/unstable/ast";
21
+ } from "@typescript/native/ast";
22
22
  import { RESOLVED_EXTENSIONS, type ParseFile } from "./configuration-files.ts";
23
23
  import { emittedFrom } from "./emit-map.ts";
24
24
  import type { Host } from "./host.ts";
package/src/overrides.ts CHANGED
@@ -16,11 +16,11 @@ import {
16
16
  SyntaxKind,
17
17
  type HeritageClauseElement,
18
18
  type Node,
19
- } from "@typescript/native/unstable/ast";
19
+ } from "@typescript/native/ast";
20
20
  import type { AliasChains } from "./alias-chain.ts";
21
21
  import { memberComponent } from "./class-members.ts";
22
22
  import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
23
- import { SymbolFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/unstable/sync";
23
+ import { SymbolFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/sync";
24
24
  import { UNANSWERED } from "./query.ts";
25
25
  import { nameComponent } from "./ref.ts";
26
26
  import type { Reference } from "./references.ts";
package/src/position.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SourceFile } from "@typescript/native/unstable/ast";
1
+ import type { SourceFile } from "@typescript/native/ast";
2
2
  import { relativePath } from "./paths.ts";
3
3
 
4
4
  /**
package/src/query.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * form where it has one and otherwise as one batch, one round trip per capped run.
8
8
  */
9
9
 
10
- import { isIdentifier, type Expression, type Node } from "@typescript/native/unstable/ast";
10
+ import { isIdentifier, type Expression, type Node } from "@typescript/native/ast";
11
11
  import type {
12
12
  APIRequestGenerator,
13
13
  Checker,
@@ -18,7 +18,7 @@ import type {
18
18
  SymbolFlags,
19
19
  Symbol as TSSymbol,
20
20
  Type,
21
- } from "@typescript/native/unstable/sync";
21
+ } from "@typescript/native/sync";
22
22
 
23
23
  /**
24
24
  * The greatest number of locations one batched request asks about. A batch is
@@ -205,6 +205,8 @@ export interface Queries {
205
205
  targetOf(type: Type): Answer<Type>;
206
206
  /** The constraint of a type parameter, where it declares one; none for another type. */
207
207
  constraintOf(type: Type): Answer<Type | undefined>;
208
+ /** The object and the index of an indexed-access type; none for another type. */
209
+ indexedAccessOf(type: Type): Answer<{ readonly object: Type; readonly index: Type } | undefined>;
208
210
  /** The symbol a type is declared by. */
209
211
  symbolOfType(type: Type): Answer<TSSymbol | undefined>;
210
212
  signaturesOf(type: Type, kind: SignatureKind): Answer<readonly Signature[]>;
@@ -516,6 +518,14 @@ export function queriesOf(
516
518
  () => type.getConstraint(),
517
519
  )
518
520
  : undefined,
521
+ indexedAccessOf: (type) =>
522
+ type.isIndexedAccessType()
523
+ ? one(
524
+ "getObjectType",
525
+ () => typeLocation(type),
526
+ () => ({ object: type.getObjectType(), index: type.getIndexType() }),
527
+ )
528
+ : undefined,
519
529
  symbolOfType: (type) =>
520
530
  one(
521
531
  "getSymbol",
package/src/references.ts CHANGED
@@ -33,8 +33,8 @@ import {
33
33
  SyntaxKind,
34
34
  type Node,
35
35
  type SourceFile,
36
- } from "@typescript/native/unstable/ast";
37
- import type { Symbol as TSSymbol } from "@typescript/native/unstable/sync";
36
+ } from "@typescript/native/ast";
37
+ import type { Symbol as TSSymbol } from "@typescript/native/sync";
38
38
  import { aliasChains, type ChainTarget } from "./alias-chain.ts";
39
39
  import { contextualUses, type FlowAt, type LiteralAt } from "./contextual-uses.ts";
40
40
  import { destructuring, propertyReadsOf, type PropertyRead } from "./destructuring.ts";
@@ -43,7 +43,10 @@ import { namespaceUses } from "./namespace-objects.ts";
43
43
  import { globExpression } from "./glob.ts";
44
44
  import { declarationsByName, nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
45
45
  import { augmentationReferences } from "./augmentations.ts";
46
+ import { elementAccessReferences } from "./element-access.ts";
46
47
  import { indexedAccessReferences } from "./indexed-access.ts";
48
+ import { keyofComparisonReferences } from "./keyof-comparisons.ts";
49
+ import { unimplementedCalleeReads } from "./unimplemented-callees.ts";
47
50
  import { moduleDeclarationReferences } from "./module-declarations.ts";
48
51
  import { overrideReferences } from "./overrides.ts";
49
52
  import { dirnamePath, queriedModulePath, relativePath, resolvePath } from "./paths.ts";
@@ -185,6 +188,13 @@ export interface ReferenceCost {
185
188
  readonly contextualBatches: number;
186
189
  /** Per-type lookups for the contextual uses: each distinct type's constituents, properties and symbol. */
187
190
  readonly contextualLookups: number;
191
+ /**
192
+ * The requests the passes that read members through the checker's types make: element
193
+ * accesses, key comparisons, instantiated signatures and values handed to unimplemented
194
+ * callees. They are
195
+ * counted where the caller measures the client, through `requestCount`, and are 0 otherwise.
196
+ */
197
+ readonly typedLookups: number;
188
198
  }
189
199
 
190
200
  /** One project's references, in position order, and what reading them cost. */
@@ -221,6 +231,8 @@ export interface ReferenceOptions {
221
231
  * reference to a declaration of `held`, the target's, is recorded.
222
232
  */
223
233
  readonly consumer?: ConsumerSide;
234
+ /** The client's running request count, where the caller measures the client. */
235
+ readonly requestCount?: () => number;
224
236
  }
225
237
 
226
238
  /**
@@ -1556,12 +1568,21 @@ export function references<Brand>(
1556
1568
  }
1557
1569
  }
1558
1570
 
1571
+ let typedLookups = 0;
1559
1572
  if (consumer === undefined) {
1560
1573
  found.push(...overrideReferences(project, held, chains, new Set(tests)));
1574
+ const before = options.requestCount?.() ?? 0;
1561
1575
  found.push(...indexedAccessReferences(project, held, chains, root, new Set(tests)));
1576
+ found.push(...elementAccessReferences(project, held, chains, root, new Set(tests)));
1577
+ found.push(...keyofComparisonReferences(project, held, chains, root, new Set(tests)));
1578
+ found.push(...unimplementedCalleeReads(project, held, chains, root, new Set(tests)));
1579
+ typedLookups = (options.requestCount?.() ?? 0) - before;
1562
1580
  found.push(...moduleDeclarationReferences(project, held, root, new Set(tests)));
1563
1581
  }
1564
- found.push(...augmentationReferences(held, found));
1582
+ const scripts = new Set(
1583
+ walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId),
1584
+ );
1585
+ found.push(...augmentationReferences(held, found, scripts));
1565
1586
  found.sort(
1566
1587
  (a, b) =>
1567
1588
  byPosition(a.position, b.position) ||
@@ -1586,6 +1607,7 @@ export function references<Brand>(
1586
1607
  patternLookups: destructured.cost.patternLookups,
1587
1608
  contextualBatches: contextual.batches,
1588
1609
  contextualLookups: contextual.lookups,
1610
+ typedLookups,
1589
1611
  },
1590
1612
  };
1591
1613
  }
@@ -22,8 +22,8 @@ import {
22
22
  isStringLiteral,
23
23
  type Node,
24
24
  type SourceFile,
25
- } from "@typescript/native/unstable/ast";
26
- import { TypeFlags, type Type } from "@typescript/native/unstable/sync";
25
+ } from "@typescript/native/ast";
26
+ import { TypeFlags, type Type } from "@typescript/native/sync";
27
27
  import type { DetectorInput, Evidence } from "./exempt.ts";
28
28
  import { memberNames } from "./member-names.ts";
29
29
  import { renderPosition } from "./position.ts";
package/src/report.ts CHANGED
@@ -19,7 +19,7 @@ import type { StaleSuppression } from "./findings/self-check.ts";
19
19
  import type { TestFileRule } from "./references.ts";
20
20
 
21
21
  /** The version of the report schema this analyzer writes a report to. */
22
- const SCHEMA_VERSION = "8.0.0";
22
+ const SCHEMA_VERSION = "8.1.0";
23
23
 
24
24
  /** Every report schema version this analyzer reads: the one the Contract it implements admits. */
25
25
  export const SCHEMA_VERSIONS_ACCEPTED: readonly string[] = [SCHEMA_VERSION];
@@ -45,6 +45,8 @@ interface WireDetails {
45
45
  readonly position: WirePosition;
46
46
  }[];
47
47
  readonly write_positions?: readonly WirePosition[];
48
+ /** Set under the name-literal rule, which no finding of this analyzer meets; SARIF still maps it. */
49
+ readonly name_literal?: WirePosition;
48
50
  readonly dependency_class?: string;
49
51
  readonly mechanism?: string;
50
52
  readonly entry?: {
@@ -160,6 +160,10 @@ function relatedOf(found: WireFinding): readonly Related[] {
160
160
  for (const at of found.details.write_positions ?? []) {
161
161
  related.push({ label: "write", at, endLine: at.end_line });
162
162
  }
163
+ if (found.details.name_literal !== undefined) {
164
+ const at = found.details.name_literal;
165
+ related.push({ label: "name", at, endLine: at.end_line });
166
+ }
163
167
  for (const one of found.component.members ?? []) {
164
168
  if (
165
169
  declarationKey(one.ref, one.position.path) !==
package/src/roots.ts CHANGED
@@ -20,7 +20,7 @@ import {
20
20
  isVariableStatement,
21
21
  type Node,
22
22
  type SourceFile,
23
- } from "@typescript/native/unstable/ast";
23
+ } from "@typescript/native/ast";
24
24
  import { aliasChains } from "./alias-chain.ts";
25
25
  import type { ParseFile } from "./configuration-files.ts";
26
26
  import { conventionEntries, type Conventions } from "./conventions.ts";
@@ -33,7 +33,7 @@ import { relativePath, resolvePath } from "./paths.ts";
33
33
  import { byPosition } from "./position.ts";
34
34
  import { UNANSWERED } from "./query.ts";
35
35
  import type { ProjectView } from "./session.ts";
36
- import type { Symbol as TSSymbol, Type } from "@typescript/native/unstable/sync";
36
+ import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
37
37
  import { sourceFilesOf } from "./source-files.ts";
38
38
  import { typeQueryAliases } from "./type-query-alias.ts";
39
39
 
@@ -1,12 +1,10 @@
1
1
  /**
2
- * The serialization-contract class: the data members and `toJSON` of every class,
3
- * interface and object type a value's static type names, and of every such type its
4
- * data members' types reach, where the value reaches `JSON.stringify` or a call
5
- * `ts.serializers` names, and those and `toString` where it is passed to an `unknown`
6
- * or `any` parameter of a function outside the analysis. A function of the project
7
- * handing such a parameter of its own to a destination is one, holding back what that
8
- * destination does, to a fixpoint. A function invoked through `Function.prototype.call`,
9
- * `apply` or `bind` is the callee, given the arguments those methods pass it.
2
+ * The serialization-contract class: the data members and `toJSON` of every type a value's
3
+ * static type reaches, where the value reaches `JSON.stringify` or a call `ts.serializers`
4
+ * names, and `toString` too where it reaches an `unknown` or `any` parameter of a function
5
+ * outside the analysis or an erased property of a type the program does not declare. A
6
+ * function of the project handing such a parameter to a destination is one, to a fixpoint,
7
+ * and a function invoked through `call`, `apply` or `bind` is the callee.
10
8
  */
11
9
 
12
10
  import {
@@ -36,14 +34,14 @@ import {
36
34
  type Expression,
37
35
  type Node,
38
36
  type ParameterDeclaration,
39
- } from "@typescript/native/unstable/ast";
37
+ } from "@typescript/native/ast";
40
38
  import {
41
39
  SignatureKind,
42
40
  SymbolFlags,
43
41
  TypeFlags,
44
42
  type Symbol as TSSymbol,
45
43
  type Type,
46
- } from "@typescript/native/unstable/sync";
44
+ } from "@typescript/native/sync";
47
45
  import { aliasChains, type AliasChains } from "./alias-chain.ts";
48
46
  import { calleeName, resolvedTargets, valuesPassed } from "./calls.ts";
49
47
  import { classMembers, memberComponent, typesAt } from "./class-members.ts";
@@ -59,6 +57,7 @@ import { nodeKey, type Inventory } from "./inventory.ts";
59
57
  import { renderPosition } from "./position.ts";
60
58
  import { must } from "./query.ts";
61
59
  import type { ProjectView } from "./session.ts";
60
+ import { erasedStores, type ErasedStores } from "./erased-stores.ts";
62
61
  import { valueReach, type Reached } from "./value-reach.ts";
63
62
 
64
63
  /** The serializer every run reads, whatever the configuration names. */
@@ -527,13 +526,20 @@ function destinations<Brand>(
527
526
  return { invocation, at, mayReceive };
528
527
  }
529
528
 
530
- /** One argument whose value carries classes and object types of the target into a destination. */
529
+ /** One value that carries classes and object types of the target into a destination. */
531
530
  interface Flow {
532
531
  readonly site: Node;
533
532
  readonly classes: ReadonlySet<string>;
534
533
  /** The members of the interfaces and object types the value carries. */
535
534
  readonly members: readonly TSSymbol[];
536
535
  readonly to: Destination;
536
+ /** What a retention names the destination by. */
537
+ readonly detail: string;
538
+ }
539
+
540
+ /** What two reaches carry together. */
541
+ function joined(a: Reached, b: Reached): Reached {
542
+ return { classes: new Set([...a.classes, ...b.classes]), members: [...a.members, ...b.members] };
537
543
  }
538
544
 
539
545
  /** Each argument of a call that can receive one whose value carries a class of the target. */
@@ -542,8 +548,9 @@ function flowsOf<Brand>(
542
548
  held: Inventory,
543
549
  sites: readonly Site[],
544
550
  to: Destinations,
551
+ stores: ErasedStores,
545
552
  ): readonly Flow[] {
546
- const reach = valueReach(project, held);
553
+ const reach = valueReach(project, held, stores.inside);
547
554
  const arguments_ = sites.flatMap((site) =>
548
555
  to.mayReceive(site.call)
549
556
  ? to.invocation(site.call).arguments.flatMap((argument, index) => {
@@ -560,7 +567,7 @@ function flowsOf<Brand>(
560
567
  const typeOf = new Map<Expression, Type | undefined>(
561
568
  asked.map((one, index) => [one.value, types[index]]),
562
569
  );
563
- return arguments_.flatMap((one) => {
570
+ const passed = arguments_.flatMap((one) => {
564
571
  const type = typeOf.get(one.value);
565
572
  let reached: Reached = { classes: new Set<string>(), members: [] };
566
573
  if (one.value.kind === SyntaxKind.ThisKeyword) {
@@ -571,12 +578,38 @@ function flowsOf<Brand>(
571
578
  } else if (type !== undefined) {
572
579
  reached = reach.reachedFrom(type);
573
580
  }
581
+ for (const stored of stores.ofVariable(one.value) ?? []) {
582
+ reached = joined(reached, reach.reachedFrom(stored));
583
+ }
574
584
  const carries = reached.classes.size > 0 || reached.members.length > 0;
575
585
  const destination = carries ? to.at(one.site.call, one.argument) : undefined;
576
586
  return destination === undefined
577
587
  ? []
578
- : [{ site: one.value, classes: reached.classes, members: reached.members, to: destination }];
588
+ : [
589
+ {
590
+ site: one.value,
591
+ classes: reached.classes,
592
+ members: reached.members,
593
+ to: destination,
594
+ detail: `passed to ${destination.callee}`,
595
+ },
596
+ ];
597
+ });
598
+ const left = stores.outside.flatMap((store) => {
599
+ const reached = reach.reachedFrom(store.type);
600
+ return reached.classes.size === 0 && reached.members.length === 0
601
+ ? []
602
+ : [
603
+ {
604
+ site: store.value,
605
+ classes: reached.classes,
606
+ members: reached.members,
607
+ to: { reach: "conversions" as const, callee: store.property },
608
+ detail: `stored in the property ${store.property} of a type the program does not declare`,
609
+ },
610
+ ];
579
611
  });
612
+ return [...passed, ...left];
580
613
  }
581
614
 
582
615
  /**
@@ -589,7 +622,13 @@ export function serializationContract<Brand>(input: DetectorInput<Brand>): reado
589
622
  const { project, held, targetRoot } = input;
590
623
  const walked = walkProject(project, held);
591
624
  const chains = aliasChains(project, held);
592
- const flows = flowsOf(project, held, walked.sites, destinations(input, chains, walked));
625
+ const flows = flowsOf(
626
+ project,
627
+ held,
628
+ walked.sites,
629
+ destinations(input, chains, walked),
630
+ erasedStores(project),
631
+ );
593
632
  if (flows.length === 0) {
594
633
  return [];
595
634
  }
@@ -609,7 +648,7 @@ export function serializationContract<Brand>(input: DetectorInput<Brand>): reado
609
648
  const found: Evidence[] = [];
610
649
  for (const flow of flows) {
611
650
  const site = renderPosition(flow.site.getSourceFile(), targetRoot, flow.site.getStart());
612
- const detail = `passed to ${flow.to.callee}`;
651
+ const { detail } = flow;
613
652
  for (const member of [...flow.classes].flatMap((id) => members.get(id) ?? [])) {
614
653
  const symbol = byId.get(member.id);
615
654
  const name = symbol === undefined ? "" : (memberComponent(symbol, byId) ?? "");
package/src/session.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { API, SymbolFlags } from "@typescript/native/unstable/sync";
1
+ import { API, SymbolFlags } from "@typescript/native/sync";
2
2
  import type {
3
3
  APIRequestGenerator,
4
4
  CreateSnapshotProgramParams,
@@ -12,8 +12,8 @@ import type {
12
12
  Snapshot,
13
13
  Symbol as TSSymbol,
14
14
  TimingInfo,
15
- } from "@typescript/native/unstable/sync";
16
- import type { Node, SourceFile } from "@typescript/native/unstable/ast";
15
+ } from "@typescript/native/sync";
16
+ import type { Node, SourceFile } from "@typescript/native/ast";
17
17
  import { componentLayer, mapperManifest } from "./component-layer.ts";
18
18
  import { DiscoveryError } from "./discover.ts";
19
19
  import type { Host, TemporaryDirectory } from "./host.ts";
@@ -28,13 +28,10 @@ import {
28
28
  } from "./query.ts";
29
29
 
30
30
  /**
31
- * The compiler the analysis reads through. It is a client over a protocol to a
32
- * separate process, so every accessor call is a round trip and a run holds one
33
- * client and one snapshot.
34
- *
35
- * It is an interface because the run's own accounting for that lifetime is not on
36
- * the client's surface: a snapshot reports whether it is disposed and not how many
37
- * a client opened, so the count is observed at this seam.
31
+ * The compiler the analysis reads through: a client over a protocol to a separate
32
+ * process, so every accessor call is a round trip and a run holds one client and one
33
+ * snapshot. It is an interface because a snapshot reports whether it is disposed and not
34
+ * how many a client opened, so the run's count of them is observed at this seam.
38
35
  */
39
36
  export interface Engine {
40
37
  /**
@@ -156,6 +153,8 @@ export function openEngine(options: EngineOptions): Engine {
156
153
  }),
157
154
  }),
158
155
  readsComponents: mapper !== undefined,
156
+ // Entries, not a resolveModuleName callback: this fallback resolves with the importing
157
+ // file's project-reference options, which the compiler never passes to a callback.
159
158
  createModuleResolver: (options, entries) =>
160
159
  api.createModuleResolver(options, {
161
160
  moduleResolutions: { fallback: "resolve", entries: [...entries] },
@@ -187,14 +186,10 @@ export function openEngine(options: EngineOptions): Engine {
187
186
  }
188
187
 
189
188
  /**
190
- * A node of one project's program, carried with that project's identity.
191
- *
192
- * `Brand` is a type parameter and never a value: a view is handed to a visitor
193
- * that introduces its own `Brand`, so two projects' views have handle types the
194
- * compiler will not exchange and a handle cannot reach another project's checker.
195
- * Handles are only meaningful inside the program that produced them, and the
196
- * cross-project step of the analysis compares rendered positions rather than
197
- * handles.
189
+ * A node of one project's program, carried with that project's identity. `Brand` is a
190
+ * type parameter and never a value: each visitor introduces its own, so two projects'
191
+ * handle types do not exchange and a handle cannot reach another project's checker. The
192
+ * cross-project step compares rendered positions, never handles.
198
193
  */
199
194
  export interface Handle<Brand> {
200
195
  readonly node: Node;
@@ -253,14 +248,11 @@ export interface ProjectView<Brand> {
253
248
  */
254
249
  aliasStepOf(symbol: TSSymbol): Answer<TSSymbol | undefined>;
255
250
  /**
256
- * The node one symbol's declaration handle names, read in this project's program,
257
- * or `undefined` where this project's program does not hold that file.
258
- *
259
- * A symbol is shared by every project of the snapshot and remembers the project it
260
- * was first seen in, so resolving its handle without saying which program to read
261
- * would answer out of whichever project reached the symbol first. The project is
262
- * named here instead, and the answer becomes a handle of this project like any
263
- * other. The read is local once the file has been fetched.
251
+ * The node one symbol's declaration handle names, read in this project's program, or
252
+ * `undefined` where this program does not hold that file. A symbol is shared by every
253
+ * project of the snapshot and remembers the one it was first seen in, so the project is
254
+ * named here, or the answer would come from whichever project reached the symbol first.
255
+ * The read is local once the file has been fetched.
264
256
  */
265
257
  declarationAt(handle: NodeHandle): Handle<Brand> | undefined;
266
258
  }
@@ -272,6 +264,13 @@ export interface ProjectView<Brand> {
272
264
  */
273
265
  export type ProjectVisitor<Result> = <Brand>(project: ProjectView<Brand>) => Result;
274
266
 
267
+ /** One project a session opened: its configuration and the files of its own program. */
268
+ export interface OpenedFiles {
269
+ readonly configFile: string;
270
+ /** The program's source files no default library and no external library holds. */
271
+ readonly fileNames: () => readonly string[];
272
+ }
273
+
275
274
  /**
276
275
  * The source files of one program that `own` admits, in the program's order, none of
277
276
  * them one of the compiler's default libraries.
@@ -487,10 +486,10 @@ function projectsOf(
487
486
  /**
488
487
  * Runs `visit` over every project the compiler configurations name, inside one never updated
489
488
  * snapshot of one client, and answers in their order. A configuration `programs` describes is
490
- * built as that program, or opened from the configuration it writes, instead of as a project.
491
- * One the snapshot holds nothing for ends the run under `"refuse"` and is named in the result
492
- * under `"record"`. `last` runs after the last visit while the client still answers; the
493
- * snapshot is disposed and the client released after it, whether the visit completed or threw.
489
+ * built as that program, or opened from the configuration it writes. One the snapshot holds
490
+ * nothing for ends the run under `"refuse"` and is named in the result under `"record"`.
491
+ * `first` runs over every opened project before the first visit and `last` after the last,
492
+ * both while the client answers; the snapshot and the client are then released.
494
493
  */
495
494
  export function runSession<Result>(
496
495
  engine: Engine,
@@ -499,6 +498,7 @@ export function runSession<Result>(
499
498
  unopened: Unopened = "refuse",
500
499
  programs: ReadonlyMap<string, WorkspaceProgram> = new Map(),
501
500
  last: () => void = () => undefined,
501
+ first: (opened: readonly OpenedFiles[]) => void = () => undefined,
502
502
  ): SessionResult<Result> {
503
503
  try {
504
504
  const built: string[] = [];
@@ -523,7 +523,7 @@ export function runSession<Result>(
523
523
  try {
524
524
  const opened = projectsOf(snapshot, built, written);
525
525
  const log = new UnansweredLog();
526
- const projects: Result[] = [];
526
+ const views: ProjectView<unknown>[] = [];
527
527
  const missing: string[] = [];
528
528
  for (const configFile of configFiles) {
529
529
  const project = opened.get(configFile);
@@ -534,8 +534,15 @@ export function runSession<Result>(
534
534
  missing.push(configFile);
535
535
  continue;
536
536
  }
537
- projects.push(visit(viewOf(project, configFile, engine, log)));
537
+ views.push(viewOf(project, configFile, engine, log));
538
538
  }
539
+ first(
540
+ views.map((view) => ({
541
+ configFile: view.configFile,
542
+ fileNames: () => view.ownSourceFiles().map((file) => file.fileName),
543
+ })),
544
+ );
545
+ const projects = views.map((view) => visit(view));
539
546
  last();
540
547
  return {
541
548
  projects,
package/src/signatures.ts CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  type FunctionExpression,
18
18
  type MethodDeclaration,
19
19
  type Node,
20
- } from "@typescript/native/unstable/ast";
20
+ } from "@typescript/native/ast";
21
21
 
22
22
  /** A function-like node that carries a body of its own. */
23
23
  export type SignatureNode =
@@ -4,7 +4,7 @@
4
4
  * compiler emits from it.
5
5
  */
6
6
 
7
- import type { SourceFile } from "@typescript/native/unstable/ast";
7
+ import type { SourceFile } from "@typescript/native/ast";
8
8
  import { commonDirectory, emittedFrom } from "./emit-map.ts";
9
9
  import { dirnamePath, relativePath, resolvePath } from "./paths.ts";
10
10
  import type { ProjectView } from "./session.ts";
package/src/stores.ts CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  isGetAccessorDeclaration,
9
9
  isSetAccessorDeclaration,
10
10
  type Node,
11
- } from "@typescript/native/unstable/ast";
11
+ } from "@typescript/native/ast";
12
12
  import { nodeKey, type Inventory } from "./inventory.ts";
13
13
  import type { Reference } from "./references.ts";
14
14
  import type { ProjectView } from "./session.ts";