@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
@@ -0,0 +1,411 @@
1
+ /**
2
+ * The members read where code the target cannot see receives a value of a target type. An
3
+ * object literal argument of a call or a `new` whose callee has no implementation in the
4
+ * target has every member of the parameter type it is checked against read, nested
5
+ * literals included. An `as` expression over an object no file of the project declares, and
6
+ * a `new` through an unimplemented construct signature, hand the target a value the
7
+ * outside code holds, so every member of the type it is given is read.
8
+ */
9
+
10
+ import {
11
+ isArrowFunction,
12
+ isAsExpression,
13
+ isCallExpression,
14
+ isClassDeclaration,
15
+ isClassExpression,
16
+ isFunctionDeclaration,
17
+ isFunctionExpression,
18
+ isHeritageClause,
19
+ isIdentifier,
20
+ isMethodDeclaration,
21
+ isNewExpression,
22
+ isObjectLiteralExpression,
23
+ isParameterDeclaration,
24
+ isParenthesizedExpression,
25
+ isPropertyAccessExpression,
26
+ isPropertyAssignment,
27
+ isPropertyDeclaration,
28
+ isSatisfiesExpression,
29
+ isTypeReferenceNode,
30
+ isVariableDeclaration,
31
+ type CallExpression,
32
+ type Expression,
33
+ type NewExpression,
34
+ type Node,
35
+ type ObjectLiteralExpression,
36
+ type ParameterDeclaration,
37
+ type SourceFile,
38
+ } from "@typescript/native/ast";
39
+ import { SymbolFlags, type Symbol as TSSymbol, type Type } from "@typescript/native/sync";
40
+ import type { AliasChains } from "./alias-chain.ts";
41
+ import { nodeKey, type Inventory } from "./inventory.ts";
42
+ import { renderPosition } from "./position.ts";
43
+ import { UNANSWERED } from "./query.ts";
44
+ import type { Reference } from "./references.ts";
45
+ import type { ProjectView } from "./session.ts";
46
+
47
+ /** One call or construction passing an object literal, and the declaration that writes it. */
48
+ interface Passing {
49
+ readonly file: SourceFile;
50
+ readonly from: string;
51
+ readonly call: CallExpression | NewExpression;
52
+ }
53
+
54
+ /** A node where an outside value takes a type of the target, and the declaration that writes it. */
55
+ interface HandedOver {
56
+ readonly file: SourceFile;
57
+ readonly from: string;
58
+ readonly node: Expression;
59
+ }
60
+
61
+ /** The value an `as` expression asserts a type of, through every `as` and parenthesis around it. */
62
+ function castOperand(node: Expression): Expression {
63
+ let at = node;
64
+ while (isAsExpression(at) || isParenthesizedExpression(at)) {
65
+ at = at.expression;
66
+ }
67
+ return at;
68
+ }
69
+
70
+ /** Whether a declaration node carries a body of statements or an expression. */
71
+ function hasBody(node: Node): boolean {
72
+ return (node as { readonly body?: Node }).body !== undefined;
73
+ }
74
+
75
+ function isFunctionLike(node: Node | undefined): boolean {
76
+ return (
77
+ node !== undefined &&
78
+ (isArrowFunction(node) ||
79
+ isFunctionExpression(node) ||
80
+ isFunctionDeclaration(node) ||
81
+ isClassExpression(node))
82
+ );
83
+ }
84
+
85
+ /** The name a declaration is declared under, where it has one an identifier spells. */
86
+ function nameOf(node: Node): string | undefined {
87
+ const name = (node as { readonly name?: Node }).name;
88
+ return name !== undefined && isIdentifier(name) ? name.text : undefined;
89
+ }
90
+
91
+ /**
92
+ * Whether an overload signature has its implementation beside it: a declaration of the same
93
+ * name and kind, in the same statement list or class body, that carries a body.
94
+ */
95
+ function overloadImplemented(node: Node): boolean {
96
+ if (!isFunctionDeclaration(node) && !isMethodDeclaration(node)) {
97
+ return false;
98
+ }
99
+ const name = nameOf(node);
100
+ let implemented = false;
101
+ node.parent.forEachChild((sibling) => {
102
+ implemented ||= sibling.kind === node.kind && nameOf(sibling) === name && hasBody(sibling);
103
+ });
104
+ return implemented;
105
+ }
106
+
107
+ /** Every object literal one argument holds, the argument itself included, through nested property values. */
108
+ function literalsOf(argument: Expression): readonly ObjectLiteralExpression[] {
109
+ if (!isObjectLiteralExpression(argument)) {
110
+ return [];
111
+ }
112
+ return [
113
+ argument,
114
+ ...argument.properties.flatMap((property) =>
115
+ isPropertyAssignment(property) ? literalsOf(property.initializer) : [],
116
+ ),
117
+ ];
118
+ }
119
+
120
+ /**
121
+ * The reads the object-literal arguments of one project's own files make of the members of
122
+ * the types they are checked against, where the callee has no implementation in the target.
123
+ */
124
+ export function unimplementedCalleeReads<Brand>(
125
+ project: ProjectView<Brand>,
126
+ held: Inventory,
127
+ chains: AliasChains,
128
+ root: string,
129
+ testFiles: ReadonlySet<string>,
130
+ ): readonly Reference[] {
131
+ const queries = project.queries;
132
+ const own = project.ownPaths();
133
+ const passing: Passing[] = [];
134
+ const implementing: Node[] = [];
135
+ const asserted: (HandedOver & { readonly operand: Expression })[] = [];
136
+ const constructed: Passing[] = [];
137
+ const handing: (CallExpression | NewExpression)[] = [];
138
+ for (const file of project.ownSourceFiles()) {
139
+ const fileId = held.declarations.get(nodeKey(file, file));
140
+ if (fileId === undefined) {
141
+ continue;
142
+ }
143
+ const visit = (node: Node, from: string): void => {
144
+ const holder = held.declarations.get(nodeKey(file, node)) ?? from;
145
+ if (
146
+ (isCallExpression(node) || isNewExpression(node)) &&
147
+ (node.arguments ?? []).some((argument) => isObjectLiteralExpression(argument))
148
+ ) {
149
+ passing.push({ file, from: holder, call: node });
150
+ }
151
+ if (isNewExpression(node)) {
152
+ constructed.push({ file, from: holder, call: node });
153
+ }
154
+ if (
155
+ (isCallExpression(node) || isNewExpression(node)) &&
156
+ (node.arguments ?? []).some(
157
+ (argument) => isFunctionLike(argument) || isIdentifier(argument),
158
+ )
159
+ ) {
160
+ handing.push(node);
161
+ }
162
+ if (isAsExpression(node) && !isAsExpression(node.parent)) {
163
+ asserted.push({ file, from: holder, node, operand: castOperand(node) });
164
+ }
165
+ if (isClassDeclaration(node) || isClassExpression(node)) {
166
+ for (const clause of node.heritageClauses ?? []) {
167
+ if (isHeritageClause(clause)) {
168
+ implementing.push(
169
+ ...clause.types.map((one) =>
170
+ isTypeReferenceNode(one) ? one.typeName : one.expression,
171
+ ),
172
+ );
173
+ }
174
+ }
175
+ }
176
+ // An object literal typed as an interface implements that interface's methods.
177
+ if (
178
+ isVariableDeclaration(node) &&
179
+ node.type !== undefined &&
180
+ isTypeReferenceNode(node.type) &&
181
+ node.initializer !== undefined &&
182
+ isObjectLiteralExpression(node.initializer)
183
+ ) {
184
+ implementing.push(node.type.typeName);
185
+ }
186
+ if (
187
+ (isSatisfiesExpression(node) || isAsExpression(node)) &&
188
+ isObjectLiteralExpression(node.expression) &&
189
+ isTypeReferenceNode(node.type)
190
+ ) {
191
+ implementing.push(node.type.typeName);
192
+ }
193
+ node.forEachChild((child) => {
194
+ visit(child, holder);
195
+ });
196
+ };
197
+ file.forEachChild((child) => {
198
+ visit(child, fileId);
199
+ });
200
+ }
201
+ if (passing.length === 0 && asserted.length === 0 && constructed.length === 0) {
202
+ return [];
203
+ }
204
+
205
+ const resolved = (symbol: TSSymbol | undefined | typeof UNANSWERED): TSSymbol | undefined => {
206
+ if (symbol === UNANSWERED || symbol === undefined) {
207
+ return undefined;
208
+ }
209
+ if ((symbol.flags & SymbolFlags.Alias) === 0) {
210
+ return symbol;
211
+ }
212
+ const target = queries.aliased(symbol);
213
+ return target === UNANSWERED ? undefined : target;
214
+ };
215
+ const declarationsOf = (symbol: TSSymbol | undefined): readonly Node[] =>
216
+ (symbol?.declarations ?? []).flatMap((handle) => {
217
+ const node = project.declarationAt(handle)?.node;
218
+ return node === undefined ? [] : [node];
219
+ });
220
+
221
+ const implemented = new Set<Node>(
222
+ queries.symbolsAt(implementing).flatMap((symbol) => declarationsOf(resolved(symbol))),
223
+ );
224
+
225
+ /** Whether a declaration is a function or a class of the target, or a variable or property it initializes. */
226
+ const ownFunction = (node: Node): boolean =>
227
+ own.has(node.getSourceFile().fileName) &&
228
+ (isFunctionLike(node) ||
229
+ ((isVariableDeclaration(node) || isPropertyAssignment(node) || isPropertyDeclaration(node)) &&
230
+ isFunctionLike(node.initializer)));
231
+
232
+ /**
233
+ * Whether a function or a class of the target reaches one parameter of a function of the
234
+ * target, passed for it by a call that names the function. A call through another name
235
+ * is not followed, so its callee keeps no implementation and its literal reads more.
236
+ */
237
+ const reachedParameters = new Map<ParameterDeclaration, boolean>();
238
+ const parameterReached = (parameter: ParameterDeclaration): boolean => {
239
+ const known = reachedParameters.get(parameter);
240
+ if (known !== undefined) {
241
+ return known;
242
+ }
243
+ const fn = parameter.parent;
244
+ const fnName = nameOf(fn) ?? (isVariableDeclaration(fn.parent) ? nameOf(fn.parent) : undefined);
245
+ const at =
246
+ (fn as { readonly parameters?: readonly Node[] }).parameters?.indexOf(parameter) ?? -1;
247
+ const passed = handing.flatMap((call) => {
248
+ const callee = call.expression;
249
+ const name = isPropertyAccessExpression(callee) ? callee.name : callee;
250
+ if (fnName === undefined || !isIdentifier(name) || name.text !== fnName) {
251
+ return [];
252
+ }
253
+ const signature = queries.resolvedSignature(call);
254
+ const handle =
255
+ signature === UNANSWERED || signature === undefined ? undefined : signature.declaration;
256
+ const argument =
257
+ handle === undefined || project.declarationAt(handle)?.node !== fn
258
+ ? undefined
259
+ : (call.arguments ?? [])[at];
260
+ return argument === undefined || at < 0 ? [] : [argument];
261
+ });
262
+ const named = passed.filter((argument) => isIdentifier(argument));
263
+ const reached =
264
+ passed.some((argument) => isFunctionLike(argument)) ||
265
+ queries
266
+ .symbolsAt(named)
267
+ .some((symbol) => declarationsOf(resolved(symbol)).some((node) => ownFunction(node)));
268
+ reachedParameters.set(parameter, reached);
269
+ return reached;
270
+ };
271
+
272
+ /** Whether some function, class or object literal of the target stands for a signature. */
273
+ const standsFor = (passed: Passing): boolean => {
274
+ const signature = queries.resolvedSignature(passed.call);
275
+ const handle =
276
+ signature === UNANSWERED || signature === undefined ? undefined : signature.declaration;
277
+ const declaration = handle === undefined ? undefined : project.declarationAt(handle)?.node;
278
+ if (declaration === undefined) {
279
+ return true;
280
+ }
281
+ if (hasBody(declaration) || overloadImplemented(declaration)) {
282
+ return own.has(handle?.path ?? "");
283
+ }
284
+ const holder = declaration.parent as Node | undefined;
285
+ if (holder !== undefined && implemented.has(holder)) {
286
+ return true;
287
+ }
288
+ const callee = passed.call.expression;
289
+ const name = isPropertyAccessExpression(callee) ? callee.name : callee;
290
+ const [named] = queries.symbolsAt([name]);
291
+ return declarationsOf(resolved(named)).some((node) =>
292
+ isParameterDeclaration(node)
293
+ ? own.has(node.getSourceFile().fileName) && parameterReached(node)
294
+ : ownFunction(node) && !isFunctionLike(node),
295
+ );
296
+ };
297
+
298
+ const literals: { readonly passed: Passing; readonly literal: ObjectLiteralExpression }[] = [];
299
+ for (const passed of passing) {
300
+ if (standsFor(passed)) {
301
+ continue;
302
+ }
303
+ for (const argument of passed.call.arguments ?? []) {
304
+ literals.push(...literalsOf(argument).map((literal) => ({ passed, literal })));
305
+ }
306
+ }
307
+ const contexts = queries.contextualTypes(literals.map((one) => one.literal));
308
+
309
+ const found: Reference[] = [];
310
+ literals.forEach(({ passed, literal }, at) => {
311
+ const context = contexts[at];
312
+ if (context === UNANSWERED || context === undefined) {
313
+ return;
314
+ }
315
+ const parts = queries.constituents(context);
316
+ const position = renderPosition(passed.file, root, literal.getStart());
317
+ for (const part of parts === UNANSWERED ? [context] : parts) {
318
+ const properties = queries.propertiesOf(part);
319
+ for (const property of properties === UNANSWERED ? [] : properties) {
320
+ for (const to of chains.declarationsOf(property)) {
321
+ found.push({
322
+ from: passed.from,
323
+ to,
324
+ position,
325
+ use: "read",
326
+ resolution: "contextual",
327
+ test: testFiles.has(position.path),
328
+ });
329
+ }
330
+ }
331
+ }
332
+ });
333
+ found.push(...handedOver());
334
+ return found;
335
+
336
+ /** Whether a type is an object no file of the project declares, a platform object's among them. */
337
+ function outsideValue(type: Type): boolean {
338
+ if (!type.isObjectType()) {
339
+ return false;
340
+ }
341
+ const symbol = queries.symbolOfType(type);
342
+ const declared = symbol === UNANSWERED ? undefined : symbol;
343
+ return declared?.declarations.every((handle) => !own.has(handle.path)) === true;
344
+ }
345
+
346
+ /** Whether a type is an object type a file of the project declares. */
347
+ function ownObject(type: Type | undefined | typeof UNANSWERED): type is Type {
348
+ const known = type === UNANSWERED ? undefined : type;
349
+ if (!known?.isObjectType()) {
350
+ return false;
351
+ }
352
+ const symbol = queries.symbolOfType(known);
353
+ const declared = symbol === UNANSWERED ? undefined : symbol;
354
+ return declared?.declarations.some((handle) => own.has(handle.path)) === true;
355
+ }
356
+
357
+ /** The reads of every member of each target type an outside value is handed over as. */
358
+ function handedOver(): readonly Reference[] {
359
+ const operandTypes = queries.typesAt(asserted.map((one) => one.operand));
360
+ const fromCasts: HandedOver[] = asserted.filter((_one, at) => {
361
+ const type = operandTypes[at];
362
+ return type !== UNANSWERED && type !== undefined && outsideValue(type);
363
+ });
364
+ const newTypes = queries.typesAt(constructed.map((one) => one.call));
365
+ // A construct signature whose declared result is one of its type parameters names no
366
+ // type the target declares, so only a result the signature itself names is handed over.
367
+ const fromConstructors: HandedOver[] = constructed
368
+ .filter((one, at) => {
369
+ if (!ownObject(newTypes[at]) || standsFor(one)) {
370
+ return false;
371
+ }
372
+ const signature = queries.resolvedSignature(one.call);
373
+ const result =
374
+ signature === UNANSWERED || signature === undefined
375
+ ? undefined
376
+ : queries.returnTypeOf(signature);
377
+ return result !== UNANSWERED && result !== undefined && !result.isTypeParameter();
378
+ })
379
+ .map((one) => ({ file: one.file, from: one.from, node: one.call }));
380
+ const handed = [...fromCasts, ...fromConstructors];
381
+ const types = queries.typesAt(handed.map((one) => one.node));
382
+ const reads: Reference[] = [];
383
+ handed.forEach((one, at) => {
384
+ const type = types[at];
385
+ if (type === UNANSWERED || type === undefined) {
386
+ return;
387
+ }
388
+ const position = renderPosition(one.file, root, one.node.getStart());
389
+ const parts = queries.constituents(type);
390
+ for (const part of parts === UNANSWERED ? [type] : parts) {
391
+ if (!ownObject(part)) {
392
+ continue;
393
+ }
394
+ const properties = queries.propertiesOf(part);
395
+ for (const property of properties === UNANSWERED ? [] : properties) {
396
+ for (const to of chains.declarationsOf(property)) {
397
+ reads.push({
398
+ from: one.from,
399
+ to,
400
+ position,
401
+ use: "read",
402
+ resolution: "contextual",
403
+ test: testFiles.has(position.path),
404
+ });
405
+ }
406
+ }
407
+ }
408
+ });
409
+ return reads;
410
+ }
411
+ }
package/src/value-flow.ts CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  SyntaxKind,
27
27
  type Expression,
28
28
  type Node,
29
- } from "@typescript/native/unstable/ast";
29
+ } from "@typescript/native/ast";
30
30
 
31
31
  /** The operators of an assignment whose right side is stored into the left side's type. */
32
32
  const STORES: ReadonlySet<SyntaxKind> = new Set([
@@ -1,11 +1,10 @@
1
1
  /**
2
2
  * The classes of the target a value of one type carries, and the members of the other
3
- * object types the target writes that it carries, as a serializer walking the value
4
- * reaches them: through union and intersection constituents, array and tuple elements,
5
- * and the data members and index signatures of the target's classes and of the
6
- * interfaces and object types the target writes. The walk stops at `any`, `unknown`, a
7
- * type parameter, a function and an interface or class of another program, so an
8
- * instance held only there is missed.
3
+ * object types the target writes that it carries, as a serializer walking the value reaches
4
+ * them: through constituents, array and tuple elements, the data members and index
5
+ * signatures of the types the target writes, and the values `stored` gives for a type where
6
+ * it erases them. The walk stops at `any`, `unknown`, a type parameter, a function and an
7
+ * interface or class of another program, so an instance held only there is missed.
9
8
  */
10
9
 
11
10
  import {
@@ -14,7 +13,7 @@ import {
14
13
  type ObjectType,
15
14
  type Symbol as TSSymbol,
16
15
  type Type,
17
- } from "@typescript/native/unstable/sync";
16
+ } from "@typescript/native/sync";
18
17
  import { classNames, typesAt } from "./class-members.ts";
19
18
  import type { Inventory } from "./inventory.ts";
20
19
  import { must } from "./query.ts";
@@ -60,7 +59,11 @@ function arrayTargets<Brand>(project: ProjectView<Brand>): ReadonlySet<number> {
60
59
  * The walk over one project's types, against the classes of `held`, the same
61
60
  * project's inventory.
62
61
  */
63
- export function valueReach<Brand>(project: ProjectView<Brand>, held: Inventory): ValueReach {
62
+ export function valueReach<Brand>(
63
+ project: ProjectView<Brand>,
64
+ held: Inventory,
65
+ stored: (type: Type) => readonly Type[] = () => [],
66
+ ): ValueReach {
64
67
  const queries = project.queries;
65
68
  const names = [...classNames(project, held)];
66
69
  const declared = typesAt(
@@ -161,6 +164,7 @@ export function valueReach<Brand>(project: ProjectView<Brand>, held: Inventory):
161
164
  found = must(queries.typeArguments(type));
162
165
  }
163
166
  }
167
+ found = [...found, ...stored(type)];
164
168
  children.set(type.id, found);
165
169
  return found;
166
170
  };
@@ -26,7 +26,7 @@ import { json } from "../reporters/json.ts";
26
26
  import { RENDERINGS } from "../reporters/reporters.ts";
27
27
  import { parseTemplate, TemplateError, type Template } from "../reporters/template.ts";
28
28
  import { resolve } from "../resolve.ts";
29
- import { rowReason, writeBaseline, type Recorded } from "../suppress-file.ts";
29
+ import { otherSchemeRows, rowReason, writeBaseline, type Recorded } from "../suppress-file.ts";
30
30
  import { CONTRACT_VERSION } from "../version.ts";
31
31
  import { EXIT_CLEAN, EXIT_FAILURE, type Verb, type VerbOptions } from "./verb.ts";
32
32
 
@@ -174,13 +174,12 @@ interface Documents {
174
174
  }
175
175
 
176
176
  /**
177
- * Analyzes the scope under the configuration and writes the JSON report to the path
178
- * `--report` names, each rendering `--format` names beside it at the report's path with the
179
- * format's suffix appended, and where `--baseline-write` names a path the baseline a fixpoint
180
- * over the analysis records, the report then being its first round, which reads no
181
- * baseline. The template format renders the template `--template` names. Nothing is written to
182
- * the output stream; diagnostics go to the error stream and the verdict is the exit code, or 0
183
- * under `--exit-code=off`, and a rendering that cannot be written ends the run with 3.
177
+ * Analyzes the scope and writes the JSON report to the path `--report` names, each
178
+ * `--format` rendering beside it with the format's suffix appended, and under
179
+ * `--baseline-write` the baseline a fixpoint over the analysis records, the report being its
180
+ * first round. Diagnostics go to the error stream and nothing to the output stream; the
181
+ * verdict is the exit code, or 0 under `--exit-code=off`, and a rendering that cannot be
182
+ * written ends the run with 3.
184
183
  */
185
184
  export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option, repeated }) => {
186
185
  const reportPath = option("report");
@@ -304,7 +303,14 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
304
303
  }
305
304
  }
306
305
  if (documents.baseline !== undefined) {
307
- host.writeDocument(documents.baseline, writeBaseline(recorded, analyzerProvenance(version)));
306
+ const kept =
307
+ host.kindOf(documents.baseline) === "file"
308
+ ? otherSchemeRows(host.readFile(documents.baseline))
309
+ : [];
310
+ host.writeDocument(
311
+ documents.baseline,
312
+ writeBaseline(recorded, analyzerProvenance(version), kept),
313
+ );
308
314
  }
309
315
  } catch (error: unknown) {
310
316
  err.write(`deadset-ts: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -3,7 +3,7 @@
3
3
  * A workflow file is read as text, line by line, with no YAML parsed.
4
4
  */
5
5
 
6
- import type { SourceFile } from "@typescript/native/unstable/ast";
6
+ import type { SourceFile } from "@typescript/native/ast";
7
7
  import type { Host } from "./host.ts";
8
8
  import { scriptTokens } from "./manifest.ts";
9
9
  import { joinPath, resolvePath } from "./paths.ts";
@@ -6,16 +6,16 @@
6
6
  * member import keeps the project the compiler opens for it.
7
7
  */
8
8
 
9
- import { ModuleKind, ModuleResolutionKind } from "@typescript/native/unstable/sync";
9
+ import { ModuleKind, ModuleResolutionKind } from "@typescript/native/sync";
10
10
  import type {
11
11
  CreateSnapshotProgramParams,
12
12
  ModuleResolutionEntry,
13
13
  ModuleResolver,
14
14
  Program,
15
15
  ResolutionMode,
16
- } from "@typescript/native/unstable/sync";
17
- import { isStringLiteralLikeNode } from "@typescript/native/unstable/ast";
18
- import type { StringLiteralLikeNode } from "@typescript/native/unstable/ast";
16
+ } from "@typescript/native/sync";
17
+ import { isStringLiteralLikeNode } from "@typescript/native/ast";
18
+ import type { StringLiteralLikeNode } from "@typescript/native/ast";
19
19
  import type { Host } from "./host.ts";
20
20
  import { dirnamePath, resolvePath } from "./paths.ts";
21
21
  import type { Engine } from "./session.ts";