@cplieger/deadset-ts 8.0.0-dev.16 → 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 +58 -22
- 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/tools.js +5 -9
- package/dist/src/component-files.js +3 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +6 -9
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +4 -1
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +25 -29
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +2 -2
- package/dist/src/entries/configuration-files.js +3 -7
- package/dist/src/entries/conventions.js +2 -2
- package/dist/src/entries/html-entries.js +7 -10
- package/dist/src/entries/mdx-entries.js +2 -2
- package/dist/src/entries/static-module.js +2 -1
- package/dist/src/facts.js +4 -4
- package/dist/src/findings/completion.js +40 -4
- 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/framework-lifecycle.js +41 -14
- package/dist/src/graph.js +12 -4
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +5 -0
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/matrix.js +6 -4
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +46 -7
- package/dist/src/platform-calls.js +2 -2
- package/dist/src/platform-symbols.js +13 -19
- package/dist/src/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 +110 -1285
- package/dist/src/roots/configured-declarations.js +3 -0
- package/dist/src/roots/root-set.js +1 -1
- package/dist/src/roots/unseen-loads.js +38 -10
- package/dist/src/serialization-contract.js +8 -3
- package/dist/src/stores.js +4 -2
- package/dist/src/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} +2 -11
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +3 -3
- package/dist/src/verbs/explain.js +50 -28
- package/dist/src/verbs/print-retained.js +2 -2
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +86 -23
- package/src/augmentations.ts +41 -27
- package/src/bindings.ts +460 -0
- package/src/calls.ts +25 -7
- package/src/commands/tools.ts +5 -11
- package/src/component-files.ts +4 -2
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +8 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +4 -1
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +27 -28
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +2 -2
- package/src/entries/configuration-files.ts +11 -8
- package/src/entries/conventions.ts +2 -1
- package/src/entries/html-entries.ts +15 -14
- package/src/entries/mdx-entries.ts +2 -2
- package/src/entries/static-module.ts +2 -1
- package/src/facts.ts +5 -3
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +43 -4
- package/src/findings/emitter.ts +1 -1
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/framework-lifecycle.ts +42 -14
- package/src/graph.ts +24 -6
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/indexed-access.ts +570 -527
- package/src/interface-satisfaction.ts +1 -1
- package/src/keyof-comparisons.ts +131 -116
- package/src/load/expected-module.ts +8 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +6 -0
- package/src/load/workspace-resolution.ts +9 -2
- package/src/matrix.ts +6 -3
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +54 -8
- package/src/platform-calls.ts +3 -3
- package/src/platform-symbols.ts +11 -27
- package/src/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 +168 -1733
- package/src/report.ts +1 -1
- package/src/roots/configured-declarations.ts +3 -0
- package/src/roots/root-set.ts +1 -1
- package/src/roots/unseen-loads.ts +49 -11
- package/src/serialization-contract.ts +8 -3
- package/src/stores.ts +13 -2
- 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} +9 -20
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +3 -2
- package/src/verbs/explain.ts +74 -31
- package/src/verbs/print-retained.ts +2 -2
package/dist/src/overrides.js
CHANGED
|
@@ -24,121 +24,136 @@ function nameOf(entry) {
|
|
|
24
24
|
}
|
|
25
25
|
return isQualifiedName(entry.typeName) ? entry.typeName.right : entry.typeName;
|
|
26
26
|
}
|
|
27
|
-
/** Each class of the target whose `extends` clause names a class, with the name node. */
|
|
28
|
-
function extending(project, held) {
|
|
29
|
-
const found = [];
|
|
30
|
-
for (const file of project.ownSourceFiles()) {
|
|
31
|
-
const visit = (node) => {
|
|
32
|
-
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
33
|
-
const id = held.declarations.get(nodeKey(file, node));
|
|
34
|
-
const clause = node.heritageClauses?.find((one) => one.token === SyntaxKind.ExtendsKeyword);
|
|
35
|
-
const base = clause?.types[0];
|
|
36
|
-
if (id !== undefined && base !== undefined) {
|
|
37
|
-
found.push({ id, base: nameOf(base) });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
node.forEachChild(visit);
|
|
41
|
-
};
|
|
42
|
-
file.forEachChild(visit);
|
|
43
|
-
}
|
|
44
|
-
return found;
|
|
45
|
-
}
|
|
46
27
|
/**
|
|
47
|
-
* The
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
28
|
+
* The reads each base class member makes of its overrides, for the classes of the target that
|
|
29
|
+
* extend another class of the target, and each class makes of its members that override a
|
|
30
|
+
* member of a class declared outside the target. A member a nearer base already overrides is
|
|
31
|
+
* reached through that override. A private member is not overridden. Each read is placed at
|
|
32
|
+
* the override. A class whose base the checker leaves unanswered holds every member it could
|
|
33
|
+
* override.
|
|
52
34
|
*/
|
|
53
|
-
export
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
const bases = new Map();
|
|
73
|
-
const outsideBases = new Map();
|
|
74
|
-
const own = project.ownPaths();
|
|
75
|
-
const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
|
|
76
|
-
classes.forEach(({ id }, index) => {
|
|
77
|
-
const symbol = symbols[index];
|
|
78
|
-
if (symbol !== undefined && symbol !== UNANSWERED) {
|
|
79
|
-
const inTarget = chains
|
|
80
|
-
.chainOf(symbol)
|
|
81
|
-
.map((target) => target.id)
|
|
82
|
-
.filter((target) => byId.get(target)?.kind === "class" && target !== id);
|
|
83
|
-
bases.set(id, inTarget);
|
|
84
|
-
const declared = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
|
|
85
|
-
if (inTarget.length === 0 &&
|
|
86
|
-
declared !== UNANSWERED &&
|
|
87
|
-
(((declared.flags & SymbolFlags.Class) !== 0 &&
|
|
88
|
-
// The target's augmentation of an outside class merges an interface into it,
|
|
89
|
-
// so only a class declaration says where the class is.
|
|
90
|
-
declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) ||
|
|
91
|
-
((declared.flags & SymbolFlags.Interface) !== 0 &&
|
|
92
|
-
declared.declarations.some((handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path))))) {
|
|
93
|
-
outsideBases.set(id, declared);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
/** The nearest member of one component the class or a class it extends declares. */
|
|
98
|
-
const declaredAbove = (classId, component, seen) => {
|
|
99
|
-
if (seen.has(classId)) {
|
|
100
|
-
return [];
|
|
101
|
-
}
|
|
102
|
-
seen.add(classId);
|
|
103
|
-
const own = members.get(classId)?.get(component);
|
|
104
|
-
if (own !== undefined) {
|
|
105
|
-
return [own.id];
|
|
106
|
-
}
|
|
107
|
-
return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
|
|
108
|
-
};
|
|
109
|
-
const found = [];
|
|
110
|
-
const outsideNames = outsideMemberNames(project, bases, outsideBases);
|
|
111
|
-
for (const [classId, names] of outsideNames) {
|
|
112
|
-
for (const [component, member] of members.get(classId) ?? []) {
|
|
113
|
-
if (names.has(component)) {
|
|
114
|
-
found.push({
|
|
115
|
-
from: classId,
|
|
116
|
-
to: member.id,
|
|
117
|
-
position: member.position,
|
|
118
|
-
use: "read",
|
|
119
|
-
resolution: "override",
|
|
120
|
-
test: testFiles.has(member.position.path),
|
|
35
|
+
export const overrides = {
|
|
36
|
+
start() {
|
|
37
|
+
const extending = [];
|
|
38
|
+
return {
|
|
39
|
+
visit(node, { file }) {
|
|
40
|
+
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
41
|
+
const clause = node.heritageClauses?.find((one) => one.token === SyntaxKind.ExtendsKeyword);
|
|
42
|
+
const base = clause?.types[0];
|
|
43
|
+
if (base !== undefined) {
|
|
44
|
+
extending.push({ node, file, base: nameOf(base) });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
reads({ project, held, chains }) {
|
|
49
|
+
const found = [];
|
|
50
|
+
const classes = extending.flatMap((one) => {
|
|
51
|
+
const id = held.declarations.get(nodeKey(one.file, one.node));
|
|
52
|
+
return id === undefined ? [] : [{ ...one, id }];
|
|
121
53
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
54
|
+
if (classes.length === 0) {
|
|
55
|
+
return found;
|
|
56
|
+
}
|
|
57
|
+
const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
|
|
58
|
+
const members = new Map();
|
|
59
|
+
for (const symbol of held.symbols) {
|
|
60
|
+
const component = memberComponent(symbol, byId);
|
|
61
|
+
if (component === undefined ||
|
|
62
|
+
!OVERRIDABLE.has(symbol.kind) ||
|
|
63
|
+
symbol.visibility === "private" ||
|
|
64
|
+
symbol.visibility === "private-name") {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const own = members.get(symbol.parent) ?? new Map();
|
|
68
|
+
own.set(component, symbol);
|
|
69
|
+
members.set(symbol.parent, own);
|
|
70
|
+
}
|
|
71
|
+
const bases = new Map();
|
|
72
|
+
const outsideBases = new Map();
|
|
73
|
+
const fileOf = new Map(classes.map((one) => [one.id, one.file]));
|
|
74
|
+
const own = project.ownPaths();
|
|
75
|
+
const unanswered = new Set();
|
|
76
|
+
const symbols = project.symbolsAt(classes.map((one) => project.handle(one.base)));
|
|
77
|
+
classes.forEach(({ id }, index) => {
|
|
78
|
+
const symbol = symbols[index];
|
|
79
|
+
if (symbol === UNANSWERED) {
|
|
80
|
+
unanswered.add(id);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (symbol === undefined) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const inTarget = chains
|
|
87
|
+
.chainOf(symbol)
|
|
88
|
+
.map((target) => target.id)
|
|
89
|
+
.filter((target) => byId.get(target)?.kind === "class" && target !== id);
|
|
90
|
+
bases.set(id, inTarget);
|
|
91
|
+
const declared = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
|
|
92
|
+
if (declared === UNANSWERED) {
|
|
93
|
+
unanswered.add(id);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (inTarget.length === 0 &&
|
|
97
|
+
(((declared.flags & SymbolFlags.Class) !== 0 &&
|
|
98
|
+
// The target's augmentation of an outside class merges an interface into it,
|
|
99
|
+
// so only a class declaration says where the class is.
|
|
100
|
+
declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) ||
|
|
101
|
+
((declared.flags & SymbolFlags.Interface) !== 0 &&
|
|
102
|
+
declared.declarations.some((handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path))))) {
|
|
103
|
+
outsideBases.set(id, declared);
|
|
104
|
+
}
|
|
136
105
|
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
106
|
+
/** The nearest member of one component the class or a class it extends declares. */
|
|
107
|
+
const declaredAbove = (classId, component, seen) => {
|
|
108
|
+
if (seen.has(classId)) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
seen.add(classId);
|
|
112
|
+
const member = members.get(classId)?.get(component);
|
|
113
|
+
if (member !== undefined) {
|
|
114
|
+
return [member.id];
|
|
115
|
+
}
|
|
116
|
+
return (bases.get(classId) ?? []).flatMap((base) => declaredAbove(base, component, seen));
|
|
117
|
+
};
|
|
118
|
+
const read = (classId, from, member) => {
|
|
119
|
+
const file = fileOf.get(classId);
|
|
120
|
+
if (file !== undefined) {
|
|
121
|
+
found.push({
|
|
122
|
+
kind: "named",
|
|
123
|
+
from,
|
|
124
|
+
to: member.id,
|
|
125
|
+
file,
|
|
126
|
+
at: member.position,
|
|
127
|
+
use: "read",
|
|
128
|
+
via: "override",
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const outside = outsideMemberNames(project, bases, outsideBases);
|
|
133
|
+
for (const classId of [...unanswered, ...outside.unanswered]) {
|
|
134
|
+
const ids = [...(members.get(classId)?.values() ?? [])].map((member) => member.id);
|
|
135
|
+
found.push({ kind: "held", from: classId, ids });
|
|
136
|
+
}
|
|
137
|
+
for (const [classId, names] of outside.names) {
|
|
138
|
+
for (const [component, member] of members.get(classId) ?? []) {
|
|
139
|
+
if (names.has(component)) {
|
|
140
|
+
read(classId, classId, member);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const [classId, baseIds] of bases) {
|
|
145
|
+
for (const [component, member] of members.get(classId) ?? []) {
|
|
146
|
+
const seen = new Set([classId]);
|
|
147
|
+
for (const base of baseIds.flatMap((one) => declaredAbove(one, component, seen))) {
|
|
148
|
+
read(classId, base, member);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return found;
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
};
|
|
142
157
|
/**
|
|
143
158
|
* Per class of the target with a class declared outside the target in its `extends`
|
|
144
159
|
* chain, the member components that outside class's instances and constructor carry, a
|
|
@@ -146,10 +161,17 @@ export function overrideReferences(project, held, chains, testFiles) {
|
|
|
146
161
|
*/
|
|
147
162
|
function outsideMemberNames(project, bases, outsideBases) {
|
|
148
163
|
const direct = new Map();
|
|
164
|
+
const unanswered = [];
|
|
149
165
|
const error = errorType(project);
|
|
150
166
|
for (const [classId, base] of outsideBases) {
|
|
151
167
|
const instance = project.queries.declaredTypeOf(base);
|
|
152
168
|
const statics = project.queries.typeOfSymbol(base);
|
|
169
|
+
if (instance === UNANSWERED ||
|
|
170
|
+
statics === UNANSWERED ||
|
|
171
|
+
project.queries.propertiesOf(instance) === UNANSWERED ||
|
|
172
|
+
(statics !== undefined && project.queries.propertiesOf(statics) === UNANSWERED)) {
|
|
173
|
+
unanswered.push(classId);
|
|
174
|
+
}
|
|
153
175
|
const components = [
|
|
154
176
|
...namesOf(project, instance === UNANSWERED ? undefined : instance).map(nameComponent),
|
|
155
177
|
...(instance !== UNANSWERED && reaches(project, instance, error) ? ["toString"] : []),
|
|
@@ -176,7 +198,7 @@ function outsideMemberNames(project, bases, outsideBases) {
|
|
|
176
198
|
names.set(classId, found);
|
|
177
199
|
}
|
|
178
200
|
}
|
|
179
|
-
return names;
|
|
201
|
+
return { names, unanswered };
|
|
180
202
|
}
|
|
181
203
|
/** The global `Error` instance type's identifier, where the program's library declares one. */
|
|
182
204
|
function errorType(project) {
|
package/dist/src/paths.js
CHANGED
|
@@ -8,6 +8,50 @@ const SEPARATOR = "/";
|
|
|
8
8
|
export function isAbsolutePath(path) {
|
|
9
9
|
return path.startsWith(SEPARATOR);
|
|
10
10
|
}
|
|
11
|
+
/** Whether a specifier starts a path relative to the file that writes it: `./` or `../`. */
|
|
12
|
+
export function startsRelativePath(specifier) {
|
|
13
|
+
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
14
|
+
}
|
|
15
|
+
/** Whether a specifier names a path relative to the file that writes it: `.`, `..`, `./` or `../` first. */
|
|
16
|
+
export function isRelativeSpecifier(specifier) {
|
|
17
|
+
return specifier === "." || specifier === ".." || startsRelativePath(specifier);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Whether a module specifier names a path rather than a package, as the compiler reads one:
|
|
21
|
+
* a relative specifier or an absolute path.
|
|
22
|
+
*/
|
|
23
|
+
export function isPathSpecifier(specifier) {
|
|
24
|
+
return isRelativeSpecifier(specifier) || isAbsolutePath(specifier);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Whether a string starts with `./`, `../` or `/`, the specifiers a browser reads as a URL
|
|
28
|
+
* rather than a bare name, so a bare `.` or `..` stays a request:
|
|
29
|
+
* https://html.spec.whatwg.org/multipage/webappapis.html#resolve-a-url-like-module-specifier
|
|
30
|
+
*/
|
|
31
|
+
export function isExplicitPath(text) {
|
|
32
|
+
return startsRelativePath(text) || isAbsolutePath(text);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Whether a string starts with `.` or `/`, which no npm package name does, so a reader that
|
|
36
|
+
* takes any other string as a package's takes it as a path:
|
|
37
|
+
* https://docs.npmjs.com/cli/v11/configuring-npm/package-json#name
|
|
38
|
+
*/
|
|
39
|
+
export function cannotStartPackageName(text) {
|
|
40
|
+
return text.startsWith(".") || isAbsolutePath(text);
|
|
41
|
+
}
|
|
42
|
+
/** A URL scheme, `file:` and `node:` among them: https://url.spec.whatwg.org/#url-scheme-string */
|
|
43
|
+
const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
|
|
44
|
+
/** Whether a string starts with a URL scheme. */
|
|
45
|
+
export function hasURLScheme(text) {
|
|
46
|
+
return URL_SCHEME.test(text);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Whether a URL reference is a relative one that does not start with a solidus, the bare
|
|
50
|
+
* name included: https://url.spec.whatwg.org/#relative-url-string
|
|
51
|
+
*/
|
|
52
|
+
export function isPathRelativeURL(reference) {
|
|
53
|
+
return reference !== "" && !reference.startsWith(SEPARATOR) && !hasURLScheme(reference);
|
|
54
|
+
}
|
|
11
55
|
/**
|
|
12
56
|
* One path with `.` removed, `..` applied to the segment before it, and repeated
|
|
13
57
|
* separators collapsed. A `..` that walks past the root of an absolute path is
|
|
@@ -88,17 +132,12 @@ export function extensionOf(path) {
|
|
|
88
132
|
export function resolvePath(against, path) {
|
|
89
133
|
return isAbsolutePath(path) ? normalizePath(path) : joinPath(against, path);
|
|
90
134
|
}
|
|
91
|
-
/**
|
|
92
|
-
* A URL reference's scheme. A reference with none that does not start with a solidus is
|
|
93
|
-
* relative, the bare name included: https://url.spec.whatwg.org/#relative-url-string
|
|
94
|
-
*/
|
|
95
|
-
const URL_SCHEME = /^[a-z][a-z\d+.-]*:/iu;
|
|
96
135
|
/**
|
|
97
136
|
* The path a relative URL reference names against `dir`, its query and its fragment
|
|
98
137
|
* removed, which name no part of the file; undefined where the reference is not relative.
|
|
99
138
|
*/
|
|
100
139
|
export function relativeURLPath(dir, reference) {
|
|
101
|
-
if (
|
|
140
|
+
if (!isPathRelativeURL(reference)) {
|
|
102
141
|
return undefined;
|
|
103
142
|
}
|
|
104
143
|
const cut = reference.search(/[?#]/u);
|
|
@@ -112,7 +151,7 @@ export function relativeURLPath(dir, reference) {
|
|
|
112
151
|
*/
|
|
113
152
|
export function queriedModulePath(specifier) {
|
|
114
153
|
const query = specifier.indexOf("?");
|
|
115
|
-
if (query < 0 || !(specifier
|
|
154
|
+
if (query < 0 || !startsRelativePath(specifier)) {
|
|
116
155
|
return undefined;
|
|
117
156
|
}
|
|
118
157
|
const raw = specifier
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Which calls are the platform's `require` and which constructions its `URL`
|
|
3
|
-
* references-ts.1), decided once over the bindings of the names a callee is written with: the
|
|
2
|
+
* Which calls are the platform's `require` or AMD's `define` and which constructions its `URL`
|
|
3
|
+
* (roots.23, references-ts.37, references-ts.1), decided once over the bindings of the names a callee is written with: the
|
|
4
4
|
* checker's in a program, the file's own scopes in a module read outside every program.
|
|
5
5
|
*/
|
|
6
6
|
import { isBindingElement, isCallExpression, isExportSpecifier, isIdentifier, isImportSpecifier, isPropertyAccessExpression, isStringLiteral, isVariableDeclaration, } from "@typescript/native/ast";
|
|
@@ -11,11 +11,10 @@ import { isAmbient } from "./inventory.js";
|
|
|
11
11
|
import { callsPlatformBy, carriedNames } from "./platform-calls.js";
|
|
12
12
|
import { unwrapped } from "./unwrap.js";
|
|
13
13
|
/** The symbol one symbol names through its aliases, or the symbol itself where it is none. */
|
|
14
|
-
function dealiased(project, symbol
|
|
14
|
+
function dealiased(project, symbol) {
|
|
15
15
|
if ((symbol.flags & SymbolFlags.Alias) === 0) {
|
|
16
16
|
return symbol;
|
|
17
17
|
}
|
|
18
|
-
asked();
|
|
19
18
|
return project.queries.aliased(symbol);
|
|
20
19
|
}
|
|
21
20
|
/** Each declaration of `symbol` in a file of the target, read in this project's program. */
|
|
@@ -50,7 +49,7 @@ function writesBody(node) {
|
|
|
50
49
|
* prove it the target's own where one {@link writesBody}. A chain that ends at no
|
|
51
50
|
* declaration, or that the checker leaves unanswered, reaches none.
|
|
52
51
|
*/
|
|
53
|
-
function boundBy(project, symbol
|
|
52
|
+
function boundBy(project, symbol) {
|
|
54
53
|
if (typeof symbol !== "object") {
|
|
55
54
|
return undefined;
|
|
56
55
|
}
|
|
@@ -66,28 +65,23 @@ function boundBy(project, symbol, asked) {
|
|
|
66
65
|
if ((at.flags & SymbolFlags.Alias) === 0) {
|
|
67
66
|
return { names, own, provenOwn: own.some(writesBody) };
|
|
68
67
|
}
|
|
69
|
-
asked();
|
|
70
68
|
const next = project.aliasStepOf(at);
|
|
71
69
|
at = typeof next === "object" ? next : undefined;
|
|
72
70
|
}
|
|
73
71
|
return { names, own: [], provenOwn: false };
|
|
74
72
|
}
|
|
75
73
|
/** Each written name's binding, as this project's checker answers it. */
|
|
76
|
-
function checkerBinding(project
|
|
77
|
-
return (written) =>
|
|
78
|
-
asked();
|
|
79
|
-
return boundBy(project, project.symbolsAt([project.handle(written)])[0], asked);
|
|
80
|
-
};
|
|
74
|
+
function checkerBinding(project) {
|
|
75
|
+
return (written) => boundBy(project, project.symbolsAt([project.handle(written)])[0]);
|
|
81
76
|
}
|
|
82
77
|
/**
|
|
83
78
|
* Whether a call or construction through `callee`, the batch's answer for which is `symbol`,
|
|
84
|
-
* is one of the platform's `require` or `URL` (roots.23, references-ts.
|
|
85
|
-
* {@link callsPlatformBy} decides it over this project's checker
|
|
86
|
-
* counted by `asked`.
|
|
79
|
+
* is one of the platform's `require`, AMD's `define` or `URL` (roots.23, references-ts.37,
|
|
80
|
+
* references-ts.1), as {@link callsPlatformBy} decides it over this project's checker.
|
|
87
81
|
*/
|
|
88
|
-
export function callsPlatform(project, name, callee, symbol
|
|
89
|
-
const others = checkerBinding(project
|
|
90
|
-
return callsPlatformBy(name, callee, (written) => written === callee ? boundBy(project, symbol
|
|
82
|
+
export function callsPlatform(project, name, callee, symbol) {
|
|
83
|
+
const others = checkerBinding(project);
|
|
84
|
+
return callsPlatformBy(name, callee, (written) => written === callee ? boundBy(project, symbol) : others(written));
|
|
91
85
|
}
|
|
92
86
|
/**
|
|
93
87
|
* Whether a worker's constructor or `navigator`, the batch's answer for which is `symbol`, is
|
|
@@ -122,16 +116,16 @@ function inThreadsModule(node) {
|
|
|
122
116
|
* (references-ts.2): its symbol resolves to a declaration of `worker_threads`, or one of its
|
|
123
117
|
* own declarations binds the module's `Worker`, or `holder`, the namespace it is read
|
|
124
118
|
* through, binds the module, each binding read as {@link boundExports} reads one, which
|
|
125
|
-
* decides where the module has no declaration.
|
|
119
|
+
* decides where the module has no declaration.
|
|
126
120
|
*/
|
|
127
|
-
export function startsThread(project, symbol, holder
|
|
128
|
-
const bindingOf = checkerBinding(project
|
|
121
|
+
export function startsThread(project, symbol, holder) {
|
|
122
|
+
const bindingOf = checkerBinding(project);
|
|
129
123
|
const declared = (one, member) => typeof one === "object" &&
|
|
130
124
|
ownDeclarations(project, one).some((node) => boundExports(node, (specifier) => THREADS.has(specifier), bindingOf).includes(member));
|
|
131
125
|
if (declared(symbol, "Worker") || declared(holder, NAMESPACE)) {
|
|
132
126
|
return true;
|
|
133
127
|
}
|
|
134
|
-
const named = typeof symbol === "object" ? dealiased(project, symbol
|
|
128
|
+
const named = typeof symbol === "object" ? dealiased(project, symbol) : undefined;
|
|
135
129
|
return (typeof named === "object" &&
|
|
136
130
|
named.declarations.some((handle) => {
|
|
137
131
|
const node = project.declarationAt(handle)?.node;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { elementAccess } from "../element-access.js";
|
|
2
|
+
import { indexedAccess } from "../indexed-access.js";
|
|
3
|
+
import { keyofComparisons } from "../keyof-comparisons.js";
|
|
4
|
+
import { moduleDeclarations } from "../module-declarations.js";
|
|
5
|
+
import { overrides } from "../overrides.js";
|
|
6
|
+
import { isAnswered } from "../query.js";
|
|
7
|
+
import { iteration } from "../symbol-keys.js";
|
|
8
|
+
import { typeParameterReads } from "../type-parameter-reads.js";
|
|
9
|
+
import { unimplementedCallees } from "../unimplemented-callees.js";
|
|
10
|
+
/** The member kinds a hold of every member names. */
|
|
11
|
+
const MEMBER_KINDS = new Set([
|
|
12
|
+
"method",
|
|
13
|
+
"class-member",
|
|
14
|
+
"interface-method",
|
|
15
|
+
"type-member",
|
|
16
|
+
"enum-member",
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Every read rule, each run over every side's walked files with the reference sources:
|
|
20
|
+
* a consumer's read of a target member is a reference as the target's own is.
|
|
21
|
+
*/
|
|
22
|
+
export const READ_RULES = [
|
|
23
|
+
overrides,
|
|
24
|
+
indexedAccess,
|
|
25
|
+
elementAccess,
|
|
26
|
+
keyofComparisons,
|
|
27
|
+
iteration,
|
|
28
|
+
typeParameterReads,
|
|
29
|
+
unimplementedCallees,
|
|
30
|
+
moduleDeclarations,
|
|
31
|
+
];
|
|
32
|
+
/** The declarations one hold names, with the declaration whose read it is. */
|
|
33
|
+
export function heldBy(read, { project, held, chains }) {
|
|
34
|
+
const ids = [...(read.from === "" ? [] : [read.from]), ...(read.ids ?? [])];
|
|
35
|
+
for (const name of read.names ?? []) {
|
|
36
|
+
ids.push(...(held.byName.get(name) ?? []));
|
|
37
|
+
}
|
|
38
|
+
for (const type of read.types ?? []) {
|
|
39
|
+
const properties = project.queries.propertiesOf(type);
|
|
40
|
+
for (const property of isAnswered(properties) ? properties : []) {
|
|
41
|
+
ids.push(...chains.declarationsOf(property));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (read.members === true) {
|
|
45
|
+
ids.push(...held.symbols.filter((one) => MEMBER_KINDS.has(one.kind)).map((one) => one.id));
|
|
46
|
+
}
|
|
47
|
+
return ids;
|
|
48
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { isBinaryExpression, isParenthesizedExpression, isPropertyAccessExpression, SyntaxKind, } from "@typescript/native/ast";
|
|
2
|
+
import { bindingsOf } from "../import-bindings.js";
|
|
3
|
+
import { byPosition, renderPosition } from "../position.js";
|
|
4
|
+
import { bytewise } from "../text/compare.js";
|
|
5
|
+
/**
|
|
6
|
+
* Where every reference of one pass is made: it renders the position against the side's
|
|
7
|
+
* root, takes the writing file's test class from the file classes, marks a use only a
|
|
8
|
+
* declaration's name answered `guessed` and an alias step's `alias`, tags a consumer's
|
|
9
|
+
* reference with the consumer, and orders the records.
|
|
10
|
+
*/
|
|
11
|
+
export class Sink {
|
|
12
|
+
#root;
|
|
13
|
+
#testFile;
|
|
14
|
+
#consumer;
|
|
15
|
+
#found = [];
|
|
16
|
+
#valued = new Map();
|
|
17
|
+
#held = new Set();
|
|
18
|
+
constructor(root, testFile, consumer) {
|
|
19
|
+
this.#root = root;
|
|
20
|
+
this.#testFile = testFile;
|
|
21
|
+
this.#consumer = consumer;
|
|
22
|
+
}
|
|
23
|
+
/** Whether a test file wrote one walked file's uses. */
|
|
24
|
+
isTest(file) {
|
|
25
|
+
return this.#testFile(file);
|
|
26
|
+
}
|
|
27
|
+
/** The position one node of a walked file renders at. */
|
|
28
|
+
position(file, at) {
|
|
29
|
+
return renderPosition(file, this.#root, at.getStart());
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Records one use of each target, made by `from` at `at` in `file`. A write that stores
|
|
33
|
+
* `value` is remembered for the import bindings only its values use. A `production` use is
|
|
34
|
+
* production code's whoever writes it.
|
|
35
|
+
*/
|
|
36
|
+
add(file, at, from, targets, use, via, value, production = false) {
|
|
37
|
+
const position = "column" in at ? at : this.position(file, at);
|
|
38
|
+
const test = !production && this.#testFile(file);
|
|
39
|
+
for (const one of targets) {
|
|
40
|
+
const target = typeof one === "string" ? { id: one } : one;
|
|
41
|
+
this.#found.push({
|
|
42
|
+
from,
|
|
43
|
+
to: target.id,
|
|
44
|
+
position,
|
|
45
|
+
use,
|
|
46
|
+
resolution: target.guessed === true ? "by-name" : target.stepped === true ? "alias" : via,
|
|
47
|
+
test,
|
|
48
|
+
...(target.guessed === true ? { guessed: true } : {}),
|
|
49
|
+
});
|
|
50
|
+
if (use === "write" && value !== undefined) {
|
|
51
|
+
const valued = this.#valued.get(file) ?? [];
|
|
52
|
+
valued.push({ index: this.#found.length - 1, value });
|
|
53
|
+
this.#valued.set(file, valued);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Holds declarations an unanswered question could have made live, and the one whose read asked it. */
|
|
58
|
+
hold(ids) {
|
|
59
|
+
for (const id of ids) {
|
|
60
|
+
this.#held.add(id);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/** The records made so far, before {@link finish} orders them. */
|
|
64
|
+
get made() {
|
|
65
|
+
return this.#found;
|
|
66
|
+
}
|
|
67
|
+
/** Appends records another stage derived from these, rendered already. */
|
|
68
|
+
extend(references) {
|
|
69
|
+
this.#found.push(...references);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The records in position order, then the declaration named, the referencing declaration
|
|
73
|
+
* and the use, each write marked with the import bindings only its values use, and the
|
|
74
|
+
* held declarations. `usesOf` gives each walked file's name sites.
|
|
75
|
+
*/
|
|
76
|
+
finish(usesOf) {
|
|
77
|
+
const found = this.#found;
|
|
78
|
+
for (const [file, writes] of this.#valued) {
|
|
79
|
+
for (const { index, names } of aloneIn(file, usesOf.get(file) ?? [], writes, found)) {
|
|
80
|
+
const reference = found[index];
|
|
81
|
+
if (reference !== undefined) {
|
|
82
|
+
found[index] = { ...reference, alone: names };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
found.sort((a, b) => byPosition(a.position, b.position) ||
|
|
87
|
+
bytewise(a.to, b.to) ||
|
|
88
|
+
bytewise(a.from, b.from) ||
|
|
89
|
+
bytewise(a.use, b.use));
|
|
90
|
+
const consumer = this.#consumer;
|
|
91
|
+
return {
|
|
92
|
+
references: consumer === undefined
|
|
93
|
+
? found
|
|
94
|
+
: found.map((reference) => ({ ...reference, consumer: consumer.id })),
|
|
95
|
+
held: [...this.#held].sort(bytewise),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** The value one assignment stores through the name it writes, where it is a plain assignment. */
|
|
100
|
+
export function storedValue(name) {
|
|
101
|
+
let target = name;
|
|
102
|
+
if (isPropertyAccessExpression(name.parent) && name.parent.name === name) {
|
|
103
|
+
target = name.parent;
|
|
104
|
+
}
|
|
105
|
+
while (isParenthesizedExpression(target.parent)) {
|
|
106
|
+
target = target.parent;
|
|
107
|
+
}
|
|
108
|
+
const parent = target.parent;
|
|
109
|
+
return isBinaryExpression(parent) &&
|
|
110
|
+
parent.operatorToken.kind === SyntaxKind.EqualsToken &&
|
|
111
|
+
parent.left === target
|
|
112
|
+
? parent.right
|
|
113
|
+
: undefined;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The import bindings of one file that only the values written into one declaration
|
|
117
|
+
* use, for each write into it. A binding is named by an identifier that spells its
|
|
118
|
+
* local name and is not the member a property access names.
|
|
119
|
+
*/
|
|
120
|
+
function aloneIn(file, uses, writes, found) {
|
|
121
|
+
const bindings = new Set(bindingsOf(file).map((binding) => binding.name.text));
|
|
122
|
+
if (bindings.size === 0) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
const occurrences = new Map();
|
|
126
|
+
for (const site of uses) {
|
|
127
|
+
const text = site.node.getText();
|
|
128
|
+
const parent = site.node.parent;
|
|
129
|
+
if (!bindings.has(text) || (isPropertyAccessExpression(parent) && parent.name === site.node)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!occurrences.get(text)?.includes(site.node)) {
|
|
133
|
+
occurrences.set(text, [...(occurrences.get(text) ?? []), site.node]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const inside = (node, value) => node.pos >= value.pos && node.end <= value.end;
|
|
137
|
+
const result = [];
|
|
138
|
+
for (const write of writes) {
|
|
139
|
+
const to = found[write.index]?.to;
|
|
140
|
+
const sameTarget = writes.filter((one) => found[one.index]?.to === to);
|
|
141
|
+
const names = [...occurrences]
|
|
142
|
+
.filter(([, nodes]) => nodes.some((node) => inside(node, write.value)) &&
|
|
143
|
+
nodes.every((node) => sameTarget.some((one) => inside(node, one.value))))
|
|
144
|
+
.map(([name]) => name)
|
|
145
|
+
.sort(bytewise);
|
|
146
|
+
if (names.length > 0) {
|
|
147
|
+
result.push({ index: write.index, names });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|