@cplieger/deadset-ts 8.0.0-dev.16 → 8.0.0-dev.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.
Files changed (161) 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 +82 -91
  4. package/dist/src/augmentations.js +24 -25
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +279 -43
  7. package/dist/src/commands/tools.js +5 -9
  8. package/dist/src/component-files.js +3 -2
  9. package/dist/src/conformance-record.js +1 -1
  10. package/dist/src/consumers.js +6 -9
  11. package/dist/src/contextual-uses.js +1 -17
  12. package/dist/src/contract/config.generated.js +4 -1
  13. package/dist/src/contract/contract.generated.js +2 -2
  14. package/dist/src/contract/report.generated.js +6 -2
  15. package/dist/src/dependencies.js +25 -29
  16. package/dist/src/destructuring.js +1 -19
  17. package/dist/src/element-access.js +179 -164
  18. package/dist/src/entries/build-scripts.js +2 -2
  19. package/dist/src/entries/configuration-files.js +3 -7
  20. package/dist/src/entries/conventions.js +2 -2
  21. package/dist/src/entries/html-entries.js +7 -10
  22. package/dist/src/entries/mdx-entries.js +2 -2
  23. package/dist/src/entries/static-module.js +2 -1
  24. package/dist/src/exempt.js +0 -26
  25. package/dist/src/exposure.js +400 -0
  26. package/dist/src/facts.js +4 -4
  27. package/dist/src/findings/completion.js +44 -21
  28. package/dist/src/findings/emitters.js +22 -33
  29. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  30. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  31. package/dist/src/findings/intra-function.js +3 -3
  32. package/dist/src/findings/reads-and-writes.js +71 -15
  33. package/dist/src/findings/unused-declarations.js +2 -22
  34. package/dist/src/findings/visibility-narrowing.js +14 -20
  35. package/dist/src/framework-lifecycle.js +41 -14
  36. package/dist/src/import-bindings.js +73 -28
  37. package/dist/src/index/sites.js +557 -0
  38. package/dist/src/indexed-access.js +523 -478
  39. package/dist/src/interface-satisfaction.js +1 -1
  40. package/dist/src/inventory.js +18 -0
  41. package/dist/src/keyof-comparisons.js +124 -103
  42. package/dist/src/{components.js → liveness/components.js} +1 -1
  43. package/dist/src/{graph.js → liveness/graph.js} +45 -20
  44. package/dist/src/liveness/matrix.js +99 -0
  45. package/dist/src/liveness/sweep.js +558 -0
  46. package/dist/src/load/expected-module.js +2 -2
  47. package/dist/src/load/specifiers.js +2 -4
  48. package/dist/src/load/synthesize.js +5 -0
  49. package/dist/src/load/workspace-resolution.js +2 -2
  50. package/dist/src/module-declarations.js +70 -85
  51. package/dist/src/overrides.js +134 -112
  52. package/dist/src/paths.js +46 -7
  53. package/dist/src/platform-calls.js +2 -2
  54. package/dist/src/platform-symbols.js +13 -19
  55. package/dist/src/query.js +46 -0
  56. package/dist/src/references/read-rule.js +1 -0
  57. package/dist/src/references/read-rules.js +48 -0
  58. package/dist/src/references/sink.js +237 -0
  59. package/dist/src/references/sources.js +450 -0
  60. package/dist/src/references.js +110 -1285
  61. package/dist/src/roots/configured-declarations.js +3 -0
  62. package/dist/src/roots/published-reach.js +4 -4
  63. package/dist/src/roots/root-set.js +1 -1
  64. package/dist/src/roots/unseen-loads.js +38 -10
  65. package/dist/src/serialization-contract.js +8 -3
  66. package/dist/src/stores.js +4 -2
  67. package/dist/src/symbol-keys.js +89 -83
  68. package/dist/src/tests/file-class.js +76 -0
  69. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  70. package/dist/src/{test-support.js → tests/support.js} +2 -11
  71. package/dist/src/type-parameter-reads.js +178 -139
  72. package/dist/src/unimplemented-callees.js +262 -242
  73. package/dist/src/verbs/analyze.js +3 -3
  74. package/dist/src/verbs/explain.js +75 -132
  75. package/dist/src/verbs/print-retained.js +3 -3
  76. package/jsr.json +1 -1
  77. package/package.json +2 -2
  78. package/src/alias-chain.ts +0 -7
  79. package/src/amd.ts +130 -0
  80. package/src/analysis.ts +125 -128
  81. package/src/augmentations.ts +42 -28
  82. package/src/bindings.ts +460 -0
  83. package/src/calls.ts +338 -46
  84. package/src/commands/tools.ts +5 -11
  85. package/src/component-files.ts +4 -2
  86. package/src/conformance-record.ts +1 -1
  87. package/src/consumers.ts +8 -11
  88. package/src/contextual-uses.ts +2 -22
  89. package/src/contract/config.generated.ts +4 -1
  90. package/src/contract/contract.generated.ts +2 -2
  91. package/src/contract/report.generated.ts +6 -2
  92. package/src/dependencies.ts +27 -28
  93. package/src/destructuring.ts +1 -31
  94. package/src/element-access.ts +188 -182
  95. package/src/entries/build-scripts.ts +2 -2
  96. package/src/entries/configuration-files.ts +11 -8
  97. package/src/entries/conventions.ts +2 -1
  98. package/src/entries/html-entries.ts +15 -14
  99. package/src/entries/mdx-entries.ts +2 -2
  100. package/src/entries/static-module.ts +2 -1
  101. package/src/exempt.ts +31 -46
  102. package/src/exposure.ts +492 -0
  103. package/src/facts.ts +5 -3
  104. package/src/finding.ts +1 -1
  105. package/src/findings/boundary.ts +9 -0
  106. package/src/findings/completion.ts +48 -23
  107. package/src/findings/emitter.ts +1 -1
  108. package/src/findings/emitters.ts +23 -41
  109. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  110. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  111. package/src/findings/interfaces.ts +3 -3
  112. package/src/findings/intra-function.ts +3 -3
  113. package/src/findings/reads-and-writes.ts +99 -26
  114. package/src/findings/unused-declarations.ts +4 -28
  115. package/src/findings/visibility-narrowing.ts +25 -26
  116. package/src/framework-lifecycle.ts +42 -14
  117. package/src/import-bindings.ts +98 -29
  118. package/src/index/sites.ts +781 -0
  119. package/src/indexed-access.ts +570 -527
  120. package/src/interface-satisfaction.ts +2 -2
  121. package/src/inventory.ts +19 -0
  122. package/src/keyof-comparisons.ts +131 -116
  123. package/src/{components.ts → liveness/components.ts} +3 -3
  124. package/src/{graph.ts → liveness/graph.ts} +93 -27
  125. package/src/liveness/matrix.ts +162 -0
  126. package/src/liveness/sweep.ts +855 -0
  127. package/src/load/expected-module.ts +8 -2
  128. package/src/load/specifiers.ts +8 -5
  129. package/src/load/synthesize.ts +6 -0
  130. package/src/load/workspace-resolution.ts +9 -2
  131. package/src/module-declarations.ts +74 -98
  132. package/src/overrides.ts +162 -134
  133. package/src/paths.ts +54 -8
  134. package/src/platform-calls.ts +3 -3
  135. package/src/platform-symbols.ts +11 -27
  136. package/src/query.ts +81 -0
  137. package/src/references/read-rule.ts +60 -0
  138. package/src/references/read-rules.ts +55 -0
  139. package/src/references/sink.ts +314 -0
  140. package/src/references/sources.ts +632 -0
  141. package/src/references.ts +175 -1733
  142. package/src/report.ts +1 -1
  143. package/src/roots/configured-declarations.ts +3 -0
  144. package/src/roots/published-reach.ts +9 -4
  145. package/src/roots/root-set.ts +1 -1
  146. package/src/roots/unseen-loads.ts +49 -11
  147. package/src/serialization-contract.ts +8 -3
  148. package/src/stores.ts +15 -3
  149. package/src/symbol-keys.ts +102 -102
  150. package/src/tests/file-class.ts +135 -0
  151. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  152. package/src/{test-support.ts → tests/support.ts} +9 -20
  153. package/src/type-parameter-reads.ts +187 -158
  154. package/src/unimplemented-callees.ts +297 -280
  155. package/src/verbs/analyze.ts +3 -2
  156. package/src/verbs/explain.ts +83 -158
  157. package/src/verbs/print-retained.ts +3 -3
  158. package/dist/src/matrix.js +0 -273
  159. package/dist/src/sweep.js +0 -222
  160. package/src/matrix.ts +0 -463
  161. package/src/sweep.ts +0 -361
@@ -7,11 +7,13 @@
7
7
  * itself is that dependency's need, not the target's.
8
8
  */
9
9
  import { isArrayLiteralExpression, isCallExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isMetaProperty, isNamespaceExport, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isStringLiteral, isNoSubstitutionTemplateLiteral, SyntaxKind, } from "@typescript/native/ast";
10
+ import { amdLoadOf, amdPlatform } from "./amd.js";
10
11
  import { automaticTypes } from "./automatic-types.js";
11
12
  import { declaredIn as declaredBeside, dependenciesNamed, moduleStrings, outsideChain, } from "./entries/configuration-files.js";
13
+ import { declarationBindings } from "./import-bindings.js";
12
14
  import { nodeKey } from "./inventory.js";
13
15
  import { packageScope } from "./load/package-scope.js";
14
- import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, } from "./paths.js";
16
+ import { ancestors, cannotStartPackageName, dirnamePath, joinPath, relativePath, resolvePath, } from "./paths.js";
15
17
  import { UNANSWERED } from "./query.js";
16
18
  import { sourceFilesOf } from "./source-files.js";
17
19
  import { manifestCommands, nodemonTexts } from "./roots/manifest.js";
@@ -153,7 +155,7 @@ function configurationPackages(host, configFile, options, programPackages, parse
153
155
  }
154
156
  seen.add(file);
155
157
  for (const entry of extendsOf(host, file, parse)) {
156
- if (entry.startsWith(".") || isAbsolutePath(entry)) {
158
+ if (cannotStartPackageName(entry)) {
157
159
  const local = resolvePath(dirnamePath(file), entry);
158
160
  pending.push(local.endsWith(".json") ? local : `${local}.json`);
159
161
  }
@@ -178,27 +180,9 @@ function importOf(specifier) {
178
180
  }
179
181
  /** The local names one import declaration binds. */
180
182
  function bindingsOf(node) {
181
- if (node === undefined) {
182
- return [];
183
- }
184
- if (isImportEqualsDeclaration(node)) {
185
- return [node.name];
186
- }
187
- if (!isImportDeclaration(node) || node.importClause === undefined) {
188
- return [];
189
- }
190
- const clause = node.importClause;
191
- const names = clause.name === undefined ? [] : [clause.name];
192
- const bindings = clause.namedBindings;
193
- if (bindings !== undefined) {
194
- if (bindings.kind === SyntaxKind.NamespaceImport) {
195
- names.push(bindings.name);
196
- }
197
- else {
198
- names.push(...bindings.elements.map((element) => element.name));
199
- }
200
- }
201
- return names;
183
+ return node !== undefined && (isImportDeclaration(node) || isImportEqualsDeclaration(node))
184
+ ? declarationBindings(node).map((binding) => binding.name)
185
+ : [];
202
186
  }
203
187
  /**
204
188
  * Every package one project needs, and which declaration each use is written in.
@@ -428,6 +412,7 @@ function fileNeeds(project, held, file, { programPackages, automatic }, use) {
428
412
  }
429
413
  const loads = runtimeLoads(file);
430
414
  const callees = loads.flatMap((load) => load.callees);
415
+ const amdCallees = new Set(loads.flatMap((load) => (load.amd ? load.callees : [])));
431
416
  const asked = [
432
417
  ...specifiers,
433
418
  ...bindings.map(({ node }) => node),
@@ -517,10 +502,11 @@ function fileNeeds(project, held, file, { programPackages, automatic }, use) {
517
502
  unansweredCallees.add(callee);
518
503
  return;
519
504
  }
520
- // The decision reads the callee's symbol and its written name, and nothing else of it.
521
- const key = symbol === undefined ? undefined : `${symbol.id}\u0000${callee.text}`;
505
+ // The decision reads the callee's symbol, its written name and its platform name, and nothing else.
506
+ const platform = amdCallees.has(callee) ? amdPlatform(callee) : "require";
507
+ const key = symbol === undefined ? undefined : `${symbol.id}\u0000${platform}\u0000${callee.text}`;
522
508
  const answer = (key === undefined ? undefined : decided.get(key)) ??
523
- callsPlatform(project, "require", callee, symbol, () => undefined);
509
+ callsPlatform(project, platform, callee, symbol);
524
510
  if (key !== undefined) {
525
511
  decided.set(key, answer);
526
512
  }
@@ -543,12 +529,22 @@ function fileNeeds(project, held, file, { programPackages, automatic }, use) {
543
529
  * argument is literal text naming a package: a `require()` call, which the compiler lists among
544
530
  * a file's imports only in a JavaScript file and only through the bare name; a `.resolve` of
545
531
  * another name, which needs it where that name is a `require`; and `import.meta.resolve` or
546
- * a `.resolve` of the name `require`, whatever it names, a use where it stands.
532
+ * a `.resolve` of the name `require`, whatever it names, a use where it stands; and each id of
533
+ * an AMD dependency list (references-ts.37), which needs it where the call is AMD's.
547
534
  */
548
535
  function runtimeLoads(file) {
549
536
  const found = [];
550
537
  const visit = (node) => {
551
- if (isCallExpression(node)) {
538
+ const amd = amdLoadOf(node);
539
+ if (amd !== undefined) {
540
+ for (const { id } of amd.ids) {
541
+ const named = packageOfSpecifier(id.text);
542
+ if (named !== undefined) {
543
+ found.push({ specifier: id, named, callees: [amd.callee], amd: true });
544
+ }
545
+ }
546
+ }
547
+ else if (isCallExpression(node)) {
552
548
  const [argument] = node.arguments;
553
549
  if (argument !== undefined && isLiteralText(argument)) {
554
550
  const named = packageOfSpecifier(argument.text);
@@ -566,7 +562,7 @@ function runtimeLoads(file) {
566
562
  receiver === undefined ? undefined : writtenName(receiver),
567
563
  ].filter((one) => one !== undefined);
568
564
  if (named !== undefined && (stands || callees.length > 0)) {
569
- found.push({ specifier: argument, named, callees });
565
+ found.push({ specifier: argument, named, callees, amd: false });
570
566
  }
571
567
  }
572
568
  }
@@ -156,8 +156,6 @@ function sourceOf(target) {
156
156
  export function destructuring(project, cap, properties = propertyTables(project.queries)) {
157
157
  const queries = project.queries;
158
158
  const tables = new Map();
159
- let patternBatches = 0;
160
- let patternLookups = 0;
161
159
  const tableOf = (type) => {
162
160
  const known = tables.get(type.id);
163
161
  if (known !== undefined) {
@@ -166,7 +164,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
166
164
  let table = new Map();
167
165
  if ((type.flags & TypeFlags.AnyOrUnknown) === 0) {
168
166
  const read = properties.of(type);
169
- patternLookups += read.asked ? 1 : 0;
170
167
  table = isAnswered(read.properties)
171
168
  ? new Map(read.properties.map((property) => [property.name, property]))
172
169
  : UNANSWERED;
@@ -178,7 +175,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
178
175
  if ((type.flags & TypeFlags.Union) === 0) {
179
176
  return type;
180
177
  }
181
- patternLookups += 1;
182
178
  return queries.nonNullable(type);
183
179
  };
184
180
  /**
@@ -193,11 +189,9 @@ export function destructuring(project, cap, properties = propertyTables(project.
193
189
  if (index === undefined) {
194
190
  return undefined;
195
191
  }
196
- patternLookups += 1;
197
192
  const elements = queries.typeArguments(type);
198
193
  return isAnswered(elements) ? elements[index] : UNANSWERED;
199
194
  }
200
- patternLookups += 1;
201
195
  const array = queries.isArray(type);
202
196
  if (!isAnswered(array)) {
203
197
  return UNANSWERED;
@@ -205,7 +199,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
205
199
  if (!array) {
206
200
  return undefined;
207
201
  }
208
- patternLookups += 1;
209
202
  const elements = queries.typeArguments(type);
210
203
  return isAnswered(elements) ? elements[0] : UNANSWERED;
211
204
  };
@@ -222,14 +215,12 @@ export function destructuring(project, cap, properties = propertyTables(project.
222
215
  if (more.length > 0) {
223
216
  return UNANSWERED;
224
217
  }
225
- patternLookups += 1;
226
218
  return queries.typeOfSymbol(property);
227
219
  };
228
220
  const resultOf = (callee) => {
229
221
  if (callee === undefined || callee === UNANSWERED) {
230
222
  return callee;
231
223
  }
232
- patternLookups += 1;
233
224
  const signatures = queries.signaturesOf(callee, SignatureKind.Call);
234
225
  if (!isAnswered(signatures)) {
235
226
  return UNANSWERED;
@@ -238,7 +229,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
238
229
  if (signature === undefined) {
239
230
  return undefined;
240
231
  }
241
- patternLookups += 1;
242
232
  return queries.returnTypeOf(signature);
243
233
  };
244
234
  const iterations = new Map();
@@ -266,7 +256,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
266
256
  if (result !== undefined && result !== UNANSWERED) {
267
257
  let parts = [result];
268
258
  if (result.isUnionType()) {
269
- patternLookups += 1;
270
259
  parts = queries.constituents(result);
271
260
  }
272
261
  const values = isAnswered(parts)
@@ -296,7 +285,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
296
285
  if (property === undefined) {
297
286
  return undefined;
298
287
  }
299
- patternLookups += 1;
300
288
  return queries.typeOfSymbol(property);
301
289
  }
302
290
  return elementOf(defined, next.index);
@@ -317,7 +305,6 @@ export function destructuring(project, cap, properties = propertyTables(project.
317
305
  }
318
306
  }
319
307
  const asked = [...asking];
320
- patternBatches += Math.ceil(asked.length / cap);
321
308
  const answered = new Map();
322
309
  queries.typesAt(asked, cap).forEach((type, index) => {
323
310
  const node = asked[index];
@@ -347,10 +334,5 @@ export function destructuring(project, cap, properties = propertyTables(project.
347
334
  return isAnswered(table) ? table.get(read.name) : UNANSWERED;
348
335
  });
349
336
  };
350
- return {
351
- resolve,
352
- get cost() {
353
- return { patternBatches, patternLookups };
354
- },
355
- };
337
+ return { resolve };
356
338
  }
@@ -5,8 +5,6 @@
5
5
  * target holds, the members are read at each call, by the type of the argument it passes.
6
6
  */
7
7
  import { isArrayLiteralExpression, isArrowFunction, isBinaryExpression, isCallExpression, isElementAccessExpression, isForInStatement, isForOfStatement, isFunctionDeclaration, isFunctionExpression, isIdentifier, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
8
- import { nodeKey } from "./inventory.js";
9
- import { renderPosition } from "./position.js";
10
8
  import { UNANSWERED } from "./query.js";
11
9
  import { parenthesesRemoved } from "./unwrap.js";
12
10
  /** The assignment operators that read their target before they write it. */
@@ -153,174 +151,191 @@ function assigns(body, name) {
153
151
  return found;
154
152
  }
155
153
  /**
156
- * The references the element accesses of one project's own files make to the members their
157
- * index's literal type names, each made by the declaration holding the access or the call.
154
+ * The reads the element accesses of a side's files make of the members their index's literal
155
+ * type names, each made by the declaration holding the access or the call. A type the checker
156
+ * leaves unanswered holds what its answer could have read.
158
157
  */
159
- export function elementAccessReferences(project, held, chains, root, testFiles) {
160
- const queries = project.queries;
161
- const candidates = [];
162
- const calls = new Map();
163
- const names = [];
164
- for (const file of project.ownSourceFiles()) {
165
- const fileId = held.declarations.get(nodeKey(file, file));
166
- if (fileId === undefined) {
167
- continue;
168
- }
169
- const visit = (node, from) => {
170
- const holder = held.declarations.get(nodeKey(file, node)) ?? from;
171
- if (isElementAccessExpression(node) && !isLiteralIndex(node.argumentExpression)) {
172
- candidates.push({ file, from: holder, access: node, use: useOf(node) });
173
- }
174
- if (isCallExpression(node)) {
175
- calls.set(node, { file, from: holder });
176
- }
177
- if (isIdentifier(node)) {
178
- names.push(node);
179
- }
180
- node.forEachChild((child) => {
181
- visit(child, holder);
182
- });
183
- };
184
- file.forEachChild((child) => {
185
- visit(child, fileId);
186
- });
187
- }
188
- if (candidates.length === 0) {
189
- return [];
190
- }
191
- // A parameter index is decided at the calls only where the function is a callable every
192
- // use of which is a call, so the symbols of the parameters and of every name are read.
193
- const parameterOf = new Map();
194
- const byCallable = new Map();
195
- for (const candidate of candidates) {
196
- const index = parenthesesRemoved(candidate.access.argumentExpression);
197
- const fn = isIdentifier(index) ? enclosingFunction(candidate.access) : undefined;
198
- const callable = fn === undefined ? undefined : callableOf(fn);
199
- if (callable === undefined || !isIdentifier(index)) {
200
- continue;
201
- }
202
- const at = callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === index.text);
203
- if (at < 0 || assigns(callable.body, index.text)) {
204
- continue;
205
- }
206
- parameterOf.set(candidate, { callable, at });
207
- byCallable.set(callable.name, [...(byCallable.get(callable.name) ?? []), candidate]);
208
- }
209
- const callableNames = [...byCallable.keys()];
210
- const spelled = new Set(callableNames.map((name) => name.text));
211
- const sameName = names.filter((name) => spelled.has(name.text));
212
- const symbols = queries.symbolsAt([...callableNames, ...sameName]);
213
- const symbolOf = new Map();
214
- [...callableNames, ...sameName].forEach((node, at) => {
215
- const symbol = symbols[at];
216
- if (symbol !== UNANSWERED && symbol !== undefined) {
217
- symbolOf.set(node, symbol);
218
- }
219
- });
220
- const callsOf = new Map();
221
- for (const name of callableNames) {
222
- const symbol = symbolOf.get(name);
223
- let found = symbol === undefined ? undefined : [];
224
- for (const use of sameName) {
225
- if (found === undefined) {
226
- break;
227
- }
228
- if (use === name || symbolOf.get(use) !== symbol) {
229
- continue;
230
- }
231
- const parent = use.parent;
232
- found =
233
- isCallExpression(parent) && parent.expression === use ? [...found, parent] : undefined;
234
- }
235
- callsOf.set(name, found);
236
- }
237
- const lookups = [];
238
- for (const candidate of candidates) {
239
- const deferred = parameterOf.get(candidate);
240
- const sites = deferred === undefined ? undefined : callsOf.get(deferred.callable.name);
241
- if (deferred === undefined || sites === undefined) {
242
- lookups.push({
243
- file: candidate.file,
244
- from: candidate.from,
245
- at: candidate.access.argumentExpression,
246
- object: candidate.access.expression,
247
- index: candidate.access.argumentExpression,
248
- use: candidate.use,
249
- });
250
- continue;
251
- }
252
- // An object that is a parameter too is read as the argument each call passes there.
253
- const object = parenthesesRemoved(candidate.access.expression);
254
- const objectAt = isIdentifier(object)
255
- ? deferred.callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === object.text)
256
- : -1;
257
- for (const call of sites) {
258
- const parameter = deferred.callable.parameters[deferred.at];
259
- const index = call.arguments[deferred.at] ?? parameter?.initializer;
260
- const site = calls.get(call);
261
- if (index === undefined || site === undefined) {
262
- continue;
263
- }
264
- lookups.push({
265
- file: site.file,
266
- from: site.from,
267
- at: call,
268
- object: (objectAt < 0 ? undefined : call.arguments[objectAt]) ?? candidate.access.expression,
269
- index,
270
- use: candidate.use,
271
- });
272
- }
273
- }
274
- const nodes = lookups.flatMap((one) => [one.index, one.object]);
275
- const types = queries.typesAt(nodes);
276
- const typeOf = (node, at) => {
277
- const type = types[at];
278
- return nodes[at] === node && type !== UNANSWERED ? type : undefined;
279
- };
280
- // A type parameter's body serves every instantiation, so it reads what its constraint admits.
281
- const keysOf = (type) => {
282
- if (!type.isTypeParameter()) {
283
- return type;
284
- }
285
- const constraint = queries.constraintOf(type);
286
- return constraint === UNANSWERED || constraint === undefined ? type : constraint;
287
- };
288
- const partsOf = (type) => {
289
- const parts = queries.constituents(type);
290
- return parts === UNANSWERED ? [type] : parts;
291
- };
292
- const found = [];
293
- lookups.forEach((lookup, at) => {
294
- const indexType = typeOf(lookup.index, 2 * at);
295
- const objectType = typeOf(lookup.object, 2 * at + 1);
296
- const literals = indexType === undefined ? undefined : literalNames(partsOf(keysOf(indexType)));
297
- if (objectType === undefined || literals === undefined) {
298
- return;
299
- }
300
- const position = renderPosition(lookup.file, root, lookup.at.getStart());
301
- for (const part of partsOf(objectType)) {
302
- const properties = queries.propertiesOf(part);
303
- for (const property of properties === UNANSWERED ? [] : properties) {
304
- if (!literals.has(property.escapedName)) {
305
- continue;
158
+ export const elementAccess = {
159
+ start() {
160
+ const candidates = [];
161
+ const calls = new Map();
162
+ const names = [];
163
+ return {
164
+ visit(node, { file, from }) {
165
+ if (isElementAccessExpression(node) && !isLiteralIndex(node.argumentExpression)) {
166
+ candidates.push({ file, from, access: node, use: useOf(node) });
167
+ }
168
+ if (isCallExpression(node)) {
169
+ calls.set(node, { file, from });
170
+ }
171
+ if (isIdentifier(node)) {
172
+ names.push(node);
306
173
  }
307
- for (const to of chains.declarationsOf(property)) {
308
- for (const use of lookup.use === "both" ? ["read", "write"] : [lookup.use]) {
174
+ },
175
+ reads({ project, chains }) {
176
+ const found = [];
177
+ if (candidates.length === 0) {
178
+ return found;
179
+ }
180
+ const queries = project.queries;
181
+ // A parameter index is decided at the calls only where the function is a callable every
182
+ // use of which is a call, so the symbols of the parameters and of every name are read.
183
+ const parameterOf = new Map();
184
+ const byCallable = new Map();
185
+ for (const candidate of candidates) {
186
+ const index = parenthesesRemoved(candidate.access.argumentExpression);
187
+ const fn = isIdentifier(index) ? enclosingFunction(candidate.access) : undefined;
188
+ const callable = fn === undefined ? undefined : callableOf(fn);
189
+ if (callable === undefined || !isIdentifier(index)) {
190
+ continue;
191
+ }
192
+ const at = callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === index.text);
193
+ if (at < 0 || assigns(callable.body, index.text)) {
194
+ continue;
195
+ }
196
+ parameterOf.set(candidate, { callable, at });
197
+ byCallable.set(callable.name, [...(byCallable.get(callable.name) ?? []), candidate]);
198
+ }
199
+ const callableNames = [...byCallable.keys()];
200
+ const spelled = new Set(callableNames.map((name) => name.text));
201
+ const sameName = names.filter((name) => spelled.has(name.text));
202
+ const asked = [...callableNames, ...sameName];
203
+ const symbols = queries.symbolsAt(asked);
204
+ const symbolOf = new Map();
205
+ asked.forEach((node, at) => {
206
+ const symbol = symbols[at];
207
+ if (symbol !== undefined) {
208
+ symbolOf.set(node, symbol);
209
+ }
210
+ });
211
+ const callsOf = new Map();
212
+ for (const name of callableNames) {
213
+ const symbol = symbolOf.get(name);
214
+ let sites = symbol === undefined || symbol === UNANSWERED ? undefined : [];
215
+ for (const use of sameName) {
216
+ if (sites === undefined) {
217
+ break;
218
+ }
219
+ const other = symbolOf.get(use);
220
+ if (use === name || (other !== symbol && other !== UNANSWERED)) {
221
+ continue;
222
+ }
223
+ // A use the checker could not resolve may be the function's, read where it is not called.
224
+ const parent = use.parent;
225
+ sites =
226
+ other !== UNANSWERED && isCallExpression(parent) && parent.expression === use
227
+ ? [...sites, parent]
228
+ : undefined;
229
+ }
230
+ callsOf.set(name, sites);
231
+ }
232
+ const lookups = [];
233
+ for (const candidate of candidates) {
234
+ const deferred = parameterOf.get(candidate);
235
+ const sites = deferred === undefined ? undefined : callsOf.get(deferred.callable.name);
236
+ if (deferred === undefined || sites === undefined) {
237
+ lookups.push({
238
+ file: candidate.file,
239
+ from: candidate.from,
240
+ at: candidate.access.argumentExpression,
241
+ object: candidate.access.expression,
242
+ index: candidate.access.argumentExpression,
243
+ use: candidate.use,
244
+ });
245
+ continue;
246
+ }
247
+ // An object that is a parameter too is read as the argument each call passes there.
248
+ const object = parenthesesRemoved(candidate.access.expression);
249
+ const objectAt = isIdentifier(object)
250
+ ? deferred.callable.parameters.findIndex((parameter) => isIdentifier(parameter.name) && parameter.name.text === object.text)
251
+ : -1;
252
+ for (const call of sites) {
253
+ const parameter = deferred.callable.parameters[deferred.at];
254
+ const index = call.arguments[deferred.at] ?? parameter?.initializer;
255
+ const site = calls.get(call);
256
+ if (index === undefined || site === undefined) {
257
+ continue;
258
+ }
259
+ lookups.push({
260
+ file: site.file,
261
+ from: site.from,
262
+ at: call,
263
+ object: (objectAt < 0 ? undefined : call.arguments[objectAt]) ??
264
+ candidate.access.expression,
265
+ index,
266
+ use: candidate.use,
267
+ });
268
+ }
269
+ }
270
+ const nodes = lookups.flatMap((one) => [one.index, one.object]);
271
+ const types = queries.typesAt(nodes);
272
+ // A type parameter's body serves every instantiation, so it reads what its constraint admits.
273
+ const keysOf = (type) => {
274
+ if (!type.isTypeParameter()) {
275
+ return type;
276
+ }
277
+ const constraint = queries.constraintOf(type);
278
+ return constraint === UNANSWERED || constraint === undefined ? type : constraint;
279
+ };
280
+ const partsOf = (type) => {
281
+ const parts = queries.constituents(type);
282
+ return parts === UNANSWERED ? [type] : parts;
283
+ };
284
+ lookups.forEach((lookup, at) => {
285
+ const indexType = types[2 * at];
286
+ const objectType = types[2 * at + 1];
287
+ const literals = indexType === undefined || indexType === UNANSWERED
288
+ ? undefined
289
+ : literalNames(partsOf(keysOf(indexType)));
290
+ if (indexType === UNANSWERED || objectType === UNANSWERED) {
309
291
  found.push({
292
+ kind: "held",
310
293
  from: lookup.from,
311
- to,
312
- position,
313
- use,
314
- resolution: "keyed",
315
- test: testFiles.has(position.path),
294
+ ...(literals === undefined ? {} : { names: [...literals] }),
295
+ ...(objectType === UNANSWERED || objectType === undefined
296
+ ? literals === undefined
297
+ ? { members: true }
298
+ : {}
299
+ : { types: partsOf(objectType) }),
316
300
  });
301
+ return;
317
302
  }
318
- }
319
- }
320
- }
321
- });
322
- return found;
323
- }
303
+ if (objectType === undefined || literals === undefined) {
304
+ return;
305
+ }
306
+ for (const part of partsOf(objectType)) {
307
+ const properties = queries.propertiesOf(part);
308
+ if (properties === UNANSWERED) {
309
+ found.push({ kind: "held", from: lookup.from, names: [...literals] });
310
+ continue;
311
+ }
312
+ for (const property of properties) {
313
+ if (!literals.has(property.escapedName)) {
314
+ continue;
315
+ }
316
+ for (const to of chains.declarationsOf(property)) {
317
+ for (const use of lookup.use === "both"
318
+ ? ["read", "write"]
319
+ : [lookup.use]) {
320
+ found.push({
321
+ kind: "named",
322
+ from: lookup.from,
323
+ to,
324
+ file: lookup.file,
325
+ at: lookup.at,
326
+ use,
327
+ via: "keyed",
328
+ });
329
+ }
330
+ }
331
+ }
332
+ }
333
+ });
334
+ return found;
335
+ },
336
+ };
337
+ },
338
+ };
324
339
  /** The names a union of literal types spells, or none where a constituent is no string or number literal. */
325
340
  function literalNames(parts) {
326
341
  const names = new Set();
@@ -7,7 +7,7 @@
7
7
  import { isArrowFunction, isCallExpression, isConstructorDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isNewExpression, isSetAccessorDeclaration, isVariableDeclaration, } from "@typescript/native/ast";
8
8
  import { declarationOf, membersNamed, staticModule } from "./static-module.js";
9
9
  import { filesNamedBy } from "./configuration-files.js";
10
- import { ancestors, dirnamePath, relativePath, resolvePath } from "../paths.js";
10
+ import { ancestors, dirnamePath, isExplicitPath, relativePath, resolvePath } from "../paths.js";
11
11
  import { bytewise } from "../text/compare.js";
12
12
  import { unwrapped } from "../unwrap.js";
13
13
  import { holdsManifest, inInstalledPackages, manifestAt } from "../fs/manifest.js";
@@ -43,7 +43,7 @@ function lastMember(object, key) {
43
43
  function pathOf(value, dir, scriptDir) {
44
44
  if (value.kind === "text") {
45
45
  const { text } = value;
46
- const request = !text.startsWith("./") && !text.startsWith("../") && !text.startsWith("/");
46
+ const request = !isExplicitPath(text);
47
47
  return { path: resolvePath(dir, text), text, request };
48
48
  }
49
49
  if (value.kind === "directory") {
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/native/ast";
10
10
  import { relativeSpecifiers } from "./static-module.js";
11
- import { ancestors, dirnamePath, joinPath, relativePath, resolvePath } from "../paths.js";
11
+ import { ancestors, dirnamePath, isAbsolutePath, joinPath, relativePath, resolvePath, startsRelativePath, } from "../paths.js";
12
12
  import { bytewise } from "../text/compare.js";
13
13
  import { isRecord } from "../json-document.js";
14
14
  import { filesIn } from "../fs/tree.js";
@@ -32,8 +32,6 @@ const MODULE_FORMS = [
32
32
  ];
33
33
  /** A JSON configuration file's name. */
34
34
  const DOCUMENT_NAME = /^(?:[\w-]+(?:\.[\w-]+)?\.config\.json|\.[\w-]+rc(?:\.json)?)$/u;
35
- /** The prefixes a string names a file with, relative to the file that writes it. */
36
- const RELATIVE_PREFIXES = ["./", "../"];
37
35
  /** The JavaScript extensions a relative specifier from a configuration file stands for TypeScript under. */
38
36
  const COUNTERPARTS = [
39
37
  [".js", [".ts", ".tsx"]],
@@ -185,9 +183,7 @@ export function configurationFiles(host, targetRoot, files, parse) {
185
183
  }
186
184
  document.manifestDirs.push(next.manifestDir);
187
185
  for (const text of document.strings) {
188
- const path = RELATIVE_PREFIXES.some((prefix) => text.startsWith(prefix))
189
- ? resolvePath(dirnamePath(next.path), text)
190
- : undefined;
186
+ const path = startsRelativePath(text) ? resolvePath(dirnamePath(next.path), text) : undefined;
191
187
  if (path?.endsWith(".json") === true &&
192
188
  isProjectPath(root, path) &&
193
189
  host.kindOf(path) === "file") {
@@ -311,7 +307,7 @@ export function dependenciesNamed(text, declared) {
311
307
  * none.
312
308
  */
313
309
  export function filesNamedBy(files, dir, text) {
314
- if (text === "" || text.startsWith("/") || /[*?[{]/u.test(text)) {
310
+ if (text === "" || isAbsolutePath(text) || /[*?[{]/u.test(text)) {
315
311
  return [];
316
312
  }
317
313
  const path = resolvePath(dir, text);
@@ -11,7 +11,7 @@ import { documentStrings } from "./configuration-files.js";
11
11
  import { readCommands } from "../commands/read.js";
12
12
  import { nodeTestGlobs, } from "./test-runners.js";
13
13
  import { documentModule, membersNamed, staticModule, textsOf, valuesAt, } from "./static-module.js";
14
- import { ancestors, dirnamePath, joinPath, normalizePath, relativePath, resolvePath, } from "../paths.js";
14
+ import { ancestors, cannotStartPackageName, dirnamePath, joinPath, normalizePath, relativePath, resolvePath, } from "../paths.js";
15
15
  import { satisfies } from "../version-range.js";
16
16
  import { bytewise } from "../text/compare.js";
17
17
  import { isRecord } from "../json-document.js";
@@ -425,7 +425,7 @@ function prefixedPackage(value, reading) {
425
425
  const { prefix, marker } = reading;
426
426
  let name;
427
427
  if (marker === undefined) {
428
- if (value.includes(":") || value.startsWith(".") || value.startsWith("/")) {
428
+ if (value.includes(":") || cannotStartPackageName(value)) {
429
429
  return undefined;
430
430
  }
431
431
  const parts = value.split("/");