@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.
- package/README.md +1 -1
- package/dist/src/alias-chain.js +2 -2
- package/dist/src/analysis.js +89 -44
- package/dist/src/augmentations.js +14 -12
- package/dist/src/calls.js +2 -2
- package/dist/src/class-members.js +1 -1
- package/dist/src/components.js +62 -20
- package/dist/src/configuration-files.js +6 -6
- package/dist/src/configured-declarations.js +2 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +71 -14
- package/dist/src/convention-rows.js +6 -2
- package/dist/src/conventions.js +4 -2
- package/dist/src/dependencies.js +81 -27
- package/dist/src/deprecation.js +1 -1
- package/dist/src/destructuring.js +2 -2
- package/dist/src/discover.js +1 -1
- package/dist/src/element-access.js +341 -0
- package/dist/src/entry-points.js +2 -2
- package/dist/src/enum-group.js +2 -2
- package/dist/src/erased-stores.js +317 -0
- package/dist/src/findings/completion.js +46 -15
- package/dist/src/findings/dependencies-and-module-machinery.js +1 -1
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/self-check.js +17 -4
- package/dist/src/findings-pass.js +6 -5
- package/dist/src/framework-lifecycle.js +1 -1
- package/dist/src/html-entries.js +1 -1
- package/dist/src/implementations.js +2 -2
- package/dist/src/in-operands.js +30 -0
- package/dist/src/indexed-access.js +264 -125
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +2 -2
- package/dist/src/intra-function-parts.js +26 -12
- package/dist/src/inventory.js +26 -1
- package/dist/src/keyof-comparisons.js +170 -0
- package/dist/src/kinds.js +17 -7
- package/dist/src/manifest.js +6 -16
- package/dist/src/matrix.js +20 -2
- package/dist/src/member-names.js +1 -1
- package/dist/src/module-declarations.js +1 -1
- package/dist/src/namespace-objects.js +1 -1
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +2 -2
- package/dist/src/query.js +4 -1
- package/dist/src/references.js +13 -2
- package/dist/src/reflective-lookup.js +2 -2
- package/dist/src/report.js +1 -1
- package/dist/src/reporters/sarif.js +4 -0
- package/dist/src/roots.js +1 -1
- package/dist/src/serialization-contract.js +45 -16
- package/dist/src/session.js +15 -8
- package/dist/src/signatures.js +1 -1
- package/dist/src/stores.js +1 -1
- package/dist/src/suppress-file.js +58 -6
- package/dist/src/suppress-inline.js +14 -5
- package/dist/src/suppress.js +54 -24
- package/dist/src/symbol-keys.js +1 -1
- package/dist/src/type-errors.js +1 -1
- package/dist/src/type-query-alias.js +1 -1
- package/dist/src/unimplemented-callees.js +312 -0
- package/dist/src/value-flow.js +1 -1
- package/dist/src/value-reach.js +8 -8
- package/dist/src/verbs/analyze.js +11 -9
- package/dist/src/workspace-programs.js +2 -2
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +2 -2
- package/src/analysis.ts +135 -52
- package/src/augmentations.ts +14 -11
- package/src/calls.ts +2 -2
- package/src/class-members.ts +2 -2
- package/src/component-layer.ts +1 -1
- package/src/components.ts +103 -21
- package/src/configuration-files.ts +6 -5
- package/src/configured-declarations.ts +2 -6
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +82 -18
- package/src/convention-rows.ts +6 -2
- package/src/conventions.ts +11 -2
- package/src/decorator.ts +1 -1
- package/src/dependencies.ts +108 -31
- package/src/deprecation.ts +1 -1
- package/src/destructuring.ts +2 -2
- package/src/discover.ts +2 -2
- package/src/element-access.ts +446 -0
- package/src/entry-points.ts +2 -2
- package/src/enum-group.ts +2 -2
- package/src/erased-stores.ts +413 -0
- package/src/file-facts.ts +3 -1
- package/src/findings/completion.ts +54 -20
- package/src/findings/dependencies-and-module-machinery.ts +1 -1
- package/src/findings/emitters.ts +1 -1
- package/src/findings/self-check.ts +18 -4
- package/src/findings-pass.ts +12 -5
- package/src/framework-lifecycle.ts +2 -2
- package/src/html-entries.ts +1 -1
- package/src/implementations.ts +2 -2
- package/src/in-operands.ts +45 -0
- package/src/indexed-access.ts +304 -146
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +2 -2
- package/src/intra-function-parts.ts +42 -17
- package/src/inventory.ts +34 -2
- package/src/keyof-comparisons.ts +211 -0
- package/src/kinds.ts +20 -7
- package/src/manifest.ts +6 -21
- package/src/matrix.ts +34 -2
- package/src/member-names.ts +1 -1
- package/src/module-declarations.ts +1 -1
- package/src/namespace-objects.ts +1 -1
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +2 -2
- package/src/position.ts +1 -1
- package/src/query.ts +12 -2
- package/src/references.ts +25 -3
- package/src/reflective-lookup.ts +2 -2
- package/src/report.ts +3 -1
- package/src/reporters/sarif.ts +4 -0
- package/src/roots.ts +2 -2
- package/src/serialization-contract.ts +55 -16
- package/src/session.ts +39 -32
- package/src/signatures.ts +1 -1
- package/src/source-files.ts +1 -1
- package/src/stores.ts +1 -1
- package/src/suppress-file.ts +68 -5
- package/src/suppress-inline.ts +15 -5
- package/src/suppress.ts +63 -28
- package/src/symbol-keys.ts +2 -2
- package/src/type-errors.ts +2 -2
- package/src/type-query-alias.ts +1 -1
- package/src/unimplemented-callees.ts +411 -0
- package/src/value-flow.ts +1 -1
- package/src/value-reach.ts +12 -8
- package/src/verbs/analyze.ts +15 -9
- package/src/workflow-steps.ts +1 -1
- package/src/workspace-programs.ts +4 -4
package/src/analysis.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One run's passes, composed: the projects discovery names, opened in one snapshot
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* carries a project's identity in its type so a handle cannot cross to another
|
|
8
|
-
* project's checker, and that holds only for views the session itself hands out.
|
|
2
|
+
* One run's passes, composed: the projects discovery names, opened in one snapshot of one
|
|
3
|
+
* client, each read through the stages in dependency order, and the per-project answers
|
|
4
|
+
* merged into the run's. The stages take a project's view, which only this composition
|
|
5
|
+
* makes: a view carries a project's identity in its type so a handle cannot cross to
|
|
6
|
+
* another project's checker, which holds only for views the session hands out.
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
9
|
import { lineOf, readComponent } from "./component-files.ts";
|
|
@@ -40,10 +38,12 @@ import {
|
|
|
40
38
|
readConventions,
|
|
41
39
|
type AppliedConvention,
|
|
42
40
|
type ConventionManifest,
|
|
41
|
+
type Conventions,
|
|
43
42
|
} from "./conventions.ts";
|
|
44
43
|
import { readEdgeSides } from "./edges.ts";
|
|
45
44
|
import { enumGroup } from "./enum-group.ts";
|
|
46
45
|
import { generatedFile, generatedFilesOf } from "./generated-file.ts";
|
|
46
|
+
import { inOperandNames } from "./in-operands.ts";
|
|
47
47
|
import { sourceFilesOf } from "./source-files.ts";
|
|
48
48
|
import {
|
|
49
49
|
evidenceRecords,
|
|
@@ -94,13 +94,26 @@ import {
|
|
|
94
94
|
} from "./type-errors.ts";
|
|
95
95
|
import { reflectiveLookup } from "./reflective-lookup.ts";
|
|
96
96
|
import { references, testFileRulesOf, type Reference, type TestFileRule } from "./references.ts";
|
|
97
|
-
import {
|
|
97
|
+
import {
|
|
98
|
+
roots,
|
|
99
|
+
unmatchedEverywhere,
|
|
100
|
+
type RootKind,
|
|
101
|
+
type RootOptions,
|
|
102
|
+
type Roots,
|
|
103
|
+
} from "./roots.ts";
|
|
98
104
|
import type { Scope } from "./scope.ts";
|
|
99
105
|
import { serializationContract } from "./serialization-contract.ts";
|
|
100
106
|
import { outsideTestReferences, testFilesAt, type OutsideTest } from "./outside-tests.ts";
|
|
101
107
|
import { joinedTestFiles, withJoinedTests } from "./test-joins.ts";
|
|
102
108
|
import { supportReferences, testSupportFiles } from "./test-support.ts";
|
|
103
|
-
import {
|
|
109
|
+
import {
|
|
110
|
+
diagnosticsOf,
|
|
111
|
+
ownedAs,
|
|
112
|
+
runSession,
|
|
113
|
+
type Engine,
|
|
114
|
+
type OpenedFiles,
|
|
115
|
+
type ProjectView,
|
|
116
|
+
} from "./session.ts";
|
|
104
117
|
import { accessorsOf, storesOf } from "./stores.ts";
|
|
105
118
|
import { isPartKind, type SuppressionRecord, type Suppressions, type Verdict } from "./suppress.ts";
|
|
106
119
|
import { bindRows, readBaseline, readIgnoreFile, type Recorded } from "./suppress-file.ts";
|
|
@@ -225,32 +238,73 @@ interface ReadProjects<Answer> {
|
|
|
225
238
|
readonly conventionUses: readonly string[];
|
|
226
239
|
/** The directory of every workspace member, absolute. */
|
|
227
240
|
readonly memberDirs: readonly string[];
|
|
241
|
+
/** The directory of every nested package, absolute. */
|
|
242
|
+
readonly nestedDirs: readonly string[];
|
|
228
243
|
/** The test files below the target root no project the run reads holds, parsed. */
|
|
229
244
|
readonly outsideTests: readonly OutsideTest[];
|
|
230
245
|
}
|
|
231
246
|
|
|
232
247
|
/**
|
|
233
248
|
* The manifests the analysis reads, each once: the target's own, then every workspace
|
|
234
|
-
* member's below the target root.
|
|
249
|
+
* member's and every nested package's below the target root.
|
|
235
250
|
*/
|
|
236
251
|
function conventionManifests(
|
|
237
252
|
host: Host,
|
|
238
253
|
targetRoot: string,
|
|
239
254
|
memberDirs: readonly string[],
|
|
255
|
+
nestedDirs: readonly string[],
|
|
240
256
|
): readonly ConventionManifest[] {
|
|
241
257
|
const found = new Map<string, ConventionManifest>();
|
|
242
258
|
if (host.kindOf(joinPath(targetRoot, "package.json")) === "file") {
|
|
243
259
|
found.set(targetRoot, { dir: targetRoot, path: "package.json" });
|
|
244
260
|
}
|
|
245
|
-
for (const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
261
|
+
for (const [dirs, nested] of [
|
|
262
|
+
[memberDirs, false],
|
|
263
|
+
[nestedDirs, true],
|
|
264
|
+
] as const) {
|
|
265
|
+
for (const dir of dirs) {
|
|
266
|
+
const below = relativePath(targetRoot, dir);
|
|
267
|
+
if (below !== undefined && !found.has(dir)) {
|
|
268
|
+
found.set(dir, { dir, path: `${below}/package.json`, nested });
|
|
269
|
+
}
|
|
249
270
|
}
|
|
250
271
|
}
|
|
251
272
|
return [...found.values()];
|
|
252
273
|
}
|
|
253
274
|
|
|
275
|
+
/**
|
|
276
|
+
* The nested packages of a run: each directory below the target root, in no `node_modules`
|
|
277
|
+
* directory and none of `known`, that holds a `package.json` and lies at or above one of
|
|
278
|
+
* `files`, the program's source files and the compiler configuration files, in path order.
|
|
279
|
+
*/
|
|
280
|
+
function nestedPackageDirs(
|
|
281
|
+
host: Host,
|
|
282
|
+
targetRoot: string,
|
|
283
|
+
files: Iterable<string>,
|
|
284
|
+
known: ReadonlySet<string>,
|
|
285
|
+
): readonly string[] {
|
|
286
|
+
const seen = new Set<string>();
|
|
287
|
+
const found: string[] = [];
|
|
288
|
+
for (const file of files) {
|
|
289
|
+
for (
|
|
290
|
+
let dir = dirnamePath(file);
|
|
291
|
+
dir !== targetRoot && !seen.has(dir) && relativePath(targetRoot, dir) !== undefined;
|
|
292
|
+
dir = dirnamePath(dir)
|
|
293
|
+
) {
|
|
294
|
+
seen.add(dir);
|
|
295
|
+
const below = relativePath(targetRoot, dir) ?? "";
|
|
296
|
+
if (
|
|
297
|
+
!below.split("/").includes("node_modules") &&
|
|
298
|
+
!known.has(dir) &&
|
|
299
|
+
host.kindOf(joinPath(dir, "package.json")) === "file"
|
|
300
|
+
) {
|
|
301
|
+
found.push(dir);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return found.sort(compare);
|
|
306
|
+
}
|
|
307
|
+
|
|
254
308
|
/**
|
|
255
309
|
* Reads every project the scope discovers, and each declared consumer's, in one snapshot,
|
|
256
310
|
* each checked for errors first. `stage` is handed a target project and enumerates and roots
|
|
@@ -268,6 +322,12 @@ function readProjects<Answer>(
|
|
|
268
322
|
): ReadProjects<Answer> {
|
|
269
323
|
const targetRoot = scope.target.path;
|
|
270
324
|
const discovered = discoverProjects(engine, host, scope, config.analysis.configurations);
|
|
325
|
+
if (discovered.configFiles.length === 0 && discovered.notBuilt.length === 0) {
|
|
326
|
+
throw new DiscoveryError(
|
|
327
|
+
`${targetRoot} holds no compiler configuration, so the run holds no program`,
|
|
328
|
+
[],
|
|
329
|
+
);
|
|
330
|
+
}
|
|
271
331
|
const ids = new Map(discovered.projects.map((project) => [project.configFile, project.id]));
|
|
272
332
|
const loads = consumerLoads(engine, host, scope, discovered.configFiles);
|
|
273
333
|
const consumerOf = new Map(
|
|
@@ -283,33 +343,45 @@ function readProjects<Answer>(
|
|
|
283
343
|
discovered.configFiles,
|
|
284
344
|
readManifest(host, targetRoot),
|
|
285
345
|
);
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
346
|
+
const parse = (fileName: string, text: string) => engine.parseSourceFile(fileName, text);
|
|
347
|
+
const memberDirs = workspace?.memberDirs ?? [];
|
|
348
|
+
// The nested packages are known only once every program is open, and every visit reads
|
|
349
|
+
// the conventions and options they decide, so both are set in the session's first step.
|
|
350
|
+
let nestedDirs: readonly string[] = [];
|
|
351
|
+
let conventions = readConventions(host, parse, [], config.ts.disabledConventions);
|
|
352
|
+
let options: RootOptions & { readonly parse: typeof parse; readonly conventions: Conventions };
|
|
353
|
+
const readManifests = (opened: readonly OpenedFiles[]): void => {
|
|
354
|
+
nestedDirs = nestedPackageDirs(
|
|
355
|
+
host,
|
|
356
|
+
normalizePath(absoluteRoot),
|
|
357
|
+
opened
|
|
358
|
+
.filter((one) => !consumerOf.has(one.configFile))
|
|
359
|
+
.flatMap((one) => [one.configFile, ...one.fileNames()]),
|
|
360
|
+
new Set(memberDirs),
|
|
361
|
+
);
|
|
362
|
+
conventions = readConventions(
|
|
363
|
+
host,
|
|
364
|
+
parse,
|
|
365
|
+
conventionManifests(host, absoluteRoot, memberDirs, nestedDirs),
|
|
366
|
+
config.ts.disabledConventions,
|
|
367
|
+
);
|
|
368
|
+
options = {
|
|
369
|
+
host,
|
|
370
|
+
manifest: workspace?.manifest ?? readManifest(host, targetRoot),
|
|
371
|
+
otherPackages: [
|
|
372
|
+
...(workspace?.entries ?? []),
|
|
373
|
+
...packageEntries(host, normalizePath(absoluteRoot), nestedDirs),
|
|
374
|
+
],
|
|
375
|
+
patterns: config.rootPatterns,
|
|
376
|
+
entryFiles: config.ts.entryFiles,
|
|
377
|
+
conventions,
|
|
378
|
+
testFiles: config.ts.testFiles,
|
|
379
|
+
parse,
|
|
380
|
+
publishedAPI: config.targetKind === "library",
|
|
381
|
+
publishedMembers: new Set(
|
|
382
|
+
(workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir),
|
|
302
383
|
),
|
|
303
|
-
|
|
304
|
-
patterns: config.rootPatterns,
|
|
305
|
-
entryFiles: config.ts.entryFiles,
|
|
306
|
-
conventions,
|
|
307
|
-
testFiles: config.ts.testFiles,
|
|
308
|
-
parse: (fileName: string, text: string) => engine.parseSourceFile(fileName, text),
|
|
309
|
-
publishedAPI: config.targetKind === "library",
|
|
310
|
-
publishedMembers: new Set(
|
|
311
|
-
(workspace?.entries ?? []).filter((entry) => entry.published).map((entry) => entry.dir),
|
|
312
|
-
),
|
|
384
|
+
};
|
|
313
385
|
};
|
|
314
386
|
const notBuilt: NotBuilt[] = [...discovered.notBuilt];
|
|
315
387
|
const dropped = (configFile: string, error: string): void => {
|
|
@@ -477,9 +549,10 @@ function readProjects<Answer>(
|
|
|
477
549
|
sourcePaths(host, targetRoot).filter(
|
|
478
550
|
(path) => !heldFiles.has(path) && testPatterns.some((pattern) => pattern.test(path)),
|
|
479
551
|
),
|
|
480
|
-
|
|
552
|
+
parse,
|
|
481
553
|
);
|
|
482
554
|
},
|
|
555
|
+
readManifests,
|
|
483
556
|
);
|
|
484
557
|
for (const configFile of session.unopened) {
|
|
485
558
|
if (consumerOf.has(configFile) || !isGuess(discovered, configFile)) {
|
|
@@ -518,7 +591,8 @@ function readProjects<Answer>(
|
|
|
518
591
|
componentsUnread: workspace?.componentsUnread ?? [],
|
|
519
592
|
conventionsApplied: conventions.applied,
|
|
520
593
|
conventionUses: conventions.uses,
|
|
521
|
-
memberDirs
|
|
594
|
+
memberDirs,
|
|
595
|
+
nestedDirs,
|
|
522
596
|
outsideTests,
|
|
523
597
|
};
|
|
524
598
|
}
|
|
@@ -707,6 +781,8 @@ interface ReadRun<Extra> {
|
|
|
707
781
|
readonly conventionUses: readonly string[];
|
|
708
782
|
/** The directory of every workspace member, absolute. */
|
|
709
783
|
readonly memberDirs: readonly string[];
|
|
784
|
+
/** The directory of every nested package, absolute. */
|
|
785
|
+
readonly nestedDirs: readonly string[];
|
|
710
786
|
}
|
|
711
787
|
|
|
712
788
|
/**
|
|
@@ -726,6 +802,7 @@ function readRun<Extra>(
|
|
|
726
802
|
project: ProjectView<Brand>,
|
|
727
803
|
read: ProjectRead,
|
|
728
804
|
references: readonly Reference[],
|
|
805
|
+
testFiles: readonly string[],
|
|
729
806
|
) => Extra,
|
|
730
807
|
): ReadRun<Extra> {
|
|
731
808
|
const targetRoot = scope.target.path;
|
|
@@ -771,7 +848,7 @@ function readRun<Extra>(
|
|
|
771
848
|
reason: warning.reason,
|
|
772
849
|
})),
|
|
773
850
|
),
|
|
774
|
-
extra: extra(project, projectRead, resolved.references),
|
|
851
|
+
extra: extra(project, projectRead, resolved.references, resolved.testFilePaths),
|
|
775
852
|
};
|
|
776
853
|
});
|
|
777
854
|
const outsidePaths = read.outsideTests.map((one) => one.path);
|
|
@@ -816,6 +893,9 @@ function readRun<Extra>(
|
|
|
816
893
|
...(projectRead.referenceOnly.length === 0
|
|
817
894
|
? {}
|
|
818
895
|
: { referenceOnly: projectRead.referenceOnly }),
|
|
896
|
+
...(projectRead.held.dependents.size === 0
|
|
897
|
+
? {}
|
|
898
|
+
: { dependents: projectRead.held.dependents }),
|
|
819
899
|
};
|
|
820
900
|
return {
|
|
821
901
|
configFile: projectRead.configFile,
|
|
@@ -865,6 +945,7 @@ function readRun<Extra>(
|
|
|
865
945
|
conventionsApplied: read.conventionsApplied,
|
|
866
946
|
conventionUses: read.conventionUses,
|
|
867
947
|
memberDirs: read.memberDirs,
|
|
948
|
+
nestedDirs: read.nestedDirs,
|
|
868
949
|
};
|
|
869
950
|
}
|
|
870
951
|
|
|
@@ -1010,14 +1091,12 @@ function sweptOf<Extra>(
|
|
|
1010
1091
|
}
|
|
1011
1092
|
|
|
1012
1093
|
/**
|
|
1013
|
-
* Sweeps the run the scope and the configuration describe. Each project's references
|
|
1014
|
-
*
|
|
1015
|
-
*
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1019
|
-
* configuration is a use. A project that carries an error ends the run before any
|
|
1020
|
-
* project is swept.
|
|
1094
|
+
* Sweeps the run the scope and the configuration describe. Each project's references and
|
|
1095
|
+
* exemptions are read against its own inventory while its view is open; the projects then
|
|
1096
|
+
* merge into one matrix in run order, swept under the caller's marks and mode with every
|
|
1097
|
+
* project's exemptions. A record found in one configuration holds in every configuration
|
|
1098
|
+
* that holds its declaration, because an exemption evidences a use the analysis cannot see.
|
|
1099
|
+
* A project that carries an error ends the run before any project is swept.
|
|
1021
1100
|
*/
|
|
1022
1101
|
export function runSweep(
|
|
1023
1102
|
engine: Engine,
|
|
@@ -1041,6 +1120,7 @@ interface EmitterExtra {
|
|
|
1041
1120
|
readonly implementations: ReturnType<typeof implementations>;
|
|
1042
1121
|
readonly heldByInclusion: readonly string[];
|
|
1043
1122
|
readonly generated: readonly string[];
|
|
1123
|
+
readonly inOperands: readonly string[];
|
|
1044
1124
|
readonly rooted: Roots;
|
|
1045
1125
|
readonly directives: readonly InlineDirective[];
|
|
1046
1126
|
readonly declarationsNamed: readonly boolean[];
|
|
@@ -1064,7 +1144,7 @@ export function readEmitterRun(
|
|
|
1064
1144
|
config,
|
|
1065
1145
|
mode,
|
|
1066
1146
|
detectors,
|
|
1067
|
-
(project, read, references) => ({
|
|
1147
|
+
(project, read, references, testFiles) => ({
|
|
1068
1148
|
deprecation: deprecatedDeclarations(project, read.held),
|
|
1069
1149
|
accessors: accessorsOf(project, read.held),
|
|
1070
1150
|
needs: projectNeeds(project, read.held, host, targetRoot, (fileName, text) =>
|
|
@@ -1073,6 +1153,7 @@ export function readEmitterRun(
|
|
|
1073
1153
|
implementations: implementations(project, read.held),
|
|
1074
1154
|
heldByInclusion: heldByInclusion(project, targetRoot),
|
|
1075
1155
|
generated: [...read.generated.keys()],
|
|
1156
|
+
inOperands: inOperandNames(project, targetRoot, testFiles),
|
|
1076
1157
|
rooted: read.rooted,
|
|
1077
1158
|
directives: inlineDirectives(project, read.held, targetRoot).filter(
|
|
1078
1159
|
(directive) => !read.referenceOnly.includes(directive.site.path),
|
|
@@ -1165,12 +1246,14 @@ export function emitterInputOver(
|
|
|
1165
1246
|
swept.sweep.candidates.map((candidate) => candidate.id),
|
|
1166
1247
|
members.read,
|
|
1167
1248
|
members.unbuilt,
|
|
1249
|
+
read.nestedDirs,
|
|
1168
1250
|
),
|
|
1169
1251
|
implementations: mergeImplementations(projects.map((one) => one.extra.implementations)),
|
|
1170
1252
|
files: {
|
|
1171
1253
|
tree: sourceTree(host, targetRoot),
|
|
1172
1254
|
heldByInclusion: new Set(projects.flatMap((one) => one.extra.heldByInclusion)),
|
|
1173
1255
|
generated: new Set(projects.flatMap((one) => one.extra.generated)),
|
|
1256
|
+
inOperands: new Set(projects.flatMap((one) => one.extra.inOperands)),
|
|
1174
1257
|
},
|
|
1175
1258
|
boundary: {
|
|
1176
1259
|
consumers: {
|
package/src/augmentations.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The references a use of an augmentation's declaration makes to what holds it.
|
|
3
|
-
*
|
|
4
|
-
* block
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The references a use of an augmentation's declaration makes to what holds it. A use of
|
|
3
|
+
* a declaration a `declare global` block or a module declaration holds is a use of the
|
|
4
|
+
* block, which no name spells or no import may name. An interface or namespace such a
|
|
5
|
+
* block, or the top level of a file that is no module, holds merges with the one of its
|
|
6
|
+
* name, which the language or a library types values by where the target never names it,
|
|
7
|
+
* as `import.meta` or `process.env`, so a use of its member is a use of it.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import type { Inventory, InventorySymbol } from "./inventory.ts";
|
|
@@ -24,19 +25,21 @@ function isGlobalBlock(
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
|
-
* For each reference to a declaration an augmentation block
|
|
28
|
-
*
|
|
29
|
-
* itself.
|
|
28
|
+
* For each reference to a declaration an augmentation block, or the global scope of a
|
|
29
|
+
* file in `scripts`, holds, a read at the same place of every interface and namespace
|
|
30
|
+
* between it and the block or the file, and of the block itself. `scripts` holds the
|
|
31
|
+
* files that are no module, by identifier.
|
|
30
32
|
*/
|
|
31
33
|
export function augmentationReferences(
|
|
32
34
|
held: Inventory,
|
|
33
35
|
found: readonly Reference[],
|
|
36
|
+
scripts: ReadonlySet<string> = new Set(),
|
|
34
37
|
): readonly Reference[] {
|
|
35
38
|
const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
|
|
36
39
|
const globals = new Set(
|
|
37
40
|
held.symbols.filter((one) => isGlobalBlock(one, held, files)).map((one) => one.id),
|
|
38
41
|
);
|
|
39
|
-
if (globals.size === 0 && held.modules.size === 0) {
|
|
42
|
+
if (globals.size === 0 && held.modules.size === 0 && scripts.size === 0) {
|
|
40
43
|
return [];
|
|
41
44
|
}
|
|
42
45
|
const byId = new Map(held.symbols.map((one) => [one.id, one]));
|
|
@@ -47,10 +50,10 @@ export function augmentationReferences(
|
|
|
47
50
|
at !== undefined;
|
|
48
51
|
at = byId.get(at.parent)
|
|
49
52
|
) {
|
|
50
|
-
if (globals.has(at.id)) {
|
|
53
|
+
if (globals.has(at.id) || held.modules.has(at.id)) {
|
|
51
54
|
return [...ids, at.id];
|
|
52
55
|
}
|
|
53
|
-
if (
|
|
56
|
+
if (scripts.has(at.id)) {
|
|
54
57
|
return ids;
|
|
55
58
|
}
|
|
56
59
|
if (at.kind === "interface" || at.kind === "namespace") {
|
package/src/calls.ts
CHANGED
|
@@ -22,8 +22,8 @@ import {
|
|
|
22
22
|
type Decorator,
|
|
23
23
|
type Expression,
|
|
24
24
|
type Node,
|
|
25
|
-
} from "@typescript/native/
|
|
26
|
-
import { SymbolFlags, type Symbol as TSSymbol } from "@typescript/native/
|
|
25
|
+
} from "@typescript/native/ast";
|
|
26
|
+
import { SymbolFlags, type Symbol as TSSymbol } from "@typescript/native/sync";
|
|
27
27
|
import type { AliasChains } from "./alias-chain.ts";
|
|
28
28
|
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
29
29
|
import { must } from "./query.ts";
|
package/src/class-members.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* that extends it and a member a subclass overrides is the subclass's own.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { isClassDeclaration, isClassExpression, type Node } from "@typescript/native/
|
|
8
|
-
import type { Symbol as TSSymbol, SymbolFlags, Type } from "@typescript/native/
|
|
7
|
+
import { isClassDeclaration, isClassExpression, type Node } from "@typescript/native/ast";
|
|
8
|
+
import type { Symbol as TSSymbol, SymbolFlags, Type } from "@typescript/native/sync";
|
|
9
9
|
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
10
10
|
import { must } from "./query.ts";
|
|
11
11
|
import type { ProjectView } from "./session.ts";
|
package/src/component-layer.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `node_modules` beside each configuration, to a directory holding only its manifest.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { CreateSnapshotParams } from "@typescript/native/
|
|
9
|
+
import type { CreateSnapshotParams } from "@typescript/native/sync";
|
|
10
10
|
import type { Host } from "./host.ts";
|
|
11
11
|
import { dirnamePath, joinPath } from "./paths.ts";
|
|
12
12
|
|
package/src/components.ts
CHANGED
|
@@ -37,8 +37,27 @@ export interface Component {
|
|
|
37
37
|
* cycle with a live container is one, so a component has at least one.
|
|
38
38
|
*/
|
|
39
39
|
readonly roots: readonly string[];
|
|
40
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* The source lines one deletion of the component removes: the distinct lines its
|
|
42
|
+
* members span, and none for a component of held declarations.
|
|
43
|
+
*/
|
|
41
44
|
readonly deletableLines: number;
|
|
45
|
+
/**
|
|
46
|
+
* Whether its members are held declarations, which no mechanical edit deletes: a dead
|
|
47
|
+
* declaration whose deletion changes a live declaration's value, and every dead
|
|
48
|
+
* declaration such a one references or whose deletion would change its value.
|
|
49
|
+
*/
|
|
50
|
+
readonly held: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* What the held-declaration rule reads of a graph, by place: the places of the later
|
|
55
|
+
* declarations whose value each one's deletion changes, and whether reachability holds
|
|
56
|
+
* each one live in some configuration.
|
|
57
|
+
*/
|
|
58
|
+
export interface Positional {
|
|
59
|
+
readonly dependents: readonly (readonly number[])[];
|
|
60
|
+
readonly live: readonly boolean[];
|
|
42
61
|
}
|
|
43
62
|
|
|
44
63
|
/** What a report carries for one dead component under one cascade mode. */
|
|
@@ -63,41 +82,97 @@ export function listing(component: Component, cascade: Cascade): Listing {
|
|
|
63
82
|
/**
|
|
64
83
|
* Groups the declarations `dead` marks into components, ordered by their first member's
|
|
65
84
|
* site; `dead`, `testOfDeadCode` and `unreferencedTest` hold one flag per declaration of
|
|
66
|
-
* the graph. Root members sit on the cycles no other cycle references. A
|
|
67
|
-
*
|
|
68
|
-
*
|
|
85
|
+
* the graph. Root members sit on the cycles no other cycle references. A component is a
|
|
86
|
+
* weakly connected part of the dead subgraph, because a cycle two roots reach is dead only
|
|
87
|
+
* through both. The held declarations `positional` decides form components of their own,
|
|
88
|
+
* and a dead declaration that is not held shares one with each of its dead dependents.
|
|
69
89
|
*/
|
|
70
90
|
export function componentsOf(
|
|
71
91
|
graph: Graph,
|
|
72
92
|
dead: readonly boolean[],
|
|
73
93
|
testOfDeadCode: readonly boolean[],
|
|
74
94
|
unreferencedTest: readonly boolean[] = [],
|
|
95
|
+
positional: Positional = { dependents: [], live: [] },
|
|
75
96
|
): readonly Component[] {
|
|
76
|
-
const { at, adjacent } = deadSubgraph(
|
|
97
|
+
const { at, position, adjacent, references } = deadSubgraph(
|
|
98
|
+
graph,
|
|
99
|
+
dead,
|
|
100
|
+
testOfDeadCode,
|
|
101
|
+
unreferencedTest,
|
|
102
|
+
);
|
|
77
103
|
if (at.length === 0) {
|
|
78
104
|
return [];
|
|
79
105
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
106
|
+
const dependents = at.map((index) =>
|
|
107
|
+
(positional.dependents[index] ?? []).map((to) => ({
|
|
108
|
+
to: position[to] ?? UNVISITED,
|
|
109
|
+
live: positional.live[to] === true,
|
|
110
|
+
})),
|
|
111
|
+
);
|
|
112
|
+
const held = heldSet(dependents, references);
|
|
113
|
+
const kept = adjacent.map((targets, from) => targets.filter((to) => held[to] === held[from]));
|
|
114
|
+
const { componentOf, members } = connected(kept);
|
|
115
|
+
const referenced = referencedCycles(kept, componentOf, members.length);
|
|
116
|
+
const groups = joined(
|
|
117
|
+
kept.map((targets, from) => [
|
|
118
|
+
...targets,
|
|
119
|
+
...(held[from] === true
|
|
120
|
+
? []
|
|
121
|
+
: (dependents[from] ?? []).flatMap(({ to }) =>
|
|
122
|
+
to === UNVISITED || held[to] === true ? [] : [to],
|
|
123
|
+
)),
|
|
124
|
+
]),
|
|
125
|
+
);
|
|
126
|
+
const idOf = (place: number): string => graph.symbols[at[place] ?? 0]?.id ?? "";
|
|
84
127
|
|
|
85
128
|
return groups.map((group, place) => {
|
|
86
|
-
const roots = group.filter((
|
|
129
|
+
const roots = group.filter((one) => {
|
|
87
130
|
// A dead member's dead container is on the member's own cycle, and the container
|
|
88
131
|
// is where the deletion starts.
|
|
89
|
-
const container = graph.parent[at[
|
|
90
|
-
return referenced[componentOf[
|
|
132
|
+
const container = graph.parent[at[one] ?? 0] ?? OUTSIDE;
|
|
133
|
+
return referenced[componentOf[one] ?? 0] !== true && dead[container] !== true;
|
|
91
134
|
});
|
|
135
|
+
const isHeld = group.every((one) => held[one] === true);
|
|
92
136
|
return {
|
|
93
137
|
id: componentId(place + 1),
|
|
94
138
|
members: group.map(idOf),
|
|
95
139
|
roots: roots.map(idOf),
|
|
96
|
-
deletableLines: linesSpanned(group.map((
|
|
140
|
+
deletableLines: isHeld ? 0 : linesSpanned(group.map((one) => graph.symbols[at[one] ?? 0])),
|
|
141
|
+
held: isHeld,
|
|
97
142
|
};
|
|
98
143
|
});
|
|
99
144
|
}
|
|
100
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Per place of the dead subgraph, whether the declaration is held: one with a live
|
|
148
|
+
* dependent, and each dead declaration a held one references or has as a dependent,
|
|
149
|
+
* until no further one joins.
|
|
150
|
+
*/
|
|
151
|
+
function heldSet(
|
|
152
|
+
dependents: readonly (readonly { readonly to: number; readonly live: boolean }[])[],
|
|
153
|
+
references: readonly (readonly number[])[],
|
|
154
|
+
): readonly boolean[] {
|
|
155
|
+
const held = dependents.map((after) => after.some((one) => one.live));
|
|
156
|
+
const before: number[][] = dependents.map(() => []);
|
|
157
|
+
dependents.forEach((after, from) => {
|
|
158
|
+
for (const { to } of after) {
|
|
159
|
+
if (to !== UNVISITED) {
|
|
160
|
+
before[to]?.push(from);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
const pending = held.flatMap((one, place) => (one ? [place] : []));
|
|
165
|
+
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
|
166
|
+
for (const reached of [...(references[next] ?? []), ...(before[next] ?? [])]) {
|
|
167
|
+
if (held[reached] !== true) {
|
|
168
|
+
held[reached] = true;
|
|
169
|
+
pending.push(reached);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return held;
|
|
174
|
+
}
|
|
175
|
+
|
|
101
176
|
/**
|
|
102
177
|
* The distinct source lines a set of declarations spans, each declaration at least the
|
|
103
178
|
* line it starts on. A member written inside its container's span adds no line, because
|
|
@@ -133,19 +208,24 @@ function linesSpanned(symbols: readonly (InventorySymbol | undefined)[]): number
|
|
|
133
208
|
}
|
|
134
209
|
|
|
135
210
|
/**
|
|
136
|
-
* The dead declarations by
|
|
137
|
-
*
|
|
138
|
-
* each reference one makes to another, except an unreferenced test-file declaration's
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
211
|
+
* The dead declarations by subgraph place (`at`, and `position` per graph declaration),
|
|
212
|
+
* a test file's only where it tests dead code or nothing the tests reach. `adjacent` holds
|
|
213
|
+
* each reference one makes to another, except an unreferenced test-file declaration's to
|
|
214
|
+
* production code, which decides nothing of its target; an edge each way between a dead
|
|
215
|
+
* member and its dead container; and one back from each production target of an admitted
|
|
216
|
+
* test, which puts the test in its subject's component. `references` holds the first alone.
|
|
142
217
|
*/
|
|
143
218
|
function deadSubgraph(
|
|
144
219
|
graph: Graph,
|
|
145
220
|
dead: readonly boolean[],
|
|
146
221
|
testOfDeadCode: readonly boolean[],
|
|
147
222
|
unreferencedTest: readonly boolean[],
|
|
148
|
-
): {
|
|
223
|
+
): {
|
|
224
|
+
readonly at: readonly number[];
|
|
225
|
+
readonly position: readonly number[];
|
|
226
|
+
readonly adjacent: readonly (readonly number[])[];
|
|
227
|
+
readonly references: readonly (readonly number[])[];
|
|
228
|
+
} {
|
|
149
229
|
const at: number[] = [];
|
|
150
230
|
const position = graph.symbols.map((_symbol, index) => {
|
|
151
231
|
const judged = testOfDeadCode[index] === true || unreferencedTest[index] === true;
|
|
@@ -156,6 +236,7 @@ function deadSubgraph(
|
|
|
156
236
|
return at.length - 1;
|
|
157
237
|
});
|
|
158
238
|
const adjacent: number[][] = at.map(() => []);
|
|
239
|
+
const references: number[][] = at.map(() => []);
|
|
159
240
|
at.forEach((index, from) => {
|
|
160
241
|
for (const edge of graph.out[index] ?? []) {
|
|
161
242
|
const to = position[edge.to] ?? UNVISITED;
|
|
@@ -163,6 +244,7 @@ function deadSubgraph(
|
|
|
163
244
|
continue;
|
|
164
245
|
}
|
|
165
246
|
adjacent[from]?.push(to);
|
|
247
|
+
references[from]?.push(to);
|
|
166
248
|
if (
|
|
167
249
|
testOfDeadCode[index] === true &&
|
|
168
250
|
graph.test[edge.to] !== true &&
|
|
@@ -177,7 +259,7 @@ function deadSubgraph(
|
|
|
177
259
|
adjacent[container]?.push(from);
|
|
178
260
|
}
|
|
179
261
|
});
|
|
180
|
-
return { at, adjacent };
|
|
262
|
+
return { at, position, adjacent, references };
|
|
181
263
|
}
|
|
182
264
|
|
|
183
265
|
/** One frame of the component walk: a declaration, and the next of its edges to follow. */
|