@cplieger/deadset-ts 8.0.0-dev.16 → 8.0.0-dev.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/alias-chain.js +0 -5
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +82 -91
- package/dist/src/augmentations.js +24 -25
- package/dist/src/bindings.js +382 -0
- package/dist/src/calls.js +279 -43
- package/dist/src/commands/tools.js +5 -9
- package/dist/src/component-files.js +3 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +6 -9
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +4 -1
- package/dist/src/contract/contract.generated.js +2 -2
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +25 -29
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +2 -2
- package/dist/src/entries/configuration-files.js +3 -7
- package/dist/src/entries/conventions.js +2 -2
- package/dist/src/entries/html-entries.js +7 -10
- package/dist/src/entries/mdx-entries.js +2 -2
- package/dist/src/entries/static-module.js +2 -1
- package/dist/src/exempt.js +0 -26
- package/dist/src/exposure.js +400 -0
- package/dist/src/facts.js +4 -4
- package/dist/src/findings/completion.js +44 -21
- package/dist/src/findings/emitters.js +22 -33
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
- package/dist/src/findings/intra-function.js +3 -3
- package/dist/src/findings/reads-and-writes.js +71 -15
- package/dist/src/findings/unused-declarations.js +2 -22
- package/dist/src/findings/visibility-narrowing.js +14 -20
- package/dist/src/framework-lifecycle.js +41 -14
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/inventory.js +18 -0
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/{components.js → liveness/components.js} +1 -1
- package/dist/src/{graph.js → liveness/graph.js} +45 -20
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +5 -0
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +46 -7
- package/dist/src/platform-calls.js +2 -2
- package/dist/src/platform-symbols.js +13 -19
- package/dist/src/query.js +46 -0
- package/dist/src/references/read-rule.js +1 -0
- package/dist/src/references/read-rules.js +48 -0
- package/dist/src/references/sink.js +237 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +110 -1285
- package/dist/src/roots/configured-declarations.js +3 -0
- package/dist/src/roots/published-reach.js +4 -4
- package/dist/src/roots/root-set.js +1 -1
- package/dist/src/roots/unseen-loads.js +38 -10
- package/dist/src/serialization-contract.js +8 -3
- package/dist/src/stores.js +4 -2
- package/dist/src/symbol-keys.js +89 -83
- package/dist/src/tests/file-class.js +76 -0
- package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
- package/dist/src/{test-support.js → tests/support.js} +2 -11
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +3 -3
- package/dist/src/verbs/explain.js +75 -132
- package/dist/src/verbs/print-retained.js +3 -3
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +125 -128
- package/src/augmentations.ts +42 -28
- package/src/bindings.ts +460 -0
- package/src/calls.ts +338 -46
- package/src/commands/tools.ts +5 -11
- package/src/component-files.ts +4 -2
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +8 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +4 -1
- package/src/contract/contract.generated.ts +2 -2
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +27 -28
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +2 -2
- package/src/entries/configuration-files.ts +11 -8
- package/src/entries/conventions.ts +2 -1
- package/src/entries/html-entries.ts +15 -14
- package/src/entries/mdx-entries.ts +2 -2
- package/src/entries/static-module.ts +2 -1
- package/src/exempt.ts +31 -46
- package/src/exposure.ts +492 -0
- package/src/facts.ts +5 -3
- package/src/finding.ts +1 -1
- package/src/findings/boundary.ts +9 -0
- package/src/findings/completion.ts +48 -23
- package/src/findings/emitter.ts +1 -1
- package/src/findings/emitters.ts +23 -41
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/findings/interfaces.ts +3 -3
- package/src/findings/intra-function.ts +3 -3
- package/src/findings/reads-and-writes.ts +99 -26
- package/src/findings/unused-declarations.ts +4 -28
- package/src/findings/visibility-narrowing.ts +25 -26
- package/src/framework-lifecycle.ts +42 -14
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/indexed-access.ts +570 -527
- package/src/interface-satisfaction.ts +2 -2
- package/src/inventory.ts +19 -0
- package/src/keyof-comparisons.ts +131 -116
- package/src/{components.ts → liveness/components.ts} +3 -3
- package/src/{graph.ts → liveness/graph.ts} +93 -27
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/load/expected-module.ts +8 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +6 -0
- package/src/load/workspace-resolution.ts +9 -2
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +54 -8
- package/src/platform-calls.ts +3 -3
- package/src/platform-symbols.ts +11 -27
- package/src/query.ts +81 -0
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +314 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +175 -1733
- package/src/report.ts +1 -1
- package/src/roots/configured-declarations.ts +3 -0
- package/src/roots/published-reach.ts +9 -4
- package/src/roots/root-set.ts +1 -1
- package/src/roots/unseen-loads.ts +49 -11
- package/src/serialization-contract.ts +8 -3
- package/src/stores.ts +15 -3
- package/src/symbol-keys.ts +102 -102
- package/src/tests/file-class.ts +135 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +9 -20
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +3 -2
- package/src/verbs/explain.ts +83 -158
- package/src/verbs/print-retained.ts +3 -3
- package/dist/src/matrix.js +0 -273
- package/dist/src/sweep.js +0 -222
- package/src/matrix.ts +0 -463
- package/src/sweep.ts +0 -361
package/dist/src/alias-chain.js
CHANGED
|
@@ -19,7 +19,6 @@ export function aliasChains(project, held) {
|
|
|
19
19
|
const ownFiles = project.ownPaths();
|
|
20
20
|
const steps = new Map();
|
|
21
21
|
const chains = new Map();
|
|
22
|
-
let asked = 0;
|
|
23
22
|
const declarationsOf = (symbol) => {
|
|
24
23
|
const ids = [];
|
|
25
24
|
for (const handle of symbol.declarations) {
|
|
@@ -52,7 +51,6 @@ export function aliasChains(project, held) {
|
|
|
52
51
|
return steps.get(symbol.id);
|
|
53
52
|
}
|
|
54
53
|
const next = project.aliasStepOf(symbol);
|
|
55
|
-
asked += 1;
|
|
56
54
|
steps.set(symbol.id, next);
|
|
57
55
|
return next;
|
|
58
56
|
};
|
|
@@ -145,8 +143,5 @@ export function aliasChains(project, held) {
|
|
|
145
143
|
declarationsOf,
|
|
146
144
|
chainOf,
|
|
147
145
|
nextDeclared,
|
|
148
|
-
get steps() {
|
|
149
|
-
return asked;
|
|
150
|
-
},
|
|
151
146
|
};
|
|
152
147
|
}
|
package/dist/src/amd.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AMD's module forms (references-ts.37), read from the syntax because the binder declares
|
|
3
|
+
* none of them: `define([<ids>], factory)`, `define("<name>", [<ids>], factory)` and
|
|
4
|
+
* `require([<ids>], callback)` (https://github.com/amdjs/amdjs-api/blob/master/AMD.md).
|
|
5
|
+
*/
|
|
6
|
+
import { isArrayLiteralExpression, isArrowFunction, isFunctionExpression, isCallExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/native/ast";
|
|
7
|
+
import { hasURLScheme, isAbsolutePath, resolvePath, startsRelativePath } from "./paths.js";
|
|
8
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
9
|
+
/** The ids AMD binds to the factory's CommonJS parameters, which load no module. */
|
|
10
|
+
const FREE_VARIABLES = new Set(["require", "exports", "module"]);
|
|
11
|
+
/** The platform function `callee` is written as. */
|
|
12
|
+
export function amdPlatform(callee) {
|
|
13
|
+
return callee.text === "define" ? "define" : "require";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The dependency list one call writes, where it is a call of a bare `define` or `require` in
|
|
17
|
+
* one of AMD's forms. A `define` with no list, of a factory or a value alone, loads nothing;
|
|
18
|
+
* a `require` whose first argument is no array literal is CommonJS's.
|
|
19
|
+
*/
|
|
20
|
+
export function amdLoadOf(node) {
|
|
21
|
+
if (!isCallExpression(node)) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const callee = parenthesesRemoved(node.expression);
|
|
25
|
+
if (!isIdentifier(callee) || (callee.text !== "define" && callee.text !== "require")) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const written = node.arguments.map((one) => parenthesesRemoved(one));
|
|
29
|
+
const [first] = written;
|
|
30
|
+
if (first === undefined) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const define = callee.text === "define";
|
|
34
|
+
const named = define && isLiteralText(first);
|
|
35
|
+
const list = written[named ? 1 : 0];
|
|
36
|
+
if (list !== undefined && isArrayLiteralExpression(list)) {
|
|
37
|
+
const elements = list.elements.map((one) => parenthesesRemoved(one));
|
|
38
|
+
const factory = written[named ? 2 : 1];
|
|
39
|
+
const ids = elements.flatMap((one, at) => isLiteralText(one) && !FREE_VARIABLES.has(one.text)
|
|
40
|
+
? [{ id: one, received: receives(factory, at) }]
|
|
41
|
+
: []);
|
|
42
|
+
return { callee, ids, computed: elements.some((one) => !isLiteralText(one)) };
|
|
43
|
+
}
|
|
44
|
+
if (!define) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
// A factory after the list's place makes what stands there a list, here a computed one.
|
|
48
|
+
return { callee, ids: [], computed: written.length > (named ? 2 : 1) };
|
|
49
|
+
}
|
|
50
|
+
function isLiteralText(node) {
|
|
51
|
+
return isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node);
|
|
52
|
+
}
|
|
53
|
+
/** Whether a factory may receive the dependency at place `at` of its list. */
|
|
54
|
+
function receives(factory, at) {
|
|
55
|
+
if (factory === undefined) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (!isFunctionExpression(factory) && !isArrowFunction(factory)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
const { parameters } = factory;
|
|
62
|
+
return at < parameters.length || parameters.some((one) => one.dotDotDotToken !== undefined);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The path a dependency id names against the calling file's directory: a relative module id,
|
|
66
|
+
* `./` or `../` with no `.js` ending, with `.js` appended. RequireJS reads any other id
|
|
67
|
+
* through its configuration or as a URL against the page, which the analysis does not know.
|
|
68
|
+
*/
|
|
69
|
+
export function amdPath(dir, id) {
|
|
70
|
+
return startsRelativePath(id) && !id.endsWith(".js") ? resolvePath(dir, `${id}.js`) : undefined;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Whether one call may load a file the analysis cannot name: a computed list, or an id that
|
|
74
|
+
* RequireJS reads as a URL, one ending in `.js`, starting with `/` or carrying a scheme. Any
|
|
75
|
+
* other id {@link amdPath} reads no path from is a package's.
|
|
76
|
+
*/
|
|
77
|
+
export function amdUnresolved(load) {
|
|
78
|
+
return (load.computed ||
|
|
79
|
+
load.ids.some(({ id }) => amdPath("/", id.text) === undefined &&
|
|
80
|
+
(id.text.endsWith(".js") || isAbsolutePath(id.text) || hasURLScheme(id.text))));
|
|
81
|
+
}
|
package/dist/src/analysis.js
CHANGED
|
@@ -12,6 +12,7 @@ import { consumerReferences } from "./consumers.js";
|
|
|
12
12
|
import { configuredDeclarations, configuredRef, declarationsNamed, } from "./roots/configured-declarations.js";
|
|
13
13
|
import { decorator } from "./decorator.js";
|
|
14
14
|
import { configurationNeeds, dependenciesOf, projectNeeds } from "./dependencies.js";
|
|
15
|
+
import { carriersOf, NO_CARRIERS } from "./exposure.js";
|
|
15
16
|
import { deprecatedDeclarations } from "./deprecation.js";
|
|
16
17
|
import { moduleIdentity } from "./load/discover.js";
|
|
17
18
|
import { loadRun } from "./load/load.js";
|
|
@@ -19,36 +20,40 @@ import { readSources } from "./entries/sources.js";
|
|
|
19
20
|
import { readWorkflows } from "./entries/workflow-steps.js";
|
|
20
21
|
import { readEdgeSides } from "./edges.js";
|
|
21
22
|
import { enumGroup } from "./enum-group.js";
|
|
23
|
+
import { bindingsOf, distinctBindings } from "./bindings.js";
|
|
22
24
|
import { generatedFile, generatedFilesOf } from "./generated-file.js";
|
|
23
|
-
import { inOperandNames } from "./in-operands.js";
|
|
25
|
+
import { inOperandNames } from "./findings/in-operands.js";
|
|
24
26
|
import { sourceFilesOf } from "./source-files.js";
|
|
25
|
-
import { evidenceRecords, heldRecords, disabledClasses, exemptionsOf,
|
|
27
|
+
import { evidenceRecords, heldRecords, disabledClasses, exemptionsOf, isUnansweredRecord, writtenRecords, } from "./exempt.js";
|
|
26
28
|
import { heldByInclusion, sourcePaths, sourceTree } from "./file-facts.js";
|
|
27
29
|
import { findingsPass, recordedFindings, rowKey } from "./findings-pass.js";
|
|
28
30
|
import { unmatchedRootFindings } from "./findings/self-check.js";
|
|
29
31
|
import { frameworkLifecycle } from "./framework-lifecycle.js";
|
|
30
|
-
import { implementations, mergeImplementations } from "./implementations.js";
|
|
32
|
+
import { implementations, mergeImplementations } from "./findings/implementations.js";
|
|
31
33
|
import { injectionContainer } from "./injection-container.js";
|
|
32
34
|
import { interfaceSatisfaction, settledSatisfaction } from "./interface-satisfaction.js";
|
|
33
35
|
import { intraFunctionFacts, projectParts } from "./intra-function-parts.js";
|
|
34
36
|
import {} from "./inventory.js";
|
|
35
37
|
import { inventory } from "./inventory/enumerate.js";
|
|
36
38
|
import { readManifest } from "./roots/manifest.js";
|
|
37
|
-
import {
|
|
39
|
+
import { matrixOf, referenceKey } from "./liveness/matrix.js";
|
|
38
40
|
import { ancestors, dirnamePath, extensionOf, joinPath, relativePath, resolvePath, } from "./paths.js";
|
|
39
41
|
import { byPosition, isComponentFile } from "./position.js";
|
|
40
42
|
import { skippedUnits } from "./type-errors.js";
|
|
41
43
|
import { reflectiveLookup } from "./reflective-lookup.js";
|
|
42
|
-
import { references
|
|
44
|
+
import { references } from "./references.js";
|
|
43
45
|
import { rootSet, unmatchedEverywhere, } from "./roots/root-set.js";
|
|
44
46
|
import { decodedMembers, serializationContract } from "./serialization-contract.js";
|
|
45
|
-
import { outsideTestReferences, testFilesAt } from "./outside
|
|
47
|
+
import { outsideTestReferences, testFilesAt } from "./tests/outside.js";
|
|
48
|
+
import { formedForNode } from "./load/synthesize.js";
|
|
46
49
|
import { GLOBALS_RUNNER, namesTestGlobals } from "./load/test-globals.js";
|
|
47
|
-
import {
|
|
50
|
+
import { targetTestFile, testFilePaths, testFileRulesOf, withheldBy, } from "./tests/file-class.js";
|
|
51
|
+
import { testSupportFiles } from "./tests/support.js";
|
|
48
52
|
import { accessorsOf, storesOf } from "./stores.js";
|
|
49
53
|
import { isPartKind } from "./suppress.js";
|
|
50
54
|
import { bindRows, readBaseline, readIgnoreFile } from "./suppress-file.js";
|
|
51
55
|
import { inlineDirectives, inlineSuppressions } from "./suppress-inline.js";
|
|
56
|
+
import { sweep } from "./liveness/sweep.js";
|
|
52
57
|
import { readTemplates, templateField } from "./template-field.js";
|
|
53
58
|
import { bytewise } from "./text/compare.js";
|
|
54
59
|
import { holdsManifest, manifestAt } from "./fs/manifest.js";
|
|
@@ -109,6 +114,7 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
109
114
|
workflows,
|
|
110
115
|
});
|
|
111
116
|
const rooted = rootSet(project, held, targetRoot, { ...options, sources, skips });
|
|
117
|
+
const generated = generatedFilesOf(own, sources.generated);
|
|
112
118
|
return {
|
|
113
119
|
configuration: planned.id,
|
|
114
120
|
configFile: project.configFile,
|
|
@@ -120,8 +126,12 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
120
126
|
.ownSourceFiles()
|
|
121
127
|
.filter((file) => referenceOnly(file.fileName))
|
|
122
128
|
.flatMap((file) => relativePath(absoluteRoot, file.fileName) ?? []),
|
|
123
|
-
generated
|
|
129
|
+
generated,
|
|
130
|
+
bindings: bindingsOf(own.byPath, generated, new Map(held.symbols
|
|
131
|
+
.filter((one) => one.kind === "file")
|
|
132
|
+
.map((one) => [one.position.path, one.ref]))),
|
|
124
133
|
sources,
|
|
134
|
+
formedForNode: formedForNode(planned),
|
|
125
135
|
};
|
|
126
136
|
};
|
|
127
137
|
for (const file of project.ownSourceFiles()) {
|
|
@@ -130,14 +140,25 @@ function readProjects(engine, host, scope, config, stage) {
|
|
|
130
140
|
return stage(project, { configuration: planned.id, read });
|
|
131
141
|
});
|
|
132
142
|
const consumed = new Map();
|
|
143
|
+
const consumerHeld = new Set();
|
|
133
144
|
for (const { project, consumer } of classified.consumed) {
|
|
134
145
|
const side = { id: consumer.id, root: consumer.path };
|
|
135
|
-
|
|
146
|
+
const read = consumerReferences(project, host, targetRoot, side, config, parse);
|
|
147
|
+
for (const reference of read.references) {
|
|
136
148
|
consumed.set(referenceKey(reference), reference);
|
|
137
149
|
}
|
|
150
|
+
for (const id of read.held) {
|
|
151
|
+
consumerHeld.add(id);
|
|
152
|
+
}
|
|
138
153
|
}
|
|
139
154
|
const outsideTests = testFilesAt(host, targetRoot, sourcePaths(host, targetRoot).filter((path) => !heldFiles.has(path) && testPatterns.some((pattern) => pattern.test(path))), parse);
|
|
140
|
-
return {
|
|
155
|
+
return {
|
|
156
|
+
answers,
|
|
157
|
+
references: [...consumed.values()],
|
|
158
|
+
consumerHeld: [...consumerHeld],
|
|
159
|
+
outsideTests,
|
|
160
|
+
workflows,
|
|
161
|
+
};
|
|
141
162
|
});
|
|
142
163
|
}
|
|
143
164
|
/**
|
|
@@ -202,6 +223,13 @@ export function runRoots(engine, host, scope, config, provenance) {
|
|
|
202
223
|
unanswered: read.unanswered,
|
|
203
224
|
};
|
|
204
225
|
}
|
|
226
|
+
/** The production mode a configuration sets: a consumer's test reference is classed as `analysis.consumer_tests` says. */
|
|
227
|
+
export function productionMode(config) {
|
|
228
|
+
return {
|
|
229
|
+
production: true,
|
|
230
|
+
consumerTestsProduction: config.analysis.consumer_tests === "production",
|
|
231
|
+
};
|
|
232
|
+
}
|
|
205
233
|
/** The exemption classes this analyzer detects, each by its detector; any other retains nothing. */
|
|
206
234
|
export const DETECTORS = new Map([
|
|
207
235
|
["interface-satisfaction", interfaceSatisfaction],
|
|
@@ -268,11 +296,13 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
268
296
|
const directories = runDirectories(host, targetRoot, (fileName, text) => engine.parseSourceFile(fileName, text));
|
|
269
297
|
const read = readProjects(engine, host, scope, config, (project, opened) => {
|
|
270
298
|
const projectRead = opened.read();
|
|
299
|
+
const testFile = targetTestFile(projectRead.sources.testFiles);
|
|
271
300
|
const resolved = references(project, projectRead.held, targetRoot, {
|
|
272
|
-
|
|
273
|
-
testFileNames: projectRead.sources.testFiles,
|
|
301
|
+
testFile,
|
|
274
302
|
directories,
|
|
303
|
+
formedForNode: projectRead.formedForNode,
|
|
275
304
|
});
|
|
305
|
+
const testFiles = testFilePaths(project, targetRoot, testFile);
|
|
276
306
|
const evidence = evidenceRecords({
|
|
277
307
|
project,
|
|
278
308
|
held: projectRead.held,
|
|
@@ -292,7 +322,9 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
292
322
|
return {
|
|
293
323
|
projectRead,
|
|
294
324
|
references: resolved.references,
|
|
295
|
-
|
|
325
|
+
held: resolved.held,
|
|
326
|
+
possiblyLoaded: resolved.possiblyLoaded,
|
|
327
|
+
testFiles,
|
|
296
328
|
evidence,
|
|
297
329
|
componentFiles: components.map(pathOf),
|
|
298
330
|
imports: new Map(project.ownSourceFiles().flatMap((file) => {
|
|
@@ -316,8 +348,8 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
316
348
|
line: lineOf(file.originalText, warning.offset),
|
|
317
349
|
reason: warning.reason,
|
|
318
350
|
}))),
|
|
319
|
-
decoded: decodedMembers(project, projectRead.held, productionFiles(project, targetRoot,
|
|
320
|
-
extra: extra(project, projectRead, resolved.references,
|
|
351
|
+
decoded: decodedMembers(project, projectRead.held, productionFiles(project, targetRoot, testFiles), consumers),
|
|
352
|
+
extra: extra(project, projectRead, resolved.references, testFiles, resolved.unseenLoads.map((position) => ({
|
|
321
353
|
configuration: projectRead.configuration,
|
|
322
354
|
position,
|
|
323
355
|
}))),
|
|
@@ -359,7 +391,7 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
359
391
|
testFiles: new Set([...one.testFiles, ...support]),
|
|
360
392
|
});
|
|
361
393
|
const exempt = detected.records;
|
|
362
|
-
const made =
|
|
394
|
+
const made = [...one.references, ...(satisfied?.references ?? [])];
|
|
363
395
|
const held = new Set(projectRead.held.symbols.map((symbol) => symbol.position.path));
|
|
364
396
|
const supportFiles = [...support].filter((path) => held.has(path)).sort(bytewise);
|
|
365
397
|
const testUsedFiles = [...testUsed].filter((path) => held.has(path)).sort(bytewise);
|
|
@@ -386,13 +418,13 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
386
418
|
unanswered: [
|
|
387
419
|
...projectRead.held.unanswered,
|
|
388
420
|
...projectRead.rooted.unanswered,
|
|
389
|
-
...made
|
|
390
|
-
|
|
391
|
-
.map((reference) => reference.to),
|
|
421
|
+
...made.filter((reference) => reference.guessed === true).map((reference) => reference.to),
|
|
422
|
+
...one.held,
|
|
392
423
|
...exempt.filter(isUnansweredRecord).map((record) => record.id),
|
|
393
424
|
...one.decoded.unanswered,
|
|
394
425
|
],
|
|
395
426
|
filledOutside: one.decoded.filled,
|
|
427
|
+
possiblyLoaded: one.possiblyLoaded,
|
|
396
428
|
typeErrorSkips: projectRead.typeErrorSkips,
|
|
397
429
|
unmappedEntries: projectRead.rooted.unmappedEntries,
|
|
398
430
|
heldScripts: projectRead.rooted.heldScripts,
|
|
@@ -418,11 +450,13 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
418
450
|
unansweredHeld: [
|
|
419
451
|
...new Set([
|
|
420
452
|
...answers.flatMap((one) => one.unanswered),
|
|
421
|
-
...consumed.filter((one) => one.
|
|
453
|
+
...consumed.filter((one) => one.guessed === true).map((one) => one.to),
|
|
454
|
+
...read.result.consumerHeld,
|
|
422
455
|
]),
|
|
423
456
|
],
|
|
424
457
|
projects,
|
|
425
458
|
consumers: read.plan.discovery.consumers,
|
|
459
|
+
declaresConsumers: scope.consumers.length > 0,
|
|
426
460
|
notBuilt: read.classified.notBuilt,
|
|
427
461
|
unanswered: read.unanswered,
|
|
428
462
|
componentsUnread: read.plan.workspace?.componentsUnread ?? [],
|
|
@@ -436,29 +470,6 @@ function readRun(engine, host, scope, config, mode, detectors, extra) {
|
|
|
436
470
|
workflows: read.result.workflows,
|
|
437
471
|
};
|
|
438
472
|
}
|
|
439
|
-
/**
|
|
440
|
-
* The run swept under one set of marks with the records that hold. A record a component
|
|
441
|
-
* file's markup holds counts only while the file is live, and what it holds may be what
|
|
442
|
-
* keeps the file live, so each file's markup joins only once a sweep without it finds the
|
|
443
|
-
* file live, until a sweep finds no further file live.
|
|
444
|
-
*/
|
|
445
|
-
function holdingSweep(read, input) {
|
|
446
|
-
const paths = new Set(read.exempt.flatMap((record) => record.whileLive ?? []));
|
|
447
|
-
let live = new Set();
|
|
448
|
-
for (;;) {
|
|
449
|
-
const swept = {
|
|
450
|
-
...input,
|
|
451
|
-
exempt: holdingWhile(read.exempt, live),
|
|
452
|
-
unanswered: read.unansweredHeld,
|
|
453
|
-
};
|
|
454
|
-
const result = sweepMatrix(read.matrix, swept);
|
|
455
|
-
const next = new Set(liveFilesAt(read.matrix, result, paths).map((one) => one.position.path));
|
|
456
|
-
if (next.size === live.size) {
|
|
457
|
-
return { swept, result };
|
|
458
|
-
}
|
|
459
|
-
live = next;
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
473
|
/**
|
|
463
474
|
* The test references exemption evidence written in a test file makes: each is a read of
|
|
464
475
|
* the declaration it would retain, made by the test file at the evidence's site.
|
|
@@ -491,7 +502,7 @@ function evidenceReferences(symbols, inTestFiles) {
|
|
|
491
502
|
* makes being a test's.
|
|
492
503
|
*/
|
|
493
504
|
function countedWrites(references, support, mode) {
|
|
494
|
-
return new Set(
|
|
505
|
+
return new Set(references.flatMap((reference) => reference.use === "write" && !withheldBy(mode, reference, support) ? [reference.to] : []));
|
|
495
506
|
}
|
|
496
507
|
/**
|
|
497
508
|
* The declarations every reference the mode counts stores into, a loaded consumer's
|
|
@@ -501,8 +512,9 @@ function writtenOnly(read, mode) {
|
|
|
501
512
|
const written = new Set();
|
|
502
513
|
const readSome = new Set();
|
|
503
514
|
for (const { configured } of read.projects) {
|
|
515
|
+
const support = new Set(configured.supportFiles ?? []);
|
|
504
516
|
for (const reference of configured.references) {
|
|
505
|
-
if (mode
|
|
517
|
+
if (withheldBy(mode, reference, support)) {
|
|
506
518
|
continue;
|
|
507
519
|
}
|
|
508
520
|
if (reference.use === "write" && reference.consumer === undefined) {
|
|
@@ -515,55 +527,22 @@ function writtenOnly(read, mode) {
|
|
|
515
527
|
}
|
|
516
528
|
return new Set([...written].filter((id) => !readSome.has(id)));
|
|
517
529
|
}
|
|
518
|
-
/**
|
|
519
|
-
* The sweep of a run read once, under one set of marks. Where a question went unanswered,
|
|
520
|
-
* the run is swept once more without what that question could have kept live and without
|
|
521
|
-
* the references that stood in for its answer, and every declaration that sweep judges
|
|
522
|
-
* dead and this one judges live, or dead by another rule, is held by the gap too, as is
|
|
523
|
-
* every declaration that makes such a reference.
|
|
524
|
-
*/
|
|
530
|
+
/** The sweep of a run read once, under one set of marks. */
|
|
525
531
|
function sweptOf(read, input) {
|
|
526
|
-
const
|
|
527
|
-
const exempt = swept.exempt ?? [];
|
|
528
|
-
const held = new Set(read.unansweredHeld);
|
|
529
|
-
if (held.size > 0) {
|
|
530
|
-
// What a declaration reads where the checker did not answer is unknown, so a rule
|
|
531
|
-
// over what it reads, the test of dead code among them, cannot judge it.
|
|
532
|
-
for (const { configured } of read.projects) {
|
|
533
|
-
for (const reference of configured.references) {
|
|
534
|
-
if (reference.resolution === "by-name") {
|
|
535
|
-
held.add(reference.from);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
const dead = new Map(result.candidates.map((candidate) => [candidate.id, candidate]));
|
|
540
|
-
const answered = matrixOf(read.projects.map(({ configured }) => ({
|
|
541
|
-
...configured,
|
|
542
|
-
references: configured.references.filter((one) => one.resolution !== "by-name"),
|
|
543
|
-
})));
|
|
544
|
-
const bare = {
|
|
545
|
-
...swept,
|
|
546
|
-
exempt: exempt.filter((record) => !isUnansweredRecord(record)),
|
|
547
|
-
unanswered: [],
|
|
548
|
-
};
|
|
549
|
-
for (const candidate of sweepMatrix(answered, bare).candidates) {
|
|
550
|
-
const kept = dead.get(candidate.id);
|
|
551
|
-
if (kept?.testOfDeadCode !== candidate.testOfDeadCode ||
|
|
552
|
-
kept.relation !== candidate.relation) {
|
|
553
|
-
held.add(candidate.id);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}
|
|
532
|
+
const componentFiles = [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise);
|
|
557
533
|
return {
|
|
558
534
|
matrix: read.matrix,
|
|
559
|
-
sweep:
|
|
560
|
-
|
|
561
|
-
|
|
535
|
+
sweep: sweep(read.matrix, {
|
|
536
|
+
...input,
|
|
537
|
+
exempt: read.exempt,
|
|
538
|
+
unanswered: read.unansweredHeld,
|
|
539
|
+
componentFiles,
|
|
540
|
+
unseenCallers: !read.declaresConsumers,
|
|
541
|
+
writeOnly: writtenOnly(read, input.mode),
|
|
542
|
+
}),
|
|
562
543
|
notBuilt: read.notBuilt,
|
|
563
544
|
unanswered: read.unanswered,
|
|
564
|
-
heldByUnanswered: [...held],
|
|
565
545
|
skipped: read.projects.flatMap((one) => one.skipped),
|
|
566
|
-
componentFiles: [...new Set(read.projects.flatMap((one) => one.componentFiles))].sort(bytewise),
|
|
567
546
|
};
|
|
568
547
|
}
|
|
569
548
|
/**
|
|
@@ -592,12 +571,15 @@ export function readEmitterRun(engine, host, scope, config, mode, detectors) {
|
|
|
592
571
|
implementations: implementations(project, read.held),
|
|
593
572
|
heldByInclusion: heldByInclusion(project, targetRoot),
|
|
594
573
|
generated: read.generated,
|
|
574
|
+
bindings: read.bindings,
|
|
595
575
|
inOperands: inOperandNames(project, targetRoot, testFiles),
|
|
596
576
|
rooted: read.rooted,
|
|
597
577
|
directives: inlineDirectives(project, read.held, targetRoot).filter((directive) => !read.referenceOnly.includes(directive.site.path)),
|
|
598
578
|
declarationsNamed: declarationsNamed(project, read.held, configuredDeclarations(config)),
|
|
599
579
|
parts: projectParts(project, read.held, references, targetRoot),
|
|
600
580
|
unseenLoads,
|
|
581
|
+
// Only a closed world reports an unnecessary export, which is all the carriers decide.
|
|
582
|
+
carriers: config.consumers.complete ? carriersOf(project, read.held) : NO_CARRIERS,
|
|
601
583
|
}));
|
|
602
584
|
// Whether a declaration carries the marker decides its code, so one whose answer went
|
|
603
585
|
// unread is reported by nothing.
|
|
@@ -659,8 +641,9 @@ export function emitterInputOver(host, scope, config, read, input, selfCheck) {
|
|
|
659
641
|
deprecated: new Set(projects.flatMap((one) => one.extra.deprecation.deprecated)),
|
|
660
642
|
stores: storesOf(projects.map((one) => ({
|
|
661
643
|
references: one.configured.references,
|
|
644
|
+
support: new Set(one.configured.supportFiles ?? []),
|
|
662
645
|
accessors: one.extra.accessors,
|
|
663
|
-
})), swept.exempt, input.mode),
|
|
646
|
+
})), swept.sweep.exempt, input.mode),
|
|
664
647
|
dependencies: dependenciesOf(host, targetRoot, read.workflows, [...projects.map((one) => one.extra.needs), configurationNeeds(read.conventionUses)], swept.sweep.candidates.map((candidate) => candidate.id), members.read, members.unbuilt, read.nestedDirs),
|
|
665
648
|
implementations: mergeImplementations(projects.map((one) => one.extra.implementations)),
|
|
666
649
|
files: {
|
|
@@ -678,9 +661,16 @@ export function emitterInputOver(host, scope, config, read, input, selfCheck) {
|
|
|
678
661
|
encapsulated: readManifest(host, targetRoot).declaresExports,
|
|
679
662
|
edges: readEdgeSides(host, targetRoot),
|
|
680
663
|
filledOutside: new Set(projects.flatMap((one) => one.filledOutside)),
|
|
664
|
+
possiblyLoaded: new Set(projects.flatMap((one) => one.possiblyLoaded)),
|
|
665
|
+
carriers: {
|
|
666
|
+
named: new Map([
|
|
667
|
+
...Map.groupBy(projects.flatMap((one) => [...one.extra.carriers.named]), ([id]) => id),
|
|
668
|
+
].map(([id, entries]) => [id, [...new Set(entries.flatMap(([, named]) => named))]])),
|
|
669
|
+
unread: new Set(projects.flatMap((one) => [...one.extra.carriers.unread])),
|
|
670
|
+
},
|
|
681
671
|
},
|
|
682
672
|
selfCheck,
|
|
683
|
-
intraFunction: intraFunctionFacts(projects.map((one) => one.extra.parts), projects.flatMap((one) => one.configured.references), new Set([...swept.exempt.map((one) => one.id), ...swept.
|
|
673
|
+
intraFunction: intraFunctionFacts(projects.map((one) => one.extra.parts), projects.flatMap((one) => one.configured.references), new Set([...swept.sweep.exempt.map((one) => one.id), ...swept.sweep.held])),
|
|
684
674
|
};
|
|
685
675
|
}
|
|
686
676
|
/**
|
|
@@ -725,6 +715,7 @@ function readAnalysis(engine, host, scope, config, provenance, mode, detectors)
|
|
|
725
715
|
heldScripts: read.projects.flatMap((one) => one.heldScripts.map((held) => ({ ...held, configFile: one.configFile }))),
|
|
726
716
|
unseenLoads: read.projects.flatMap((one) => one.extra.unseenLoads),
|
|
727
717
|
generated: new Map(read.projects.flatMap((one) => [...one.extra.generated])),
|
|
718
|
+
bindings: distinctBindings(read.projects.flatMap((one) => one.extra.bindings)),
|
|
728
719
|
},
|
|
729
720
|
configured: read.projects.map((one) => one.configured),
|
|
730
721
|
symbols,
|
|
@@ -792,7 +783,7 @@ export function runAnalysis(engine, host, scope, config, provenance, mode, detec
|
|
|
792
783
|
* declarations of its own the run holds for them.
|
|
793
784
|
*/
|
|
794
785
|
export function unansweredCounts(analysis) {
|
|
795
|
-
const held = new Set(analysis.swept.
|
|
786
|
+
const held = new Set(analysis.swept.sweep.held);
|
|
796
787
|
return analysis.run.projects.flatMap((project) => {
|
|
797
788
|
const questions = analysis.run.unanswered.filter((one) => one.configFile === project.configFile).length +
|
|
798
789
|
analysis.run.unread.filter((one) => one.kind === "manifest" && one.configFile === project.configFile).length +
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { isInterfaceDeclaration, isModuleBlock, isModuleDeclaration, } from "@typescript/native/ast";
|
|
10
10
|
import { nodeKey } from "./inventory.js";
|
|
11
|
-
import { referenceKey } from "./matrix.js";
|
|
11
|
+
import { referenceKey } from "./liveness/matrix.js";
|
|
12
12
|
/** Whether one declaration is a `declare global` block: a module-level namespace named `global`. */
|
|
13
13
|
export function isGlobalBlock(symbol, held, files) {
|
|
14
14
|
return (symbol.kind === "namespace" &&
|
|
@@ -50,19 +50,15 @@ function holdersOf(held, scripts) {
|
|
|
50
50
|
* file or a `declare` holds that a `declare global` block, or the top level of a file in
|
|
51
51
|
* `scripts`, declares, directly or inside a namespace there.
|
|
52
52
|
*/
|
|
53
|
-
|
|
53
|
+
function ambientGlobalInterfaces(held, interfaces, scripts) {
|
|
54
54
|
const holders = holdersOf(held, scripts);
|
|
55
55
|
return holders === undefined
|
|
56
56
|
? []
|
|
57
57
|
: interfaces.filter((id) => held.ambient.has(id) && holders(id).global);
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
*
|
|
61
|
-
* declared outside them,
|
|
62
|
-
* global scope: the outside declaration names it wherever a value is typed by it. Where
|
|
63
|
-
* every outside declaration is a package's, not the compiler's library, and declares no
|
|
64
|
-
* member, each member it declares as well: such an interface is a slot the package reads
|
|
65
|
-
* by type, as `keyof` does, in declarations the analysis does not walk.
|
|
60
|
+
* Which ambient global interfaces of the project's own files, `files`, merge into an interface
|
|
61
|
+
* declared outside them, and what each merge keeps.
|
|
66
62
|
*/
|
|
67
63
|
export function mergedGlobalInterfaces(project, held, files) {
|
|
68
64
|
const scripts = new Set();
|
|
@@ -88,7 +84,7 @@ export function mergedGlobalInterfaces(project, held, files) {
|
|
|
88
84
|
const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
|
|
89
85
|
const asked = [...names].filter(([id]) => global.has(id));
|
|
90
86
|
if (asked.length === 0) {
|
|
91
|
-
return [];
|
|
87
|
+
return { scripts, interfaces: new Set(), kept: [] };
|
|
92
88
|
}
|
|
93
89
|
const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
|
|
94
90
|
const holders = holdersOf(held, scripts);
|
|
@@ -99,22 +95,25 @@ export function mergedGlobalInterfaces(project, held, files) {
|
|
|
99
95
|
const node = project.declarationAt(handle)?.node;
|
|
100
96
|
return node !== undefined && isInterfaceDeclaration(node) && node.members.length === 0;
|
|
101
97
|
};
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
const interfaces = new Set();
|
|
99
|
+
const kept = new Set();
|
|
100
|
+
asked.forEach(([id], at) => {
|
|
101
|
+
const symbol = answers[at];
|
|
102
|
+
const outside = typeof symbol === "object"
|
|
103
|
+
? symbol.declarations.filter((handle) => !files.byName.has(handle.path))
|
|
104
|
+
: [];
|
|
105
|
+
if (outside.length === 0) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
interfaces.add(id);
|
|
109
|
+
const members = outside.every(empty)
|
|
110
|
+
? held.symbols.filter((one) => one.parent === id).map((one) => one.id)
|
|
111
|
+
: [];
|
|
112
|
+
for (const one of [id, ...(holders?.(id).ids ?? []), ...members]) {
|
|
113
|
+
kept.add(one);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return { scripts, interfaces, kept: [...kept] };
|
|
118
117
|
}
|
|
119
118
|
/**
|
|
120
119
|
* For each reference to a declaration an augmentation block, or the global scope of a
|