@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
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one syntax walk of a file every reference source and read rule shares: it finds the
|
|
3
|
+
* name nodes that are uses, the stores, the re-exports, the evaluated modules, the module
|
|
4
|
+
* URLs, the workers, the load calls, the decorators, the parameter properties and the keyed
|
|
5
|
+
* accesses, and offers every node to the read rules with its enclosing declaration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
isArrayLiteralExpression,
|
|
10
|
+
isAsExpression,
|
|
11
|
+
isBinaryExpression,
|
|
12
|
+
isCallExpression,
|
|
13
|
+
isDecorator,
|
|
14
|
+
isDeleteExpression,
|
|
15
|
+
isElementAccessExpression,
|
|
16
|
+
isExportDeclaration,
|
|
17
|
+
isExpressionStatement,
|
|
18
|
+
isExternalModuleReference,
|
|
19
|
+
isForInStatement,
|
|
20
|
+
isForOfStatement,
|
|
21
|
+
isForStatement,
|
|
22
|
+
isIdentifier,
|
|
23
|
+
isImportDeclaration,
|
|
24
|
+
isImportEqualsDeclaration,
|
|
25
|
+
isMetaProperty,
|
|
26
|
+
isNamespaceExport,
|
|
27
|
+
isNewExpression,
|
|
28
|
+
isNoSubstitutionTemplateLiteral,
|
|
29
|
+
isObjectLiteralExpression,
|
|
30
|
+
isParenthesizedExpression,
|
|
31
|
+
isPostfixUnaryExpression,
|
|
32
|
+
isPrefixUnaryExpression,
|
|
33
|
+
isPropertyAccessExpression,
|
|
34
|
+
isPropertyAssignment,
|
|
35
|
+
isShorthandPropertyAssignment,
|
|
36
|
+
isSpreadAssignment,
|
|
37
|
+
isSpreadElement,
|
|
38
|
+
isStringLiteral,
|
|
39
|
+
isVoidExpression,
|
|
40
|
+
SyntaxKind,
|
|
41
|
+
type Identifier,
|
|
42
|
+
type Node,
|
|
43
|
+
type ParameterDeclaration,
|
|
44
|
+
type SourceFile,
|
|
45
|
+
type StringLiteralLikeNode,
|
|
46
|
+
} from "@typescript/native/ast";
|
|
47
|
+
import { amdLoadOf } from "../amd.ts";
|
|
48
|
+
import { writtenValue } from "../entries/static-module.ts";
|
|
49
|
+
import type { FlowAt, LiteralAt } from "../contextual-uses.ts";
|
|
50
|
+
import { propertyReadsOf, type PropertyRead } from "../destructuring.ts";
|
|
51
|
+
import { isParameterProperty, nodeKey } from "../inventory.ts";
|
|
52
|
+
import { namespaceUses } from "../namespace-objects.ts";
|
|
53
|
+
import { startsRelativePath } from "../paths.ts";
|
|
54
|
+
import { writtenName } from "../platform-calls.ts";
|
|
55
|
+
import type { Use } from "../references.ts";
|
|
56
|
+
import { isLoadCall, type LoadCall } from "../roots/unseen-loads.ts";
|
|
57
|
+
import { keyNameOf, type KeyedAccess } from "../symbol-keys.ts";
|
|
58
|
+
import { parenthesesRemoved } from "../unwrap.ts";
|
|
59
|
+
import { handedOn, valuesOf } from "../value-flow.ts";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The operators whose left side one assignment stores into. A compound assignment is
|
|
63
|
+
* among them: where its value is discarded it reads its target only to write the result
|
|
64
|
+
* back, so nothing else receives what it read.
|
|
65
|
+
*/
|
|
66
|
+
const ASSIGNMENTS: ReadonlySet<SyntaxKind> = new Set([
|
|
67
|
+
SyntaxKind.EqualsToken,
|
|
68
|
+
SyntaxKind.PlusEqualsToken,
|
|
69
|
+
SyntaxKind.MinusEqualsToken,
|
|
70
|
+
SyntaxKind.AsteriskEqualsToken,
|
|
71
|
+
SyntaxKind.AsteriskAsteriskEqualsToken,
|
|
72
|
+
SyntaxKind.SlashEqualsToken,
|
|
73
|
+
SyntaxKind.PercentEqualsToken,
|
|
74
|
+
SyntaxKind.LessThanLessThanEqualsToken,
|
|
75
|
+
SyntaxKind.GreaterThanGreaterThanEqualsToken,
|
|
76
|
+
SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
|
|
77
|
+
SyntaxKind.AmpersandEqualsToken,
|
|
78
|
+
SyntaxKind.BarEqualsToken,
|
|
79
|
+
SyntaxKind.BarBarEqualsToken,
|
|
80
|
+
SyntaxKind.AmpersandAmpersandEqualsToken,
|
|
81
|
+
SyntaxKind.QuestionQuestionEqualsToken,
|
|
82
|
+
SyntaxKind.CaretEqualsToken,
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The assignments that read their target to decide whether to store at all, so the read
|
|
87
|
+
* carries information out whatever becomes of the result.
|
|
88
|
+
*/
|
|
89
|
+
const LOGICAL_ASSIGNMENTS: ReadonlySet<SyntaxKind> = new Set([
|
|
90
|
+
SyntaxKind.BarBarEqualsToken,
|
|
91
|
+
SyntaxKind.AmpersandAmpersandEqualsToken,
|
|
92
|
+
SyntaxKind.QuestionQuestionEqualsToken,
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether the value of one store expression is discarded: it is a statement of its own, a
|
|
97
|
+
* clause of a `for` loop, the operand of `void`, or the left operand of a comma.
|
|
98
|
+
*/
|
|
99
|
+
function valueDiscarded(node: Node): boolean {
|
|
100
|
+
let child = node;
|
|
101
|
+
let parent = node.parent;
|
|
102
|
+
while (isParenthesizedExpression(parent)) {
|
|
103
|
+
child = parent;
|
|
104
|
+
parent = parent.parent;
|
|
105
|
+
}
|
|
106
|
+
if (isExpressionStatement(parent) || isVoidExpression(parent)) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
if (isForStatement(parent)) {
|
|
110
|
+
return parent.initializer === child || parent.incrementor === child;
|
|
111
|
+
}
|
|
112
|
+
return (
|
|
113
|
+
isBinaryExpression(parent) &&
|
|
114
|
+
parent.operatorToken.kind === SyntaxKind.CommaToken &&
|
|
115
|
+
parent.left === child
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Whether one node is an identifier or a private name, the two forms a name takes. */
|
|
120
|
+
function isName(node: Node): boolean {
|
|
121
|
+
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Records the name one store writes into.
|
|
126
|
+
*
|
|
127
|
+
* A property access writes the member it names. An element access writes the collection
|
|
128
|
+
* it stores into, since a collection a module only stores into holds nothing anything
|
|
129
|
+
* reads, and joins `accesses` for the member a unique-symbol key selects. A destructuring
|
|
130
|
+
* target writes each name it binds, through its spreads and nested patterns.
|
|
131
|
+
*/
|
|
132
|
+
function markWrite(target: Node, writes: Set<number>, accesses: Set<Node>): void {
|
|
133
|
+
const held = parenthesesRemoved(target);
|
|
134
|
+
if (isName(held)) {
|
|
135
|
+
writes.add(held.pos);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (isPropertyAccessExpression(held)) {
|
|
139
|
+
if (isName(held.name)) {
|
|
140
|
+
writes.add(held.name.pos);
|
|
141
|
+
}
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (isElementAccessExpression(held)) {
|
|
145
|
+
accesses.add(held);
|
|
146
|
+
// A store through a member's value lands in storage every holder of the value
|
|
147
|
+
// reads, so it reads the member; a variable only ever stored into stays written.
|
|
148
|
+
if (!isPropertyAccessExpression(parenthesesRemoved(held.expression))) {
|
|
149
|
+
markWrite(held.expression, writes, accesses);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (isArrayLiteralExpression(held)) {
|
|
154
|
+
for (const element of held.elements) {
|
|
155
|
+
markWrite(element, writes, accesses);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (isObjectLiteralExpression(held)) {
|
|
160
|
+
for (const property of held.properties) {
|
|
161
|
+
if (isPropertyAssignment(property)) {
|
|
162
|
+
markWrite(property.initializer, writes, accesses);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (isShorthandPropertyAssignment(property)) {
|
|
166
|
+
markWrite(property.name, writes, accesses);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (isSpreadAssignment(property)) {
|
|
170
|
+
markWrite(property.expression, writes, accesses);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (isSpreadElement(held)) {
|
|
176
|
+
markWrite(held.expression, writes, accesses);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Records every store one node performs: an assignment, a compound assignment, an
|
|
182
|
+
* increment or a decrement, a `delete`, and an iteration that assigns to a name declared
|
|
183
|
+
* elsewhere. An iteration that declares its own name stores into nothing the inventory
|
|
184
|
+
* holds. A store that reads its target as well records it in `reads`: a logical
|
|
185
|
+
* assignment, and a compound assignment, an increment or a decrement whose value is used.
|
|
186
|
+
*/
|
|
187
|
+
function markStores(
|
|
188
|
+
node: Node,
|
|
189
|
+
writes: Set<number>,
|
|
190
|
+
reads: Set<number>,
|
|
191
|
+
accesses: { readonly written: Set<Node>; readonly read: Set<Node> },
|
|
192
|
+
): void {
|
|
193
|
+
if (isBinaryExpression(node)) {
|
|
194
|
+
const operator = node.operatorToken.kind;
|
|
195
|
+
if (ASSIGNMENTS.has(operator)) {
|
|
196
|
+
markWrite(node.left, writes, accesses.written);
|
|
197
|
+
if (
|
|
198
|
+
LOGICAL_ASSIGNMENTS.has(operator) ||
|
|
199
|
+
(operator !== SyntaxKind.EqualsToken && !valueDiscarded(node))
|
|
200
|
+
) {
|
|
201
|
+
markWrite(node.left, reads, accesses.read);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (isPrefixUnaryExpression(node) || isPostfixUnaryExpression(node)) {
|
|
207
|
+
if (
|
|
208
|
+
node.operator === SyntaxKind.PlusPlusToken ||
|
|
209
|
+
node.operator === SyntaxKind.MinusMinusToken
|
|
210
|
+
) {
|
|
211
|
+
markWrite(node.operand, writes, accesses.written);
|
|
212
|
+
if (!valueDiscarded(node)) {
|
|
213
|
+
markWrite(node.operand, reads, accesses.read);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (isDeleteExpression(node)) {
|
|
219
|
+
markWrite(node.expression, writes, accesses.written);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (isForInStatement(node) || isForOfStatement(node)) {
|
|
223
|
+
if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
|
|
224
|
+
markWrite(node.initializer, writes, accesses.written);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** One name node the walk found, and what the walk knows about it. */
|
|
230
|
+
export interface Site {
|
|
231
|
+
/** The name node, whose position the reference carries. */
|
|
232
|
+
readonly node: Node;
|
|
233
|
+
/**
|
|
234
|
+
* The shorthand property assignment this name belongs to, where it belongs to one.
|
|
235
|
+
* Its value is the declaration the name reads, while the name itself declares the
|
|
236
|
+
* property the literal carries.
|
|
237
|
+
*/
|
|
238
|
+
readonly shorthand: Node | undefined;
|
|
239
|
+
readonly from: string;
|
|
240
|
+
readonly use: Use;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* One re-export the walk found, which names what it carries forward without a name
|
|
245
|
+
* that is a use: an export specifier, a namespace export, or a bare star re-export.
|
|
246
|
+
*/
|
|
247
|
+
export interface Link {
|
|
248
|
+
/**
|
|
249
|
+
* The node whose symbol says what the re-export carries: the alias a specifier or a
|
|
250
|
+
* namespace export declares, or the module a bare star re-export names.
|
|
251
|
+
*/
|
|
252
|
+
readonly node: Node;
|
|
253
|
+
/** Where the reference is written: the name a specifier re-exports, or the module specifier. */
|
|
254
|
+
readonly at: Node;
|
|
255
|
+
/** The re-export's own declaration, or for a bare star the declaration enclosing it. */
|
|
256
|
+
readonly from: string;
|
|
257
|
+
/** Whether the node's symbol is an alias, which is followed one link along. */
|
|
258
|
+
readonly alias: boolean;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** One place the walk found that evaluates a module, and the declaration that holds it. */
|
|
262
|
+
export interface Evaluation {
|
|
263
|
+
/** The module specifier, whose symbol is the module evaluated. */
|
|
264
|
+
readonly node: Node;
|
|
265
|
+
readonly from: string;
|
|
266
|
+
/** The name a `require()` call is written with; {@link callsPlatform} owns which one loads. */
|
|
267
|
+
readonly require?: Identifier;
|
|
268
|
+
/** Whether the call's value is used, which reads the loaded file's export table. */
|
|
269
|
+
readonly whole?: boolean;
|
|
270
|
+
/** Whether the id is one of an AMD call's dependencies, `require` naming its callee. */
|
|
271
|
+
readonly amd?: true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* One module URL a source file builds: `new URL(<literal>, import.meta.url)` or
|
|
276
|
+
* `import.meta.resolve(<literal>)`, the literal relative. A runtime starts the file it
|
|
277
|
+
* names as a module, so it is an import of that file for its effects.
|
|
278
|
+
*/
|
|
279
|
+
interface ModuleURL {
|
|
280
|
+
readonly specifier: string;
|
|
281
|
+
/** The name the constructor is written with; {@link callsPlatform} owns which one is `URL`. */
|
|
282
|
+
readonly constructor: Identifier | undefined;
|
|
283
|
+
readonly at: Node;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/** One module URL, and the declaration whose code builds it. */
|
|
287
|
+
interface ModuleURLAt extends ModuleURL {
|
|
288
|
+
readonly from: string;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** One worker a call starts by a literal address (references-ts.2), and the declaration whose code starts it. */
|
|
292
|
+
interface WorkerSite {
|
|
293
|
+
readonly address: string;
|
|
294
|
+
/**
|
|
295
|
+
* Whether the address is a `resolve` or `join` of the calling file's own directory and
|
|
296
|
+
* literals, a path only Node's `Worker` reads, against that directory.
|
|
297
|
+
*/
|
|
298
|
+
readonly ownDirectory: boolean;
|
|
299
|
+
/** The name the constructor or `navigator` is written with. */
|
|
300
|
+
readonly constructor: Identifier;
|
|
301
|
+
/** The namespace the constructor is read through, as `threads` in `threads.Worker`. */
|
|
302
|
+
readonly holder: Identifier | undefined;
|
|
303
|
+
/** Whether an options literal writes an `eval` other than `false`, which makes a thread's address code. */
|
|
304
|
+
readonly evaluated: boolean;
|
|
305
|
+
readonly at: Node;
|
|
306
|
+
readonly from: string;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** One use of a module namespace object that may read every export of the module. */
|
|
310
|
+
interface WholeRead {
|
|
311
|
+
/** The module specifier, whose symbol is the module read. */
|
|
312
|
+
readonly specifier: Node;
|
|
313
|
+
/** The use, whose position the reference carries. */
|
|
314
|
+
readonly at: Node;
|
|
315
|
+
readonly from: string;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** One decorator expression, and the declaration it is attached to. */
|
|
319
|
+
interface Decorated {
|
|
320
|
+
readonly decorator: Node;
|
|
321
|
+
readonly declaration: string;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** One parameter property, which its constructor writes, and the declaration that writes it. */
|
|
325
|
+
interface ParameterWrite {
|
|
326
|
+
readonly name: Node;
|
|
327
|
+
readonly from: string;
|
|
328
|
+
readonly member: string;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** One property a pattern reads, and the declaration the pattern is written inside. */
|
|
332
|
+
interface PatternRead extends PropertyRead {
|
|
333
|
+
readonly from: string;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Everything one file's walk found to resolve, and the decorators it needs no resolution for. */
|
|
337
|
+
export interface FileSites {
|
|
338
|
+
readonly uses: readonly Site[];
|
|
339
|
+
readonly literals: readonly LiteralAt[];
|
|
340
|
+
readonly flows: readonly FlowAt[];
|
|
341
|
+
readonly reads: readonly PatternRead[];
|
|
342
|
+
readonly links: readonly Link[];
|
|
343
|
+
readonly evaluations: readonly Evaluation[];
|
|
344
|
+
readonly wholes: readonly WholeRead[];
|
|
345
|
+
readonly urls: readonly ModuleURLAt[];
|
|
346
|
+
readonly workers: readonly WorkerSite[];
|
|
347
|
+
readonly loadCalls: readonly LoadCall[];
|
|
348
|
+
readonly decorated: readonly Decorated[];
|
|
349
|
+
readonly parameterWrites: readonly ParameterWrite[];
|
|
350
|
+
readonly keyed: readonly KeyedAccess[];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Whether one node is `import.meta.url`. */
|
|
354
|
+
function isImportMetaURL(node: Node): boolean {
|
|
355
|
+
const at = parenthesesRemoved(node);
|
|
356
|
+
return (
|
|
357
|
+
isPropertyAccessExpression(at) &&
|
|
358
|
+
isMetaProperty(at.expression) &&
|
|
359
|
+
at.expression.keywordToken === SyntaxKind.ImportKeyword &&
|
|
360
|
+
at.name.text === "url"
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** The module URL one node builds, where it builds one. */
|
|
365
|
+
function moduleURLOf(node: Node): ModuleURL | undefined {
|
|
366
|
+
const literal = (argument: Node | undefined): string | undefined => {
|
|
367
|
+
const at = argument === undefined ? undefined : parenthesesRemoved(argument);
|
|
368
|
+
return isLiteralText(at) ? at.text : undefined;
|
|
369
|
+
};
|
|
370
|
+
const constructor = isNewExpression(node) ? writtenName(node.expression) : undefined;
|
|
371
|
+
if (isNewExpression(node) && constructor !== undefined) {
|
|
372
|
+
const [reference, base] = node.arguments ?? [];
|
|
373
|
+
const specifier = literal(reference);
|
|
374
|
+
return specifier !== undefined && base !== undefined && isImportMetaURL(base)
|
|
375
|
+
? { specifier, constructor, at: node }
|
|
376
|
+
: undefined;
|
|
377
|
+
}
|
|
378
|
+
if (isCallExpression(node)) {
|
|
379
|
+
const callee = parenthesesRemoved(node.expression);
|
|
380
|
+
const specifier = literal(node.arguments[0]);
|
|
381
|
+
if (
|
|
382
|
+
specifier !== undefined &&
|
|
383
|
+
startsRelativePath(specifier) &&
|
|
384
|
+
isPropertyAccessExpression(callee) &&
|
|
385
|
+
isMetaProperty(callee.expression) &&
|
|
386
|
+
callee.expression.keywordToken === SyntaxKind.ImportKeyword &&
|
|
387
|
+
callee.name.text === "resolve"
|
|
388
|
+
) {
|
|
389
|
+
return { specifier, constructor: undefined, at: node };
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** The constructors whose first argument addresses a worker. */
|
|
396
|
+
export const WORKER_CONSTRUCTORS: ReadonlySet<string> = new Set(["Worker", "SharedWorker"]);
|
|
397
|
+
|
|
398
|
+
/** Whether an options argument is an object literal whose `eval` member is written as anything but `false`. */
|
|
399
|
+
function evaluatesCode(options: Node | undefined): boolean {
|
|
400
|
+
const at = options === undefined ? undefined : parenthesesRemoved(options);
|
|
401
|
+
return (
|
|
402
|
+
at !== undefined &&
|
|
403
|
+
isObjectLiteralExpression(at) &&
|
|
404
|
+
at.properties.some(
|
|
405
|
+
(property) =>
|
|
406
|
+
(isShorthandPropertyAssignment(property) &&
|
|
407
|
+
isIdentifier(property.name) &&
|
|
408
|
+
property.name.text === "eval") ||
|
|
409
|
+
(isPropertyAssignment(property) &&
|
|
410
|
+
(isIdentifier(property.name) || isStringLiteral(property.name)) &&
|
|
411
|
+
property.name.text === "eval" &&
|
|
412
|
+
parenthesesRemoved(property.initializer).kind !== SyntaxKind.FalseKeyword),
|
|
413
|
+
)
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** The worker one node starts by a literal address or a path of its own directory, where it starts one. */
|
|
418
|
+
function workerOf(node: Node): Omit<WorkerSite, "from"> | undefined {
|
|
419
|
+
if (isNewExpression(node)) {
|
|
420
|
+
const constructor = writtenName(node.expression);
|
|
421
|
+
const [address, options] = node.arguments ?? [];
|
|
422
|
+
const literal = address === undefined ? undefined : parenthesesRemoved(address);
|
|
423
|
+
const value = literal === undefined ? undefined : writtenValue(literal);
|
|
424
|
+
const ownDirectory = value?.kind === "directory";
|
|
425
|
+
const text =
|
|
426
|
+
value?.kind === "directory"
|
|
427
|
+
? value.segments.join("/")
|
|
428
|
+
: isLiteralText(literal)
|
|
429
|
+
? literal.text
|
|
430
|
+
: undefined;
|
|
431
|
+
// A thread's constructor may be imported under another name, whose path is relative.
|
|
432
|
+
if (
|
|
433
|
+
constructor === undefined ||
|
|
434
|
+
text === undefined ||
|
|
435
|
+
(!WORKER_CONSTRUCTORS.has(constructor.text) && !ownDirectory && !startsRelativePath(text))
|
|
436
|
+
) {
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
const callee = parenthesesRemoved(node.expression);
|
|
440
|
+
const holder = isPropertyAccessExpression(callee)
|
|
441
|
+
? parenthesesRemoved(callee.expression)
|
|
442
|
+
: undefined;
|
|
443
|
+
return {
|
|
444
|
+
address: text,
|
|
445
|
+
ownDirectory,
|
|
446
|
+
constructor,
|
|
447
|
+
holder: holder !== undefined && isIdentifier(holder) ? holder : undefined,
|
|
448
|
+
evaluated: evaluatesCode(options),
|
|
449
|
+
at: node,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
if (!isCallExpression(node)) {
|
|
453
|
+
return undefined;
|
|
454
|
+
}
|
|
455
|
+
const callee = parenthesesRemoved(node.expression);
|
|
456
|
+
const registry =
|
|
457
|
+
isPropertyAccessExpression(callee) && callee.name.text === "register"
|
|
458
|
+
? parenthesesRemoved(callee.expression)
|
|
459
|
+
: undefined;
|
|
460
|
+
const navigator =
|
|
461
|
+
registry !== undefined &&
|
|
462
|
+
isPropertyAccessExpression(registry) &&
|
|
463
|
+
registry.name.text === "serviceWorker"
|
|
464
|
+
? writtenName(registry.expression)
|
|
465
|
+
: undefined;
|
|
466
|
+
const [address] = node.arguments;
|
|
467
|
+
const literal = address === undefined ? undefined : parenthesesRemoved(address);
|
|
468
|
+
return navigator?.text === "navigator" && isLiteralText(literal)
|
|
469
|
+
? {
|
|
470
|
+
address: literal.text,
|
|
471
|
+
ownDirectory: false,
|
|
472
|
+
constructor: navigator,
|
|
473
|
+
holder: undefined,
|
|
474
|
+
evaluated: false,
|
|
475
|
+
at: node,
|
|
476
|
+
}
|
|
477
|
+
: undefined;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** Whether one node is a string literal or a template literal with no substitution. */
|
|
481
|
+
export function isLiteralText(node: Node | undefined): node is StringLiteralLikeNode {
|
|
482
|
+
return node !== undefined && (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* The module specifier of the module one node evaluates, if it evaluates one: an import
|
|
487
|
+
* declaration, an import-equals declaration of an external module, an export
|
|
488
|
+
* declaration that names a module, an `import()` call whose argument is literal text, and
|
|
489
|
+
* a call that may be a `require()` of literal text: one written as the name `require`, or
|
|
490
|
+
* one of a relative specifier through another name. A type-only form evaluates nothing.
|
|
491
|
+
* An `import()` of a computed argument names no module this pass can know.
|
|
492
|
+
*/
|
|
493
|
+
function evaluationOf(node: Node): Omit<Evaluation, "from"> | undefined {
|
|
494
|
+
if (isCallExpression(node) && node.expression.kind !== SyntaxKind.ImportKeyword) {
|
|
495
|
+
const [argument] = node.arguments;
|
|
496
|
+
const require = writtenName(node.expression);
|
|
497
|
+
if (
|
|
498
|
+
require === undefined ||
|
|
499
|
+
!isLiteralText(argument) ||
|
|
500
|
+
(require.text !== "require" && !startsRelativePath(argument.text))
|
|
501
|
+
) {
|
|
502
|
+
return undefined;
|
|
503
|
+
}
|
|
504
|
+
return { node: argument, require, whole: !discarded(node) };
|
|
505
|
+
}
|
|
506
|
+
const specifier = evaluatedBy(node);
|
|
507
|
+
return specifier === undefined ? undefined : { node: specifier };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/** Whether an expression's value is thrown away: it is a statement of its own, or `void`'s operand. */
|
|
511
|
+
function discarded(expression: Node): boolean {
|
|
512
|
+
let at = expression.parent;
|
|
513
|
+
while (isParenthesizedExpression(at)) {
|
|
514
|
+
at = at.parent;
|
|
515
|
+
}
|
|
516
|
+
return isExpressionStatement(at) || isVoidExpression(at);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function evaluatedBy(node: Node): Node | undefined {
|
|
520
|
+
if (isImportDeclaration(node)) {
|
|
521
|
+
return node.importClause?.phaseModifier === SyntaxKind.TypeKeyword
|
|
522
|
+
? undefined
|
|
523
|
+
: node.moduleSpecifier;
|
|
524
|
+
}
|
|
525
|
+
if (isExportDeclaration(node)) {
|
|
526
|
+
return node.isTypeOnly ? undefined : node.moduleSpecifier;
|
|
527
|
+
}
|
|
528
|
+
if (isImportEqualsDeclaration(node)) {
|
|
529
|
+
const reference = node.moduleReference;
|
|
530
|
+
return !node.isTypeOnly && isExternalModuleReference(reference)
|
|
531
|
+
? reference.expression
|
|
532
|
+
: undefined;
|
|
533
|
+
}
|
|
534
|
+
if (isCallExpression(node) && node.expression.kind === SyntaxKind.ImportKeyword) {
|
|
535
|
+
const [argument] = node.arguments;
|
|
536
|
+
return isLiteralText(argument) ? argument : undefined;
|
|
537
|
+
}
|
|
538
|
+
return undefined;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* The re-exports one node writes, if it is an export declaration. A specifier and a
|
|
543
|
+
* namespace export are declarations whose names are where they are written, so no use is
|
|
544
|
+
* read from them; each references what it carries forward instead. A bare star declares
|
|
545
|
+
* nothing, so the declaration enclosing it, at the top level the file, references the
|
|
546
|
+
* module it names.
|
|
547
|
+
*/
|
|
548
|
+
function linksOf(
|
|
549
|
+
file: SourceFile,
|
|
550
|
+
node: Node,
|
|
551
|
+
declarations: ReadonlyMap<string, string>,
|
|
552
|
+
enclosing: string,
|
|
553
|
+
): Link[] {
|
|
554
|
+
if (!isExportDeclaration(node)) {
|
|
555
|
+
return [];
|
|
556
|
+
}
|
|
557
|
+
const clause = node.exportClause;
|
|
558
|
+
const specifier = node.moduleSpecifier;
|
|
559
|
+
if (clause === undefined) {
|
|
560
|
+
return specifier === undefined
|
|
561
|
+
? []
|
|
562
|
+
: [{ node: specifier, at: specifier, from: enclosing, alias: false }];
|
|
563
|
+
}
|
|
564
|
+
if (isNamespaceExport(clause)) {
|
|
565
|
+
const from = declarations.get(nodeKey(file, clause));
|
|
566
|
+
return from === undefined
|
|
567
|
+
? []
|
|
568
|
+
: [{ node: clause.name, at: specifier ?? clause.name, from, alias: true }];
|
|
569
|
+
}
|
|
570
|
+
const found: Link[] = [];
|
|
571
|
+
for (const element of clause.elements) {
|
|
572
|
+
const from = declarations.get(nodeKey(file, element));
|
|
573
|
+
if (from !== undefined) {
|
|
574
|
+
found.push({
|
|
575
|
+
node: element.name,
|
|
576
|
+
at: element.propertyName ?? element.name,
|
|
577
|
+
from,
|
|
578
|
+
alias: true,
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return found;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Whether one object literal is a destructuring target, written on the left of an
|
|
587
|
+
* assignment or as a `for` loop's initializer, through the literals and spreads it nests in.
|
|
588
|
+
*/
|
|
589
|
+
function isAssignmentPattern(node: Node): boolean {
|
|
590
|
+
let child = node;
|
|
591
|
+
let parent = node.parent;
|
|
592
|
+
while (
|
|
593
|
+
isParenthesizedExpression(parent) ||
|
|
594
|
+
isArrayLiteralExpression(parent) ||
|
|
595
|
+
isObjectLiteralExpression(parent) ||
|
|
596
|
+
isSpreadElement(parent) ||
|
|
597
|
+
isSpreadAssignment(parent) ||
|
|
598
|
+
(isPropertyAssignment(parent) && parent.initializer === child)
|
|
599
|
+
) {
|
|
600
|
+
child = parent;
|
|
601
|
+
parent = parent.parent;
|
|
602
|
+
}
|
|
603
|
+
if (isBinaryExpression(parent)) {
|
|
604
|
+
return parent.operatorToken.kind === SyntaxKind.EqualsToken && parent.left === child;
|
|
605
|
+
}
|
|
606
|
+
return (isForInStatement(parent) || isForOfStatement(parent)) && parent.initializer === child;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Every name node one file holds that is a use rather than a declaration, in source
|
|
611
|
+
* order, each with its enclosing declaration and its use, and every re-export the file
|
|
612
|
+
* writes. One walk answers all of it, because a second would have to agree with this one
|
|
613
|
+
* about which nodes are names. A declaration's own declared name is left out, which is
|
|
614
|
+
* what makes a declaration referenced only from its own site an unreferenced one, and so
|
|
615
|
+
* is every node of `unread`, a subtree that is no use.
|
|
616
|
+
*/
|
|
617
|
+
export function sitesOf(
|
|
618
|
+
file: SourceFile,
|
|
619
|
+
declarations: ReadonlyMap<string, string>,
|
|
620
|
+
fileId: string,
|
|
621
|
+
unread: ReadonlySet<Node>,
|
|
622
|
+
offer: (node: Node, from: string) => void,
|
|
623
|
+
): FileSites {
|
|
624
|
+
const found: Site[] = [];
|
|
625
|
+
const reads: PatternRead[] = [];
|
|
626
|
+
const links: Link[] = [];
|
|
627
|
+
const evaluations: Evaluation[] = [];
|
|
628
|
+
const wholes: WholeRead[] = [];
|
|
629
|
+
const urls: ModuleURLAt[] = [];
|
|
630
|
+
const workers: WorkerSite[] = [];
|
|
631
|
+
const loadCalls: LoadCall[] = [];
|
|
632
|
+
const decorated: Decorated[] = [];
|
|
633
|
+
const parameterWrites: ParameterWrite[] = [];
|
|
634
|
+
const namespaces = namespaceUses(file);
|
|
635
|
+
const literals: LiteralAt[] = [];
|
|
636
|
+
const flows: FlowAt[] = [];
|
|
637
|
+
const writes = new Set<number>();
|
|
638
|
+
const alsoRead = new Set<number>();
|
|
639
|
+
const accesses = { written: new Set<Node>(), read: new Set<Node>() };
|
|
640
|
+
const keyed: KeyedAccess[] = [];
|
|
641
|
+
const declared = new Set<number>();
|
|
642
|
+
const shorthands = new Map<number, Node>();
|
|
643
|
+
let enclosing = fileId;
|
|
644
|
+
|
|
645
|
+
const markNames = (node: Node): void => {
|
|
646
|
+
if (isShorthandPropertyAssignment(node)) {
|
|
647
|
+
shorthands.set(node.name.pos, node);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
// A property access carries the member it names where a declaration carries its
|
|
651
|
+
// declared name, and it is the one form of the grammar that does.
|
|
652
|
+
if (isPropertyAccessExpression(node)) {
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
const named = node as { readonly name?: Node; readonly propertyName?: Node };
|
|
656
|
+
for (const name of [named.name, named.propertyName]) {
|
|
657
|
+
if (name !== undefined && isName(name)) {
|
|
658
|
+
declared.add(name.pos);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// A read rule sees every node, an unread subtree's too, each with its enclosing declaration.
|
|
664
|
+
const offerAll = (node: Node): void => {
|
|
665
|
+
offer(node, enclosing);
|
|
666
|
+
node.forEachChild(offerAll);
|
|
667
|
+
};
|
|
668
|
+
const visit = (node: Node): void => {
|
|
669
|
+
if (unread.has(node)) {
|
|
670
|
+
offerAll(node);
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
offer(node, enclosing);
|
|
674
|
+
const namespace = namespaces.get(node);
|
|
675
|
+
if (namespace?.kind === "whole") {
|
|
676
|
+
wholes.push({ specifier: namespace.specifier, at: node, from: enclosing });
|
|
677
|
+
} else if (namespace?.kind === "indexed") {
|
|
678
|
+
found.push({ node: namespace.literal, shorthand: undefined, from: enclosing, use: "read" });
|
|
679
|
+
}
|
|
680
|
+
if (isName(node)) {
|
|
681
|
+
if (!declared.has(node.pos)) {
|
|
682
|
+
const site = { node, shorthand: shorthands.get(node.pos), from: enclosing };
|
|
683
|
+
if (writes.has(node.pos)) {
|
|
684
|
+
found.push({ ...site, use: "write" });
|
|
685
|
+
}
|
|
686
|
+
if (!writes.has(node.pos) || alsoRead.has(node.pos)) {
|
|
687
|
+
found.push({ ...site, use: "read" });
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
markNames(node);
|
|
693
|
+
markStores(node, writes, alsoRead, accesses);
|
|
694
|
+
const key = isElementAccessExpression(node) ? keyNameOf(node.argumentExpression) : undefined;
|
|
695
|
+
if (key !== undefined && isElementAccessExpression(node)) {
|
|
696
|
+
const written = accesses.written.has(node);
|
|
697
|
+
keyed.push({
|
|
698
|
+
access: node,
|
|
699
|
+
key,
|
|
700
|
+
from: enclosing,
|
|
701
|
+
use: written ? (accesses.read.has(node) ? "both" : "write") : "read",
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
if (isObjectLiteralExpression(node) && !isAssignmentPattern(node)) {
|
|
705
|
+
literals.push({ literal: node, from: enclosing });
|
|
706
|
+
}
|
|
707
|
+
// An assertion needs only comparability, so no member of its operand is read by it.
|
|
708
|
+
for (const written of isAsExpression(node) ? [] : handedOn(node)) {
|
|
709
|
+
flows.push(...valuesOf(written).map((value) => ({ value, from: enclosing })));
|
|
710
|
+
}
|
|
711
|
+
for (const read of propertyReadsOf(node)) {
|
|
712
|
+
reads.push({ ...read, from: enclosing });
|
|
713
|
+
}
|
|
714
|
+
links.push(...linksOf(file, node, declarations, enclosing));
|
|
715
|
+
const evaluated = evaluationOf(node);
|
|
716
|
+
if (evaluated !== undefined) {
|
|
717
|
+
evaluations.push({ ...evaluated, from: enclosing });
|
|
718
|
+
}
|
|
719
|
+
const amd = amdLoadOf(node);
|
|
720
|
+
if (amd !== undefined) {
|
|
721
|
+
const { callee, ids } = amd;
|
|
722
|
+
for (const { id, received } of ids) {
|
|
723
|
+
evaluations.push({
|
|
724
|
+
node: id,
|
|
725
|
+
require: callee,
|
|
726
|
+
whole: received,
|
|
727
|
+
amd: true,
|
|
728
|
+
from: enclosing,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
const url = moduleURLOf(node);
|
|
733
|
+
if (url !== undefined) {
|
|
734
|
+
urls.push({ ...url, from: enclosing });
|
|
735
|
+
}
|
|
736
|
+
const worker = workerOf(node);
|
|
737
|
+
if (worker !== undefined) {
|
|
738
|
+
workers.push({ ...worker, from: enclosing });
|
|
739
|
+
}
|
|
740
|
+
if (isLoadCall(node)) {
|
|
741
|
+
loadCalls.push({ call: node, from: enclosing });
|
|
742
|
+
}
|
|
743
|
+
const held = declarations.get(nodeKey(file, node));
|
|
744
|
+
const outer = enclosing;
|
|
745
|
+
// A parameter property's annotation, initializer and decorators stay with the
|
|
746
|
+
// parameter when the member goes, so they are written inside the constructor's class.
|
|
747
|
+
if (held !== undefined && isParameterProperty(node)) {
|
|
748
|
+
parameterWrites.push({
|
|
749
|
+
name: (node as ParameterDeclaration).name,
|
|
750
|
+
from: outer,
|
|
751
|
+
member: held,
|
|
752
|
+
});
|
|
753
|
+
} else if (held !== undefined) {
|
|
754
|
+
enclosing = held;
|
|
755
|
+
for (const modifier of (node as { readonly modifiers?: readonly Node[] }).modifiers ?? []) {
|
|
756
|
+
if (isDecorator(modifier)) {
|
|
757
|
+
decorated.push({ decorator: modifier, declaration: held });
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
node.forEachChild(visit);
|
|
762
|
+
enclosing = outer;
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
file.forEachChild(visit);
|
|
766
|
+
return {
|
|
767
|
+
uses: found,
|
|
768
|
+
literals,
|
|
769
|
+
flows,
|
|
770
|
+
reads,
|
|
771
|
+
links,
|
|
772
|
+
evaluations,
|
|
773
|
+
wholes,
|
|
774
|
+
urls,
|
|
775
|
+
workers,
|
|
776
|
+
loadCalls,
|
|
777
|
+
decorated,
|
|
778
|
+
parameterWrites,
|
|
779
|
+
keyed,
|
|
780
|
+
};
|
|
781
|
+
}
|