@cplieger/deadset-ts 8.0.0-dev.15 → 8.0.0-dev.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/src/alias-chain.js +0 -5
  2. package/dist/src/amd.js +81 -0
  3. package/dist/src/analysis.js +94 -54
  4. package/dist/src/augmentations.js +23 -24
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +25 -8
  7. package/dist/src/commands/read.js +6 -7
  8. package/dist/src/commands/tools.js +5 -9
  9. package/dist/src/component-files.js +3 -2
  10. package/dist/src/conformance-record.js +1 -1
  11. package/dist/src/consumers.js +9 -10
  12. package/dist/src/contextual-uses.js +1 -17
  13. package/dist/src/contract/config.generated.js +5 -2
  14. package/dist/src/contract/contract.generated.js +119 -2
  15. package/dist/src/contract/report.generated.js +6 -2
  16. package/dist/src/dependencies.js +98 -71
  17. package/dist/src/destructuring.js +1 -19
  18. package/dist/src/element-access.js +179 -164
  19. package/dist/src/entries/build-scripts.js +222 -0
  20. package/dist/src/{configuration-files.js → entries/configuration-files.js} +22 -64
  21. package/dist/src/{convention-rows.js → entries/convention-rows.js} +120 -27
  22. package/dist/src/entries/conventions.js +739 -0
  23. package/dist/src/{html-entries.js → entries/html-entries.js} +9 -12
  24. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +3 -3
  25. package/dist/src/entries/run-directories.js +77 -0
  26. package/dist/src/entries/sources.js +144 -0
  27. package/dist/src/entries/static-module.js +670 -0
  28. package/dist/src/entries/test-runners.js +684 -0
  29. package/dist/src/{workflow-steps.js → entries/workflow-steps.js} +18 -8
  30. package/dist/src/extensions.js +37 -0
  31. package/dist/src/facts.js +38 -0
  32. package/dist/src/findings/completion.js +40 -4
  33. package/dist/src/findings/emitters.js +5 -8
  34. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  35. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  36. package/dist/src/findings/self-check.js +36 -20
  37. package/dist/src/findings-pass.js +5 -6
  38. package/dist/src/framework-lifecycle.js +42 -15
  39. package/dist/src/fs/manifest.js +15 -20
  40. package/dist/src/generated-file.js +172 -29
  41. package/dist/src/graph.js +14 -8
  42. package/dist/src/import-bindings.js +73 -28
  43. package/dist/src/index/sites.js +557 -0
  44. package/dist/src/index.js +1 -1
  45. package/dist/src/indexed-access.js +523 -478
  46. package/dist/src/injection-container.js +1 -1
  47. package/dist/src/interface-satisfaction.js +1 -1
  48. package/dist/src/inventory/commonjs.js +62 -17
  49. package/dist/src/inventory/enumerate.js +2 -1
  50. package/dist/src/javascript-file.js +26 -11
  51. package/dist/src/keyof-comparisons.js +124 -103
  52. package/dist/src/load/classify.js +28 -9
  53. package/dist/src/load/diagnostics.js +8 -1
  54. package/dist/src/load/discover.js +15 -7
  55. package/dist/src/load/expected-module.js +2 -2
  56. package/dist/src/load/plan.js +2 -2
  57. package/dist/src/load/specifiers.js +2 -4
  58. package/dist/src/load/synthesize.js +10 -5
  59. package/dist/src/load/workspace-manifests.js +1 -1
  60. package/dist/src/load/workspace-resolution.js +2 -2
  61. package/dist/src/matrix.js +12 -17
  62. package/dist/src/module-declarations.js +70 -85
  63. package/dist/src/overrides.js +134 -112
  64. package/dist/src/paths.js +56 -1
  65. package/dist/src/platform-calls.js +78 -0
  66. package/dist/src/platform-symbols.js +134 -0
  67. package/dist/src/references/read-rule.js +1 -0
  68. package/dist/src/references/read-rules.js +48 -0
  69. package/dist/src/references/sink.js +151 -0
  70. package/dist/src/references/sources.js +450 -0
  71. package/dist/src/references.js +111 -1116
  72. package/dist/src/resolve.js +1 -1
  73. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +7 -4
  74. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +5 -8
  75. package/dist/src/{manifest.js → roots/manifest.js} +4 -4
  76. package/dist/src/roots/published-reach.js +412 -0
  77. package/dist/src/roots/root-set.js +489 -0
  78. package/dist/src/roots/unseen-loads.js +236 -0
  79. package/dist/src/serialization-contract.js +9 -4
  80. package/dist/src/source-files.js +5 -9
  81. package/dist/src/stores.js +4 -2
  82. package/dist/src/suppress.js +30 -32
  83. package/dist/src/sweep.js +25 -13
  84. package/dist/src/symbol-keys.js +89 -83
  85. package/dist/src/tests/file-class.js +76 -0
  86. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  87. package/dist/src/{test-support.js → tests/support.js} +5 -12
  88. package/dist/src/type-parameter-reads.js +178 -139
  89. package/dist/src/unimplemented-callees.js +262 -242
  90. package/dist/src/verbs/analyze.js +15 -3
  91. package/dist/src/verbs/describe.js +5 -3
  92. package/dist/src/verbs/explain.js +50 -28
  93. package/dist/src/verbs/print-retained.js +2 -2
  94. package/jsr.json +1 -1
  95. package/package.json +3 -3
  96. package/src/alias-chain.ts +0 -7
  97. package/src/amd.ts +130 -0
  98. package/src/analysis.ts +156 -70
  99. package/src/augmentations.ts +41 -27
  100. package/src/automatic-types.ts +1 -1
  101. package/src/bindings.ts +460 -0
  102. package/src/calls.ts +25 -7
  103. package/src/commands/read.ts +6 -8
  104. package/src/commands/tools.ts +5 -11
  105. package/src/component-files.ts +4 -2
  106. package/src/conformance-record.ts +1 -1
  107. package/src/consumers.ts +12 -11
  108. package/src/contextual-uses.ts +2 -22
  109. package/src/contract/config.generated.ts +5 -2
  110. package/src/contract/contract.generated.ts +138 -2
  111. package/src/contract/report.generated.ts +6 -2
  112. package/src/dependencies.ts +117 -71
  113. package/src/destructuring.ts +1 -31
  114. package/src/element-access.ts +188 -182
  115. package/src/entries/build-scripts.ts +292 -0
  116. package/src/{configuration-files.ts → entries/configuration-files.ts} +33 -82
  117. package/src/{convention-rows.ts → entries/convention-rows.ts} +167 -28
  118. package/src/{conventions.ts → entries/conventions.ts} +349 -523
  119. package/src/{html-entries.ts → entries/html-entries.ts} +19 -18
  120. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +5 -5
  121. package/src/entries/run-directories.ts +108 -0
  122. package/src/entries/sources.ts +266 -0
  123. package/src/entries/static-module.ts +858 -0
  124. package/src/entries/test-runners.ts +868 -0
  125. package/src/{workflow-steps.ts → entries/workflow-steps.ts} +29 -11
  126. package/src/exempt.ts +3 -2
  127. package/src/extensions.ts +45 -0
  128. package/src/facts.ts +46 -0
  129. package/src/file-facts.ts +1 -1
  130. package/src/findings/boundary.ts +2 -0
  131. package/src/findings/completion.ts +43 -4
  132. package/src/findings/emitter.ts +1 -1
  133. package/src/findings/emitters.ts +14 -10
  134. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  135. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  136. package/src/findings/self-check.ts +41 -22
  137. package/src/findings/visibility-narrowing.ts +1 -1
  138. package/src/findings-pass.ts +5 -12
  139. package/src/framework-lifecycle.ts +43 -15
  140. package/src/fs/manifest.ts +25 -21
  141. package/src/generated-file.ts +240 -36
  142. package/src/graph.ts +27 -11
  143. package/src/import-bindings.ts +98 -29
  144. package/src/index/sites.ts +781 -0
  145. package/src/index.ts +1 -1
  146. package/src/indexed-access.ts +570 -527
  147. package/src/injection-container.ts +1 -1
  148. package/src/interface-satisfaction.ts +1 -1
  149. package/src/inventory/commonjs.ts +102 -17
  150. package/src/inventory/enumerate.ts +2 -1
  151. package/src/javascript-file.ts +29 -12
  152. package/src/keyof-comparisons.ts +131 -116
  153. package/src/load/classify.ts +49 -13
  154. package/src/load/diagnostics.ts +11 -1
  155. package/src/load/discover.ts +27 -11
  156. package/src/load/expected-module.ts +8 -2
  157. package/src/load/plan.ts +11 -2
  158. package/src/load/specifiers.ts +8 -5
  159. package/src/load/synthesize.ts +12 -6
  160. package/src/load/test-globals.ts +1 -1
  161. package/src/load/workspace-manifests.ts +1 -1
  162. package/src/load/workspace-resolution.ts +9 -2
  163. package/src/load/workspace-run.ts +1 -1
  164. package/src/matrix.ts +13 -17
  165. package/src/module-declarations.ts +74 -98
  166. package/src/overrides.ts +162 -134
  167. package/src/paths.ts +65 -1
  168. package/src/platform-calls.ts +119 -0
  169. package/src/platform-symbols.ts +189 -0
  170. package/src/references/read-rule.ts +60 -0
  171. package/src/references/read-rules.ts +55 -0
  172. package/src/references/sink.ts +213 -0
  173. package/src/references/sources.ts +632 -0
  174. package/src/references.ts +174 -1492
  175. package/src/report.ts +2 -2
  176. package/src/resolve.ts +1 -1
  177. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +10 -7
  178. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +5 -8
  179. package/src/{manifest.ts → roots/manifest.ts} +5 -5
  180. package/src/roots/published-reach.ts +481 -0
  181. package/src/{roots.ts → roots/root-set.ts} +203 -630
  182. package/src/roots/unseen-loads.ts +345 -0
  183. package/src/serialization-contract.ts +9 -4
  184. package/src/source-files.ts +5 -10
  185. package/src/stores.ts +13 -2
  186. package/src/suppress.ts +48 -33
  187. package/src/sweep.ts +37 -14
  188. package/src/symbol-keys.ts +102 -102
  189. package/src/tests/file-class.ts +118 -0
  190. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  191. package/src/{test-support.ts → tests/support.ts} +15 -21
  192. package/src/type-parameter-reads.ts +187 -158
  193. package/src/unimplemented-callees.ts +297 -280
  194. package/src/verbs/analyze.ts +19 -5
  195. package/src/verbs/describe.ts +5 -2
  196. package/src/verbs/explain.ts +75 -32
  197. package/src/verbs/print-retained.ts +2 -2
  198. package/dist/src/build-scripts.js +0 -418
  199. package/dist/src/conventions.js +0 -907
  200. package/dist/src/entry-points.js +0 -444
  201. package/dist/src/own-body.js +0 -28
  202. package/dist/src/roots.js +0 -865
  203. package/dist/src/unseen-loads.js +0 -189
  204. package/src/build-scripts.ts +0 -536
  205. package/src/entry-points.ts +0 -584
  206. package/src/own-body.ts +0 -42
  207. package/src/unseen-loads.ts +0 -282
@@ -7,9 +7,7 @@
7
7
  */
8
8
  import { isComputedPropertyName, isForOfStatement, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isPropertyAccessExpression, isSpreadElement, isStringLiteral, } from "@typescript/native/ast";
9
9
  import { parenthesesRemoved } from "./unwrap.js";
10
- import { nodeKey } from "./inventory.js";
11
- import { renderPosition } from "./position.js";
12
- import { isAnswered } from "./query.js";
10
+ import { isAnswered, UNANSWERED } from "./query.js";
13
11
  /** The identifier an element access's argument names its key by, where it is a name. */
14
12
  export function keyNameOf(argument) {
15
13
  const at = parenthesesRemoved(argument);
@@ -127,87 +125,95 @@ function wellKnownName(name) {
127
125
  : undefined;
128
126
  }
129
127
  /**
130
- * The references the iterations of one project's own files make to the iteration members
131
- * of the values they iterate: a `for…of` statement and a spread element read
132
- * `[Symbol.iterator]`, and a `for await…of` statement reads `[Symbol.asyncIterator]` and,
133
- * which it falls back to, `[Symbol.iterator]`. Each is made by the declaration holding it.
128
+ * The reads the iterations of a side's files make of the iteration members of the values they
129
+ * iterate: a `for…of` statement and a spread element read `[Symbol.iterator]`, and a
130
+ * `for await…of` statement reads `[Symbol.asyncIterator]` and, which it falls back to,
131
+ * `[Symbol.iterator]`. Each is made by the declaration holding it. A value whose type or
132
+ * members the checker leaves unanswered holds the members those names could spell.
134
133
  */
135
- export function iterationReferences(project, held, chains, root, testFiles) {
136
- const queries = project.queries;
137
- const ownPaths = project.ownPaths();
138
- const iterated = [];
139
- for (const file of project.ownSourceFiles()) {
140
- const fileId = held.declarations.get(nodeKey(file, file));
141
- if (fileId === undefined) {
142
- continue;
143
- }
144
- const visit = (node, from) => {
145
- const holder = held.declarations.get(nodeKey(file, node)) ?? from;
146
- if (isForOfStatement(node)) {
147
- const protocols = node.awaitModifier === undefined ? ["iterator"] : ["asyncIterator", "iterator"];
148
- iterated.push({ file, from: holder, operand: node.expression, protocols });
149
- }
150
- else if (isSpreadElement(node)) {
151
- iterated.push({ file, from: holder, operand: node.expression, protocols: ["iterator"] });
152
- }
153
- node.forEachChild((child) => {
154
- visit(child, holder);
155
- });
156
- };
157
- file.forEachChild((child) => {
158
- visit(child, fileId);
159
- });
160
- }
161
- if (iterated.length === 0) {
162
- return [];
163
- }
164
- const types = queries.typesAt(iterated.map((one) => one.operand));
165
- const members = new Map();
166
- const membersOf = (type) => {
167
- const known = members.get(type.id);
168
- if (known !== undefined) {
169
- return known;
170
- }
171
- const keyed = [];
172
- const properties = queries.propertiesOf(type);
173
- for (const property of isAnswered(properties) ? properties : []) {
174
- for (const handle of property.declarations) {
175
- const node = ownPaths.has(handle.path) ? project.declarationAt(handle)?.node : undefined;
176
- const name = wellKnownName(node?.name);
177
- if (name !== undefined) {
178
- keyed.push({ name, ids: chains.declarationsOf(property) });
179
- break;
134
+ export const iteration = {
135
+ start() {
136
+ const iterated = [];
137
+ return {
138
+ visit(node, { file, from }) {
139
+ if (isForOfStatement(node)) {
140
+ const protocols = node.awaitModifier === undefined ? ["iterator"] : ["asyncIterator", "iterator"];
141
+ iterated.push({ file, from, operand: node.expression, protocols });
180
142
  }
181
- }
182
- }
183
- members.set(type.id, keyed);
184
- return keyed;
185
- };
186
- const found = [];
187
- iterated.forEach((one, index) => {
188
- const type = types[index];
189
- if (type === undefined || !isAnswered(type)) {
190
- return;
191
- }
192
- const parts = type.isUnionType() || type.isIntersectionType() ? queries.constituents(type) : [type];
193
- const position = renderPosition(one.file, root, one.operand.getStart());
194
- for (const part of isAnswered(parts) ? parts : []) {
195
- for (const member of membersOf(part)) {
196
- if (!one.protocols.includes(member.name)) {
197
- continue;
143
+ else if (isSpreadElement(node)) {
144
+ iterated.push({ file, from, operand: node.expression, protocols: ["iterator"] });
198
145
  }
199
- for (const to of member.ids) {
200
- found.push({
201
- from: one.from,
202
- to,
203
- position,
204
- use: "read",
205
- resolution: "keyed",
206
- test: testFiles.has(position.path),
207
- });
146
+ },
147
+ reads({ project, chains }) {
148
+ const found = [];
149
+ if (iterated.length === 0) {
150
+ return found;
208
151
  }
209
- }
210
- }
211
- });
212
- return found;
213
- }
152
+ const queries = project.queries;
153
+ const ownPaths = project.ownPaths();
154
+ const types = queries.typesAt(iterated.map((one) => one.operand));
155
+ const members = new Map();
156
+ const membersOf = (type) => {
157
+ const known = members.get(type.id);
158
+ if (known !== undefined) {
159
+ return known;
160
+ }
161
+ const properties = queries.propertiesOf(type);
162
+ const keyed = [];
163
+ for (const property of isAnswered(properties) ? properties : []) {
164
+ for (const handle of property.declarations) {
165
+ const node = ownPaths.has(handle.path)
166
+ ? project.declarationAt(handle)?.node
167
+ : undefined;
168
+ const name = wellKnownName(node?.name);
169
+ if (name !== undefined) {
170
+ keyed.push({ name, ids: chains.declarationsOf(property) });
171
+ break;
172
+ }
173
+ }
174
+ }
175
+ const result = isAnswered(properties) ? keyed : UNANSWERED;
176
+ members.set(type.id, result);
177
+ return result;
178
+ };
179
+ // A member a well-known symbol keys is named by the symbol's computed name.
180
+ const spelled = (protocols) => protocols.map((protocol) => `Symbol.${protocol}`);
181
+ iterated.forEach((one, index) => {
182
+ const type = types[index];
183
+ if (type === UNANSWERED) {
184
+ found.push({ kind: "held", from: one.from, names: spelled(one.protocols) });
185
+ return;
186
+ }
187
+ if (type === undefined) {
188
+ return;
189
+ }
190
+ const parts = type.isUnionType() || type.isIntersectionType() ? queries.constituents(type) : [type];
191
+ for (const part of isAnswered(parts) ? parts : [type]) {
192
+ const keyed = membersOf(part);
193
+ if (keyed === UNANSWERED) {
194
+ found.push({ kind: "held", from: one.from, names: spelled(one.protocols) });
195
+ continue;
196
+ }
197
+ for (const member of keyed) {
198
+ if (!one.protocols.includes(member.name)) {
199
+ continue;
200
+ }
201
+ for (const to of member.ids) {
202
+ found.push({
203
+ kind: "named",
204
+ from: one.from,
205
+ to,
206
+ file: one.file,
207
+ at: one.operand,
208
+ use: "read",
209
+ via: "keyed",
210
+ });
211
+ }
212
+ }
213
+ }
214
+ });
215
+ return found;
216
+ },
217
+ };
218
+ },
219
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The file classes the run counts references by. A test file is one a `ts.test_files`
3
+ * pattern or a test runner's row names (the test-runner entry source decides which), a test
4
+ * file no project holds, or a consumer's file a pattern names below the consumer's root.
5
+ * A test-support file is test code as well, decided once the references are known
6
+ * (`support.ts`), so a reference carries only whether a test file made it and the counters
7
+ * read the support class beside it.
8
+ */
9
+ import { renderPosition } from "../position.js";
10
+ import { bytewise } from "../text/compare.js";
11
+ import { globExpression } from "../text/glob.js";
12
+ /** The test files of the target: each file the test-runner source names, by its file name. */
13
+ export function targetTestFile(named) {
14
+ return (file) => named.has(file.fileName);
15
+ }
16
+ /** The test files a pattern names, each by its path below `root`: a consumer's, below the consumer's root. */
17
+ export function patternTestFile(patterns, root) {
18
+ const expressions = patterns.map(globExpression);
19
+ return (file) => {
20
+ const path = renderPosition(file, root, 0).path;
21
+ return expressions.some((expression) => expression.test(path));
22
+ };
23
+ }
24
+ /** The own files of a project that are test files, by their path below `root`, ascending. */
25
+ export function testFilePaths(project, root, testFile) {
26
+ return project
27
+ .ownSourceFiles()
28
+ .filter(testFile)
29
+ .map((file) => renderPosition(file, root, 0).path)
30
+ .sort(bytewise);
31
+ }
32
+ /** Whether test code made one reference: a test file, or a test-support file of the target. */
33
+ export function madeByTestCode(reference, support) {
34
+ return (reference.test || (reference.consumer === undefined && support.has(reference.position.path)));
35
+ }
36
+ /** Whether the mode classes a reference test code made as a test one: any but a loaded consumer's it classes as production. */
37
+ export function classedAsTest(test, consumer, mode) {
38
+ return test && !(consumer && mode.consumerTestsProduction === true);
39
+ }
40
+ /** Whether a production mode counts no reference of this one: test code made it, as the mode classes it. */
41
+ export function withheldBy(mode, reference, support) {
42
+ return (mode.production &&
43
+ classedAsTest(madeByTestCode(reference, support), reference.consumer !== undefined, mode));
44
+ }
45
+ /** Whether a production mode follows no edge like this one: {@link withheldBy} for a graph edge. */
46
+ export function edgeWithheldBy(mode, edge) {
47
+ return mode.production && classedAsTest(edge.test, edge.consumer, mode);
48
+ }
49
+ /** The rule a file the analysis classified as test-support code is counted under. */
50
+ const TEST_SUPPORT_RULE = "test-support";
51
+ /**
52
+ * The name one test-file rule takes. A rule's name is a word list the report's own
53
+ * vocabulary spells, which cannot carry a pattern, so a rule is named by the place of
54
+ * its pattern in the configured list.
55
+ */
56
+ function patternRule(index) {
57
+ return `test-file-pattern-${String(index + 1)}`;
58
+ }
59
+ /**
60
+ * The rules over the test files of several projects at once, ordered by rule: a file two
61
+ * projects hold is one file, so each rule counts the distinct paths it matches.
62
+ */
63
+ export function testFileRulesOf(testFiles, paths, supportFiles = []) {
64
+ const distinct = [...new Set(paths)];
65
+ const support = new Set(supportFiles).size;
66
+ return [
67
+ ...testFiles.map((pattern, index) => {
68
+ const expression = globExpression(pattern);
69
+ return {
70
+ rule: patternRule(index),
71
+ matched: distinct.filter((path) => expression.test(path)).length,
72
+ };
73
+ }),
74
+ ...(support === 0 ? [] : [{ rule: TEST_SUPPORT_RULE, matched: support }]),
75
+ ].sort((a, b) => bytewise(a.rule, b.rule));
76
+ }
@@ -7,15 +7,15 @@
7
7
  * it names is not known.
8
8
  */
9
9
  import { isCallExpression, isExportDeclaration, isImportDeclaration, isNamespaceExport, isNamespaceImport, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
10
- import { RESOLVED_EXTENSIONS } from "./configuration-files.js";
11
- import { emittedFrom } from "./load/emit-map.js";
12
- import { namesImported } from "./module-declarations.js";
13
- import { dirnamePath, joinPath, normalizePath, queriedModulePath } from "./paths.js";
14
- import { renderPosition } from "./position.js";
15
- import { nameComponent } from "./ref.js";
10
+ import { RESOLVED_EXTENSIONS } from "../entries/configuration-files.js";
11
+ import { emittedFrom } from "../load/emit-map.js";
12
+ import { namesImported } from "../import-bindings.js";
13
+ import { dirnamePath, joinPath, normalizePath, queriedModulePath, startsRelativePath, } from "../paths.js";
14
+ import { renderPosition } from "../position.js";
15
+ import { nameComponent } from "../ref.js";
16
16
  /** The paths below the target root one relative specifier may name, written path first. */
17
17
  function candidates(dir, specifier) {
18
- if (!specifier.startsWith("./") && !specifier.startsWith("../")) {
18
+ if (!startsRelativePath(specifier)) {
19
19
  return [];
20
20
  }
21
21
  const path = normalizePath(dir === "" ? specifier : `${dir}/${specifier}`);
@@ -5,8 +5,8 @@
5
5
  * another such file, so it is judged with the tests: what it references is referenced by
6
6
  * test code.
7
7
  */
8
- import { CONVENTION_ROWS, TEST_LIBRARIES, TEST_LIBRARY_SCOPES } from "./convention-rows.js";
9
- import { packageOfSpecifier } from "./dependencies.js";
8
+ import { CONVENTION_ROWS, TEST_LIBRARIES, TEST_LIBRARY_SCOPES, } from "../entries/convention-rows.js";
9
+ import { packageOfSpecifier } from "../dependencies.js";
10
10
  /** The root kind that never takes a file out of test-support code: the skip only withholds. */
11
11
  const SKIP_ROOT = "type-error";
12
12
  /**
@@ -14,7 +14,9 @@ const SKIP_ROOT = "type-error";
14
14
  * convention row reads test files for, and the convention table's test libraries.
15
15
  */
16
16
  const TEST_PACKAGES = new Set([
17
- ...CONVENTION_ROWS.filter((row) => row.testFiles !== undefined).map((row) => row.package),
17
+ ...CONVENTION_ROWS.filter((row) => row.testFiles !== undefined ||
18
+ row.testDirectories !== undefined ||
19
+ row.testRunner !== undefined).map((row) => row.package),
18
20
  ...TEST_LIBRARIES,
19
21
  ]);
20
22
  /**
@@ -161,12 +163,3 @@ export function testSupportFiles(symbols, references, roots, testFiles, evidence
161
163
  const support = withoutOutsideUsers(new Set([...onlyTests].filter((path) => carries(onlyTests, path))));
162
164
  return { support, testUsed: new Set([...onlyTests].filter((path) => !support.has(path))) };
163
165
  }
164
- /** The references with every one a test-support file makes counted as a test reference. */
165
- export function supportReferences(references, support) {
166
- if (support.size === 0) {
167
- return references;
168
- }
169
- return references.map((reference) => !reference.test && support.has(reference.position.path)
170
- ? { ...reference, test: true }
171
- : reference);
172
- }
@@ -5,11 +5,9 @@
5
5
  * call's value is what the read reads, whatever member of the constraint the name resolves
6
6
  * to. A class's members a constraint requires are interface satisfaction's.
7
7
  */
8
- import { isBinaryExpression, isCallExpression, isIdentifier, isNewExpression, isPropertyAccessExpression, isTypeParameterDeclaration, SyntaxKind, } from "@typescript/native/ast";
8
+ import { isBinaryExpression, isCallExpression, isIdentifier, isNewExpression, isPropertyAccessExpression, isSourceFile, isTypeParameterDeclaration, SyntaxKind, } from "@typescript/native/ast";
9
9
  import { SymbolFlags } from "@typescript/native/sync";
10
- import { nodeKey } from "./inventory.js";
11
- import { renderPosition } from "./position.js";
12
- import { isAnswered } from "./query.js";
10
+ import { isAnswered, UNANSWERED } from "./query.js";
13
11
  /** How deep a parameter's type is matched against the type a call passes: `readonly T[]` is one level. */
14
12
  const MATCH_DEPTH = 3;
15
13
  /** The name a call reaches one generic by: its own, or that of what holds it. */
@@ -26,147 +24,188 @@ function assignedTo(access) {
26
24
  parent.operatorToken.kind === SyntaxKind.EqualsToken &&
27
25
  parent.left === access);
28
26
  }
29
- /**
30
- * The references the reads through type-parameter-typed values of one project's own files
31
- * make to the members of the types its calls pass, each made by the declaration holding
32
- * the read.
33
- */
34
- export function typeParameterReads(project, held, chains, root, testFiles) {
35
- const queries = project.queries;
36
- const reads = [];
37
- const calls = [];
38
- for (const file of project.ownSourceFiles()) {
39
- const fileId = held.declarations.get(nodeKey(file, file));
40
- if (fileId === undefined) {
41
- continue;
27
+ /** Whether one node is written inside a declaration that declares type parameters, itself included. */
28
+ function inGeneric(node) {
29
+ for (let at = node; !isSourceFile(at); at = at.parent) {
30
+ if (at.typeParameters !== undefined) {
31
+ return true;
42
32
  }
43
- const visit = (node, from, generic) => {
44
- const holder = held.declarations.get(nodeKey(file, node)) ?? from;
45
- const inside = generic ||
46
- node.typeParameters !== undefined;
47
- if (inside &&
48
- isPropertyAccessExpression(node) &&
49
- isIdentifier(node.name) &&
50
- !assignedTo(node)) {
51
- reads.push({ file, from: holder, access: node });
52
- }
53
- if (isCallExpression(node) || isNewExpression(node)) {
54
- calls.push(node);
55
- }
56
- node.forEachChild((child) => {
57
- visit(child, holder, inside);
58
- });
59
- };
60
- file.forEachChild((child) => {
61
- visit(child, fileId, false);
62
- });
63
33
  }
64
- if (reads.length === 0) {
65
- return [];
66
- }
67
- // The reads whose receiver the checker types as a type parameter, by the generic declaring it.
68
- const receivers = queries.typesAt(reads.map((read) => read.access.expression));
69
- const byGeneric = new Map();
70
- reads.forEach((read, at) => {
71
- const type = receivers[at];
72
- if (type === undefined || !isAnswered(type) || !type.isTypeParameter()) {
73
- return;
74
- }
75
- const symbol = queries.symbolOfType(type);
76
- const declaration = symbol === undefined || !isAnswered(symbol)
77
- ? undefined
78
- : symbol.declarations
79
- .map((handle) => project.declarationAt(handle)?.node)
80
- .find((node) => node !== undefined && isTypeParameterDeclaration(node));
81
- const generic = declaration?.parent;
82
- if (generic !== undefined) {
83
- byGeneric.set(generic, [...(byGeneric.get(generic) ?? []), { read, parameter: type }]);
84
- }
85
- });
86
- if (byGeneric.size === 0) {
87
- return [];
88
- }
89
- /** The types `instantiated` holds where `declared` holds `parameter`, matched part for part. */
90
- const passedFor = (parameter, declared, instantiated, depth) => {
91
- if (declared.id === parameter.id) {
92
- return [instantiated];
93
- }
94
- if (depth >= MATCH_DEPTH) {
95
- return [];
96
- }
97
- const a = queries.typeArguments(declared);
98
- const b = queries.typeArguments(instantiated);
99
- if (!isAnswered(a) || !isAnswered(b) || a.length !== b.length) {
100
- return [];
101
- }
102
- return a.flatMap((one, at) => {
103
- const other = b[at];
104
- return other === undefined ? [] : passedFor(parameter, one, other, depth + 1);
105
- });
106
- };
107
- // Only a call spelling a generic's name is resolved: one request each.
108
- const spelled = new Set([...byGeneric.keys()].flatMap((generic) => nameOf(generic) ?? []));
109
- const found = new Map();
110
- for (const call of calls) {
111
- const callee = call.expression;
112
- const written = isIdentifier(callee)
113
- ? callee.text
114
- : isPropertyAccessExpression(callee) && isIdentifier(callee.name)
115
- ? callee.name.text
116
- : undefined;
117
- if (written === undefined || !spelled.has(written)) {
118
- continue;
119
- }
120
- const resolved = queries.resolvedSignature(call);
121
- if (resolved === undefined || !isAnswered(resolved) || resolved.declaration === undefined) {
122
- continue;
123
- }
124
- const generic = project.declarationAt(resolved.declaration)?.node;
125
- const pending = generic === undefined ? undefined : byGeneric.get(generic);
126
- const declared = pending === undefined ? undefined : queries.genericOf(resolved);
127
- if (pending === undefined || declared === undefined || !isAnswered(declared)) {
128
- continue;
129
- }
130
- const count = (generic?.parameters ?? []).length;
131
- for (const { read, parameter } of pending) {
132
- const passed = [];
133
- for (let at = 0; at < count; at += 1) {
134
- const own = queries.parameterType(declared, at);
135
- const given = queries.typeAtPosition(resolved, at);
136
- if (own !== undefined && isAnswered(own) && isAnswered(given)) {
137
- passed.push(...passedFor(parameter, own, given, 0));
34
+ return false;
35
+ }
36
+ /**
37
+ * The reads the reads through type-parameter-typed values of a side's files make of the
38
+ * members of the types its calls pass, each made by the declaration holding the read. A
39
+ * question the checker leaves unanswered holds every member the read's name spells.
40
+ */
41
+ export const typeParameterReads = {
42
+ start() {
43
+ const reads = [];
44
+ const calls = [];
45
+ return {
46
+ visit(node, { file, from }) {
47
+ if (isPropertyAccessExpression(node) &&
48
+ isIdentifier(node.name) &&
49
+ !assignedTo(node) &&
50
+ inGeneric(node)) {
51
+ reads.push({ file, from, access: node });
138
52
  }
139
- }
140
- const name = read.access.name.text;
141
- const position = renderPosition(read.file, root, read.access.name.getStart());
142
- for (const type of passed) {
143
- const parts = type.isUnionType() ? queries.constituents(type) : [type];
144
- for (const part of isAnswered(parts) ? parts : []) {
145
- const owner = queries.symbolOfType(part);
146
- if (!isAnswered(owner) ||
147
- owner === undefined ||
148
- (owner.flags & SymbolFlags.Class) !== 0) {
53
+ if (isCallExpression(node) || isNewExpression(node)) {
54
+ calls.push(node);
55
+ }
56
+ },
57
+ reads({ project, chains }) {
58
+ const out = [];
59
+ if (reads.length === 0) {
60
+ return out;
61
+ }
62
+ const queries = project.queries;
63
+ const hold = (read) => {
64
+ out.push({ kind: "held", from: read.from, names: [read.access.name.text] });
65
+ };
66
+ // The reads whose receiver the checker types as a type parameter, by the generic declaring it.
67
+ const receivers = queries.typesAt(reads.map((read) => read.access.expression));
68
+ const byGeneric = new Map();
69
+ reads.forEach((read, at) => {
70
+ const type = receivers[at];
71
+ if (type === UNANSWERED) {
72
+ hold(read);
73
+ return;
74
+ }
75
+ if (type?.isTypeParameter() !== true) {
76
+ return;
77
+ }
78
+ const symbol = queries.symbolOfType(type);
79
+ if (symbol === UNANSWERED) {
80
+ hold(read);
81
+ return;
82
+ }
83
+ const declaration = symbol === undefined
84
+ ? undefined
85
+ : symbol.declarations
86
+ .map((handle) => project.declarationAt(handle)?.node)
87
+ .find((node) => node !== undefined && isTypeParameterDeclaration(node));
88
+ const generic = declaration?.parent;
89
+ if (generic !== undefined) {
90
+ byGeneric.set(generic, [...(byGeneric.get(generic) ?? []), { read, parameter: type }]);
91
+ }
92
+ });
93
+ if (byGeneric.size === 0) {
94
+ return out;
95
+ }
96
+ /** The types `instantiated` holds where `declared` holds `parameter`, matched part for part. */
97
+ const passedFor = (read, parameter, declared, instantiated, depth) => {
98
+ if (declared.id === parameter.id) {
99
+ return [instantiated];
100
+ }
101
+ if (depth >= MATCH_DEPTH) {
102
+ return [];
103
+ }
104
+ const a = queries.typeArguments(declared);
105
+ const b = queries.typeArguments(instantiated);
106
+ if (!isAnswered(a) || !isAnswered(b)) {
107
+ hold(read);
108
+ return [];
109
+ }
110
+ if (a.length !== b.length) {
111
+ return [];
112
+ }
113
+ return a.flatMap((one, at) => {
114
+ const other = b[at];
115
+ return other === undefined ? [] : passedFor(read, parameter, one, other, depth + 1);
116
+ });
117
+ };
118
+ // Only a call spelling a generic's name is resolved: one request each.
119
+ const bySpelling = new Map();
120
+ for (const [generic, pending] of byGeneric) {
121
+ const name = nameOf(generic);
122
+ if (name !== undefined) {
123
+ bySpelling.set(name, [
124
+ ...(bySpelling.get(name) ?? []),
125
+ ...pending.map((one) => one.read),
126
+ ]);
127
+ }
128
+ }
129
+ const found = new Map();
130
+ for (const call of calls) {
131
+ const callee = call.expression;
132
+ const written = isIdentifier(callee)
133
+ ? callee.text
134
+ : isPropertyAccessExpression(callee) && isIdentifier(callee.name)
135
+ ? callee.name.text
136
+ : undefined;
137
+ const spelledBy = written === undefined ? undefined : bySpelling.get(written);
138
+ if (spelledBy === undefined) {
139
+ continue;
140
+ }
141
+ const resolved = queries.resolvedSignature(call);
142
+ if (resolved === UNANSWERED) {
143
+ spelledBy.forEach(hold);
149
144
  continue;
150
145
  }
151
- const properties = queries.propertiesOf(part);
152
- for (const property of isAnswered(properties) ? properties : []) {
153
- if (property.name !== name) {
154
- continue;
146
+ if (resolved?.declaration === undefined) {
147
+ continue;
148
+ }
149
+ const generic = project.declarationAt(resolved.declaration)?.node;
150
+ const pending = generic === undefined ? undefined : byGeneric.get(generic);
151
+ const declared = pending === undefined ? undefined : queries.genericOf(resolved);
152
+ if (declared === UNANSWERED) {
153
+ pending?.forEach((one) => {
154
+ hold(one.read);
155
+ });
156
+ continue;
157
+ }
158
+ if (pending === undefined || declared === undefined) {
159
+ continue;
160
+ }
161
+ const count = (generic?.parameters ?? []).length;
162
+ for (const { read, parameter } of pending) {
163
+ const passed = [];
164
+ for (let at = 0; at < count; at += 1) {
165
+ const own = queries.parameterType(declared, at);
166
+ const given = queries.typeAtPosition(resolved, at);
167
+ if (own === UNANSWERED || given === UNANSWERED) {
168
+ hold(read);
169
+ }
170
+ else if (own !== undefined) {
171
+ passed.push(...passedFor(read, parameter, own, given, 0));
172
+ }
155
173
  }
156
- for (const to of chains.declarationsOf(property)) {
157
- found.set(`${read.from}\u0000${to}\u0000${String(read.access.name.pos)}`, {
158
- from: read.from,
159
- to,
160
- position,
161
- use: "read",
162
- resolution: "batch",
163
- test: testFiles.has(position.path),
164
- });
174
+ const name = read.access.name.text;
175
+ for (const type of passed) {
176
+ const parts = type.isUnionType() ? queries.constituents(type) : [type];
177
+ for (const part of isAnswered(parts) ? parts : [type]) {
178
+ const owner = queries.symbolOfType(part);
179
+ const properties = queries.propertiesOf(part);
180
+ if (owner === UNANSWERED || properties === UNANSWERED) {
181
+ hold(read);
182
+ continue;
183
+ }
184
+ if (owner === undefined || (owner.flags & SymbolFlags.Class) !== 0) {
185
+ continue;
186
+ }
187
+ for (const property of properties) {
188
+ if (property.name !== name) {
189
+ continue;
190
+ }
191
+ for (const to of chains.declarationsOf(property)) {
192
+ found.set(`${read.from}\u0000${to}\u0000${String(read.access.name.pos)}`, {
193
+ kind: "named",
194
+ from: read.from,
195
+ to,
196
+ file: read.file,
197
+ at: read.access.name,
198
+ use: "read",
199
+ via: "batch",
200
+ });
201
+ }
202
+ }
203
+ }
165
204
  }
166
205
  }
167
206
  }
168
- }
169
- }
170
- }
171
- return [...found.values()];
172
- }
207
+ return [...out, ...found.values()];
208
+ },
209
+ };
210
+ },
211
+ };