@cplieger/deadset-ts 8.0.0-dev.15 → 8.0.0-dev.17
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 +94 -54
- package/dist/src/augmentations.js +23 -24
- package/dist/src/bindings.js +382 -0
- package/dist/src/calls.js +25 -8
- package/dist/src/commands/read.js +6 -7
- 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 +9 -10
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +5 -2
- package/dist/src/contract/contract.generated.js +119 -2
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +98 -71
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +22 -64
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
- package/dist/src/entries/conventions.js +739 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +9 -12
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +3 -3
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +670 -0
- package/dist/src/entries/test-runners.js +684 -0
- package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/findings/completion.js +40 -4
- package/dist/src/findings/emitters.js +5 -8
- 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/self-check.js +36 -20
- package/dist/src/findings-pass.js +5 -6
- package/dist/src/framework-lifecycle.js +42 -15
- package/dist/src/fs/manifest.js +15 -20
- package/dist/src/generated-file.js +172 -29
- package/dist/src/graph.js +14 -8
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/inventory/commonjs.js +62 -17
- package/dist/src/inventory/enumerate.js +2 -1
- package/dist/src/javascript-file.js +26 -11
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/load/classify.js +28 -9
- package/dist/src/load/diagnostics.js +8 -1
- package/dist/src/load/discover.js +15 -7
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/plan.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +10 -5
- package/dist/src/load/workspace-manifests.js +1 -1
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/matrix.js +12 -17
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +56 -1
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +134 -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 +151 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +111 -1116
- package/dist/src/resolve.js +1 -1
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +7 -4
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
- package/dist/src/{manifest.js → roots/manifest.js} +4 -4
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +489 -0
- package/dist/src/roots/unseen-loads.js +236 -0
- package/dist/src/serialization-contract.js +9 -4
- package/dist/src/source-files.js +5 -9
- package/dist/src/stores.js +4 -2
- package/dist/src/suppress.js +30 -32
- package/dist/src/sweep.js +25 -13
- 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} +5 -12
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +15 -3
- package/dist/src/verbs/describe.js +5 -3
- package/dist/src/verbs/explain.js +50 -28
- package/dist/src/verbs/print-retained.js +2 -2
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +156 -70
- package/src/augmentations.ts +41 -27
- package/src/automatic-types.ts +1 -1
- package/src/bindings.ts +460 -0
- package/src/calls.ts +25 -7
- package/src/commands/read.ts +6 -8
- 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 +12 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +5 -2
- package/src/contract/contract.generated.ts +138 -2
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +117 -71
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +33 -82
- package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
- package/src/{conventions.ts → entries/conventions.ts} +349 -523
- package/src/{html-entries.ts → entries/html-entries.ts} +19 -18
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +5 -5
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +858 -0
- package/src/entries/test-runners.ts +868 -0
- package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
- package/src/exempt.ts +3 -2
- package/src/extensions.ts +45 -0
- package/src/facts.ts +46 -0
- package/src/file-facts.ts +1 -1
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +43 -4
- package/src/findings/emitter.ts +1 -1
- package/src/findings/emitters.ts +14 -10
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/findings/self-check.ts +41 -22
- package/src/findings/visibility-narrowing.ts +1 -1
- package/src/findings-pass.ts +5 -12
- package/src/framework-lifecycle.ts +43 -15
- package/src/fs/manifest.ts +25 -21
- package/src/generated-file.ts +240 -36
- package/src/graph.ts +27 -11
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +570 -527
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +1 -1
- package/src/inventory/commonjs.ts +102 -17
- package/src/inventory/enumerate.ts +2 -1
- package/src/javascript-file.ts +29 -12
- package/src/keyof-comparisons.ts +131 -116
- package/src/load/classify.ts +49 -13
- package/src/load/diagnostics.ts +11 -1
- package/src/load/discover.ts +27 -11
- package/src/load/expected-module.ts +8 -2
- package/src/load/plan.ts +11 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +12 -6
- package/src/load/test-globals.ts +1 -1
- package/src/load/workspace-manifests.ts +1 -1
- package/src/load/workspace-resolution.ts +9 -2
- package/src/load/workspace-run.ts +1 -1
- package/src/matrix.ts +13 -17
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +65 -1
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +189 -0
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +213 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +174 -1492
- package/src/report.ts +2 -2
- package/src/resolve.ts +1 -1
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +10 -7
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
- package/src/{manifest.ts → roots/manifest.ts} +5 -5
- package/src/roots/published-reach.ts +481 -0
- package/src/{roots.ts → roots/root-set.ts} +203 -630
- package/src/roots/unseen-loads.ts +345 -0
- package/src/serialization-contract.ts +9 -4
- package/src/source-files.ts +5 -10
- package/src/stores.ts +13 -2
- package/src/suppress.ts +48 -33
- package/src/sweep.ts +37 -14
- package/src/symbol-keys.ts +102 -102
- package/src/tests/file-class.ts +118 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +15 -21
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +19 -5
- package/src/verbs/describe.ts +5 -2
- package/src/verbs/explain.ts +75 -32
- package/src/verbs/print-retained.ts +2 -2
- package/dist/src/build-scripts.js +0 -418
- package/dist/src/conventions.js +0 -907
- package/dist/src/entry-points.js +0 -444
- package/dist/src/own-body.js +0 -28
- package/dist/src/roots.js +0 -865
- package/dist/src/unseen-loads.js +0 -189
- package/src/build-scripts.ts +0 -536
- package/src/entry-points.ts +0 -584
- package/src/own-body.ts +0 -42
- package/src/unseen-loads.ts +0 -282
package/src/references.ts
CHANGED
|
@@ -1,86 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
isExpressionStatement,
|
|
11
|
-
isExternalModuleReference,
|
|
12
|
-
isForInStatement,
|
|
13
|
-
isForOfStatement,
|
|
14
|
-
isForStatement,
|
|
15
|
-
isIdentifier,
|
|
16
|
-
isImportDeclaration,
|
|
17
|
-
isImportEqualsDeclaration,
|
|
18
|
-
isInterfaceDeclaration,
|
|
19
|
-
isMetaProperty,
|
|
20
|
-
isModuleBlock,
|
|
21
|
-
isModuleDeclaration,
|
|
22
|
-
isNewExpression,
|
|
23
|
-
isNamespaceExport,
|
|
24
|
-
isNoSubstitutionTemplateLiteral,
|
|
25
|
-
isObjectLiteralExpression,
|
|
26
|
-
isParenthesizedExpression,
|
|
27
|
-
isPostfixUnaryExpression,
|
|
28
|
-
isPrefixUnaryExpression,
|
|
29
|
-
isPropertyAccessExpression,
|
|
30
|
-
isPropertyAssignment,
|
|
31
|
-
isShorthandPropertyAssignment,
|
|
32
|
-
isSpreadAssignment,
|
|
33
|
-
isSpreadElement,
|
|
34
|
-
isStringLiteral,
|
|
35
|
-
isStringLiteralLikeNode,
|
|
36
|
-
isVoidExpression,
|
|
37
|
-
SyntaxKind,
|
|
38
|
-
type Node,
|
|
39
|
-
type ParameterDeclaration,
|
|
40
|
-
type SourceFile,
|
|
41
|
-
} from "@typescript/native/ast";
|
|
42
|
-
import { parenthesesRemoved } from "./unwrap.ts";
|
|
1
|
+
/**
|
|
2
|
+
* Every use one side's files make of the target's declarations: one walk and one batched
|
|
3
|
+
* resolution per file, never a reference query per symbol, whose answers would depend on the
|
|
4
|
+
* order the symbols are asked in. A reference carries its use, its position, the declaration
|
|
5
|
+
* enclosing it and whether a test file made it; it carries no mode: which references a run
|
|
6
|
+
* counts is the sweep's reading of this set.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { SyntaxKind, type Node } from "@typescript/native/ast";
|
|
43
10
|
import type { Symbol as TSSymbol } from "@typescript/native/sync";
|
|
44
11
|
import { aliasChains, type ChainTarget } from "./alias-chain.ts";
|
|
45
|
-
import {
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import { unimplementedCalleeReads } from "./unimplemented-callees.ts";
|
|
55
|
-
import { moduleDeclarationReferences } from "./module-declarations.ts";
|
|
56
|
-
import { overrideReferences } from "./overrides.ts";
|
|
57
|
-
import { dirnamePath, queriedModulePath, relativePath, resolvePath } from "./paths.ts";
|
|
58
|
-
import { byPosition, isComponentFile, renderPosition, type Position } from "./position.ts";
|
|
12
|
+
import { augmentationReferences, mergedGlobalInterfaces } from "./augmentations.ts";
|
|
13
|
+
import { contextualUses } from "./contextual-uses.ts";
|
|
14
|
+
import { destructuring } from "./destructuring.ts";
|
|
15
|
+
import type { RunDirectories } from "./entries/run-directories.ts";
|
|
16
|
+
import { bindingsOf } from "./import-bindings.ts";
|
|
17
|
+
import { sitesOf } from "./index/sites.ts";
|
|
18
|
+
import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
|
|
19
|
+
import { relativePath } from "./paths.ts";
|
|
20
|
+
import type { Position } from "./position.ts";
|
|
59
21
|
import { DEFAULT_BATCH_CAP, propertyTables, UNANSWERED, type Answer } from "./query.ts";
|
|
22
|
+
import { heldBy, READ_RULES } from "./references/read-rules.ts";
|
|
23
|
+
import { Sink } from "./references/sink.ts";
|
|
24
|
+
import { emitFile, prepared, type Pass } from "./references/sources.ts";
|
|
60
25
|
import type { ProjectView } from "./session.ts";
|
|
61
|
-
import {
|
|
62
|
-
import { typeQueryAliases } from "./type-query-alias.ts";
|
|
63
|
-
import { hasOwnBody } from "./own-body.ts";
|
|
64
|
-
import { computedLoadsOf, isLoadCall, type LoadCall } from "./unseen-loads.ts";
|
|
65
|
-
import {
|
|
66
|
-
computedKeyNames,
|
|
67
|
-
iterationReferences,
|
|
68
|
-
keyNameOf,
|
|
69
|
-
keyedUses,
|
|
70
|
-
type KeyedAccess,
|
|
71
|
-
} from "./symbol-keys.ts";
|
|
72
|
-
import { handedOn, valuesOf } from "./value-flow.ts";
|
|
26
|
+
import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
|
|
73
27
|
import { bytewise } from "./text/compare.ts";
|
|
74
|
-
import {
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Every use one project's own files make of the project's own declarations: one local walk
|
|
78
|
-
* and one batched resolution per file, never a reference query per symbol, whose answers
|
|
79
|
-
* would depend on the order the symbols are asked in. A reference carries its use, its
|
|
80
|
-
* position, the declaration enclosing it and whether a test file made it; every import of
|
|
81
|
-
* one of the project's modules is an evaluation of that module's file. It carries no mode:
|
|
82
|
-
* which references a run counts is the sweep's reading of this set.
|
|
83
|
-
*/
|
|
28
|
+
import type { TestFile } from "./tests/file-class.ts";
|
|
29
|
+
import { typeQueryAliases } from "./type-query-alias.ts";
|
|
84
30
|
|
|
85
31
|
/**
|
|
86
32
|
* How one reference uses the declaration it names. An `evaluation` names a file: the
|
|
@@ -89,17 +35,17 @@ import { globExpression } from "./text/glob.ts";
|
|
|
89
35
|
* `decorator` names the declaration a decorator expression is attached to, which the
|
|
90
36
|
* decorator receives when the class is defined.
|
|
91
37
|
*/
|
|
92
|
-
type Use = "read" | "write" | "evaluation" | "decorator";
|
|
38
|
+
export type Use = "read" | "write" | "evaluation" | "decorator";
|
|
93
39
|
|
|
94
40
|
/**
|
|
95
|
-
* Which path answered for one reference: `batch`, a file's batched lookup;
|
|
96
|
-
* per-node lookup for its residue; `shorthand`, the value `{ a }` reads;
|
|
97
|
-
* import or export chain; `syntax`, the tree alone
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* member a
|
|
41
|
+
* Which path answered for one reference, provenance only: `batch`, a file's batched lookup;
|
|
42
|
+
* `resolved-symbol`, the per-node lookup for its residue; `shorthand`, the value `{ a }` reads;
|
|
43
|
+
* `alias`, a step along an import or export chain; `syntax`, the tree alone; `destructured`,
|
|
44
|
+
* the property a pattern names; `contextual`, the member a literal property writes or a
|
|
45
|
+
* value's position reads; `override`, the override a base call dispatches to; `keyed`, the
|
|
46
|
+
* member a key selects; `by-name`, each declaration so spelled, where the checker failed.
|
|
101
47
|
*/
|
|
102
|
-
type Resolution =
|
|
48
|
+
export type Resolution =
|
|
103
49
|
| "batch"
|
|
104
50
|
| "resolved-symbol"
|
|
105
51
|
| "shorthand"
|
|
@@ -126,8 +72,13 @@ export interface Reference {
|
|
|
126
72
|
readonly position: Position;
|
|
127
73
|
readonly use: Use;
|
|
128
74
|
readonly resolution: Resolution;
|
|
129
|
-
/** Whether
|
|
75
|
+
/** Whether a test file made the reference (`tests/file-class.ts`). */
|
|
130
76
|
readonly test: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Present where only the name answered, the checker having failed (program.33): the
|
|
79
|
+
* reference may be a use of any declaration so spelled, so the run holds what it names.
|
|
80
|
+
*/
|
|
81
|
+
readonly guessed?: true;
|
|
131
82
|
/**
|
|
132
83
|
* The identifier of the consumer whose file made the reference, absent for one the
|
|
133
84
|
* target made. A consumer's declarations are no declarations of the run, so such a
|
|
@@ -153,68 +104,24 @@ export interface ConsumerSide {
|
|
|
153
104
|
readonly root: string;
|
|
154
105
|
}
|
|
155
106
|
|
|
156
|
-
/** One
|
|
157
|
-
export interface
|
|
158
|
-
|
|
159
|
-
readonly rule: string;
|
|
160
|
-
readonly matched: number;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** What one project's reference pass cost at the client boundary. */
|
|
164
|
-
export interface ReferenceCost {
|
|
165
|
-
/**
|
|
166
|
-
* The name nodes the batches asked about, summed over the files. It is the size of
|
|
167
|
-
* the question rather than a cost of its own, and it is reported so that the number
|
|
168
|
-
* of batches is readable against it.
|
|
169
|
-
*/
|
|
170
|
-
readonly batched: number;
|
|
171
|
-
/**
|
|
172
|
-
* Batched lookups: per file, the name-node count divided by the batch cap, rounded
|
|
173
|
-
* up. It is what keeps the pass's cost off the identifier count.
|
|
174
|
-
*/
|
|
175
|
-
readonly fileBatches: number;
|
|
176
|
-
/** Per-node lookups for the name nodes a batch left unresolved. */
|
|
177
|
-
readonly residueFallbacks: number;
|
|
178
|
-
/** Per-node lookups for a shorthand property assignment's value. */
|
|
179
|
-
readonly shorthandLookups: number;
|
|
180
|
-
/** Per-symbol lookups that step from one alias to what it names, one per alias. */
|
|
181
|
-
readonly aliasSteps: number;
|
|
182
|
-
/**
|
|
183
|
-
* Batched type lookups for the object patterns: per file, the patterns and the values
|
|
184
|
-
* assigned to destructuring targets, divided by the batch cap, rounded up.
|
|
185
|
-
*/
|
|
186
|
-
readonly patternBatches: number;
|
|
107
|
+
/** One side's references, in position order. */
|
|
108
|
+
export interface References {
|
|
109
|
+
readonly references: readonly Reference[];
|
|
187
110
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
111
|
+
* The declarations a read rule's unanswered question could have made live, and those
|
|
112
|
+
* making such a read, each once, ascending: the run holds them (program.33).
|
|
190
113
|
*/
|
|
191
|
-
readonly
|
|
114
|
+
readonly held: readonly string[];
|
|
192
115
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
116
|
+
* Each load by a computed path a production file of the target writes whose files the
|
|
117
|
+
* analysis cannot name (confidence.26), in file then source order; none for a consumer.
|
|
195
118
|
*/
|
|
196
|
-
readonly
|
|
197
|
-
/** Per-type lookups for the contextual uses: each distinct type's constituents, properties and symbol. */
|
|
198
|
-
readonly contextualLookups: number;
|
|
119
|
+
readonly unseenLoads: readonly Position[];
|
|
199
120
|
/**
|
|
200
|
-
* The
|
|
201
|
-
*
|
|
202
|
-
* unimplemented callees and the modules a `declare module` names. They are counted
|
|
203
|
-
* where the caller measures the client, through `requestCount`, and are 0 otherwise.
|
|
121
|
+
* The files, and the declarations an import binds there, that only a bundler's retry of a
|
|
122
|
+
* specifier Node leaves unresolved names (program.38), each once, ascending.
|
|
204
123
|
*/
|
|
205
|
-
readonly
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** One project's references, in position order, and what reading them cost. */
|
|
209
|
-
export interface References {
|
|
210
|
-
readonly references: readonly Reference[];
|
|
211
|
-
/**
|
|
212
|
-
* Every file the rules classified as a test file, by its path below the target root,
|
|
213
|
-
* ascending. A declaration such a file holds is a test declaration, by the same
|
|
214
|
-
* classification that marks the references the file makes.
|
|
215
|
-
*/
|
|
216
|
-
readonly testFilePaths: readonly string[];
|
|
217
|
-
readonly cost: ReferenceCost;
|
|
124
|
+
readonly possiblyLoaded: readonly string[];
|
|
218
125
|
}
|
|
219
126
|
|
|
220
127
|
/** How one run resolves references. */
|
|
@@ -225,611 +132,19 @@ export interface ReferenceOptions {
|
|
|
225
132
|
* makes one answer proportional to that file. Absent takes {@link DEFAULT_BATCH_CAP}.
|
|
226
133
|
*/
|
|
227
134
|
readonly batchCap?: number;
|
|
228
|
-
/**
|
|
229
|
-
|
|
230
|
-
* Each pattern is a rule of its own, which {@link testFileRulesOf} counts.
|
|
231
|
-
*/
|
|
232
|
-
readonly testFiles: readonly string[];
|
|
233
|
-
/** The files, absolute, that are test files beside those the patterns name. */
|
|
234
|
-
readonly testFileNames?: ReadonlySet<string>;
|
|
135
|
+
/** Which of the side's files are test files. */
|
|
136
|
+
readonly testFile: TestFile;
|
|
235
137
|
/**
|
|
236
138
|
* The consumer whose files the pass walks instead of the target's. Its files are the
|
|
237
139
|
* program's own files below its root and not below the target root, each position is
|
|
238
|
-
* rendered against its root and
|
|
239
|
-
*
|
|
140
|
+
* rendered against its root, and only a reference to a declaration of `held`, the
|
|
141
|
+
* target's, is recorded.
|
|
240
142
|
*/
|
|
241
143
|
readonly consumer?: ConsumerSide;
|
|
242
|
-
/** The
|
|
243
|
-
readonly
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* The operators whose left side one assignment stores into. A compound assignment is
|
|
248
|
-
* among them: where its value is discarded it reads its target only to write the result
|
|
249
|
-
* back, so nothing else receives what it read.
|
|
250
|
-
*/
|
|
251
|
-
const ASSIGNMENTS: ReadonlySet<SyntaxKind> = new Set([
|
|
252
|
-
SyntaxKind.EqualsToken,
|
|
253
|
-
SyntaxKind.PlusEqualsToken,
|
|
254
|
-
SyntaxKind.MinusEqualsToken,
|
|
255
|
-
SyntaxKind.AsteriskEqualsToken,
|
|
256
|
-
SyntaxKind.AsteriskAsteriskEqualsToken,
|
|
257
|
-
SyntaxKind.SlashEqualsToken,
|
|
258
|
-
SyntaxKind.PercentEqualsToken,
|
|
259
|
-
SyntaxKind.LessThanLessThanEqualsToken,
|
|
260
|
-
SyntaxKind.GreaterThanGreaterThanEqualsToken,
|
|
261
|
-
SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
|
|
262
|
-
SyntaxKind.AmpersandEqualsToken,
|
|
263
|
-
SyntaxKind.BarEqualsToken,
|
|
264
|
-
SyntaxKind.BarBarEqualsToken,
|
|
265
|
-
SyntaxKind.AmpersandAmpersandEqualsToken,
|
|
266
|
-
SyntaxKind.QuestionQuestionEqualsToken,
|
|
267
|
-
SyntaxKind.CaretEqualsToken,
|
|
268
|
-
]);
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* The assignments that read their target to decide whether to store at all, so the read
|
|
272
|
-
* carries information out whatever becomes of the result.
|
|
273
|
-
*/
|
|
274
|
-
const LOGICAL_ASSIGNMENTS: ReadonlySet<SyntaxKind> = new Set([
|
|
275
|
-
SyntaxKind.BarBarEqualsToken,
|
|
276
|
-
SyntaxKind.AmpersandAmpersandEqualsToken,
|
|
277
|
-
SyntaxKind.QuestionQuestionEqualsToken,
|
|
278
|
-
]);
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Whether the value of one store expression is discarded: it is a statement of its own, a
|
|
282
|
-
* clause of a `for` loop, the operand of `void`, or the left operand of a comma.
|
|
283
|
-
*/
|
|
284
|
-
function valueDiscarded(node: Node): boolean {
|
|
285
|
-
let child = node;
|
|
286
|
-
let parent = node.parent;
|
|
287
|
-
while (isParenthesizedExpression(parent)) {
|
|
288
|
-
child = parent;
|
|
289
|
-
parent = parent.parent;
|
|
290
|
-
}
|
|
291
|
-
if (isExpressionStatement(parent) || isVoidExpression(parent)) {
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
if (isForStatement(parent)) {
|
|
295
|
-
return parent.initializer === child || parent.incrementor === child;
|
|
296
|
-
}
|
|
297
|
-
return (
|
|
298
|
-
isBinaryExpression(parent) &&
|
|
299
|
-
parent.operatorToken.kind === SyntaxKind.CommaToken &&
|
|
300
|
-
parent.left === child
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/** Whether one node is an identifier or a private name, the two forms a name takes. */
|
|
305
|
-
function isName(node: Node): boolean {
|
|
306
|
-
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Records the name one store writes into.
|
|
311
|
-
*
|
|
312
|
-
* A property access writes the member it names. An element access writes the collection
|
|
313
|
-
* it stores into, since a collection a module only stores into holds nothing anything
|
|
314
|
-
* reads, and joins `accesses` for the member a unique-symbol key selects. A destructuring
|
|
315
|
-
* target writes each name it binds, through its spreads and nested patterns.
|
|
316
|
-
*/
|
|
317
|
-
function markWrite(target: Node, writes: Set<number>, accesses: Set<Node>): void {
|
|
318
|
-
const held = parenthesesRemoved(target);
|
|
319
|
-
if (isName(held)) {
|
|
320
|
-
writes.add(held.pos);
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
if (isPropertyAccessExpression(held)) {
|
|
324
|
-
if (isName(held.name)) {
|
|
325
|
-
writes.add(held.name.pos);
|
|
326
|
-
}
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
if (isElementAccessExpression(held)) {
|
|
330
|
-
accesses.add(held);
|
|
331
|
-
// A store through a member's value lands in storage every holder of the value
|
|
332
|
-
// reads, so it reads the member; a variable only ever stored into stays written.
|
|
333
|
-
if (!isPropertyAccessExpression(parenthesesRemoved(held.expression))) {
|
|
334
|
-
markWrite(held.expression, writes, accesses);
|
|
335
|
-
}
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
if (isArrayLiteralExpression(held)) {
|
|
339
|
-
for (const element of held.elements) {
|
|
340
|
-
markWrite(element, writes, accesses);
|
|
341
|
-
}
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
if (isObjectLiteralExpression(held)) {
|
|
345
|
-
for (const property of held.properties) {
|
|
346
|
-
if (isPropertyAssignment(property)) {
|
|
347
|
-
markWrite(property.initializer, writes, accesses);
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
if (isShorthandPropertyAssignment(property)) {
|
|
351
|
-
markWrite(property.name, writes, accesses);
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
if (isSpreadAssignment(property)) {
|
|
355
|
-
markWrite(property.expression, writes, accesses);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
if (isSpreadElement(held)) {
|
|
361
|
-
markWrite(held.expression, writes, accesses);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Records every store one node performs: an assignment, a compound assignment, an
|
|
367
|
-
* increment or a decrement, a `delete`, and an iteration that assigns to a name declared
|
|
368
|
-
* elsewhere. An iteration that declares its own name stores into nothing the inventory
|
|
369
|
-
* holds. A store that reads its target as well records it in `reads`: a logical
|
|
370
|
-
* assignment, and a compound assignment, an increment or a decrement whose value is used.
|
|
371
|
-
*/
|
|
372
|
-
function markStores(
|
|
373
|
-
node: Node,
|
|
374
|
-
writes: Set<number>,
|
|
375
|
-
reads: Set<number>,
|
|
376
|
-
accesses: { readonly written: Set<Node>; readonly read: Set<Node> },
|
|
377
|
-
): void {
|
|
378
|
-
if (isBinaryExpression(node)) {
|
|
379
|
-
const operator = node.operatorToken.kind;
|
|
380
|
-
if (ASSIGNMENTS.has(operator)) {
|
|
381
|
-
markWrite(node.left, writes, accesses.written);
|
|
382
|
-
if (
|
|
383
|
-
LOGICAL_ASSIGNMENTS.has(operator) ||
|
|
384
|
-
(operator !== SyntaxKind.EqualsToken && !valueDiscarded(node))
|
|
385
|
-
) {
|
|
386
|
-
markWrite(node.left, reads, accesses.read);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
if (isPrefixUnaryExpression(node) || isPostfixUnaryExpression(node)) {
|
|
392
|
-
if (
|
|
393
|
-
node.operator === SyntaxKind.PlusPlusToken ||
|
|
394
|
-
node.operator === SyntaxKind.MinusMinusToken
|
|
395
|
-
) {
|
|
396
|
-
markWrite(node.operand, writes, accesses.written);
|
|
397
|
-
if (!valueDiscarded(node)) {
|
|
398
|
-
markWrite(node.operand, reads, accesses.read);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
if (isDeleteExpression(node)) {
|
|
404
|
-
markWrite(node.expression, writes, accesses.written);
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
if (isForInStatement(node) || isForOfStatement(node)) {
|
|
408
|
-
if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
|
|
409
|
-
markWrite(node.initializer, writes, accesses.written);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/** One name node the walk found, and what the walk knows about it. */
|
|
415
|
-
interface Site {
|
|
416
|
-
/** The name node, whose position the reference carries. */
|
|
417
|
-
readonly node: Node;
|
|
418
|
-
/**
|
|
419
|
-
* The shorthand property assignment this name belongs to, where it belongs to one.
|
|
420
|
-
* Its value is the declaration the name reads, while the name itself declares the
|
|
421
|
-
* property the literal carries.
|
|
422
|
-
*/
|
|
423
|
-
readonly shorthand: Node | undefined;
|
|
424
|
-
readonly from: string;
|
|
425
|
-
readonly use: Use;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* One re-export the walk found, which names what it carries forward without a name
|
|
430
|
-
* that is a use: an export specifier, a namespace export, or a bare star re-export.
|
|
431
|
-
*/
|
|
432
|
-
interface Link {
|
|
433
|
-
/**
|
|
434
|
-
* The node whose symbol says what the re-export carries: the alias a specifier or a
|
|
435
|
-
* namespace export declares, or the module a bare star re-export names.
|
|
436
|
-
*/
|
|
437
|
-
readonly node: Node;
|
|
438
|
-
/** Where the reference is written: the name a specifier re-exports, or the module specifier. */
|
|
439
|
-
readonly at: Node;
|
|
440
|
-
/** The re-export's own declaration, or for a bare star the declaration enclosing it. */
|
|
441
|
-
readonly from: string;
|
|
442
|
-
/** Whether the node's symbol is an alias, which is followed one link along. */
|
|
443
|
-
readonly alias: boolean;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/** One place the walk found that evaluates a module, and the declaration that holds it. */
|
|
447
|
-
interface Evaluation {
|
|
448
|
-
/** The module specifier, whose symbol is the module evaluated. */
|
|
449
|
-
readonly node: Node;
|
|
450
|
-
readonly from: string;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* One module URL a source file builds: `new URL(<literal>, import.meta.url)` or
|
|
455
|
-
* `import.meta.resolve(<literal>)`, the literal relative. A runtime starts the file it
|
|
456
|
-
* names as a module, so it is an import of that file for its effects.
|
|
457
|
-
*/
|
|
458
|
-
interface ModuleURL {
|
|
459
|
-
readonly specifier: string;
|
|
460
|
-
/** The `URL` the constructor names; {@link hasOwnBody} owns which one loads nothing. */
|
|
461
|
-
readonly constructor: Node | undefined;
|
|
462
|
-
readonly at: Node;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/** One module URL, and the declaration whose code builds it. */
|
|
466
|
-
interface ModuleURLAt extends ModuleURL {
|
|
467
|
-
readonly from: string;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/** One use of a module namespace object that may read every export of the module. */
|
|
471
|
-
interface WholeRead {
|
|
472
|
-
/** The module specifier, whose symbol is the module read. */
|
|
473
|
-
readonly specifier: Node;
|
|
474
|
-
/** The use, whose position the reference carries. */
|
|
475
|
-
readonly at: Node;
|
|
476
|
-
readonly from: string;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
/** One decorator expression, and the declaration it is attached to. */
|
|
480
|
-
interface Decorated {
|
|
481
|
-
readonly decorator: Node;
|
|
482
|
-
readonly declaration: string;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/** One parameter property, which its constructor writes, and the declaration that writes it. */
|
|
486
|
-
interface ParameterWrite {
|
|
487
|
-
readonly name: Node;
|
|
488
|
-
readonly from: string;
|
|
489
|
-
readonly member: string;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/** One property a pattern reads, and the declaration the pattern is written inside. */
|
|
493
|
-
interface Read extends PropertyRead {
|
|
494
|
-
readonly from: string;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
/** Everything one file's walk found to resolve, and the decorators it needs no resolution for. */
|
|
498
|
-
interface FileSites {
|
|
499
|
-
readonly uses: readonly Site[];
|
|
500
|
-
readonly literals: readonly LiteralAt[];
|
|
501
|
-
readonly flows: readonly FlowAt[];
|
|
502
|
-
readonly reads: readonly Read[];
|
|
503
|
-
readonly links: readonly Link[];
|
|
504
|
-
readonly evaluations: readonly Evaluation[];
|
|
505
|
-
readonly wholes: readonly WholeRead[];
|
|
506
|
-
readonly urls: readonly ModuleURLAt[];
|
|
507
|
-
readonly loadCalls: readonly LoadCall[];
|
|
508
|
-
readonly decorated: readonly Decorated[];
|
|
509
|
-
readonly parameterWrites: readonly ParameterWrite[];
|
|
510
|
-
readonly keyed: readonly KeyedAccess[];
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
/** Whether one node is `import.meta.url`. */
|
|
514
|
-
function isImportMetaURL(node: Node): boolean {
|
|
515
|
-
const at = parenthesesRemoved(node);
|
|
516
|
-
return (
|
|
517
|
-
isPropertyAccessExpression(at) &&
|
|
518
|
-
isMetaProperty(at.expression) &&
|
|
519
|
-
at.expression.keywordToken === SyntaxKind.ImportKeyword &&
|
|
520
|
-
at.name.text === "url"
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/** The module URL one node builds, where it builds one. */
|
|
525
|
-
function moduleURLOf(node: Node): ModuleURL | undefined {
|
|
526
|
-
const relative = (argument: Node | undefined): string | undefined => {
|
|
527
|
-
const at = argument === undefined ? undefined : parenthesesRemoved(argument);
|
|
528
|
-
return at !== undefined &&
|
|
529
|
-
(isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at)) &&
|
|
530
|
-
/^\.\.?\//u.test(at.text)
|
|
531
|
-
? at.text
|
|
532
|
-
: undefined;
|
|
533
|
-
};
|
|
534
|
-
if (isNewExpression(node) && isIdentifier(node.expression) && node.expression.text === "URL") {
|
|
535
|
-
const [reference, base] = node.arguments ?? [];
|
|
536
|
-
const specifier = relative(reference);
|
|
537
|
-
return specifier !== undefined && base !== undefined && isImportMetaURL(base)
|
|
538
|
-
? { specifier, constructor: node.expression, at: node }
|
|
539
|
-
: undefined;
|
|
540
|
-
}
|
|
541
|
-
if (isCallExpression(node)) {
|
|
542
|
-
const callee = parenthesesRemoved(node.expression);
|
|
543
|
-
const specifier = relative(node.arguments[0]);
|
|
544
|
-
if (
|
|
545
|
-
specifier !== undefined &&
|
|
546
|
-
isPropertyAccessExpression(callee) &&
|
|
547
|
-
isMetaProperty(callee.expression) &&
|
|
548
|
-
callee.expression.keywordToken === SyntaxKind.ImportKeyword &&
|
|
549
|
-
callee.name.text === "resolve"
|
|
550
|
-
) {
|
|
551
|
-
return { specifier, constructor: undefined, at: node };
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
return undefined;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/** The file of the target a relative specifier written in `file` names, by its source or output name. */
|
|
558
|
-
function fileNamed(
|
|
559
|
-
file: SourceFile,
|
|
560
|
-
specifier: string,
|
|
561
|
-
fileIdByName: ReadonlyMap<string, string>,
|
|
562
|
-
): string | undefined {
|
|
563
|
-
return specifierCandidates(dirnamePath(file.fileName), specifier)
|
|
564
|
-
.map((path) => fileIdByName.get(path))
|
|
565
|
-
.find((id) => id !== undefined);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/** Whether one node is a string literal or a template literal with no substitution. */
|
|
569
|
-
function isLiteralText(node: Node | undefined): node is Node {
|
|
570
|
-
return node !== undefined && (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node));
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* The module specifier of the module one node evaluates, if it evaluates one: an import
|
|
575
|
-
* declaration, an import-equals declaration of an external module, an export
|
|
576
|
-
* declaration that names a module, and an `import()` call whose argument is literal
|
|
577
|
-
* text. A type-only form evaluates nothing. An `import()` of a computed argument names
|
|
578
|
-
* no module this pass can know.
|
|
579
|
-
*/
|
|
580
|
-
function evaluatedBy(node: Node): Node | undefined {
|
|
581
|
-
if (isImportDeclaration(node)) {
|
|
582
|
-
return node.importClause?.phaseModifier === SyntaxKind.TypeKeyword
|
|
583
|
-
? undefined
|
|
584
|
-
: node.moduleSpecifier;
|
|
585
|
-
}
|
|
586
|
-
if (isExportDeclaration(node)) {
|
|
587
|
-
return node.isTypeOnly ? undefined : node.moduleSpecifier;
|
|
588
|
-
}
|
|
589
|
-
if (isImportEqualsDeclaration(node)) {
|
|
590
|
-
const reference = node.moduleReference;
|
|
591
|
-
return !node.isTypeOnly && isExternalModuleReference(reference)
|
|
592
|
-
? reference.expression
|
|
593
|
-
: undefined;
|
|
594
|
-
}
|
|
595
|
-
if (isCallExpression(node) && node.expression.kind === SyntaxKind.ImportKeyword) {
|
|
596
|
-
const [argument] = node.arguments;
|
|
597
|
-
return isLiteralText(argument) ? argument : undefined;
|
|
598
|
-
}
|
|
599
|
-
return undefined;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* The own file a module specifier with a query evaluates ({@link queriedModulePath}): the
|
|
604
|
-
* checker resolves the whole specifier to whatever module declaration matches it.
|
|
605
|
-
*/
|
|
606
|
-
function queriedFile(
|
|
607
|
-
file: SourceFile,
|
|
608
|
-
specifier: Node,
|
|
609
|
-
fileIdByName: ReadonlyMap<string, string>,
|
|
610
|
-
): string | undefined {
|
|
611
|
-
if (!isStringLiteral(specifier) && !isNoSubstitutionTemplateLiteral(specifier)) {
|
|
612
|
-
return undefined;
|
|
613
|
-
}
|
|
614
|
-
const path = queriedModulePath(specifier.text);
|
|
615
|
-
return path === undefined
|
|
616
|
-
? undefined
|
|
617
|
-
: fileIdByName.get(resolvePath(dirnamePath(file.fileName), path));
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* The re-exports one node writes, if it is an export declaration. A specifier and a
|
|
622
|
-
* namespace export are declarations whose names are where they are written, so no use is
|
|
623
|
-
* read from them; each references what it carries forward instead. A bare star declares
|
|
624
|
-
* nothing, so the declaration enclosing it, at the top level the file, references the
|
|
625
|
-
* module it names.
|
|
626
|
-
*/
|
|
627
|
-
function linksOf(
|
|
628
|
-
file: SourceFile,
|
|
629
|
-
node: Node,
|
|
630
|
-
declarations: ReadonlyMap<string, string>,
|
|
631
|
-
enclosing: string,
|
|
632
|
-
): Link[] {
|
|
633
|
-
if (!isExportDeclaration(node)) {
|
|
634
|
-
return [];
|
|
635
|
-
}
|
|
636
|
-
const clause = node.exportClause;
|
|
637
|
-
const specifier = node.moduleSpecifier;
|
|
638
|
-
if (clause === undefined) {
|
|
639
|
-
return specifier === undefined
|
|
640
|
-
? []
|
|
641
|
-
: [{ node: specifier, at: specifier, from: enclosing, alias: false }];
|
|
642
|
-
}
|
|
643
|
-
if (isNamespaceExport(clause)) {
|
|
644
|
-
const from = declarations.get(nodeKey(file, clause));
|
|
645
|
-
return from === undefined
|
|
646
|
-
? []
|
|
647
|
-
: [{ node: clause.name, at: specifier ?? clause.name, from, alias: true }];
|
|
648
|
-
}
|
|
649
|
-
const found: Link[] = [];
|
|
650
|
-
for (const element of clause.elements) {
|
|
651
|
-
const from = declarations.get(nodeKey(file, element));
|
|
652
|
-
if (from !== undefined) {
|
|
653
|
-
found.push({
|
|
654
|
-
node: element.name,
|
|
655
|
-
at: element.propertyName ?? element.name,
|
|
656
|
-
from,
|
|
657
|
-
alias: true,
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
return found;
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
/**
|
|
665
|
-
* Whether one object literal is a destructuring target, written on the left of an
|
|
666
|
-
* assignment or as a `for` loop's initializer, through the literals and spreads it nests in.
|
|
667
|
-
*/
|
|
668
|
-
function isAssignmentPattern(node: Node): boolean {
|
|
669
|
-
let child = node;
|
|
670
|
-
let parent = node.parent;
|
|
671
|
-
while (
|
|
672
|
-
isParenthesizedExpression(parent) ||
|
|
673
|
-
isArrayLiteralExpression(parent) ||
|
|
674
|
-
isObjectLiteralExpression(parent) ||
|
|
675
|
-
isSpreadElement(parent) ||
|
|
676
|
-
isSpreadAssignment(parent) ||
|
|
677
|
-
(isPropertyAssignment(parent) && parent.initializer === child)
|
|
678
|
-
) {
|
|
679
|
-
child = parent;
|
|
680
|
-
parent = parent.parent;
|
|
681
|
-
}
|
|
682
|
-
if (isBinaryExpression(parent)) {
|
|
683
|
-
return parent.operatorToken.kind === SyntaxKind.EqualsToken && parent.left === child;
|
|
684
|
-
}
|
|
685
|
-
return (isForInStatement(parent) || isForOfStatement(parent)) && parent.initializer === child;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* Every name node one file holds that is a use rather than a declaration, in source
|
|
690
|
-
* order, each with its enclosing declaration and its use, and every re-export the file
|
|
691
|
-
* writes. One walk answers all of it, because a second would have to agree with this one
|
|
692
|
-
* about which nodes are names. A declaration's own declared name is left out, which is
|
|
693
|
-
* what makes a declaration referenced only from its own site an unreferenced one, and so
|
|
694
|
-
* is every node of `unread`, a subtree that is no use.
|
|
695
|
-
*/
|
|
696
|
-
function sitesOf(
|
|
697
|
-
file: SourceFile,
|
|
698
|
-
declarations: ReadonlyMap<string, string>,
|
|
699
|
-
fileId: string,
|
|
700
|
-
unread: ReadonlySet<Node>,
|
|
701
|
-
): FileSites {
|
|
702
|
-
const found: Site[] = [];
|
|
703
|
-
const reads: Read[] = [];
|
|
704
|
-
const links: Link[] = [];
|
|
705
|
-
const evaluations: Evaluation[] = [];
|
|
706
|
-
const wholes: WholeRead[] = [];
|
|
707
|
-
const urls: ModuleURLAt[] = [];
|
|
708
|
-
const loadCalls: LoadCall[] = [];
|
|
709
|
-
const decorated: Decorated[] = [];
|
|
710
|
-
const parameterWrites: ParameterWrite[] = [];
|
|
711
|
-
const namespaces = namespaceUses(file);
|
|
712
|
-
const literals: LiteralAt[] = [];
|
|
713
|
-
const flows: FlowAt[] = [];
|
|
714
|
-
const writes = new Set<number>();
|
|
715
|
-
const alsoRead = new Set<number>();
|
|
716
|
-
const accesses = { written: new Set<Node>(), read: new Set<Node>() };
|
|
717
|
-
const keyed: KeyedAccess[] = [];
|
|
718
|
-
const declared = new Set<number>();
|
|
719
|
-
const shorthands = new Map<number, Node>();
|
|
720
|
-
let enclosing = fileId;
|
|
721
|
-
|
|
722
|
-
const markNames = (node: Node): void => {
|
|
723
|
-
if (isShorthandPropertyAssignment(node)) {
|
|
724
|
-
shorthands.set(node.name.pos, node);
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
// A property access carries the member it names where a declaration carries its
|
|
728
|
-
// declared name, and it is the one form of the grammar that does.
|
|
729
|
-
if (isPropertyAccessExpression(node)) {
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
const named = node as { readonly name?: Node; readonly propertyName?: Node };
|
|
733
|
-
for (const name of [named.name, named.propertyName]) {
|
|
734
|
-
if (name !== undefined && isName(name)) {
|
|
735
|
-
declared.add(name.pos);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
const visit = (node: Node): void => {
|
|
741
|
-
if (unread.has(node)) {
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
const namespace = namespaces.get(node);
|
|
745
|
-
if (namespace?.kind === "whole") {
|
|
746
|
-
wholes.push({ specifier: namespace.specifier, at: node, from: enclosing });
|
|
747
|
-
} else if (namespace?.kind === "indexed") {
|
|
748
|
-
found.push({ node: namespace.literal, shorthand: undefined, from: enclosing, use: "read" });
|
|
749
|
-
}
|
|
750
|
-
if (isName(node)) {
|
|
751
|
-
if (!declared.has(node.pos)) {
|
|
752
|
-
const site = { node, shorthand: shorthands.get(node.pos), from: enclosing };
|
|
753
|
-
if (writes.has(node.pos)) {
|
|
754
|
-
found.push({ ...site, use: "write" });
|
|
755
|
-
}
|
|
756
|
-
if (!writes.has(node.pos) || alsoRead.has(node.pos)) {
|
|
757
|
-
found.push({ ...site, use: "read" });
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
markNames(node);
|
|
763
|
-
markStores(node, writes, alsoRead, accesses);
|
|
764
|
-
const key = isElementAccessExpression(node) ? keyNameOf(node.argumentExpression) : undefined;
|
|
765
|
-
if (key !== undefined && isElementAccessExpression(node)) {
|
|
766
|
-
const written = accesses.written.has(node);
|
|
767
|
-
keyed.push({
|
|
768
|
-
access: node,
|
|
769
|
-
key,
|
|
770
|
-
from: enclosing,
|
|
771
|
-
use: written ? (accesses.read.has(node) ? "both" : "write") : "read",
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
if (isObjectLiteralExpression(node) && !isAssignmentPattern(node)) {
|
|
775
|
-
literals.push({ literal: node, from: enclosing });
|
|
776
|
-
}
|
|
777
|
-
// An assertion needs only comparability, so no member of its operand is read by it.
|
|
778
|
-
for (const written of isAsExpression(node) ? [] : handedOn(node)) {
|
|
779
|
-
flows.push(...valuesOf(written).map((value) => ({ value, from: enclosing })));
|
|
780
|
-
}
|
|
781
|
-
for (const read of propertyReadsOf(node)) {
|
|
782
|
-
reads.push({ ...read, from: enclosing });
|
|
783
|
-
}
|
|
784
|
-
links.push(...linksOf(file, node, declarations, enclosing));
|
|
785
|
-
const evaluated = evaluatedBy(node);
|
|
786
|
-
if (evaluated !== undefined) {
|
|
787
|
-
evaluations.push({ node: evaluated, from: enclosing });
|
|
788
|
-
}
|
|
789
|
-
const url = moduleURLOf(node);
|
|
790
|
-
if (url !== undefined) {
|
|
791
|
-
urls.push({ ...url, from: enclosing });
|
|
792
|
-
}
|
|
793
|
-
if (isLoadCall(node)) {
|
|
794
|
-
loadCalls.push({ call: node, from: enclosing });
|
|
795
|
-
}
|
|
796
|
-
const held = declarations.get(nodeKey(file, node));
|
|
797
|
-
const outer = enclosing;
|
|
798
|
-
// A parameter property's annotation, initializer and decorators stay with the
|
|
799
|
-
// parameter when the member goes, so they are written inside the constructor's class.
|
|
800
|
-
if (held !== undefined && isParameterProperty(node)) {
|
|
801
|
-
parameterWrites.push({
|
|
802
|
-
name: (node as ParameterDeclaration).name,
|
|
803
|
-
from: outer,
|
|
804
|
-
member: held,
|
|
805
|
-
});
|
|
806
|
-
} else if (held !== undefined) {
|
|
807
|
-
enclosing = held;
|
|
808
|
-
for (const modifier of (node as { readonly modifiers?: readonly Node[] }).modifiers ?? []) {
|
|
809
|
-
if (isDecorator(modifier)) {
|
|
810
|
-
decorated.push({ decorator: modifier, declaration: held });
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
node.forEachChild(visit);
|
|
815
|
-
enclosing = outer;
|
|
816
|
-
};
|
|
817
|
-
|
|
818
|
-
file.forEachChild(visit);
|
|
819
|
-
return {
|
|
820
|
-
uses: found,
|
|
821
|
-
literals,
|
|
822
|
-
flows,
|
|
823
|
-
reads,
|
|
824
|
-
links,
|
|
825
|
-
evaluations,
|
|
826
|
-
wholes,
|
|
827
|
-
urls,
|
|
828
|
-
loadCalls,
|
|
829
|
-
decorated,
|
|
830
|
-
parameterWrites,
|
|
831
|
-
keyed,
|
|
832
|
-
};
|
|
144
|
+
/** The directories a thread's path and a glob's root are read against. */
|
|
145
|
+
readonly directories: RunDirectories;
|
|
146
|
+
/** Whether the program is one the run formed to resolve as Node does (program.36). */
|
|
147
|
+
readonly formedForNode?: boolean;
|
|
833
148
|
}
|
|
834
149
|
|
|
835
150
|
/** The declaration kinds of a property an object literal declares. */
|
|
@@ -841,184 +156,10 @@ const LITERAL_PROPERTY_KINDS: ReadonlySet<SyntaxKind> = new Set([
|
|
|
841
156
|
SyntaxKind.SetAccessor,
|
|
842
157
|
]);
|
|
843
158
|
|
|
844
|
-
/** The rule a file the analysis classified as test-support code is counted under. */
|
|
845
|
-
const TEST_SUPPORT_RULE = "test-support";
|
|
846
|
-
|
|
847
|
-
/**
|
|
848
|
-
* The name one test-file rule takes. A rule's name is a word list the report's own
|
|
849
|
-
* vocabulary spells, which cannot carry a pattern, so a rule is named by the place of
|
|
850
|
-
* its pattern in the configured list.
|
|
851
|
-
*/
|
|
852
|
-
function patternRule(index: number): string {
|
|
853
|
-
return `test-file-pattern-${String(index + 1)}`;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
/**
|
|
857
|
-
* The rules over the test files of several projects at once, ordered by rule: a file two
|
|
858
|
-
* projects hold is one file, so each rule counts the distinct paths it matches.
|
|
859
|
-
*/
|
|
860
|
-
export function testFileRulesOf(
|
|
861
|
-
testFiles: readonly string[],
|
|
862
|
-
paths: readonly string[],
|
|
863
|
-
supportFiles: readonly string[] = [],
|
|
864
|
-
): readonly TestFileRule[] {
|
|
865
|
-
const distinct = [...new Set(paths)];
|
|
866
|
-
const support = new Set(supportFiles).size;
|
|
867
|
-
return [
|
|
868
|
-
...testFiles.map((pattern, index) => {
|
|
869
|
-
const expression = globExpression(pattern);
|
|
870
|
-
return {
|
|
871
|
-
rule: patternRule(index),
|
|
872
|
-
matched: distinct.filter((path) => expression.test(path)).length,
|
|
873
|
-
};
|
|
874
|
-
}),
|
|
875
|
-
...(support === 0 ? [] : [{ rule: TEST_SUPPORT_RULE, matched: support }]),
|
|
876
|
-
].sort((a, b) => bytewise(a.rule, b.rule));
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
const importedCache = new WeakMap<
|
|
880
|
-
SourceFile,
|
|
881
|
-
ReadonlyMap<string, readonly (string | undefined)[]>
|
|
882
|
-
>();
|
|
883
|
-
|
|
884
|
-
/**
|
|
885
|
-
* Each local name one file's imports bind, mapped to the names it imports: the
|
|
886
|
-
* exported name of a named import, `default` for a default import, and `undefined` for
|
|
887
|
-
* a namespace import or an import assignment, which binds a whole module.
|
|
888
|
-
*/
|
|
889
|
-
function importedNames(file: SourceFile): ReadonlyMap<string, readonly (string | undefined)[]> {
|
|
890
|
-
const known = importedCache.get(file);
|
|
891
|
-
if (known !== undefined) {
|
|
892
|
-
return known;
|
|
893
|
-
}
|
|
894
|
-
const names = new Map<string, (string | undefined)[]>();
|
|
895
|
-
const bind = (local: string, imported: string | undefined): void => {
|
|
896
|
-
names.set(local, [...(names.get(local) ?? []), imported]);
|
|
897
|
-
};
|
|
898
|
-
for (const statement of file.statements) {
|
|
899
|
-
if (isImportEqualsDeclaration(statement)) {
|
|
900
|
-
bind(statement.name.text, undefined);
|
|
901
|
-
continue;
|
|
902
|
-
}
|
|
903
|
-
const clause = isImportDeclaration(statement) ? statement.importClause : undefined;
|
|
904
|
-
if (clause === undefined) {
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
if (clause.name !== undefined) {
|
|
908
|
-
bind(clause.name.text, "default");
|
|
909
|
-
}
|
|
910
|
-
const bindings = clause.namedBindings;
|
|
911
|
-
if (bindings === undefined) {
|
|
912
|
-
continue;
|
|
913
|
-
}
|
|
914
|
-
if (bindings.kind === SyntaxKind.NamespaceImport) {
|
|
915
|
-
bind(bindings.name.text, undefined);
|
|
916
|
-
} else {
|
|
917
|
-
for (const element of bindings.elements) {
|
|
918
|
-
bind(element.name.text, element.propertyName?.text ?? element.name.text);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
importedCache.set(file, names);
|
|
923
|
-
return names;
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
/** The name node of every import binding one file's top level declares. */
|
|
927
|
-
function importBindings(file: SourceFile): readonly { node: Node }[] {
|
|
928
|
-
const found: { node: Node }[] = [];
|
|
929
|
-
for (const statement of file.statements) {
|
|
930
|
-
if (isImportEqualsDeclaration(statement)) {
|
|
931
|
-
found.push({ node: statement.name });
|
|
932
|
-
continue;
|
|
933
|
-
}
|
|
934
|
-
const clause = isImportDeclaration(statement) ? statement.importClause : undefined;
|
|
935
|
-
if (clause === undefined) {
|
|
936
|
-
continue;
|
|
937
|
-
}
|
|
938
|
-
if (clause.name !== undefined) {
|
|
939
|
-
found.push({ node: clause.name });
|
|
940
|
-
}
|
|
941
|
-
const bindings = clause.namedBindings;
|
|
942
|
-
if (bindings === undefined) {
|
|
943
|
-
continue;
|
|
944
|
-
}
|
|
945
|
-
if (bindings.kind === SyntaxKind.NamespaceImport) {
|
|
946
|
-
found.push({ node: bindings.name });
|
|
947
|
-
} else {
|
|
948
|
-
found.push(...bindings.elements.map((element) => ({ node: element.name })));
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
return found;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
/** The value one assignment stores through the name it writes, where it is a plain assignment. */
|
|
955
|
-
function storedValue(name: Node): Node | undefined {
|
|
956
|
-
let target: Node = name;
|
|
957
|
-
if (isPropertyAccessExpression(name.parent) && name.parent.name === name) {
|
|
958
|
-
target = name.parent;
|
|
959
|
-
}
|
|
960
|
-
while (isParenthesizedExpression(target.parent)) {
|
|
961
|
-
target = target.parent;
|
|
962
|
-
}
|
|
963
|
-
const parent = target.parent;
|
|
964
|
-
return isBinaryExpression(parent) &&
|
|
965
|
-
parent.operatorToken.kind === SyntaxKind.EqualsToken &&
|
|
966
|
-
parent.left === target
|
|
967
|
-
? parent.right
|
|
968
|
-
: undefined;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
/**
|
|
972
|
-
* The import bindings of one file that only the values written into one declaration
|
|
973
|
-
* use, for each write into it. A binding is named by an identifier that spells its
|
|
974
|
-
* local name and is not the member a property access names.
|
|
975
|
-
*/
|
|
976
|
-
function aloneIn(
|
|
977
|
-
file: SourceFile,
|
|
978
|
-
uses: readonly Site[],
|
|
979
|
-
writes: readonly { index: number; value: Node }[],
|
|
980
|
-
found: readonly Reference[],
|
|
981
|
-
): readonly { index: number; names: readonly string[] }[] {
|
|
982
|
-
const bindings = new Set(importBindings(file).map((binding) => binding.node.getText()));
|
|
983
|
-
if (bindings.size === 0) {
|
|
984
|
-
return [];
|
|
985
|
-
}
|
|
986
|
-
const occurrences = new Map<string, Node[]>();
|
|
987
|
-
for (const site of uses) {
|
|
988
|
-
const text = site.node.getText();
|
|
989
|
-
const parent = site.node.parent;
|
|
990
|
-
if (!bindings.has(text) || (isPropertyAccessExpression(parent) && parent.name === site.node)) {
|
|
991
|
-
continue;
|
|
992
|
-
}
|
|
993
|
-
if (!occurrences.get(text)?.includes(site.node)) {
|
|
994
|
-
occurrences.set(text, [...(occurrences.get(text) ?? []), site.node]);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
const inside = (node: Node, value: Node): boolean =>
|
|
998
|
-
node.pos >= value.pos && node.end <= value.end;
|
|
999
|
-
const result: { index: number; names: readonly string[] }[] = [];
|
|
1000
|
-
for (const write of writes) {
|
|
1001
|
-
const to = found[write.index]?.to;
|
|
1002
|
-
const sameTarget = writes.filter((one) => found[one.index]?.to === to);
|
|
1003
|
-
const names = [...occurrences]
|
|
1004
|
-
.filter(
|
|
1005
|
-
([, nodes]) =>
|
|
1006
|
-
nodes.some((node) => inside(node, write.value)) &&
|
|
1007
|
-
nodes.every((node) => sameTarget.some((one) => inside(node, one.value))),
|
|
1008
|
-
)
|
|
1009
|
-
.map(([name]) => name)
|
|
1010
|
-
.sort(bytewise);
|
|
1011
|
-
if (names.length > 0) {
|
|
1012
|
-
result.push({ index: write.index, names });
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
return result;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
159
|
/**
|
|
1019
|
-
* Every reference one
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
160
|
+
* Every reference one side's files make to the target's declarations. `held` is the
|
|
161
|
+
* target's inventory over the files the project holds, where a resolved symbol is looked up;
|
|
162
|
+
* positions render against `targetRoot`, or a consumer's root as
|
|
1022
163
|
* {@link ReferenceOptions.consumer} states. The order is by position, then the declaration
|
|
1023
164
|
* named, the referencing declaration and the use: an alias chain or a bare star re-export
|
|
1024
165
|
* names several at one position, and one name references one declaration once.
|
|
@@ -1031,19 +172,9 @@ export function references<Brand>(
|
|
|
1031
172
|
): References {
|
|
1032
173
|
const cap = options.batchCap ?? DEFAULT_BATCH_CAP;
|
|
1033
174
|
const declarations = held.declarations;
|
|
1034
|
-
const patterns = options.testFiles.map(globExpression);
|
|
1035
|
-
const found: Reference[] = [];
|
|
1036
|
-
const tests: string[] = [];
|
|
1037
175
|
const chains = aliasChains(project, held);
|
|
1038
176
|
const tables = propertyTables(project.queries);
|
|
1039
|
-
const destructured = destructuring(project, cap, tables);
|
|
1040
|
-
let batched = 0;
|
|
1041
|
-
let fileBatches = 0;
|
|
1042
|
-
let residueFallbacks = 0;
|
|
1043
|
-
let shorthandLookups = 0;
|
|
1044
|
-
|
|
1045
177
|
const consumer = options.consumer;
|
|
1046
|
-
const root = consumer?.root ?? targetRoot;
|
|
1047
178
|
const files =
|
|
1048
179
|
consumer === undefined
|
|
1049
180
|
? project.ownSourceFiles()
|
|
@@ -1054,10 +185,14 @@ export function references<Brand>(
|
|
|
1054
185
|
relativePath(consumer.root, file.fileName) !== undefined &&
|
|
1055
186
|
relativePath(targetRoot, file.fileName) === undefined,
|
|
1056
187
|
);
|
|
188
|
+
const sink = new Sink(consumer?.root ?? targetRoot, options.testFile, consumer);
|
|
1057
189
|
const ownPaths = project.ownPaths();
|
|
1058
190
|
const fileIds = new Set(
|
|
1059
191
|
held.symbols.filter((symbol) => symbol.kind === "file").map((symbol) => symbol.id),
|
|
1060
192
|
);
|
|
193
|
+
const declaredAt = (node: Node): string | undefined =>
|
|
194
|
+
declarations.get(nodeKey(node.getSourceFile(), node));
|
|
195
|
+
|
|
1061
196
|
let moduleBodies: ReadonlyMap<string, readonly InventorySymbol[]> | undefined;
|
|
1062
197
|
/**
|
|
1063
198
|
* The declarations one module holds directly: a file's top level, or the body of a
|
|
@@ -1067,21 +202,20 @@ export function references<Brand>(
|
|
|
1067
202
|
if (moduleBodies === undefined) {
|
|
1068
203
|
const byModule = new Map<string, InventorySymbol[]>();
|
|
1069
204
|
for (const symbol of held.symbols) {
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
1072
|
-
}
|
|
1073
|
-
const siblings = byModule.get(symbol.parent);
|
|
1074
|
-
if (siblings === undefined) {
|
|
1075
|
-
byModule.set(symbol.parent, [symbol]);
|
|
1076
|
-
} else {
|
|
1077
|
-
siblings.push(symbol);
|
|
205
|
+
if (fileIds.has(symbol.parent) || held.modules.has(symbol.parent)) {
|
|
206
|
+
byModule.set(symbol.parent, [...(byModule.get(symbol.parent) ?? []), symbol]);
|
|
1078
207
|
}
|
|
1079
208
|
}
|
|
1080
209
|
moduleBodies = byModule;
|
|
1081
210
|
}
|
|
1082
211
|
return moduleBodies.get(moduleId) ?? [];
|
|
1083
212
|
};
|
|
1084
|
-
|
|
213
|
+
const exportedBy = (ids: Iterable<string>): readonly ChainTarget[] =>
|
|
214
|
+
[...ids].flatMap((id) =>
|
|
215
|
+
declaredIn(id)
|
|
216
|
+
.filter((symbol) => symbol.exported)
|
|
217
|
+
.map((symbol) => ({ id: symbol.id, stepped: false, guessed: true })),
|
|
218
|
+
);
|
|
1085
219
|
const exportsCache = new Map<number, readonly ChainTarget[]>();
|
|
1086
220
|
/**
|
|
1087
221
|
* Every declaration one module exports, each re-export walked along its chain. A module
|
|
@@ -1089,11 +223,7 @@ export function references<Brand>(
|
|
|
1089
223
|
*/
|
|
1090
224
|
const exportsRead = (module: Answer<TSSymbol>): readonly ChainTarget[] => {
|
|
1091
225
|
if (module === UNANSWERED) {
|
|
1092
|
-
return
|
|
1093
|
-
declaredIn(id)
|
|
1094
|
-
.filter((symbol) => symbol.exported)
|
|
1095
|
-
.map((symbol) => ({ id: symbol.id, stepped: false, guessed: true })),
|
|
1096
|
-
);
|
|
226
|
+
return exportedBy(fileIds);
|
|
1097
227
|
}
|
|
1098
228
|
const known = exportsCache.get(module.id);
|
|
1099
229
|
if (known !== undefined) {
|
|
@@ -1102,56 +232,37 @@ export function references<Brand>(
|
|
|
1102
232
|
const table = project.queries.exportsOfModule(module);
|
|
1103
233
|
const read =
|
|
1104
234
|
table === UNANSWERED
|
|
1105
|
-
? chains.declarationsOf(module)
|
|
1106
|
-
declaredIn(id)
|
|
1107
|
-
.filter((symbol) => symbol.exported)
|
|
1108
|
-
.map((symbol) => ({ id: symbol.id, stepped: false, guessed: true })),
|
|
1109
|
-
)
|
|
235
|
+
? exportedBy(chains.declarationsOf(module))
|
|
1110
236
|
: table.flatMap((exported) => chains.chainOf(exported));
|
|
1111
237
|
exportsCache.set(module.id, read);
|
|
1112
238
|
return read;
|
|
1113
239
|
};
|
|
1114
240
|
|
|
1115
|
-
/** The identifier of the declaration one node of this program is, where it is one. */
|
|
1116
|
-
const declaredAt = (node: Node): string | undefined =>
|
|
1117
|
-
declarations.get(nodeKey(node.getSourceFile(), node));
|
|
1118
|
-
|
|
1119
241
|
/**
|
|
1120
|
-
*
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
* per re-export is what lets a chain be followed one declaration at a time.
|
|
242
|
+
* Every declaration bearing the name one node spells, or the name an import of its file
|
|
243
|
+
* binds that name to, which is what a reference the checker could not resolve is counted
|
|
244
|
+
* as (program.33): it may be a use of any of them. A name a namespace import binds may be
|
|
245
|
+
* a use of any file.
|
|
1125
246
|
*/
|
|
1126
|
-
const
|
|
1127
|
-
link.alias
|
|
1128
|
-
? chains.nextDeclared(symbol)
|
|
1129
|
-
: chains.declarationsOf(symbol).map((id) => ({ id, stepped: false, guessed: false }));
|
|
1130
|
-
|
|
1131
|
-
/**
|
|
1132
|
-
* Every declaration bearing the name one node spells, or the name an import of its
|
|
1133
|
-
* file binds that name to, which is what a reference the checker could not resolve is
|
|
1134
|
-
* counted as: it may be a use of any of them, and a declaration it may use is kept
|
|
1135
|
-
* live. A name a namespace import binds may be a use of any file.
|
|
1136
|
-
*/
|
|
1137
|
-
const named = (node: Node): readonly string[] => {
|
|
1138
|
-
const byName = held.byName;
|
|
247
|
+
const named = (node: Node): readonly ChainTarget[] => {
|
|
1139
248
|
const text = node.getText();
|
|
1140
|
-
const imported =
|
|
249
|
+
const imported = bindingsOf(node.getSourceFile()).filter(
|
|
250
|
+
(binding) => binding.name.text === text,
|
|
251
|
+
);
|
|
1141
252
|
return [
|
|
1142
253
|
...new Set([
|
|
1143
|
-
...(byName.get(text) ?? []),
|
|
1144
|
-
...imported.flatMap((
|
|
1145
|
-
|
|
254
|
+
...(held.byName.get(text) ?? []),
|
|
255
|
+
...imported.flatMap((binding) =>
|
|
256
|
+
binding.imported === undefined ? [...fileIds] : (held.byName.get(binding.imported) ?? []),
|
|
1146
257
|
),
|
|
1147
258
|
]),
|
|
1148
|
-
];
|
|
259
|
+
].map((id) => ({ id, stepped: false, guessed: true }));
|
|
1149
260
|
};
|
|
1150
261
|
|
|
1151
262
|
// A type-query alias's own type names its export without using it; a use of the
|
|
1152
263
|
// global is the use, so it names every link of the export's chain as well.
|
|
1153
264
|
const aliases = project.ownSourceFiles().flatMap(typeQueryAliases);
|
|
1154
|
-
const unread = new Set(aliases.map((alias) => alias.type));
|
|
265
|
+
const unread = new Set<Node>(aliases.map((alias) => alias.type));
|
|
1155
266
|
const aliased = new Map<string, readonly ChainTarget[]>();
|
|
1156
267
|
if (aliases.length > 0) {
|
|
1157
268
|
const exported = project.symbolsAt(aliases.map((alias) => project.handle(alias.exported)));
|
|
@@ -1191,42 +302,34 @@ export function references<Brand>(
|
|
|
1191
302
|
return all;
|
|
1192
303
|
};
|
|
1193
304
|
|
|
1194
|
-
|
|
1195
|
-
const
|
|
1196
|
-
site: Site,
|
|
1197
|
-
answered: ReadonlyMap<Node, Answer<TSSymbol>>,
|
|
1198
|
-
): Answer<TSSymbol | undefined> => {
|
|
1199
|
-
if (site.shorthand !== undefined) {
|
|
1200
|
-
shorthandLookups += 1;
|
|
1201
|
-
return project.shorthandValueAt(project.handle(site.shorthand));
|
|
1202
|
-
}
|
|
1203
|
-
const batched = answered.get(site.node);
|
|
1204
|
-
if (batched === UNANSWERED || typeof batched === "object") {
|
|
1205
|
-
return batched;
|
|
1206
|
-
}
|
|
1207
|
-
residueFallbacks += 1;
|
|
1208
|
-
return project.resolvedSymbolAt(project.handle(site.node));
|
|
1209
|
-
};
|
|
1210
|
-
|
|
1211
|
-
const walked = files.map((file) => {
|
|
305
|
+
const rules = READ_RULES.map((rule) => rule.start());
|
|
306
|
+
const sites = files.map((file) => {
|
|
1212
307
|
const fileId = declaredAt(file) ?? "";
|
|
1213
|
-
return {
|
|
308
|
+
return {
|
|
309
|
+
file,
|
|
310
|
+
fileId,
|
|
311
|
+
...sitesOf(file, declarations, fileId, unread, (node, from) => {
|
|
312
|
+
for (const rule of rules) {
|
|
313
|
+
rule.visit(node, { file, from });
|
|
314
|
+
}
|
|
315
|
+
}),
|
|
316
|
+
};
|
|
1214
317
|
});
|
|
1215
|
-
const
|
|
1216
|
-
walked.filter((one) => fileIds.has(one.fileId)).map((one) => [one.file.fileName, one.fileId]),
|
|
1217
|
-
);
|
|
1218
|
-
const loadable = walked
|
|
318
|
+
const loadable = sites
|
|
1219
319
|
.filter((one) => fileIds.has(one.fileId))
|
|
1220
320
|
.map(({ file, fileId }) => ({ file, fileId }));
|
|
321
|
+
const directories = options.directories;
|
|
322
|
+
const destructured = destructuring(project, cap, tables);
|
|
1221
323
|
const contextual = contextualUses(
|
|
1222
324
|
project,
|
|
1223
325
|
held,
|
|
1224
326
|
chains,
|
|
1225
|
-
|
|
1226
|
-
|
|
327
|
+
sites.flatMap((one) => one.literals),
|
|
328
|
+
sites.flatMap((one) => one.flows),
|
|
1227
329
|
cap,
|
|
1228
330
|
tables,
|
|
1229
331
|
);
|
|
332
|
+
|
|
1230
333
|
const writtenBy = new Map<string, readonly string[]>();
|
|
1231
334
|
for (const write of contextual.writes) {
|
|
1232
335
|
const property = write.at.parent;
|
|
@@ -1243,510 +346,89 @@ export function references<Brand>(
|
|
|
1243
346
|
return known;
|
|
1244
347
|
}
|
|
1245
348
|
const found: ChainTarget[] = [];
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
found.push({ id, stepped: true, guessed: false });
|
|
1257
|
-
}
|
|
349
|
+
for (const handle of writtenBy.size === 0 ? [] : symbol.declarations) {
|
|
350
|
+
if (!LITERAL_PROPERTY_KINDS.has(handle.kind) || !ownPaths.has(handle.path)) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const node = project.declarationAt(handle)?.node;
|
|
354
|
+
const ids =
|
|
355
|
+
node === undefined ? undefined : writtenBy.get(nodeKey(node.getSourceFile(), node));
|
|
356
|
+
for (const id of ids ?? []) {
|
|
357
|
+
if (!found.some((target) => target.id === id)) {
|
|
358
|
+
found.push({ id, stepped: true, guessed: false });
|
|
1258
359
|
}
|
|
1259
360
|
}
|
|
1260
361
|
}
|
|
1261
362
|
forwardedCache.set(symbol.id, found);
|
|
1262
363
|
return found;
|
|
1263
364
|
};
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
365
|
+
|
|
366
|
+
let ownFiles: SourceFiles | undefined;
|
|
367
|
+
const pass: Pass<Brand> = {
|
|
368
|
+
project,
|
|
369
|
+
held,
|
|
370
|
+
chains,
|
|
371
|
+
tables,
|
|
372
|
+
cap,
|
|
373
|
+
sink,
|
|
374
|
+
consumer: consumer !== undefined,
|
|
375
|
+
directories,
|
|
376
|
+
fileIds,
|
|
377
|
+
fileIdByName: new Map(loadable.map((one) => [one.file.fileName, one.fileId])),
|
|
378
|
+
loadableById: new Map(loadable.map((one) => [one.fileId, one.file])),
|
|
379
|
+
destructured,
|
|
380
|
+
unseenLoads: [],
|
|
381
|
+
formedForNode: options.formedForNode === true,
|
|
382
|
+
possiblyLoaded: new Set(),
|
|
383
|
+
declaredIn,
|
|
384
|
+
exportsRead,
|
|
385
|
+
named,
|
|
386
|
+
targetsOf: (symbol) => {
|
|
387
|
+
const through = forwarded(symbol);
|
|
388
|
+
return through.length === 0
|
|
389
|
+
? chains.chainOf(symbol)
|
|
390
|
+
: [...chains.chainOf(symbol), ...through];
|
|
391
|
+
},
|
|
392
|
+
throughAliases,
|
|
393
|
+
ownFiles: () => (ownFiles ??= sourceFilesOf(project, targetRoot)),
|
|
1268
394
|
};
|
|
1269
|
-
|
|
1270
|
-
for (const
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
patterns.some((pattern) => pattern.test(path)) ||
|
|
1274
|
-
options.testFileNames?.has(file.fileName) === true;
|
|
1275
|
-
testFile.set(file, test);
|
|
1276
|
-
if (test) {
|
|
1277
|
-
tests.push(path);
|
|
1278
|
-
}
|
|
395
|
+
|
|
396
|
+
for (const site of contextual.writes) {
|
|
397
|
+
const file = site.at.getSourceFile();
|
|
398
|
+
sink.add(file, site.at, site.from, site.ids, "write", "contextual", site.at.parent);
|
|
1279
399
|
}
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
const holdsValue = (file: SourceFile, value: Node | undefined): void => {
|
|
1283
|
-
if (value !== undefined) {
|
|
1284
|
-
valued.set(file, [...(valued.get(file) ?? []), { index: found.length - 1, value }]);
|
|
1285
|
-
}
|
|
1286
|
-
};
|
|
1287
|
-
for (const [use, sites] of [
|
|
1288
|
-
["write", contextual.writes],
|
|
1289
|
-
["read", contextual.reads],
|
|
1290
|
-
] as const) {
|
|
1291
|
-
for (const site of sites) {
|
|
1292
|
-
const file = site.at.getSourceFile();
|
|
1293
|
-
const position = renderPosition(file, root, site.at.getStart());
|
|
1294
|
-
for (const id of site.ids) {
|
|
1295
|
-
found.push({
|
|
1296
|
-
from: site.from,
|
|
1297
|
-
to: id,
|
|
1298
|
-
position,
|
|
1299
|
-
use,
|
|
1300
|
-
resolution: "contextual",
|
|
1301
|
-
test: testFile.get(file) === true,
|
|
1302
|
-
});
|
|
1303
|
-
if (use === "write") {
|
|
1304
|
-
holdsValue(file, site.at.parent);
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
400
|
+
for (const site of contextual.reads) {
|
|
401
|
+
sink.add(site.at.getSourceFile(), site.at, site.from, site.ids, "read", "contextual");
|
|
1308
402
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
for (const {
|
|
1312
|
-
file,
|
|
1313
|
-
fileId,
|
|
1314
|
-
uses,
|
|
1315
|
-
reads,
|
|
1316
|
-
links,
|
|
1317
|
-
evaluations,
|
|
1318
|
-
wholes,
|
|
1319
|
-
urls,
|
|
1320
|
-
loadCalls,
|
|
1321
|
-
decorated,
|
|
1322
|
-
parameterWrites,
|
|
1323
|
-
keyed,
|
|
1324
|
-
} of walked) {
|
|
1325
|
-
const test = testFile.get(file) === true;
|
|
1326
|
-
const loads = computedLoadsOf(file, loadCalls, loadable);
|
|
1327
|
-
// A component file's markup may use any binding its blocks declare at the top level.
|
|
1328
|
-
const markup = isComponentFile(file);
|
|
1329
|
-
const bindings = markup ? importBindings(file) : [];
|
|
1330
|
-
|
|
1331
|
-
// A decorator is written inside the declaration it is attached to and receives it,
|
|
1332
|
-
// so the declaration is used where the decorator is written, whatever the
|
|
1333
|
-
// decorator does with it.
|
|
1334
|
-
for (const { decorator, declaration } of decorated) {
|
|
1335
|
-
found.push({
|
|
1336
|
-
from: declaration,
|
|
1337
|
-
to: declaration,
|
|
1338
|
-
position: renderPosition(file, root, decorator.getStart()),
|
|
1339
|
-
use: "decorator",
|
|
1340
|
-
resolution: "syntax",
|
|
1341
|
-
test,
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
// The constructor stores its argument in the member a parameter property declares.
|
|
1346
|
-
for (const { name, from, member } of parameterWrites) {
|
|
1347
|
-
found.push({
|
|
1348
|
-
from,
|
|
1349
|
-
to: member,
|
|
1350
|
-
position: renderPosition(file, root, name.getStart()),
|
|
1351
|
-
use: "write",
|
|
1352
|
-
resolution: "syntax",
|
|
1353
|
-
test,
|
|
1354
|
-
});
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
// One batch per capped run of the file's name nodes, the re-exports' nodes after
|
|
1358
|
-
// the uses and the evaluated modules' specifiers last, each node once: a bare star
|
|
1359
|
-
// re-export's specifier is a link and an evaluation both. A shorthand is left out
|
|
1360
|
-
// of the batch: its name resolves to the property the literal declares, so the
|
|
1361
|
-
// batch's answer for it would name something this project's inventory never holds.
|
|
1362
|
-
const batching = [
|
|
1363
|
-
...new Set([
|
|
1364
|
-
...uses.filter((site) => site.shorthand === undefined).map((site) => site.node),
|
|
1365
|
-
...links.map((link) => link.node),
|
|
1366
|
-
...evaluations.map((evaluation) => evaluation.node),
|
|
1367
|
-
...wholes.map((whole) => whole.specifier),
|
|
1368
|
-
...urls.flatMap((url) => url.constructor ?? []),
|
|
1369
|
-
...bindings.map((binding) => binding.node),
|
|
1370
|
-
...loads.flatMap((load) => [
|
|
1371
|
-
...(load.require === undefined ? [] : [load.require]),
|
|
1372
|
-
...(load.asset ? [] : load.files.map((loaded) => loaded.file)),
|
|
1373
|
-
]),
|
|
1374
|
-
]),
|
|
1375
|
-
];
|
|
1376
|
-
const answered = new Map<Node, Answer<TSSymbol>>();
|
|
1377
|
-
batched += batching.length;
|
|
1378
|
-
for (let from = 0; from < batching.length; from += cap) {
|
|
1379
|
-
const run = batching.slice(from, from + cap);
|
|
1380
|
-
const answers = project.symbolsAt(
|
|
1381
|
-
run.map((node) => project.handle(node)),
|
|
1382
|
-
cap,
|
|
1383
|
-
);
|
|
1384
|
-
fileBatches += 1;
|
|
1385
|
-
run.forEach((node, index) => {
|
|
1386
|
-
const symbol = answers[index];
|
|
1387
|
-
if (symbol !== undefined) {
|
|
1388
|
-
answered.set(node, symbol);
|
|
1389
|
-
}
|
|
1390
|
-
});
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
for (const link of links) {
|
|
1394
|
-
const symbol = answered.get(link.node);
|
|
1395
|
-
if (symbol === undefined) {
|
|
1396
|
-
continue;
|
|
1397
|
-
}
|
|
1398
|
-
const position = renderPosition(file, root, link.at.getStart());
|
|
1399
|
-
const carried =
|
|
1400
|
-
symbol === UNANSWERED
|
|
1401
|
-
? named(link.at).map((id) => ({ id, stepped: false, guessed: true }))
|
|
1402
|
-
: carriedBy(link, symbol);
|
|
1403
|
-
for (const target of carried) {
|
|
1404
|
-
found.push({
|
|
1405
|
-
from: link.from,
|
|
1406
|
-
to: target.id,
|
|
1407
|
-
position,
|
|
1408
|
-
use: "read",
|
|
1409
|
-
resolution: target.guessed ? "by-name" : link.alias ? "alias" : "batch",
|
|
1410
|
-
test,
|
|
1411
|
-
});
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
|
-
// A script declares no module, so the checker names nothing for a specifier of one;
|
|
1416
|
-
// the compiler's module resolution still names its file.
|
|
1417
|
-
const unnamed = evaluations.flatMap(({ node }) =>
|
|
1418
|
-
answered.get(node) === undefined &&
|
|
1419
|
-
isStringLiteralLikeNode(node) &&
|
|
1420
|
-
queriedFile(file, node, fileIdByName) === undefined
|
|
1421
|
-
? [node]
|
|
1422
|
-
: [],
|
|
1423
|
-
);
|
|
1424
|
-
const resolved = project.resolvedFileNames(unnamed);
|
|
1425
|
-
const scripts = new Map<Node, string | undefined>(
|
|
1426
|
-
unnamed.map((node, at) => {
|
|
1427
|
-
const name = resolved[at];
|
|
1428
|
-
return [node, name === undefined ? undefined : fileIdByName.get(name)];
|
|
1429
|
-
}),
|
|
1430
|
-
);
|
|
1431
|
-
for (const evaluation of evaluations) {
|
|
1432
|
-
const queried = queriedFile(file, evaluation.node, fileIdByName);
|
|
1433
|
-
if (queried !== undefined) {
|
|
1434
|
-
found.push({
|
|
1435
|
-
from: evaluation.from,
|
|
1436
|
-
to: queried,
|
|
1437
|
-
position: renderPosition(file, root, evaluation.node.getStart()),
|
|
1438
|
-
use: "evaluation",
|
|
1439
|
-
resolution: "syntax",
|
|
1440
|
-
test,
|
|
1441
|
-
});
|
|
1442
|
-
}
|
|
1443
|
-
const module = answered.get(evaluation.node);
|
|
1444
|
-
const script = scripts.get(evaluation.node);
|
|
1445
|
-
if (script !== undefined) {
|
|
1446
|
-
found.push({
|
|
1447
|
-
from: evaluation.from,
|
|
1448
|
-
to: script,
|
|
1449
|
-
position: renderPosition(file, root, evaluation.node.getStart()),
|
|
1450
|
-
use: "evaluation",
|
|
1451
|
-
resolution: "syntax",
|
|
1452
|
-
test,
|
|
1453
|
-
});
|
|
1454
|
-
}
|
|
1455
|
-
if (module === undefined) {
|
|
1456
|
-
continue;
|
|
1457
|
-
}
|
|
1458
|
-
const position = renderPosition(file, root, evaluation.node.getStart());
|
|
1459
|
-
// A module the checker could not resolve may be any file of the target.
|
|
1460
|
-
const evaluated = module === UNANSWERED ? [...fileIds] : chains.declarationsOf(module);
|
|
1461
|
-
for (const id of evaluated) {
|
|
1462
|
-
if (fileIds.has(id)) {
|
|
1463
|
-
found.push({
|
|
1464
|
-
from: evaluation.from,
|
|
1465
|
-
to: id,
|
|
1466
|
-
position,
|
|
1467
|
-
use: "evaluation",
|
|
1468
|
-
resolution: module === UNANSWERED ? "by-name" : "batch",
|
|
1469
|
-
test,
|
|
1470
|
-
});
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
for (const url of urls) {
|
|
1476
|
-
const constructor = url.constructor === undefined ? undefined : answered.get(url.constructor);
|
|
1477
|
-
if (typeof constructor === "object" && hasOwnBody(project, constructor)) {
|
|
1478
|
-
continue;
|
|
1479
|
-
}
|
|
1480
|
-
const loaded = fileNamed(file, url.specifier, fileIdByName);
|
|
1481
|
-
if (loaded !== undefined) {
|
|
1482
|
-
found.push({
|
|
1483
|
-
from: url.from,
|
|
1484
|
-
to: loaded,
|
|
1485
|
-
position: renderPosition(file, root, url.at.getStart()),
|
|
1486
|
-
use: "evaluation",
|
|
1487
|
-
resolution: "syntax",
|
|
1488
|
-
test,
|
|
1489
|
-
});
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
// A computed load imports each file it loads where the call is written, reading what
|
|
1494
|
-
// the file exports unless it reads the file as text or a URL; hasOwnBody owns which
|
|
1495
|
-
// `require` loads nothing.
|
|
1496
|
-
for (const load of loads) {
|
|
1497
|
-
const callee = load.require === undefined ? undefined : answered.get(load.require);
|
|
1498
|
-
if (typeof callee === "object" && hasOwnBody(project, callee)) {
|
|
1499
|
-
continue;
|
|
1500
|
-
}
|
|
1501
|
-
const position = renderPosition(file, root, load.call.getStart());
|
|
1502
|
-
for (const loaded of load.files) {
|
|
1503
|
-
found.push({
|
|
1504
|
-
from: load.eager ? fileId : load.from,
|
|
1505
|
-
to: loaded.fileId,
|
|
1506
|
-
position,
|
|
1507
|
-
use: "evaluation",
|
|
1508
|
-
resolution: "syntax",
|
|
1509
|
-
test,
|
|
1510
|
-
});
|
|
1511
|
-
const module = load.asset ? undefined : answered.get(loaded.file);
|
|
1512
|
-
for (const target of module === undefined ? [] : exportsRead(module)) {
|
|
1513
|
-
found.push({
|
|
1514
|
-
from: load.from,
|
|
1515
|
-
to: target.id,
|
|
1516
|
-
position,
|
|
1517
|
-
use: "read",
|
|
1518
|
-
resolution: target.guessed ? "by-name" : "batch",
|
|
1519
|
-
test,
|
|
1520
|
-
});
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
for (const whole of wholes) {
|
|
1526
|
-
const module = answered.get(whole.specifier);
|
|
1527
|
-
if (module === undefined) {
|
|
1528
|
-
continue;
|
|
1529
|
-
}
|
|
1530
|
-
const position = renderPosition(file, root, whole.at.getStart());
|
|
1531
|
-
for (const target of exportsRead(module)) {
|
|
1532
|
-
found.push({
|
|
1533
|
-
from: whole.from,
|
|
1534
|
-
to: target.id,
|
|
1535
|
-
position,
|
|
1536
|
-
use: "read",
|
|
1537
|
-
resolution: target.guessed ? "by-name" : "batch",
|
|
1538
|
-
test,
|
|
1539
|
-
});
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
if (markup) {
|
|
1544
|
-
for (const symbol of declaredIn(fileId)) {
|
|
1545
|
-
found.push({
|
|
1546
|
-
from: fileId,
|
|
1547
|
-
to: symbol.id,
|
|
1548
|
-
position: symbol.position,
|
|
1549
|
-
use: "read",
|
|
1550
|
-
resolution: "syntax",
|
|
1551
|
-
test,
|
|
1552
|
-
});
|
|
1553
|
-
}
|
|
1554
|
-
for (const binding of bindings) {
|
|
1555
|
-
const symbol = answered.get(binding.node);
|
|
1556
|
-
if (symbol === undefined) {
|
|
1557
|
-
continue;
|
|
1558
|
-
}
|
|
1559
|
-
const position = renderPosition(file, root, binding.node.getStart());
|
|
1560
|
-
const chained =
|
|
1561
|
-
symbol === UNANSWERED
|
|
1562
|
-
? named(binding.node).map((id) => ({ id, stepped: false, guessed: true }))
|
|
1563
|
-
: chains.chainOf(symbol);
|
|
1564
|
-
// The markup may name any export of a module a binding's chain reaches, whether a
|
|
1565
|
-
// namespace import binds it or a named or default import of a namespace re-export.
|
|
1566
|
-
const targets = chained.flatMap((target) => [
|
|
1567
|
-
target,
|
|
1568
|
-
...declaredIn(target.id)
|
|
1569
|
-
.filter((one) => one.exported)
|
|
1570
|
-
.map((one) => ({ ...target, id: one.id })),
|
|
1571
|
-
]);
|
|
1572
|
-
for (const target of targets) {
|
|
1573
|
-
found.push({
|
|
1574
|
-
from: fileId,
|
|
1575
|
-
to: target.id,
|
|
1576
|
-
position,
|
|
1577
|
-
use: "read",
|
|
1578
|
-
resolution: target.guessed ? "by-name" : target.stepped ? "alias" : "batch",
|
|
1579
|
-
test,
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
// A pattern names a property of the value it destructures, which no name node of
|
|
1586
|
-
// the pattern resolves to: a shorthand's name is the local it binds.
|
|
1587
|
-
destructured.resolve(reads).forEach((symbol, index) => {
|
|
1588
|
-
const read = reads[index];
|
|
1589
|
-
if (symbol === undefined || read === undefined) {
|
|
1590
|
-
return;
|
|
1591
|
-
}
|
|
1592
|
-
const position = renderPosition(file, root, read.key.getStart());
|
|
1593
|
-
const targets =
|
|
1594
|
-
symbol === UNANSWERED
|
|
1595
|
-
? (held.byName.get(read.name) ?? []).map((id) => ({
|
|
1596
|
-
id,
|
|
1597
|
-
stepped: false,
|
|
1598
|
-
guessed: true,
|
|
1599
|
-
}))
|
|
1600
|
-
: targetsOf(symbol);
|
|
1601
|
-
for (const target of targets) {
|
|
1602
|
-
found.push({
|
|
1603
|
-
from: read.from,
|
|
1604
|
-
to: target.id,
|
|
1605
|
-
position,
|
|
1606
|
-
use: "read",
|
|
1607
|
-
resolution: target.guessed ? "by-name" : "destructured",
|
|
1608
|
-
test,
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
});
|
|
1612
|
-
|
|
1613
|
-
const resolvedKeys = keyed.flatMap((access) => {
|
|
1614
|
-
const symbol =
|
|
1615
|
-
isIdentifier(access.key) && keyNames.has(access.key.text)
|
|
1616
|
-
? answered.get(access.key)
|
|
1617
|
-
: undefined;
|
|
1618
|
-
return typeof symbol === "object" ? [{ access, symbol }] : [];
|
|
1619
|
-
});
|
|
1620
|
-
for (const { access, ids } of keyedUses(project, chains, resolvedKeys, tables, cap)) {
|
|
1621
|
-
const position = renderPosition(file, root, access.access.argumentExpression.getStart());
|
|
1622
|
-
for (const id of ids) {
|
|
1623
|
-
for (const use of access.use === "both" ? (["read", "write"] as const) : [access.use]) {
|
|
1624
|
-
found.push({ from: access.from, to: id, position, use, resolution: "keyed", test });
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
for (const site of uses) {
|
|
1630
|
-
const direct: Resolution =
|
|
1631
|
-
site.shorthand !== undefined
|
|
1632
|
-
? "shorthand"
|
|
1633
|
-
: typeof answered.get(site.node) === "object"
|
|
1634
|
-
? "batch"
|
|
1635
|
-
: "resolved-symbol";
|
|
1636
|
-
const symbol = symbolFor(site, answered);
|
|
1637
|
-
if (symbol === undefined) {
|
|
1638
|
-
continue;
|
|
1639
|
-
}
|
|
1640
|
-
const position = renderPosition(file, root, site.node.getStart());
|
|
1641
|
-
// A re-export something imports is used and so is the declaration behind it, so
|
|
1642
|
-
// the use names every link of the chain its name stands at the head of.
|
|
1643
|
-
const targets = throughAliases(
|
|
1644
|
-
symbol === UNANSWERED
|
|
1645
|
-
? named(site.node).map((id) => ({ id, stepped: false, guessed: true }))
|
|
1646
|
-
: targetsOf(symbol),
|
|
1647
|
-
);
|
|
1648
|
-
for (const target of targets) {
|
|
1649
|
-
found.push({
|
|
1650
|
-
from: site.from,
|
|
1651
|
-
to: target.id,
|
|
1652
|
-
position,
|
|
1653
|
-
use: site.use,
|
|
1654
|
-
resolution: target.guessed ? "by-name" : target.stepped ? "alias" : direct,
|
|
1655
|
-
test,
|
|
1656
|
-
});
|
|
1657
|
-
if (site.use === "write") {
|
|
1658
|
-
holdsValue(file, storedValue(site.node));
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
403
|
+
for (const one of sites) {
|
|
404
|
+
emitFile(prepared(one, pass, loadable), pass);
|
|
1662
405
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
}
|
|
1680
|
-
const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
|
|
1681
|
-
const asked = [...names].filter(([id]) => global.has(id));
|
|
1682
|
-
if (asked.length === 0) {
|
|
1683
|
-
return new Set();
|
|
1684
|
-
}
|
|
1685
|
-
const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
|
|
1686
|
-
return new Set(
|
|
1687
|
-
asked.flatMap(([id], at) => {
|
|
1688
|
-
const symbol = answers[at];
|
|
1689
|
-
return typeof symbol === "object" &&
|
|
1690
|
-
symbol.declarations.some((handle) => !ownPaths.has(handle.path))
|
|
1691
|
-
? [id]
|
|
1692
|
-
: [];
|
|
1693
|
-
}),
|
|
1694
|
-
);
|
|
1695
|
-
};
|
|
1696
|
-
|
|
1697
|
-
const usesOf = new Map(walked.map((one) => [one.file, one.uses]));
|
|
1698
|
-
for (const [file, writes] of valued) {
|
|
1699
|
-
const uses = usesOf.get(file) ?? [];
|
|
1700
|
-
for (const { index, names } of aloneIn(file, uses, writes, found)) {
|
|
1701
|
-
const reference = found[index];
|
|
1702
|
-
if (reference !== undefined) {
|
|
1703
|
-
found[index] = { ...reference, alone: names };
|
|
406
|
+
const context = { project, held, chains };
|
|
407
|
+
for (const rule of rules) {
|
|
408
|
+
for (const read of rule.reads(context)) {
|
|
409
|
+
if (read.kind === "named") {
|
|
410
|
+
sink.add(
|
|
411
|
+
read.file,
|
|
412
|
+
read.at,
|
|
413
|
+
read.from,
|
|
414
|
+
[read.to],
|
|
415
|
+
read.use,
|
|
416
|
+
read.via,
|
|
417
|
+
undefined,
|
|
418
|
+
read.production,
|
|
419
|
+
);
|
|
420
|
+
} else {
|
|
421
|
+
sink.hold(heldBy(read, context));
|
|
1704
422
|
}
|
|
1705
423
|
}
|
|
1706
424
|
}
|
|
1707
425
|
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
found.push(...overrideReferences(project, held, chains, new Set(tests)));
|
|
1712
|
-
found.push(...indexedAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1713
|
-
found.push(...elementAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1714
|
-
found.push(...keyofComparisonReferences(project, held, chains, root, new Set(tests)));
|
|
1715
|
-
found.push(...iterationReferences(project, held, chains, root, new Set(tests)));
|
|
1716
|
-
found.push(...typeParameterReads(project, held, chains, root, new Set(tests)));
|
|
1717
|
-
found.push(...unimplementedCalleeReads(project, held, chains, root, new Set(tests)));
|
|
1718
|
-
found.push(...moduleDeclarationReferences(project, held, root, new Set(tests)));
|
|
1719
|
-
typedLookups = (options.requestCount?.() ?? 0) - before;
|
|
1720
|
-
}
|
|
1721
|
-
const scripts = new Set(
|
|
1722
|
-
walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId),
|
|
1723
|
-
);
|
|
1724
|
-
found.push(...augmentationReferences(held, found, scripts, libraryMerged(scripts)));
|
|
1725
|
-
found.sort(
|
|
1726
|
-
(a, b) =>
|
|
1727
|
-
byPosition(a.position, b.position) ||
|
|
1728
|
-
bytewise(a.to, b.to) ||
|
|
1729
|
-
bytewise(a.from, b.from) ||
|
|
1730
|
-
bytewise(a.use, b.use),
|
|
1731
|
-
);
|
|
1732
|
-
|
|
426
|
+
const merges = mergedGlobalInterfaces(project, held, pass.ownFiles());
|
|
427
|
+
sink.extend(augmentationReferences(held, sink.made, merges.scripts, merges.interfaces));
|
|
428
|
+
const finished = sink.finish(new Map(sites.map((one) => [one.file, one.uses])));
|
|
1733
429
|
return {
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
: found.map((reference) => ({ ...reference, consumer: consumer.id })),
|
|
1738
|
-
testFilePaths: tests.sort(bytewise),
|
|
1739
|
-
cost: {
|
|
1740
|
-
batched,
|
|
1741
|
-
fileBatches,
|
|
1742
|
-
residueFallbacks,
|
|
1743
|
-
shorthandLookups,
|
|
1744
|
-
aliasSteps: chains.steps,
|
|
1745
|
-
patternBatches: destructured.cost.patternBatches,
|
|
1746
|
-
patternLookups: destructured.cost.patternLookups,
|
|
1747
|
-
contextualBatches: contextual.batches,
|
|
1748
|
-
contextualLookups: contextual.lookups,
|
|
1749
|
-
typedLookups,
|
|
1750
|
-
},
|
|
430
|
+
...finished,
|
|
431
|
+
unseenLoads: pass.unseenLoads,
|
|
432
|
+
possiblyLoaded: [...pass.possiblyLoaded].sort(bytewise),
|
|
1751
433
|
};
|
|
1752
434
|
}
|