@openrewrite/rewrite 8.92.17 → 8.92.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/java/formatting-utils.d.ts +2 -1
- package/dist/java/formatting-utils.d.ts.map +1 -1
- package/dist/java/formatting-utils.js +4 -3
- package/dist/java/formatting-utils.js.map +1 -1
- package/dist/java/rpc.d.ts.map +1 -1
- package/dist/java/rpc.js +2 -0
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/tree.d.ts +8 -0
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +11 -1
- package/dist/java/tree.js.map +1 -1
- package/dist/java/type.d.ts +7 -1
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +16 -1
- package/dist/java/type.js.map +1 -1
- package/dist/javascript/add-import.d.ts +27 -21
- package/dist/javascript/add-import.d.ts.map +1 -1
- package/dist/javascript/add-import.js +303 -143
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/amd.d.ts.map +1 -1
- package/dist/javascript/amd.js +25 -10
- package/dist/javascript/amd.js.map +1 -1
- package/dist/javascript/autodetect.js +36 -45
- package/dist/javascript/autodetect.js.map +1 -1
- package/dist/javascript/binding.d.ts +6 -4
- package/dist/javascript/binding.d.ts.map +1 -1
- package/dist/javascript/binding.js +54 -18
- package/dist/javascript/binding.js.map +1 -1
- package/dist/javascript/comparator.d.ts +22 -5
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +116 -38
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format/format.d.ts +2 -0
- package/dist/javascript/format/format.d.ts.map +1 -1
- package/dist/javascript/format/format.js +11 -1
- package/dist/javascript/format/format.js.map +1 -1
- package/dist/javascript/index.d.ts +4 -2
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +10 -1
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/markers.d.ts.map +1 -1
- package/dist/javascript/markers.js +64 -0
- package/dist/javascript/markers.js.map +1 -1
- package/dist/javascript/method-matcher.d.ts +13 -2
- package/dist/javascript/method-matcher.d.ts.map +1 -1
- package/dist/javascript/method-matcher.js +32 -19
- package/dist/javascript/method-matcher.js.map +1 -1
- package/dist/javascript/package-name.d.ts +6 -0
- package/dist/javascript/package-name.d.ts.map +1 -0
- package/dist/javascript/package-name.js +47 -0
- package/dist/javascript/package-name.js.map +1 -0
- package/dist/javascript/preconditions.d.ts +4 -1
- package/dist/javascript/preconditions.d.ts.map +1 -1
- package/dist/javascript/preconditions.js +7 -4
- package/dist/javascript/preconditions.js.map +1 -1
- package/dist/javascript/recipes/change-method-target-to-import.d.ts +29 -0
- package/dist/javascript/recipes/change-method-target-to-import.d.ts.map +1 -0
- package/dist/javascript/recipes/change-method-target-to-import.js +164 -0
- package/dist/javascript/recipes/change-method-target-to-import.js.map +1 -0
- package/dist/javascript/recipes/index.d.ts +1 -0
- package/dist/javascript/recipes/index.d.ts.map +1 -1
- package/dist/javascript/recipes/index.js +1 -0
- package/dist/javascript/recipes/index.js.map +1 -1
- package/dist/javascript/scope.d.ts +60 -16
- package/dist/javascript/scope.d.ts.map +1 -1
- package/dist/javascript/scope.js +596 -131
- package/dist/javascript/scope.js.map +1 -1
- package/dist/javascript/search/uses-method.d.ts +1 -1
- package/dist/javascript/search/uses-method.d.ts.map +1 -1
- package/dist/javascript/search/uses-method.js +2 -2
- package/dist/javascript/search/uses-method.js.map +1 -1
- package/dist/javascript/search/uses-type.d.ts +14 -1
- package/dist/javascript/search/uses-type.d.ts.map +1 -1
- package/dist/javascript/search/uses-type.js +24 -9
- package/dist/javascript/search/uses-type.js.map +1 -1
- package/dist/javascript/templating/bindings.d.ts +1 -1
- package/dist/javascript/templating/bindings.d.ts.map +1 -1
- package/dist/javascript/templating/bindings.js +10 -50
- package/dist/javascript/templating/bindings.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +8 -11
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +12 -110
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +34 -24
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +200 -112
- package/dist/javascript/templating/engine.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts +11 -5
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +86 -20
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.d.ts +34 -3
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +126 -60
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/precedence.d.ts +3 -1
- package/dist/javascript/templating/precedence.d.ts.map +1 -1
- package/dist/javascript/templating/precedence.js +28 -8
- package/dist/javascript/templating/precedence.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +7 -6
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +31 -28
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +6 -3
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +10 -1
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +22 -3
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/javascript/tree-debug.d.ts +2 -0
- package/dist/javascript/tree-debug.d.ts.map +1 -1
- package/dist/javascript/tree-debug.js +6 -0
- package/dist/javascript/tree-debug.js.map +1 -1
- package/dist/javascript/tsconfig.d.ts.map +1 -1
- package/dist/javascript/tsconfig.js +7 -17
- package/dist/javascript/tsconfig.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts +34 -16
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +253 -249
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/javascript/type-report-cli.d.ts +7 -0
- package/dist/javascript/type-report-cli.d.ts.map +1 -0
- package/dist/javascript/type-report-cli.js +129 -0
- package/dist/javascript/type-report-cli.js.map +1 -0
- package/dist/javascript/type-report.d.ts +61 -0
- package/dist/javascript/type-report.d.ts.map +1 -0
- package/dist/javascript/type-report.js +488 -0
- package/dist/javascript/type-report.js.map +1 -0
- package/dist/markers.d.ts +4 -0
- package/dist/markers.d.ts.map +1 -1
- package/dist/markers.js +8 -0
- package/dist/markers.js.map +1 -1
- package/dist/recipe.d.ts +5 -0
- package/dist/recipe.d.ts.map +1 -1
- package/dist/recipe.js +5 -0
- package/dist/recipe.js.map +1 -1
- package/dist/rewrite-javascript-version.txt +1 -1
- package/dist/rpc/queue.d.ts +16 -0
- package/dist/rpc/queue.d.ts.map +1 -1
- package/dist/rpc/queue.js +27 -9
- package/dist/rpc/queue.js.map +1 -1
- package/dist/rpc/request/prepare-recipe.d.ts +3 -1
- package/dist/rpc/request/prepare-recipe.d.ts.map +1 -1
- package/dist/rpc/request/prepare-recipe.js +13 -6
- package/dist/rpc/request/prepare-recipe.js.map +1 -1
- package/dist/search/is-source-file.d.ts +1 -0
- package/dist/search/is-source-file.d.ts.map +1 -1
- package/dist/search/is-source-file.js +7 -2
- package/dist/search/is-source-file.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +5 -0
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +14 -3
- package/dist/test/rewrite-test.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +3 -1
- package/src/java/formatting-utils.ts +5 -4
- package/src/java/rpc.ts +2 -0
- package/src/java/tree.ts +15 -0
- package/src/java/type.ts +19 -2
- package/src/javascript/add-import.ts +338 -152
- package/src/javascript/amd.ts +28 -12
- package/src/javascript/autodetect.ts +32 -41
- package/src/javascript/binding.ts +64 -22
- package/src/javascript/comparator.ts +119 -43
- package/src/javascript/format/format.ts +9 -0
- package/src/javascript/index.ts +6 -3
- package/src/javascript/markers.ts +43 -1
- package/src/javascript/method-matcher.ts +34 -22
- package/src/javascript/package-name.ts +45 -0
- package/src/javascript/preconditions.ts +7 -4
- package/src/javascript/recipes/change-method-target-to-import.ts +178 -0
- package/src/javascript/recipes/index.ts +1 -0
- package/src/javascript/scope.ts +705 -169
- package/src/javascript/search/uses-method.ts +2 -2
- package/src/javascript/search/uses-type.ts +30 -9
- package/src/javascript/templating/bindings.ts +13 -38
- package/src/javascript/templating/comparator.ts +16 -127
- package/src/javascript/templating/engine.ts +229 -127
- package/src/javascript/templating/pattern.ts +107 -28
- package/src/javascript/templating/placeholder-replacement.ts +154 -62
- package/src/javascript/templating/precedence.ts +33 -11
- package/src/javascript/templating/template.ts +35 -30
- package/src/javascript/templating/types.ts +6 -3
- package/src/javascript/templating/utils.ts +24 -2
- package/src/javascript/tree-debug.ts +9 -0
- package/src/javascript/tsconfig.ts +7 -17
- package/src/javascript/type-mapping.ts +258 -252
- package/src/javascript/type-report-cli.ts +96 -0
- package/src/javascript/type-report.ts +575 -0
- package/src/markers.ts +8 -0
- package/src/recipe.ts +6 -0
- package/src/rpc/queue.ts +44 -5
- package/src/rpc/request/prepare-recipe.ts +16 -6
- package/src/search/is-source-file.ts +8 -3
- package/src/test/rewrite-test.ts +14 -3
package/dist/javascript/scope.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isValueReference = void 0;
|
|
3
4
|
exports.scopeOf = scopeOf;
|
|
4
5
|
exports.namesDeclaredIn = namesDeclaredIn;
|
|
5
6
|
exports.namesUsedIn = namesUsedIn;
|
|
@@ -12,7 +13,10 @@ exports.cursorOf = cursorOf;
|
|
|
12
13
|
exports.compilationUnitOf = compilationUnitOf;
|
|
13
14
|
exports.deconflict = deconflict;
|
|
14
15
|
exports.declarationsOf = declarationsOf;
|
|
15
|
-
exports.
|
|
16
|
+
exports.isReference = isReference;
|
|
17
|
+
exports.resolve = resolve;
|
|
18
|
+
exports.isWrite = isWrite;
|
|
19
|
+
exports.referencesOf = referencesOf;
|
|
16
20
|
/*
|
|
17
21
|
* Copyright 2025 the original author or authors.
|
|
18
22
|
* <p>
|
|
@@ -32,6 +36,28 @@ const tree_1 = require("../tree");
|
|
|
32
36
|
const java_1 = require("../java");
|
|
33
37
|
const tree_2 = require("./tree");
|
|
34
38
|
const noNames = new Set();
|
|
39
|
+
const noBindings = new Map();
|
|
40
|
+
/** Whether `bindings` holds `name` with `meaning`, or with any meaning where none is asked for. */
|
|
41
|
+
function binds(bindings, name, meaning) {
|
|
42
|
+
const binding = bindings.get(name);
|
|
43
|
+
return binding !== undefined && (meaning === undefined || binding.meanings.has(meaning));
|
|
44
|
+
}
|
|
45
|
+
function bound(identifiers, ...meanings) {
|
|
46
|
+
return identifiers.map(identifier => ({ identifier, meanings }));
|
|
47
|
+
}
|
|
48
|
+
function toBindings(bound) {
|
|
49
|
+
const bindings = new Map();
|
|
50
|
+
for (const declaration of bound) {
|
|
51
|
+
const name = declaration.identifier.simpleName;
|
|
52
|
+
let held = bindings.get(name);
|
|
53
|
+
if (!held) {
|
|
54
|
+
bindings.set(name, held = { meanings: new Set(), declarations: [] });
|
|
55
|
+
}
|
|
56
|
+
declaration.meanings.forEach(meaning => held.meanings.add(meaning));
|
|
57
|
+
held.declarations.push(declaration);
|
|
58
|
+
}
|
|
59
|
+
return bindings;
|
|
60
|
+
}
|
|
35
61
|
/**
|
|
36
62
|
* The innermost scope holding `cursor`, which a visitor's own cursor rarely is: it stands on
|
|
37
63
|
* whatever node it is visiting. Where a declaration's shape leaves its reach unreadable the answer
|
|
@@ -43,7 +69,7 @@ function scopeOf(cursor) {
|
|
|
43
69
|
}
|
|
44
70
|
function scopeAt(cursor) {
|
|
45
71
|
return {
|
|
46
|
-
names: () =>
|
|
72
|
+
names: () => new Set(bindingsAt(cursor).keys()),
|
|
47
73
|
walk: visit => {
|
|
48
74
|
for (let c = cursor; c; c = enclosingScopeCursor(c.parent)) {
|
|
49
75
|
if (!visit(scopeAt(c))) {
|
|
@@ -51,8 +77,8 @@ function scopeAt(cursor) {
|
|
|
51
77
|
}
|
|
52
78
|
}
|
|
53
79
|
},
|
|
54
|
-
declares: name => declaringScopeOf(cursor, name) !== undefined,
|
|
55
|
-
declaringScope: name => declaringScopeOf(cursor, name)
|
|
80
|
+
declares: (name, meaning) => declaringScopeOf(cursor, name, meaning) !== undefined,
|
|
81
|
+
declaringScope: (name, meaning) => declaringScopeOf(cursor, name, meaning)
|
|
56
82
|
};
|
|
57
83
|
}
|
|
58
84
|
function enclosingScopeCursor(from) {
|
|
@@ -66,8 +92,10 @@ function enclosingScopeCursor(from) {
|
|
|
66
92
|
}
|
|
67
93
|
/** The nodes {@link readBindings} answers for; everything else binds nothing and is not a scope. */
|
|
68
94
|
const scopeKinds = new Set([
|
|
69
|
-
tree_2.JS.Kind.CompilationUnit, java_1.J.Kind.Block, java_1.J.Kind.MethodDeclaration,
|
|
70
|
-
java_1.J.Kind.
|
|
95
|
+
tree_2.JS.Kind.CompilationUnit, java_1.J.Kind.Block, java_1.J.Kind.MethodDeclaration, tree_2.JS.Kind.ComputedPropertyMethodDeclaration,
|
|
96
|
+
java_1.J.Kind.Lambda, tree_2.JS.Kind.ArrowFunction, java_1.J.Kind.ClassDeclaration, java_1.J.Kind.TryCatch, java_1.J.Kind.ForLoop,
|
|
97
|
+
java_1.J.Kind.ForEachLoop, tree_2.JS.Kind.ForInLoop, tree_2.JS.Kind.NamespaceDeclaration, tree_2.JS.Kind.TypeDeclaration,
|
|
98
|
+
tree_2.JS.Kind.FunctionType, tree_2.JS.Kind.MappedType, tree_2.JS.Kind.ConditionalType
|
|
71
99
|
]);
|
|
72
100
|
/**
|
|
73
101
|
* Every name the file declares, wherever it sits. A binding the whole file shares is referenced from
|
|
@@ -114,15 +142,20 @@ function namesDeclaredWithin(node, cacheKey = node) {
|
|
|
114
142
|
}
|
|
115
143
|
const names = new Set();
|
|
116
144
|
const collect = (node) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (
|
|
145
|
+
declarationNames(node).forEach(({ identifier }) => names.add(identifier.simpleName));
|
|
146
|
+
const members = membersOf(node);
|
|
147
|
+
if (!members) {
|
|
120
148
|
return true;
|
|
121
149
|
}
|
|
122
|
-
// The walk ends at this branch, so
|
|
123
|
-
|
|
150
|
+
// The walk ends at this branch, so what else the node holds is read here.
|
|
151
|
+
if (node.kind === java_1.J.Kind.ClassDeclaration) {
|
|
152
|
+
typeParameterNames(node).forEach(({ identifier }) => names.add(identifier.simpleName));
|
|
153
|
+
}
|
|
154
|
+
else if (node.kind === java_1.J.Kind.NewClass) {
|
|
155
|
+
walk(node.arguments, collect);
|
|
156
|
+
}
|
|
124
157
|
// A member's name is not one the file binds, though the code inside one still declares names.
|
|
125
|
-
for (const member of
|
|
158
|
+
for (const member of members.statements) {
|
|
126
159
|
const element = unwrap(member);
|
|
127
160
|
walk(element, node => node === element || collect(node));
|
|
128
161
|
}
|
|
@@ -135,7 +168,8 @@ function namesDeclaredWithin(node, cacheKey = node) {
|
|
|
135
168
|
/**
|
|
136
169
|
* The names `node` reads and nothing within it binds, so each one reaches a binding further out.
|
|
137
170
|
* A name its parent introduces rather than reads is not one, nor is a name an inner scope rebinds:
|
|
138
|
-
* that reference reads the rebinding.
|
|
171
|
+
* that reference reads the rebinding. A value position is hidden only by a value binding, a type
|
|
172
|
+
* position only by a type.
|
|
139
173
|
*/
|
|
140
174
|
function namesReferencedWithin(node, cacheKey = node) {
|
|
141
175
|
if (cacheKey === null || typeof cacheKey !== 'object') {
|
|
@@ -146,122 +180,309 @@ function namesReferencedWithin(node, cacheKey = node) {
|
|
|
146
180
|
return cached;
|
|
147
181
|
}
|
|
148
182
|
const names = new Set();
|
|
149
|
-
|
|
183
|
+
treesIn(node).forEach(tree => readReferences(tree, undefined, undefined, 'value', (identifier, reading, scopes) => {
|
|
184
|
+
if (reading !== undefined && !bindingFrame(scopes, identifier.simpleName, reading)) {
|
|
185
|
+
names.add(identifier.simpleName);
|
|
186
|
+
}
|
|
187
|
+
}));
|
|
150
188
|
referenced.set(cacheKey, names);
|
|
151
189
|
return names;
|
|
152
190
|
}
|
|
153
|
-
/**
|
|
154
|
-
function
|
|
191
|
+
/** The innermost of `scopes` binding `name` as `reading` asks, or undefined where none does. */
|
|
192
|
+
function bindingFrame(scopes, name, reading) {
|
|
155
193
|
for (let scope = scopes; scope; scope = scope.outer) {
|
|
156
|
-
if (frameBindings(scope.node, scope.parent)
|
|
157
|
-
return
|
|
194
|
+
if (binds(frameBindings(scope.node, scope.parent), name, meaningRead(reading))) {
|
|
195
|
+
return scope;
|
|
158
196
|
}
|
|
159
197
|
}
|
|
160
|
-
return
|
|
198
|
+
return undefined;
|
|
161
199
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
200
|
+
/** The meaning a reading resolves against, or undefined for any. */
|
|
201
|
+
function meaningRead(reading) {
|
|
202
|
+
return reading === 'any' ? undefined : reading === 'write' ? 'value' : reading;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Walks `node` top-down, telling `onIdentifier` of every identifier. `reading` is what `node`
|
|
206
|
+
* itself reads with, or undefined under a slot that names rather than reads.
|
|
207
|
+
*/
|
|
208
|
+
function readReferences(node, parent, scopes, reading, onIdentifier, trail = []) {
|
|
209
|
+
trail.push(node);
|
|
210
|
+
if (node.kind === java_1.J.Kind.Identifier) {
|
|
211
|
+
onIdentifier(node, reading, scopes, trail);
|
|
212
|
+
}
|
|
213
|
+
const within = framesAt(node, parent, scopes);
|
|
214
|
+
for (const [key, value] of Object.entries(node)) {
|
|
215
|
+
if (key !== 'markers') {
|
|
216
|
+
const [below, reads] = descend(node, parent, key, within, reading);
|
|
217
|
+
// A naming position reads against the tree parent, so the padding between is stepped over.
|
|
218
|
+
treesIn(value).forEach(child => readReferences(child, node, below, reads, onIdentifier, trail));
|
|
219
|
+
}
|
|
166
220
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
221
|
+
trail.pop();
|
|
222
|
+
}
|
|
223
|
+
/** The scopes a node sits in, with its own in front where it is one. */
|
|
224
|
+
function framesAt(node, parent, scopes) {
|
|
225
|
+
return scopeKinds.has(node.kind) ? { node, parent, outer: scopes } : scopes;
|
|
226
|
+
}
|
|
227
|
+
/** The scopes the slot `key` of `node` resolves against, and what a read in it reads with. */
|
|
228
|
+
function descend(node, parent, key, within, reading) {
|
|
229
|
+
return [reachOf(node, parent, key, within), meaningOf(node, key, reading)];
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The scopes the slot `key` of `node` resolves against, `within` being those `node` sits in. A
|
|
233
|
+
* conditional type's `infer` names reach its extends clause and true branch alone, so its check
|
|
234
|
+
* type and false branch read past them.
|
|
235
|
+
*/
|
|
236
|
+
function reachOf(node, parent, key, within) {
|
|
237
|
+
const pastInferred = node.kind === tree_2.JS.Kind.ConditionalType && key === 'checkType' ||
|
|
238
|
+
node.kind === java_1.J.Kind.Ternary && (parent === null || parent === void 0 ? void 0 : parent.kind) === tree_2.JS.Kind.ConditionalType && key === 'falsePart';
|
|
239
|
+
return pastInferred ? within === null || within === void 0 ? void 0 : within.outer : within;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* What a read in slot `key` of `node` reads with, given what `node` itself reads with. Undefined
|
|
243
|
+
* is a slot that names rather than reads, and so is everything under it. A whole import declares
|
|
244
|
+
* what it spells, and a re-export's clause names another module's members.
|
|
245
|
+
*/
|
|
246
|
+
function meaningOf(node, key, reading) {
|
|
247
|
+
// A computed key and a binding's default value read from within a naming slot.
|
|
248
|
+
if (node.kind === tree_2.JS.Kind.ComputedPropertyName || node.kind === tree_2.JS.Kind.BindingElement && key === 'initializer') {
|
|
249
|
+
return 'value';
|
|
172
250
|
}
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
return;
|
|
251
|
+
if (reading === undefined) {
|
|
252
|
+
return undefined;
|
|
176
253
|
}
|
|
177
|
-
if (
|
|
178
|
-
return;
|
|
254
|
+
if (reading === 'write') {
|
|
255
|
+
return carriesWrite(node, key) ? 'write' : meaningOf(node, key, 'value');
|
|
256
|
+
}
|
|
257
|
+
if (key === 'typeParameters' || key === 'typeArguments') {
|
|
258
|
+
return 'type';
|
|
259
|
+
}
|
|
260
|
+
if (key === 'label' || key === namingSlots[node.kind] || key === 'name' && !readsItsName(node)) {
|
|
261
|
+
return undefined;
|
|
179
262
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
263
|
+
if (writes(node, key)) {
|
|
264
|
+
return 'write';
|
|
265
|
+
}
|
|
266
|
+
switch (node.kind) {
|
|
267
|
+
case tree_2.JS.Kind.Import:
|
|
268
|
+
// `import a = NS.b` reads `NS` as whatever it is. The rest of an import declares.
|
|
269
|
+
return key === 'initializer' ? 'any' : undefined;
|
|
270
|
+
case tree_2.JS.Kind.ExportDeclaration:
|
|
271
|
+
if (key !== 'exportClause') {
|
|
272
|
+
return reading;
|
|
273
|
+
}
|
|
274
|
+
return node.moduleSpecifier === undefined ? 'any' : undefined;
|
|
275
|
+
case java_1.J.Kind.FieldAccess:
|
|
276
|
+
// In a type, `NS.Foo` reads the namespace `NS`, as `A.B.C` reads `A`.
|
|
277
|
+
return key === 'target' && reading === 'type' ? 'namespace' : reading;
|
|
278
|
+
case tree_2.JS.Kind.TypeInfo:
|
|
279
|
+
case tree_2.JS.Kind.TypeDeclaration:
|
|
280
|
+
case java_1.J.Kind.TypeParameter:
|
|
281
|
+
return 'type';
|
|
282
|
+
case tree_2.JS.Kind.TypeQuery:
|
|
283
|
+
// `typeof x` reads the value `x` from within a type.
|
|
284
|
+
return key === 'typeExpression' ? 'value' : reading;
|
|
285
|
+
case tree_2.JS.Kind.TypePredicate:
|
|
286
|
+
// `x is Foo` names the parameter `x`, a value, from within a return type.
|
|
287
|
+
return key === 'parameterName' ? 'value' : reading;
|
|
288
|
+
case tree_2.JS.Kind.IndexSignatureDeclaration:
|
|
289
|
+
return key === 'typeExpression' ? 'type' : reading;
|
|
290
|
+
case tree_2.JS.Kind.As:
|
|
291
|
+
return key === 'right' ? 'type' : reading;
|
|
292
|
+
case tree_2.JS.Kind.SatisfiesExpression:
|
|
293
|
+
return key === 'satisfiesType' ? 'type' : reading;
|
|
294
|
+
case java_1.J.Kind.TypeCast:
|
|
295
|
+
return key === 'class' ? 'type' : reading;
|
|
296
|
+
case java_1.J.Kind.ClassDeclaration:
|
|
297
|
+
if (node.classKind.type === java_1.J.ClassDeclaration.Kind.Type.Interface) {
|
|
298
|
+
return 'type';
|
|
299
|
+
}
|
|
300
|
+
return key === 'implements' ? 'type' : reading;
|
|
301
|
+
default:
|
|
302
|
+
return reading;
|
|
183
303
|
}
|
|
184
|
-
const within = scopeKinds.has(kind) ? { node, parent, outer: scopes } : scopes;
|
|
185
|
-
Object.entries(node).forEach(([key, value]) => key !== 'markers' && collectReferences(value, node, within, names));
|
|
186
304
|
}
|
|
305
|
+
/** Whether slot `key` of `node` is one {@link isWrite} counts as assigned to. A declaring loop head names instead. */
|
|
306
|
+
function writes(node, key) {
|
|
307
|
+
switch (node.kind) {
|
|
308
|
+
case java_1.J.Kind.Assignment:
|
|
309
|
+
case java_1.J.Kind.AssignmentOperation:
|
|
310
|
+
case tree_2.JS.Kind.AssignmentOperation:
|
|
311
|
+
return key === 'variable';
|
|
312
|
+
case java_1.J.Kind.Unary:
|
|
313
|
+
return key === 'expression' && incrementing.has(node.operator.element);
|
|
314
|
+
case java_1.J.Kind.ForEachLoopControl:
|
|
315
|
+
return key === 'variable';
|
|
316
|
+
default:
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const incrementing = new Set([
|
|
321
|
+
java_1.J.Unary.Type.PreIncrement, java_1.J.Unary.Type.PreDecrement, java_1.J.Unary.Type.PostIncrement, java_1.J.Unary.Type.PostDecrement
|
|
322
|
+
]);
|
|
323
|
+
/**
|
|
324
|
+
* Whether slot `key` of `node` passes a write on to what it holds, which parentheses, a type
|
|
325
|
+
* assertion and the shapes of a destructuring target do. Anything else under a target reads, as
|
|
326
|
+
* `x` does in `x.y = 1`.
|
|
327
|
+
*/
|
|
328
|
+
function carriesWrite(node, key) {
|
|
329
|
+
switch (node.kind) {
|
|
330
|
+
case java_1.J.Kind.Parentheses:
|
|
331
|
+
return key === 'tree';
|
|
332
|
+
case tree_2.JS.Kind.As:
|
|
333
|
+
return key === 'left';
|
|
334
|
+
case tree_2.JS.Kind.ExpressionStatement:
|
|
335
|
+
case tree_2.JS.Kind.Spread:
|
|
336
|
+
case tree_2.JS.Kind.SatisfiesExpression:
|
|
337
|
+
case java_1.J.Kind.TypeCast:
|
|
338
|
+
return key === 'expression';
|
|
339
|
+
case java_1.J.Kind.NewArray:
|
|
340
|
+
return key === 'initializer';
|
|
341
|
+
case java_1.J.Kind.NewClass:
|
|
342
|
+
return key === 'body';
|
|
343
|
+
case java_1.J.Kind.Block:
|
|
344
|
+
return key === 'statements';
|
|
345
|
+
case tree_2.JS.Kind.PropertyAssignment:
|
|
346
|
+
return key === (isShorthand(node) ? 'name' : 'initializer');
|
|
347
|
+
case tree_2.JS.Kind.ArrayBindingPattern:
|
|
348
|
+
return key === 'elements';
|
|
349
|
+
case tree_2.JS.Kind.ObjectBindingPattern:
|
|
350
|
+
return key === 'bindings';
|
|
351
|
+
case tree_2.JS.Kind.BindingElement:
|
|
352
|
+
return key === 'name';
|
|
353
|
+
default:
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Whether a node's `name` slot reads rather than names. A call names a member of whatever it
|
|
359
|
+
* selects from, so with nothing selected its name is the function called, and a shorthand `{x}`
|
|
360
|
+
* reads the `x` it also names.
|
|
361
|
+
*/
|
|
362
|
+
function readsItsName(node) {
|
|
363
|
+
return node.kind === java_1.J.Kind.MethodInvocation && !node.select || isShorthand(node);
|
|
364
|
+
}
|
|
365
|
+
/** A shorthand property `{x}` or `{x = 1}`, which reads the `x` it names. */
|
|
366
|
+
function isShorthand(node) {
|
|
367
|
+
return (node === null || node === void 0 ? void 0 : node.kind) === tree_2.JS.Kind.PropertyAssignment &&
|
|
368
|
+
node.assigmentToken === tree_2.JS.PropertyAssignment.Token.Equals;
|
|
369
|
+
}
|
|
370
|
+
/** The slot, beside `name` and `label`, in which a node names rather than reads. */
|
|
371
|
+
const namingSlots = {
|
|
372
|
+
[tree_2.JS.Kind.Alias]: 'alias',
|
|
373
|
+
[tree_2.JS.Kind.BindingElement]: 'propertyName',
|
|
374
|
+
[tree_2.JS.Kind.ImportType]: 'qualifier',
|
|
375
|
+
[tree_2.JS.Kind.JsxAttribute]: 'key'
|
|
376
|
+
};
|
|
187
377
|
/**
|
|
188
378
|
* Every name a binding pattern introduces. `member` is the property a name takes its value from,
|
|
189
379
|
* which only a name an object pattern binds directly has: anything deeper reads a property of a
|
|
190
380
|
* property, an array element is chosen by position, and a rest name gathers what nothing claimed.
|
|
191
381
|
*/
|
|
192
382
|
function bindingNames(pattern) {
|
|
383
|
+
return patternBindings(pattern).map(({ identifier, member }) => member === undefined ? { name: identifier.simpleName } : { name: identifier.simpleName, member });
|
|
384
|
+
}
|
|
385
|
+
/** As {@link bindingNames}, with the identifier each name is read from. */
|
|
386
|
+
function patternBindings(pattern) {
|
|
193
387
|
var _a;
|
|
194
388
|
switch (pattern === null || pattern === void 0 ? void 0 : pattern.kind) {
|
|
195
389
|
case java_1.J.Kind.Identifier: {
|
|
196
|
-
const
|
|
197
|
-
return simpleName ? [{
|
|
390
|
+
const identifier = pattern;
|
|
391
|
+
return identifier.simpleName ? [{ identifier }] : [];
|
|
198
392
|
}
|
|
199
393
|
case tree_2.JS.Kind.Spread:
|
|
200
|
-
return
|
|
394
|
+
return patternBindings(pattern.expression);
|
|
201
395
|
case tree_2.JS.Kind.ArrayBindingPattern:
|
|
202
396
|
return unnamedMembers(pattern.elements.elements
|
|
203
|
-
.flatMap(element =>
|
|
397
|
+
.flatMap(element => patternBindings(unwrap(element))));
|
|
204
398
|
case tree_2.JS.Kind.ObjectBindingPattern:
|
|
205
399
|
return pattern.bindings.elements
|
|
206
|
-
.flatMap(element =>
|
|
400
|
+
.flatMap(element => patternBindings(unwrap(element)));
|
|
207
401
|
case tree_2.JS.Kind.BindingElement: {
|
|
208
402
|
const element = pattern;
|
|
209
403
|
if (((_a = element.name) === null || _a === void 0 ? void 0 : _a.kind) !== java_1.J.Kind.Identifier) {
|
|
210
|
-
return unnamedMembers(
|
|
404
|
+
return unnamedMembers(patternBindings(element.name));
|
|
211
405
|
}
|
|
212
|
-
const
|
|
406
|
+
const identifier = element.name;
|
|
213
407
|
const propertyName = unwrap(element.propertyName);
|
|
214
408
|
return [{
|
|
215
|
-
|
|
216
|
-
member: (propertyName === null || propertyName === void 0 ? void 0 : propertyName.kind) === java_1.J.Kind.Identifier
|
|
409
|
+
identifier,
|
|
410
|
+
member: (propertyName === null || propertyName === void 0 ? void 0 : propertyName.kind) === java_1.J.Kind.Identifier
|
|
411
|
+
? propertyName.simpleName
|
|
412
|
+
: identifier.simpleName
|
|
217
413
|
}];
|
|
218
414
|
}
|
|
219
415
|
default:
|
|
220
416
|
return [];
|
|
221
417
|
}
|
|
222
418
|
}
|
|
419
|
+
function boundIdentifiers(pattern) {
|
|
420
|
+
return patternBindings(pattern).map(bound => bound.identifier);
|
|
421
|
+
}
|
|
223
422
|
function unnamedMembers(bound) {
|
|
224
|
-
return bound.map(({
|
|
423
|
+
return bound.map(({ identifier }) => ({ identifier }));
|
|
225
424
|
}
|
|
226
|
-
|
|
227
|
-
|
|
425
|
+
/**
|
|
426
|
+
* The block of members a class, object literal or type literal holds. A member is reached through
|
|
427
|
+
* the value or type holding it, so its name binds nowhere.
|
|
428
|
+
*/
|
|
429
|
+
function membersOf(node) {
|
|
430
|
+
switch (node === null || node === void 0 ? void 0 : node.kind) {
|
|
431
|
+
case java_1.J.Kind.ClassDeclaration:
|
|
432
|
+
return node.body;
|
|
433
|
+
case java_1.J.Kind.NewClass:
|
|
434
|
+
return node.body;
|
|
435
|
+
case tree_2.JS.Kind.TypeLiteral:
|
|
436
|
+
return node.members;
|
|
437
|
+
default:
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function declaringScopeOf(cursor, name, meaning) {
|
|
228
442
|
for (let c = cursor; c; c = c.parent) {
|
|
229
|
-
if (
|
|
443
|
+
if (binds(bindingsAt(c), name, meaning)) {
|
|
230
444
|
return c.value;
|
|
231
445
|
}
|
|
232
446
|
}
|
|
233
447
|
return undefined;
|
|
234
448
|
}
|
|
449
|
+
/** What the node a cursor stands on binds, which is nothing where it stands on padding or the root. */
|
|
450
|
+
function bindingsAt(cursor) {
|
|
451
|
+
var _a;
|
|
452
|
+
const parent = (_a = cursor.parent) === null || _a === void 0 ? void 0 : _a.value;
|
|
453
|
+
return (0, tree_1.isTree)(cursor.value) ? frameBindings(cursor.value, (0, tree_1.isTree)(parent) ? parent : undefined) : noBindings;
|
|
454
|
+
}
|
|
235
455
|
/**
|
|
236
456
|
* What one node on the cursor path binds, `parent` being the node it hangs from. Only these two
|
|
237
457
|
* answers turn on the parent; a node alone settles the rest, which is what makes them cacheable.
|
|
238
458
|
*/
|
|
239
459
|
function frameBindings(node, parent) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return
|
|
460
|
+
if (node.kind === java_1.J.Kind.Block && membersOf(parent) === node) {
|
|
461
|
+
// An enum's members are in scope in its body, where an initializer may name an earlier one.
|
|
462
|
+
return isEnum(parent) ? toBindings(enumMemberNames(node)) : noBindings;
|
|
463
|
+
}
|
|
464
|
+
// An arrow binds its lambda's parameters itself, since its return type reads them.
|
|
465
|
+
if (node.kind === java_1.J.Kind.Lambda && (parent === null || parent === void 0 ? void 0 : parent.kind) === tree_2.JS.Kind.ArrowFunction) {
|
|
466
|
+
return noBindings;
|
|
243
467
|
}
|
|
244
468
|
// A function expression is the only function whose own name its body reaches: a declaration's
|
|
245
469
|
// name belongs to the enclosing block, a method's to an instance.
|
|
246
|
-
if (
|
|
247
|
-
let
|
|
248
|
-
if (!
|
|
249
|
-
const self =
|
|
250
|
-
selfNamed.set(node,
|
|
470
|
+
if (node.kind === java_1.J.Kind.MethodDeclaration && (parent === null || parent === void 0 ? void 0 : parent.kind) === tree_2.JS.Kind.StatementExpression) {
|
|
471
|
+
let bindings = selfNamed.get(node);
|
|
472
|
+
if (!bindings) {
|
|
473
|
+
const self = bound(boundIdentifiers(node.name), 'value');
|
|
474
|
+
selfNamed.set(node, bindings = toBindings([...self, ...readBindings(node)]));
|
|
251
475
|
}
|
|
252
|
-
return
|
|
476
|
+
return bindings;
|
|
253
477
|
}
|
|
254
478
|
return ownBindings(node);
|
|
255
479
|
}
|
|
256
480
|
function ownBindings(node) {
|
|
257
|
-
|
|
258
|
-
|
|
481
|
+
let bindings = frames.get(node);
|
|
482
|
+
if (!bindings) {
|
|
483
|
+
frames.set(node, bindings = toBindings(readBindings(node)));
|
|
259
484
|
}
|
|
260
|
-
|
|
261
|
-
if (!names) {
|
|
262
|
-
frames.set(node, names = new Set(readBindings(node)));
|
|
263
|
-
}
|
|
264
|
-
return names;
|
|
485
|
+
return bindings;
|
|
265
486
|
}
|
|
266
487
|
function readBindings(node) {
|
|
267
488
|
switch (node.kind) {
|
|
@@ -269,26 +490,52 @@ function readBindings(node) {
|
|
|
269
490
|
const statements = node.statements;
|
|
270
491
|
return [...declaredNames(statements), ...hoistedNames(statements)];
|
|
271
492
|
}
|
|
272
|
-
case java_1.J.Kind.Block:
|
|
273
|
-
|
|
274
|
-
|
|
493
|
+
case java_1.J.Kind.Block: {
|
|
494
|
+
const block = node;
|
|
495
|
+
// A class static block is a var scope, as a function body is.
|
|
496
|
+
return block.static.element
|
|
497
|
+
? [...blockScopedNames(block.statements), ...hoistedNames(block.statements)]
|
|
498
|
+
: blockScopedNames(block.statements);
|
|
499
|
+
}
|
|
500
|
+
case java_1.J.Kind.MethodDeclaration:
|
|
501
|
+
case tree_2.JS.Kind.ComputedPropertyMethodDeclaration: {
|
|
275
502
|
const method = node;
|
|
276
|
-
return [
|
|
503
|
+
return [
|
|
504
|
+
...typeParameterNames(method),
|
|
505
|
+
...declaredNames(method.parameters.elements),
|
|
506
|
+
...hoistedNames(method.body)
|
|
507
|
+
];
|
|
277
508
|
}
|
|
278
509
|
case java_1.J.Kind.Lambda: {
|
|
279
510
|
const lambda = node;
|
|
280
511
|
return [...declaredNames(lambda.parameters.parameters), ...hoistedNames(lambda.body)];
|
|
281
512
|
}
|
|
282
513
|
case java_1.J.Kind.ClassDeclaration:
|
|
283
|
-
return
|
|
514
|
+
return [...declarationNames(node), ...typeParameterNames(node)];
|
|
515
|
+
// A namespace is where the functions and `var`s in its body hoist to, its block binding the rest.
|
|
516
|
+
case tree_2.JS.Kind.NamespaceDeclaration:
|
|
517
|
+
return isGlobalAugmentation(node)
|
|
518
|
+
? []
|
|
519
|
+
: hoistedNames(node.body);
|
|
520
|
+
case tree_2.JS.Kind.ArrowFunction:
|
|
521
|
+
return [...typeParameterNames(node), ...readBindings(node.lambda)];
|
|
522
|
+
case tree_2.JS.Kind.TypeDeclaration:
|
|
523
|
+
return typeParameterNames(node);
|
|
524
|
+
case tree_2.JS.Kind.FunctionType:
|
|
525
|
+
return [...typeParameterNames(node), ...declaredNames(node.parameters.elements)];
|
|
526
|
+
case tree_2.JS.Kind.MappedType:
|
|
527
|
+
return declarationNames(unwrap(node.keysRemapping.typeParameter));
|
|
528
|
+
case tree_2.JS.Kind.ConditionalType:
|
|
529
|
+
return inferredNames(node.condition.element.condition);
|
|
284
530
|
case java_1.J.Kind.TryCatch:
|
|
285
531
|
return declaredNames([node.parameter.tree]);
|
|
532
|
+
// A loop head's `var` hoists past the loop, as one anywhere in a function body does.
|
|
286
533
|
case java_1.J.Kind.ForLoop:
|
|
287
|
-
return
|
|
534
|
+
return blockScopedNames(node.control.init);
|
|
288
535
|
case java_1.J.Kind.ForEachLoop:
|
|
289
|
-
return
|
|
536
|
+
return blockScopedNames([node.control.variable]);
|
|
290
537
|
case tree_2.JS.Kind.ForInLoop:
|
|
291
|
-
return
|
|
538
|
+
return blockScopedNames([node.control.variable]);
|
|
292
539
|
default:
|
|
293
540
|
return [];
|
|
294
541
|
}
|
|
@@ -321,28 +568,48 @@ function isBlockScoped(declaration) {
|
|
|
321
568
|
function declaredNames(statements) {
|
|
322
569
|
return statements.flatMap(statement => declarationNames(unwrap(statement)));
|
|
323
570
|
}
|
|
571
|
+
/** The names a declaration binds, each with the meanings it has there. */
|
|
324
572
|
function declarationNames(statement) {
|
|
325
573
|
switch (statement === null || statement === void 0 ? void 0 : statement.kind) {
|
|
326
574
|
case tree_2.JS.Kind.Import:
|
|
327
575
|
return importNames(statement);
|
|
328
576
|
case java_1.J.Kind.VariableDeclarations:
|
|
329
|
-
return statement.variables
|
|
330
|
-
|
|
331
|
-
|
|
577
|
+
return bound(statement.variables.flatMap(variable => {
|
|
578
|
+
const named = unwrap(variable);
|
|
579
|
+
return boundIdentifiers(named === null || named === void 0 ? void 0 : named.name);
|
|
580
|
+
}), 'value');
|
|
332
581
|
case tree_2.JS.Kind.ScopedVariableDeclarations:
|
|
333
582
|
return declaredNames(statement.variables);
|
|
334
583
|
case java_1.J.Kind.MethodDeclaration:
|
|
335
|
-
return
|
|
336
|
-
case java_1.J.Kind.ClassDeclaration:
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
584
|
+
return bound(boundIdentifiers(statement.name), 'value');
|
|
585
|
+
case java_1.J.Kind.ClassDeclaration: {
|
|
586
|
+
const declaration = statement;
|
|
587
|
+
switch (declaration.classKind.type) {
|
|
588
|
+
case java_1.J.ClassDeclaration.Kind.Type.Interface:
|
|
589
|
+
return bound(boundIdentifiers(declaration.name), 'type');
|
|
590
|
+
case java_1.J.ClassDeclaration.Kind.Type.Enum:
|
|
591
|
+
// An enum's members are read as `E.A` in a type, so it is a namespace too.
|
|
592
|
+
return bound(boundIdentifiers(declaration.name), 'value', 'type', 'namespace');
|
|
593
|
+
default:
|
|
594
|
+
return bound(boundIdentifiers(declaration.name), 'value', 'type');
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
case tree_2.JS.Kind.NamespaceDeclaration: {
|
|
598
|
+
const declaration = statement;
|
|
599
|
+
if (isGlobalAugmentation(declaration)) {
|
|
600
|
+
return [];
|
|
601
|
+
}
|
|
602
|
+
const names = boundIdentifiers(rootName(unwrap(declaration.name)));
|
|
603
|
+
return isInstantiated(declaration) ? bound(names, 'namespace', 'value') : bound(names, 'namespace');
|
|
604
|
+
}
|
|
340
605
|
case tree_2.JS.Kind.TypeDeclaration:
|
|
341
|
-
return
|
|
606
|
+
return bound(boundIdentifiers(unwrap(statement.name)), 'type');
|
|
342
607
|
case java_1.J.Kind.TypeParameter:
|
|
343
|
-
//
|
|
344
|
-
//
|
|
345
|
-
return
|
|
608
|
+
// A type parameter binds across the declaration carrying it, not in the scope that one
|
|
609
|
+
// sits in, so no statement list leads here.
|
|
610
|
+
return bound(boundIdentifiers(statement.name), 'type');
|
|
611
|
+
case tree_2.JS.Kind.MappedTypeParameter:
|
|
612
|
+
return bound(boundIdentifiers(statement.name), 'type');
|
|
346
613
|
case java_1.J.Kind.Case:
|
|
347
614
|
// The cases of a switch share the block it opens, so each one's declarations bind in all.
|
|
348
615
|
return declaredNames(statement.statements.elements);
|
|
@@ -350,24 +617,87 @@ function declarationNames(statement) {
|
|
|
350
617
|
return [];
|
|
351
618
|
}
|
|
352
619
|
}
|
|
620
|
+
/**
|
|
621
|
+
* Whether a namespace binds a value, which it does once its body holds something other than
|
|
622
|
+
* interfaces, type aliases, unexported imports and namespaces that do not. That is the binder's
|
|
623
|
+
* rule, under which a `const enum` counts, whatever the emitter later drops.
|
|
624
|
+
*/
|
|
625
|
+
function isInstantiated(declaration) {
|
|
626
|
+
var _a, _b;
|
|
627
|
+
return ((_b = (_a = declaration.body) === null || _a === void 0 ? void 0 : _a.statements) !== null && _b !== void 0 ? _b : []).some(statement => {
|
|
628
|
+
const element = unwrap(statement);
|
|
629
|
+
switch (element === null || element === void 0 ? void 0 : element.kind) {
|
|
630
|
+
case tree_2.JS.Kind.TypeDeclaration:
|
|
631
|
+
return false;
|
|
632
|
+
case java_1.J.Kind.ClassDeclaration:
|
|
633
|
+
return element.classKind.type !== java_1.J.ClassDeclaration.Kind.Type.Interface;
|
|
634
|
+
case tree_2.JS.Kind.Import:
|
|
635
|
+
return element.modifiers.some(modifier => modifier.keyword === 'export');
|
|
636
|
+
case tree_2.JS.Kind.NamespaceDeclaration:
|
|
637
|
+
return isInstantiated(element);
|
|
638
|
+
default:
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
/** `declare global { … }`, whose declarations the whole file sees, as if written at its top. */
|
|
644
|
+
function isGlobalAugmentation(declaration) {
|
|
645
|
+
const name = unwrap(declaration.name);
|
|
646
|
+
return declaration.keywordType.element === tree_2.JS.NamespaceDeclaration.KeywordType.Empty &&
|
|
647
|
+
(name === null || name === void 0 ? void 0 : name.kind) === java_1.J.Kind.Identifier && name.simpleName === 'global';
|
|
648
|
+
}
|
|
649
|
+
function isEnum(node) {
|
|
650
|
+
return (node === null || node === void 0 ? void 0 : node.kind) === java_1.J.Kind.ClassDeclaration &&
|
|
651
|
+
node.classKind.type === java_1.J.ClassDeclaration.Kind.Type.Enum;
|
|
652
|
+
}
|
|
653
|
+
function enumMemberNames(body) {
|
|
654
|
+
return body.statements.flatMap(statement => {
|
|
655
|
+
const element = unwrap(statement);
|
|
656
|
+
return (element === null || element === void 0 ? void 0 : element.kind) === java_1.J.Kind.EnumValueSet
|
|
657
|
+
? bound(element.enums.map(value => value.element.name), 'value')
|
|
658
|
+
: [];
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
/** The identifier a dotted name `A.B.C` declares, which is `A`: the rest are members of it. */
|
|
662
|
+
function rootName(name) {
|
|
663
|
+
return (name === null || name === void 0 ? void 0 : name.kind) === java_1.J.Kind.FieldAccess ? rootName(name.target) : name;
|
|
664
|
+
}
|
|
353
665
|
/**
|
|
354
666
|
* The names a declaration's type parameters bind. A class keeps them in a container and everything
|
|
355
667
|
* else in a `J.TypeParameters`, and both hold the same right-padded list.
|
|
356
668
|
*/
|
|
357
669
|
function typeParameterNames(node) {
|
|
358
|
-
const held = node
|
|
670
|
+
const held = node.typeParameters;
|
|
359
671
|
const parameters = (held === null || held === void 0 ? void 0 : held.kind) === java_1.J.Kind.TypeParameters
|
|
360
672
|
? held.typeParameters
|
|
361
673
|
: held === null || held === void 0 ? void 0 : held.elements;
|
|
362
674
|
return (parameters !== null && parameters !== void 0 ? parameters : []).flatMap(parameter => declarationNames(unwrap(parameter)));
|
|
363
675
|
}
|
|
364
|
-
/**
|
|
676
|
+
/**
|
|
677
|
+
* The names the `infer` declarations in a conditional type's extends clause bind. A conditional
|
|
678
|
+
* type nested in the clause keeps its own `infer` names to itself.
|
|
679
|
+
*/
|
|
680
|
+
function inferredNames(extendsType) {
|
|
681
|
+
const names = [];
|
|
682
|
+
walk(extendsType, node => {
|
|
683
|
+
if (node.kind === tree_2.JS.Kind.InferType) {
|
|
684
|
+
names.push(...declarationNames(unwrap(node.typeParameter)));
|
|
685
|
+
}
|
|
686
|
+
return node.kind !== tree_2.JS.Kind.ConditionalType;
|
|
687
|
+
});
|
|
688
|
+
return names;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* The names an import binds, which for an aliased or namespace specifier is the alias, each with
|
|
692
|
+
* every meaning. A `type` import is no exception: `typeof X` reads it as a value from within a
|
|
693
|
+
* type, and a value use outside one is the checker's error rather than a read of a free name.
|
|
694
|
+
*/
|
|
365
695
|
function importNames(jsImport) {
|
|
366
696
|
const importClause = jsImport.importClause;
|
|
367
697
|
if (!importClause) {
|
|
368
698
|
return [];
|
|
369
699
|
}
|
|
370
|
-
const names =
|
|
700
|
+
const names = boundIdentifiers(unwrap(importClause.name));
|
|
371
701
|
const namedBindings = importClause.namedBindings;
|
|
372
702
|
if ((namedBindings === null || namedBindings === void 0 ? void 0 : namedBindings.kind) === tree_2.JS.Kind.NamedImports) {
|
|
373
703
|
for (const element of namedBindings.elements.elements) {
|
|
@@ -380,11 +710,11 @@ function importNames(jsImport) {
|
|
|
380
710
|
else {
|
|
381
711
|
names.push(...aliasedName(namedBindings));
|
|
382
712
|
}
|
|
383
|
-
return names;
|
|
713
|
+
return bound(names, 'value', 'type', 'namespace');
|
|
384
714
|
}
|
|
385
715
|
function aliasedName(specifier) {
|
|
386
716
|
const name = (specifier === null || specifier === void 0 ? void 0 : specifier.kind) === tree_2.JS.Kind.Alias ? specifier.alias : specifier;
|
|
387
|
-
return
|
|
717
|
+
return boundIdentifiers(name);
|
|
388
718
|
}
|
|
389
719
|
const blockScoped = new Set(['let', 'const', 'using']);
|
|
390
720
|
// A walk of an immutable subtree has one answer, so it runs once. Keyed on the subtree itself, a
|
|
@@ -395,6 +725,7 @@ const hoisted = new WeakMap();
|
|
|
395
725
|
const declared = new WeakMap();
|
|
396
726
|
const used = new WeakMap();
|
|
397
727
|
const referenced = new WeakMap();
|
|
728
|
+
const slots = new WeakMap();
|
|
398
729
|
const frames = new WeakMap();
|
|
399
730
|
const selfNamed = new WeakMap();
|
|
400
731
|
/**
|
|
@@ -416,6 +747,8 @@ function hoistedNames(scope) {
|
|
|
416
747
|
case java_1.J.Kind.MethodDeclaration:
|
|
417
748
|
names.push(...declarationNames(node));
|
|
418
749
|
return false;
|
|
750
|
+
case tree_2.JS.Kind.ComputedPropertyMethodDeclaration:
|
|
751
|
+
return false;
|
|
419
752
|
case java_1.J.Kind.VariableDeclarations:
|
|
420
753
|
case tree_2.JS.Kind.ScopedVariableDeclarations:
|
|
421
754
|
if (!isBlockScoped(node)) {
|
|
@@ -429,8 +762,14 @@ function hoistedNames(scope) {
|
|
|
429
762
|
case tree_2.JS.Kind.StatementExpression:
|
|
430
763
|
// A function or class used as an expression names itself for its own body alone.
|
|
431
764
|
return false;
|
|
765
|
+
case tree_2.JS.Kind.NamespaceDeclaration:
|
|
766
|
+
// A namespace keeps its hoisting to itself, a global augmentation gives it to the file.
|
|
767
|
+
return isGlobalAugmentation(node);
|
|
432
768
|
case java_1.J.Kind.Lambda:
|
|
433
769
|
case java_1.J.Kind.ClassDeclaration:
|
|
770
|
+
case java_1.J.Kind.NewClass:
|
|
771
|
+
case tree_2.JS.Kind.TypeLiteral:
|
|
772
|
+
// Nothing in an expression or a type hoists past it.
|
|
434
773
|
return false;
|
|
435
774
|
default:
|
|
436
775
|
return true;
|
|
@@ -463,9 +802,13 @@ function walk(node, visit) {
|
|
|
463
802
|
Object.entries(node).forEach(([key, value]) => key !== 'markers' && walk(value, visit));
|
|
464
803
|
}
|
|
465
804
|
}
|
|
466
|
-
/** The
|
|
805
|
+
/** The tree a padding wrapper holds, or the node itself where it is a tree. */
|
|
467
806
|
function unwrap(node) {
|
|
468
|
-
|
|
807
|
+
const kind = node === null || node === void 0 ? void 0 : node.kind;
|
|
808
|
+
if (kind === java_1.J.Kind.RightPadded || kind === java_1.J.Kind.LeftPadded) {
|
|
809
|
+
return unwrap(node.element);
|
|
810
|
+
}
|
|
811
|
+
return (0, tree_1.isTree)(node) ? node : undefined;
|
|
469
812
|
}
|
|
470
813
|
/** `cursor` is protected on `TreeVisitor` and these APIs are free functions, so reaching it takes a cast. */
|
|
471
814
|
function cursorOf(visitor) {
|
|
@@ -505,52 +848,174 @@ function declarationsOf(statement) {
|
|
|
505
848
|
return [];
|
|
506
849
|
}
|
|
507
850
|
/**
|
|
508
|
-
* Whether the identifier
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
* reads: an import specifier's own name answers true, and a type position reads alike to a value.
|
|
851
|
+
* Whether the identifier may be renamed in place, which is so where its slot reads rather than
|
|
852
|
+
* names. A shorthand property `{x}` answers false though it reads `x`, since a rename has to
|
|
853
|
+
* expand it to `{x: y}`. Position is all this reads, so a type position reads alike to a value,
|
|
854
|
+
* and what `x` reads is {@link resolve}'s question.
|
|
513
855
|
*/
|
|
514
|
-
function
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
856
|
+
function isReference(cursor, identifier) {
|
|
857
|
+
const path = pathTo(cursor, identifier);
|
|
858
|
+
return !isShorthand(path[path.length - 1]) && position(path, identifier)[1] !== undefined;
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* The tree nodes from the nearest compilation unit on `cursor`, or its outermost tree, down to the
|
|
862
|
+
* one holding `identifier`, which is the cursor's own node or that node's parent.
|
|
863
|
+
*/
|
|
864
|
+
function pathTo(cursor, identifier) {
|
|
865
|
+
const path = treePath(cursor);
|
|
866
|
+
if (path[path.length - 1] === identifier) {
|
|
867
|
+
path.pop();
|
|
518
868
|
}
|
|
519
|
-
return
|
|
869
|
+
return path;
|
|
520
870
|
}
|
|
521
|
-
/** The
|
|
522
|
-
function
|
|
871
|
+
/** The cursors on `cursor`'s chain holding tree nodes, down from the nearest compilation unit or outermost tree. */
|
|
872
|
+
function treeCursors(cursor) {
|
|
523
873
|
var _a;
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
874
|
+
const cursors = [];
|
|
875
|
+
for (let c = cursor; c; c = c.parent) {
|
|
876
|
+
if ((0, tree_1.isTree)(c.value)) {
|
|
877
|
+
cursors.unshift(c);
|
|
878
|
+
}
|
|
879
|
+
if (((_a = c.value) === null || _a === void 0 ? void 0 : _a.kind) === tree_2.JS.Kind.CompilationUnit) {
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
529
882
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
883
|
+
return cursors;
|
|
884
|
+
}
|
|
885
|
+
function treePath(cursor) {
|
|
886
|
+
return treeCursors(cursor).map(c => c.value);
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* The scopes around `leaf` and what it reads with, folded down `path`, which ends at the node
|
|
890
|
+
* holding `leaf`, and what each node of `path` reads with. The fold runs to the end since a naming
|
|
891
|
+
* slot can hold a read deeper down.
|
|
892
|
+
*/
|
|
893
|
+
function position(path, leaf) {
|
|
894
|
+
var _a, _b;
|
|
895
|
+
let scopes;
|
|
896
|
+
let reading = 'value';
|
|
897
|
+
const readings = [];
|
|
898
|
+
for (let i = 0; i < path.length; i++) {
|
|
899
|
+
readings.push(reading);
|
|
900
|
+
const child = (_a = path[i + 1]) !== null && _a !== void 0 ? _a : leaf;
|
|
901
|
+
const key = slotOf(path[i], child);
|
|
902
|
+
if (key === undefined) {
|
|
903
|
+
const spelled = (_b = child.simpleName) !== null && _b !== void 0 ? _b : child.kind;
|
|
904
|
+
throw new Error(`\`${spelled}\` is not under the cursor it is resolved from`);
|
|
905
|
+
}
|
|
906
|
+
[scopes, reading] = descend(path[i], path[i - 1], key, framesAt(path[i], path[i - 1], scopes), reading);
|
|
907
|
+
}
|
|
908
|
+
return [scopes, reading, readings];
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* The slot of `node` holding `child`, by id, so a copy or a rebuilt node is found as the original.
|
|
912
|
+
* A node's slots are indexed once, through padding, containers and lists.
|
|
913
|
+
*/
|
|
914
|
+
function slotOf(node, child) {
|
|
915
|
+
let index = slots.get(node);
|
|
916
|
+
if (!index) {
|
|
917
|
+
index = new Map();
|
|
918
|
+
for (const [key, value] of Object.entries(node)) {
|
|
919
|
+
if (key !== 'markers') {
|
|
920
|
+
for (const held of treesIn(value)) {
|
|
921
|
+
index.set(held.id, key);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
slots.set(node, index);
|
|
926
|
+
}
|
|
927
|
+
return index.get(child.id);
|
|
928
|
+
}
|
|
929
|
+
/** The tree nodes a slot holds directly, through padding, containers and lists. */
|
|
930
|
+
function treesIn(slot) {
|
|
931
|
+
if (Array.isArray(slot)) {
|
|
932
|
+
return slot.flatMap(treesIn);
|
|
933
|
+
}
|
|
934
|
+
const kind = slot === null || slot === void 0 ? void 0 : slot.kind;
|
|
935
|
+
if (kind === java_1.J.Kind.RightPadded || kind === java_1.J.Kind.LeftPadded) {
|
|
936
|
+
return treesIn(slot.element);
|
|
937
|
+
}
|
|
938
|
+
if (kind === java_1.J.Kind.Container) {
|
|
939
|
+
return treesIn(slot.elements);
|
|
534
940
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
941
|
+
return (0, tree_1.isTree)(slot) ? [slot] : [];
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* @deprecated Use {@link isReference}. Unlike this function once did, it answers false for an
|
|
945
|
+
* import's own name, an alias and a re-export's specifier, which name rather than read.
|
|
946
|
+
*/
|
|
947
|
+
exports.isValueReference = isReference;
|
|
948
|
+
/**
|
|
949
|
+
* The node owning the scope that binds what `identifier` reads, or undefined where it reads a
|
|
950
|
+
* global or reads nothing. Position is what resolves it, so the cursor stands on the identifier
|
|
951
|
+
* or on the node holding it, and a node the cursor does not hold is refused.
|
|
952
|
+
*/
|
|
953
|
+
function resolve(cursor, identifier) {
|
|
954
|
+
var _a;
|
|
955
|
+
const [scopes, reading] = position(pathTo(cursor, identifier), identifier);
|
|
956
|
+
return reading && ((_a = bindingFrame(scopes, identifier.simpleName, reading)) === null || _a === void 0 ? void 0 : _a.node);
|
|
538
957
|
}
|
|
539
|
-
/**
|
|
540
|
-
|
|
541
|
-
|
|
958
|
+
/**
|
|
959
|
+
* Whether the identifier is assigned to where it stands. An assignment's target, plain or compound,
|
|
960
|
+
* is, and so are the operand of `++` or `--`, the head of a for-in or for-of declaring nothing, and
|
|
961
|
+
* a name in such a target's destructuring pattern. A write resolves as a value read, since
|
|
962
|
+
* `x = 2` assigns to the `x` it reads. The cursor is as {@link resolve} takes it.
|
|
963
|
+
*/
|
|
964
|
+
function isWrite(cursor, identifier) {
|
|
965
|
+
return position(pathTo(cursor, identifier), identifier)[1] === 'write';
|
|
542
966
|
}
|
|
543
967
|
/**
|
|
544
|
-
*
|
|
545
|
-
*
|
|
968
|
+
* Every identifier referring to the binding `name` has at `cursor`, the ones declaring it and the
|
|
969
|
+
* ones resolving to it, in the order the tree holds them, which a type annotation can depart from.
|
|
970
|
+
* The binding is the innermost one of the name with `meaning`, or with any meaning where none is
|
|
971
|
+
* asked for. A read with any meaning, as an export clause reads, refers to every meaning it finds.
|
|
972
|
+
* A name bound nowhere has no references. Each reference's cursor continues the caller's chain.
|
|
546
973
|
*/
|
|
547
|
-
function
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
const
|
|
554
|
-
|
|
974
|
+
function referencesOf(cursor, name, meaning) {
|
|
975
|
+
const cursors = treeCursors(cursor);
|
|
976
|
+
const path = cursors.map(c => c.value);
|
|
977
|
+
if (path.length === 0) {
|
|
978
|
+
return [];
|
|
979
|
+
}
|
|
980
|
+
const [scopes, reading, readings] = position(path.slice(0, -1), path[path.length - 1]);
|
|
981
|
+
readings.push(reading);
|
|
982
|
+
const here = framesAt(path[path.length - 1], path[path.length - 2], scopes);
|
|
983
|
+
const frame = bindingFrame(here, name, meaning !== null && meaning !== void 0 ? meaning : 'any');
|
|
984
|
+
if (!frame) {
|
|
985
|
+
return [];
|
|
986
|
+
}
|
|
987
|
+
const declarations = new Set(frameBindings(frame.node, frame.parent).get(name).declarations
|
|
988
|
+
.filter(declaration => meaning === undefined || declaration.meanings.includes(meaning))
|
|
989
|
+
.map(declaration => declaration.identifier.id));
|
|
990
|
+
// A class binds its own name in its body too, so a frame further out declaring the same
|
|
991
|
+
// identifier holds the same binding, and its references sit under that frame.
|
|
992
|
+
const sharesBinding = (scope) => {
|
|
993
|
+
var _a, _b;
|
|
994
|
+
return scope !== undefined &&
|
|
995
|
+
((_b = (_a = frameBindings(scope.node, scope.parent).get(name)) === null || _a === void 0 ? void 0 : _a.declarations) !== null && _b !== void 0 ? _b : [])
|
|
996
|
+
.some(declaration => declarations.has(declaration.identifier.id));
|
|
997
|
+
};
|
|
998
|
+
let root = frame;
|
|
999
|
+
for (let outer = frame.outer; outer; outer = outer.outer) {
|
|
1000
|
+
if (sharesBinding(outer)) {
|
|
1001
|
+
root = outer;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
const at = path.indexOf(root.node);
|
|
1005
|
+
const references = [];
|
|
1006
|
+
readReferences(root.node, root.parent, root.outer, readings[at], (identifier, reads, scopes, trail) => {
|
|
1007
|
+
if (identifier.simpleName !== name) {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
const declares = declarations.has(identifier.id);
|
|
1011
|
+
const refers = !declares && reads !== undefined &&
|
|
1012
|
+
(meaning === undefined || reads === 'any' || meaningRead(reads) === meaning) &&
|
|
1013
|
+
sharesBinding(bindingFrame(scopes, name, reads));
|
|
1014
|
+
if (declares || refers) {
|
|
1015
|
+
const standing = trail.reduce((above, node) => new tree_1.Cursor(node, above), cursors[at].parent);
|
|
1016
|
+
references.push({ cursor: standing, identifier, declares, writes: reads === 'write' });
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
return references;
|
|
555
1020
|
}
|
|
556
1021
|
//# sourceMappingURL=scope.js.map
|