@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.
Files changed (131) 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 +58 -22
  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/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/report.generated.js +6 -2
  14. package/dist/src/dependencies.js +25 -29
  15. package/dist/src/destructuring.js +1 -19
  16. package/dist/src/element-access.js +179 -164
  17. package/dist/src/entries/build-scripts.js +2 -2
  18. package/dist/src/entries/configuration-files.js +3 -7
  19. package/dist/src/entries/conventions.js +2 -2
  20. package/dist/src/entries/html-entries.js +7 -10
  21. package/dist/src/entries/mdx-entries.js +2 -2
  22. package/dist/src/entries/static-module.js +2 -1
  23. package/dist/src/facts.js +4 -4
  24. package/dist/src/findings/completion.js +40 -4
  25. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  26. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  27. package/dist/src/framework-lifecycle.js +41 -14
  28. package/dist/src/graph.js +12 -4
  29. package/dist/src/import-bindings.js +73 -28
  30. package/dist/src/index/sites.js +557 -0
  31. package/dist/src/indexed-access.js +523 -478
  32. package/dist/src/interface-satisfaction.js +1 -1
  33. package/dist/src/keyof-comparisons.js +124 -103
  34. package/dist/src/load/expected-module.js +2 -2
  35. package/dist/src/load/specifiers.js +2 -4
  36. package/dist/src/load/synthesize.js +5 -0
  37. package/dist/src/load/workspace-resolution.js +2 -2
  38. package/dist/src/matrix.js +6 -4
  39. package/dist/src/module-declarations.js +70 -85
  40. package/dist/src/overrides.js +134 -112
  41. package/dist/src/paths.js +46 -7
  42. package/dist/src/platform-calls.js +2 -2
  43. package/dist/src/platform-symbols.js +13 -19
  44. package/dist/src/references/read-rule.js +1 -0
  45. package/dist/src/references/read-rules.js +48 -0
  46. package/dist/src/references/sink.js +151 -0
  47. package/dist/src/references/sources.js +450 -0
  48. package/dist/src/references.js +110 -1285
  49. package/dist/src/roots/configured-declarations.js +3 -0
  50. package/dist/src/roots/root-set.js +1 -1
  51. package/dist/src/roots/unseen-loads.js +38 -10
  52. package/dist/src/serialization-contract.js +8 -3
  53. package/dist/src/stores.js +4 -2
  54. package/dist/src/sweep.js +25 -13
  55. package/dist/src/symbol-keys.js +89 -83
  56. package/dist/src/tests/file-class.js +76 -0
  57. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  58. package/dist/src/{test-support.js → tests/support.js} +2 -11
  59. package/dist/src/type-parameter-reads.js +178 -139
  60. package/dist/src/unimplemented-callees.js +262 -242
  61. package/dist/src/verbs/analyze.js +3 -3
  62. package/dist/src/verbs/explain.js +50 -28
  63. package/dist/src/verbs/print-retained.js +2 -2
  64. package/jsr.json +1 -1
  65. package/package.json +2 -2
  66. package/src/alias-chain.ts +0 -7
  67. package/src/amd.ts +130 -0
  68. package/src/analysis.ts +86 -23
  69. package/src/augmentations.ts +41 -27
  70. package/src/bindings.ts +460 -0
  71. package/src/calls.ts +25 -7
  72. package/src/commands/tools.ts +5 -11
  73. package/src/component-files.ts +4 -2
  74. package/src/conformance-record.ts +1 -1
  75. package/src/consumers.ts +8 -11
  76. package/src/contextual-uses.ts +2 -22
  77. package/src/contract/config.generated.ts +4 -1
  78. package/src/contract/report.generated.ts +6 -2
  79. package/src/dependencies.ts +27 -28
  80. package/src/destructuring.ts +1 -31
  81. package/src/element-access.ts +188 -182
  82. package/src/entries/build-scripts.ts +2 -2
  83. package/src/entries/configuration-files.ts +11 -8
  84. package/src/entries/conventions.ts +2 -1
  85. package/src/entries/html-entries.ts +15 -14
  86. package/src/entries/mdx-entries.ts +2 -2
  87. package/src/entries/static-module.ts +2 -1
  88. package/src/facts.ts +5 -3
  89. package/src/findings/boundary.ts +2 -0
  90. package/src/findings/completion.ts +43 -4
  91. package/src/findings/emitter.ts +1 -1
  92. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  93. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  94. package/src/framework-lifecycle.ts +42 -14
  95. package/src/graph.ts +24 -6
  96. package/src/import-bindings.ts +98 -29
  97. package/src/index/sites.ts +781 -0
  98. package/src/indexed-access.ts +570 -527
  99. package/src/interface-satisfaction.ts +1 -1
  100. package/src/keyof-comparisons.ts +131 -116
  101. package/src/load/expected-module.ts +8 -2
  102. package/src/load/specifiers.ts +8 -5
  103. package/src/load/synthesize.ts +6 -0
  104. package/src/load/workspace-resolution.ts +9 -2
  105. package/src/matrix.ts +6 -3
  106. package/src/module-declarations.ts +74 -98
  107. package/src/overrides.ts +162 -134
  108. package/src/paths.ts +54 -8
  109. package/src/platform-calls.ts +3 -3
  110. package/src/platform-symbols.ts +11 -27
  111. package/src/references/read-rule.ts +60 -0
  112. package/src/references/read-rules.ts +55 -0
  113. package/src/references/sink.ts +213 -0
  114. package/src/references/sources.ts +632 -0
  115. package/src/references.ts +168 -1733
  116. package/src/report.ts +1 -1
  117. package/src/roots/configured-declarations.ts +3 -0
  118. package/src/roots/root-set.ts +1 -1
  119. package/src/roots/unseen-loads.ts +49 -11
  120. package/src/serialization-contract.ts +8 -3
  121. package/src/stores.ts +13 -2
  122. package/src/sweep.ts +37 -14
  123. package/src/symbol-keys.ts +102 -102
  124. package/src/tests/file-class.ts +118 -0
  125. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  126. package/src/{test-support.ts → tests/support.ts} +9 -20
  127. package/src/type-parameter-reads.ts +187 -158
  128. package/src/unimplemented-callees.ts +297 -280
  129. package/src/verbs/analyze.ts +3 -2
  130. package/src/verbs/explain.ts +74 -31
  131. package/src/verbs/print-retained.ts +2 -2
@@ -4,10 +4,9 @@
4
4
  * that parameter, and a call of a method of a generic receiver fixes the receiver's by its
5
5
  * type arguments. The type query of a type-query alias is no reference.
6
6
  */
7
- import { isArrowFunction, isBindingElement, isCallExpression, isClassDeclaration, isClassExpression, isConstructorDeclaration, isConstructorTypeNode, isFunctionDeclaration, isFunctionExpression, isFunctionTypeNode, isIdentifier, isIndexedAccessTypeNode, isInterfaceDeclaration, isLiteralTypeNode, isMappedTypeNode, isMethodDeclaration, isMethodSignatureDeclaration, isNewExpression, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isParenthesizedTypeNode, isPropertyAccessExpression, isPropertyAssignment, isPropertySignatureDeclaration, isStringLiteral, isTypeAliasDeclaration, isTypeOperatorNode, isTypeReferenceNode, isUnionTypeNode, isVariableDeclaration, SyntaxKind, } from "@typescript/native/ast";
7
+ import { isArrowFunction, isBindingElement, isCallExpression, isClassDeclaration, isClassExpression, isConstructorDeclaration, isConstructorTypeNode, isFunctionDeclaration, isFunctionExpression, isFunctionTypeNode, isIdentifier, isIndexedAccessTypeNode, isInterfaceDeclaration, isLiteralTypeNode, isMappedTypeNode, isMethodDeclaration, isMethodSignatureDeclaration, isNewExpression, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isParenthesizedTypeNode, isPropertyAccessExpression, isPropertyAssignment, isPropertySignatureDeclaration, isSourceFile, isStringLiteral, isTypeAliasDeclaration, isTypeOperatorNode, isTypeReferenceNode, isUnionTypeNode, isVariableDeclaration, SyntaxKind, } from "@typescript/native/ast";
8
8
  import { SignatureKind, SymbolFlags, } from "@typescript/native/sync";
9
9
  import { nodeKey } from "./inventory.js";
10
- import { renderPosition } from "./position.js";
11
10
  import { UNANSWERED } from "./query.js";
12
11
  import { typeQueryAliases } from "./type-query-alias.js";
13
12
  function isGeneric(node) {
@@ -242,497 +241,543 @@ function carriedIn(generic) {
242
241
  visit(generic);
243
242
  return found;
244
243
  }
244
+ /** The generics enclosing one node, itself included, outermost first. */
245
+ function enclosingGenerics(node) {
246
+ const found = [];
247
+ for (let at = node; !isSourceFile(at); at = at.parent) {
248
+ if (isGeneric(at)) {
249
+ found.push(at);
250
+ }
251
+ }
252
+ return found.reverse();
253
+ }
245
254
  /**
246
- * The references the indexed-access types of one project's own files make to the members
247
- * they name, each made by the declaration that holds the type, the reference or the call,
248
- * or by the file.
255
+ * The reads the indexed-access types of a side's files make of the members they name, each
256
+ * made by the declaration that holds the type, the reference or the call, or by the file. A
257
+ * question the checker leaves unanswered holds what its answer could have read.
249
258
  */
250
- export function indexedAccessReferences(project, held, chains, root, testFiles) {
251
- const queries = project.queries;
252
- const accesses = [];
253
- const carried = new Map();
254
- const names = new Set();
255
- const sites = [];
256
- /** Accesses indexed by a type parameter whose constraint is no `keyof`: it requires each key it names. */
257
- const constrained = [];
258
- /** Calls passing an argument, which a generic declared outside the own files may fix. */
259
- const argumentCalls = [];
260
- const carry = (generic, one) => {
261
- const known = carried.get(generic) ?? [];
262
- const same = known.some((other) => JSON.stringify(sideKey(other)) === JSON.stringify(sideKey(one)));
263
- if (same) {
264
- return false;
265
- }
266
- carried.set(generic, [...known, one]);
267
- const name = calledName(generic);
268
- if (name !== undefined) {
269
- names.add(name);
270
- }
271
- return true;
272
- };
273
- for (const file of project.ownSourceFiles()) {
274
- const fileId = held.declarations.get(nodeKey(file, file));
275
- if (fileId === undefined) {
276
- continue;
277
- }
278
- const aliasTypes = new Set(typeQueryAliases(file).map((alias) => alias.type));
279
- const visit = (node, from, enclosing) => {
280
- const own = held.declarations.get(nodeKey(file, node));
281
- const holder = own ?? from;
282
- const inside = isGeneric(node) ? [...enclosing, node] : enclosing;
283
- if (isGeneric(node) || takesKeyof(node)) {
284
- carriedIn(node).forEach((one) => carry(node, one));
259
+ export const indexedAccess = {
260
+ start() {
261
+ const accesses = [];
262
+ const carried = new Map();
263
+ const names = new Set();
264
+ const sites = [];
265
+ /** Accesses indexed by a type parameter whose constraint is no `keyof`: it requires each key it names. */
266
+ const constrained = [];
267
+ /** Calls passing an argument, which a generic declared outside the own files may fix. */
268
+ const argumentCalls = [];
269
+ const carry = (generic, one) => {
270
+ const known = carried.get(generic) ?? [];
271
+ const same = known.some((other) => JSON.stringify(sideKey(other)) === JSON.stringify(sideKey(one)));
272
+ if (same) {
273
+ return false;
285
274
  }
286
- if (isIndexedAccessTypeNode(node) && !aliasTypes.has(node)) {
287
- const literal = literalNames(node.indexType);
288
- const parameter = literal === undefined ? namedParameter(node.indexType, inside) : undefined;
289
- const constraint = parameter === undefined
290
- ? undefined
291
- : parameter.generic.typeParameters?.[parameter.index]?.constraint;
292
- if (literal !== undefined) {
293
- accesses.push({ file, from: holder, at: node, object: node.objectType, index: literal });
294
- }
295
- else if (constraint !== undefined &&
296
- keyofOperands(constraint).length === 0 &&
297
- namedParameter(node.objectType, inside) === undefined) {
298
- constrained.push({ file, from: holder, at: node, object: node.objectType, constraint });
299
- }
275
+ carried.set(generic, [...known, one]);
276
+ const name = calledName(generic);
277
+ if (name !== undefined) {
278
+ names.add(name);
300
279
  }
301
- if (isTypeReferenceNode(node) &&
302
- node.typeArguments !== undefined &&
303
- isIdentifier(node.typeName)) {
304
- sites.push({ file, from: holder, node, name: node.typeName.text, enclosing: inside });
280
+ return true;
281
+ };
282
+ const aliasTypes = new Map();
283
+ const aliasTypesOf = (file) => {
284
+ let known = aliasTypes.get(file);
285
+ if (known === undefined) {
286
+ known = new Set(typeQueryAliases(file).map((alias) => alias.type));
287
+ aliasTypes.set(file, known);
305
288
  }
306
- if (isCallExpression(node) || isNewExpression(node)) {
307
- const callee = node.expression;
308
- const name = isIdentifier(callee)
309
- ? callee.text
310
- : isPropertyAccessExpression(callee) && isIdentifier(callee.name)
311
- ? callee.name.text
312
- : undefined;
313
- if (name !== undefined) {
314
- const site = { file, from: holder, node, name, enclosing: inside };
315
- sites.push(site);
316
- if (argumentsOf(node).length > 0) {
317
- argumentCalls.push(site);
289
+ return known;
290
+ };
291
+ return {
292
+ visit(node, { file, from }) {
293
+ const holder = from;
294
+ const inside = enclosingGenerics(node);
295
+ if (isGeneric(node) || takesKeyof(node)) {
296
+ carriedIn(node).forEach((one) => carry(node, one));
297
+ }
298
+ if (isIndexedAccessTypeNode(node) && !aliasTypesOf(file).has(node)) {
299
+ const literal = literalNames(node.indexType);
300
+ const parameter = literal === undefined ? namedParameter(node.indexType, inside) : undefined;
301
+ const constraint = parameter === undefined
302
+ ? undefined
303
+ : parameter.generic.typeParameters?.[parameter.index]?.constraint;
304
+ if (literal !== undefined) {
305
+ accesses.push({
306
+ file,
307
+ from: holder,
308
+ at: node,
309
+ object: node.objectType,
310
+ index: literal,
311
+ });
312
+ }
313
+ else if (constraint !== undefined &&
314
+ keyofOperands(constraint).length === 0 &&
315
+ namedParameter(node.objectType, inside) === undefined) {
316
+ constrained.push({ file, from: holder, at: node, object: node.objectType, constraint });
318
317
  }
319
318
  }
320
- }
321
- node.forEachChild((child) => {
322
- visit(child, holder, inside);
323
- });
324
- };
325
- file.forEachChild((child) => {
326
- visit(child, fileId, []);
327
- });
328
- }
329
- /** The literal keys one constraint names, through the type aliases it references. */
330
- const requiredNames = (node, seen) => {
331
- const type = unwrappedType(node);
332
- if (isUnionTypeNode(type)) {
333
- return type.types.flatMap((one) => requiredNames(one, seen));
334
- }
335
- if (!isTypeReferenceNode(type) || type.typeArguments !== undefined) {
336
- return literalNames(type) ?? [];
337
- }
338
- const [symbol] = queries.symbolsAt([type.typeName]);
339
- if (symbol === UNANSWERED || symbol === undefined) {
340
- return [];
341
- }
342
- return symbol.declarations.flatMap((handle) => {
343
- const declared = project.declarationAt(handle)?.node;
344
- return declared !== undefined &&
345
- isTypeAliasDeclaration(declared) &&
346
- declared.typeParameters === undefined &&
347
- !seen.has(declared)
348
- ? requiredNames(declared.type, new Set([...seen, declared]))
349
- : [];
350
- });
351
- };
352
- for (const { constraint, ...access } of constrained) {
353
- const index = requiredNames(constraint, new Set());
354
- if (index.length > 0) {
355
- accesses.push({ ...access, index });
356
- }
357
- }
358
- function partsOf(type) {
359
- const parts = queries.constituents(type);
360
- return parts === UNANSWERED ? [type] : parts;
361
- }
362
- /** The generics one symbol declares, through the alias it may be. */
363
- const genericsOf = (symbol) => {
364
- const target = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : queries.aliased(symbol);
365
- if (target === UNANSWERED) {
366
- return [];
367
- }
368
- return target.declarations.flatMap((handle) => {
369
- const node = project.declarationAt(handle)?.node;
370
- const generic = node === undefined ? undefined : genericOf(node);
371
- return generic === undefined ? [] : [generic];
372
- });
373
- };
374
- /**
375
- * The type a call's callee is read off: the receiver of a property access, the value an
376
- * object binding pattern destructures the callee from, or the value whose member a
377
- * variable holding the callee is initialized with.
378
- */
379
- const receiverTypeOf = (callee) => {
380
- let operand;
381
- if (isPropertyAccessExpression(callee)) {
382
- const type = queries.typeAt(callee.expression);
383
- operand = type === UNANSWERED ? undefined : type;
384
- const args = operand === undefined ? [] : queries.typeArguments(operand);
385
- if (operand !== undefined && args !== UNANSWERED && args.length > 0) {
386
- return operand;
387
- }
388
- }
389
- const name = isPropertyAccessExpression(callee) ? callee.name : callee;
390
- const [named] = queries.symbolsAt([name]);
391
- const symbol = named === UNANSWERED || named === undefined || (named.flags & SymbolFlags.Alias) === 0
392
- ? named
393
- : queries.aliased(named);
394
- if (symbol === UNANSWERED || symbol === undefined) {
395
- return operand;
396
- }
397
- for (const handle of symbol.declarations) {
398
- const node = project.declarationAt(handle)?.node;
399
- const holder = node !== undefined &&
400
- isBindingElement(node) &&
401
- isObjectBindingPattern(node.parent) &&
402
- isVariableDeclaration(node.parent.parent)
403
- ? node.parent.parent.initializer
404
- : node !== undefined &&
405
- isVariableDeclaration(node) &&
406
- node.initializer !== undefined &&
407
- isPropertyAccessExpression(node.initializer)
408
- ? node.initializer.expression
409
- : undefined;
410
- const type = holder === undefined ? undefined : queries.typeAt(holder);
411
- if (type !== UNANSWERED && type !== undefined) {
412
- return type;
413
- }
414
- }
415
- return operand;
416
- };
417
- /**
418
- * The instantiation of `owner` a receiver's type is, or the first one among the base
419
- * types it extends, breadth first: a subclass fixes its base's type parameters, and
420
- * `this` stands for its class.
421
- */
422
- const instantiationOf = (type, owner) => {
423
- const seen = new Set();
424
- for (let queue = [type]; queue.length > 0;) {
425
- const next = [];
426
- for (const at of queue) {
427
- if (seen.has(at.id)) {
428
- continue;
319
+ if (isTypeReferenceNode(node) &&
320
+ node.typeArguments !== undefined &&
321
+ isIdentifier(node.typeName)) {
322
+ sites.push({ file, from: holder, node, name: node.typeName.text, enclosing: inside });
429
323
  }
430
- seen.add(at.id);
431
- const target = at.isTypeReference() ? queries.targetOf(at) : at;
432
- const symbol = target === UNANSWERED ? undefined : queries.symbolOfType(target);
433
- const declares = symbol !== undefined &&
434
- symbol !== UNANSWERED &&
435
- symbol.declarations.some((handle) => project.declarationAt(handle)?.node === owner);
436
- if (declares) {
437
- return at;
324
+ if (isCallExpression(node) || isNewExpression(node)) {
325
+ const callee = node.expression;
326
+ const name = isIdentifier(callee)
327
+ ? callee.text
328
+ : isPropertyAccessExpression(callee) && isIdentifier(callee.name)
329
+ ? callee.name.text
330
+ : undefined;
331
+ if (name !== undefined) {
332
+ const site = { file, from: holder, node, name, enclosing: inside };
333
+ sites.push(site);
334
+ if (argumentsOf(node).length > 0) {
335
+ argumentCalls.push(site);
336
+ }
337
+ }
438
338
  }
439
- const constraint = at.isTypeParameter() ? queries.constraintOf(at) : undefined;
440
- const bases = at.isTypeParameter() ? [] : queries.baseTypes(at);
441
- next.push(...(bases === UNANSWERED ? [] : bases));
442
- if (constraint !== undefined && constraint !== UNANSWERED) {
443
- next.push(constraint);
339
+ },
340
+ reads({ project, chains }) {
341
+ const queries = project.queries;
342
+ const found = [];
343
+ /**
344
+ * A site whose generic the checker cannot name may fix any generic of its name, whose
345
+ * accesses may then read any member.
346
+ */
347
+ const holdSite = (site) => {
348
+ found.push({ kind: "held", from: site.from, members: true });
349
+ };
350
+ /** The literal keys one constraint names, through the type aliases it references. */
351
+ const requiredNames = (node, seen) => {
352
+ const type = unwrappedType(node);
353
+ if (isUnionTypeNode(type)) {
354
+ return type.types.flatMap((one) => requiredNames(one, seen));
355
+ }
356
+ if (!isTypeReferenceNode(type) || type.typeArguments !== undefined) {
357
+ return literalNames(type) ?? [];
358
+ }
359
+ const [symbol] = queries.symbolsAt([type.typeName]);
360
+ if (symbol === UNANSWERED || symbol === undefined) {
361
+ return [];
362
+ }
363
+ return symbol.declarations.flatMap((handle) => {
364
+ const declared = project.declarationAt(handle)?.node;
365
+ return declared !== undefined &&
366
+ isTypeAliasDeclaration(declared) &&
367
+ declared.typeParameters === undefined &&
368
+ !seen.has(declared)
369
+ ? requiredNames(declared.type, new Set([...seen, declared]))
370
+ : [];
371
+ });
372
+ };
373
+ for (const { constraint, ...access } of constrained) {
374
+ const index = requiredNames(constraint, new Set());
375
+ if (index.length > 0) {
376
+ accesses.push({ ...access, index });
377
+ }
444
378
  }
445
- }
446
- queue = next;
447
- }
448
- return type;
449
- };
450
- /** What one site fixes for one side of a carried access. */
451
- const fixedAt = (site, side, generic) => {
452
- if (side.kind === "written") {
453
- return { kind: "node", node: side.node };
454
- }
455
- const { node } = site;
456
- if (side.kind === "receiver") {
457
- const callee = isCallExpression(node) ? node.expression : undefined;
458
- const owner = receiverOf(generic);
459
- const typed = callee === undefined ? undefined : receiverTypeOf(callee);
460
- const receiver = typed === undefined || owner === undefined ? typed : instantiationOf(typed, owner);
461
- if (receiver === undefined) {
462
- return undefined;
463
- }
464
- const args = queries.typeArguments(receiver);
465
- const type = args === UNANSWERED ? undefined : args[side.index];
466
- return type === undefined ? undefined : { kind: "type", type };
467
- }
468
- if (side.kind === "argument") {
469
- const argument = isTypeReferenceNode(node) ? undefined : node.arguments?.[side.index];
470
- const type = argument === undefined ? undefined : queries.typeAt(argument);
471
- return type === UNANSWERED || type === undefined ? undefined : { kind: "type", type };
472
- }
473
- const written = node.typeArguments?.[side.index];
474
- if (written !== undefined) {
475
- const passed = namedParameter(written, site.enclosing);
476
- const mapped = passed === undefined ? mappedConstraint(written) : undefined;
477
- return passed !== undefined
478
- ? { kind: "passed", generic: passed.generic, index: passed.index }
479
- : { kind: "node", node: mapped ?? written };
480
- }
481
- if (isTypeReferenceNode(node)) {
482
- return undefined;
483
- }
484
- const at = parametersOf(generic).findIndex((parameter) => {
485
- const type = parameter.type === undefined ? undefined : unwrappedType(parameter.type);
486
- return (type !== undefined &&
487
- isTypeReferenceNode(type) &&
488
- isIdentifier(type.typeName) &&
489
- parameterAt(generic, type.typeName.text) === side.index);
490
- });
491
- if (at < 0) {
492
- return undefined;
493
- }
494
- const resolved = queries.resolvedSignature(node);
495
- if (resolved === UNANSWERED || resolved === undefined) {
496
- return undefined;
497
- }
498
- const type = queries.typeAtPosition(resolved, at);
499
- return type === UNANSWERED ? undefined : { kind: "type", type };
500
- };
501
- /** Reads one carried access at one site: an access where both sides are fixed, a carry where one is passed on. */
502
- const readAt = (site, generic, one) => {
503
- const object = fixedAt(site, one.object, generic);
504
- const index = object === undefined ? undefined : fixedAt(site, one.index, generic);
505
- if (object === undefined || index === undefined) {
506
- return false;
507
- }
508
- const passed = [object, index].find((fixed) => fixed.kind === "passed");
509
- if (passed !== undefined) {
510
- const side = (fixed) => fixed.kind === "passed"
511
- ? fixed.generic === passed.generic
512
- ? { kind: "parameter", index: fixed.index }
513
- : undefined
514
- : fixed.kind === "node"
515
- ? { kind: "written", node: fixed.node }
516
- : undefined;
517
- const objectSide = side(object);
518
- const indexSide = side(index);
519
- return objectSide !== undefined && indexSide !== undefined
520
- ? carry(passed.generic, { object: objectSide, index: indexSide })
521
- : false;
522
- }
523
- const objectAt = concrete(object);
524
- const indexAt = concrete(index);
525
- if (objectAt === undefined || indexAt === undefined) {
526
- return false;
527
- }
528
- accesses.push({
529
- file: site.file,
530
- from: site.from,
531
- at: site.node,
532
- object: objectAt,
533
- index: "kind" in indexAt ? (literalNames(indexAt) ?? indexAt) : indexAt,
534
- });
535
- return false;
536
- };
537
- // A site passing a type parameter on carries the access to its own generic, so the
538
- // sites are read again until no generic gains an access.
539
- const resolvedGenerics = new Map();
540
- const read = new Set();
541
- for (let changed = true; changed;) {
542
- changed = false;
543
- const pending = sites.filter((site) => names.has(site.name) && !resolvedGenerics.has(site));
544
- const typeSites = pending.filter((site) => isTypeReferenceNode(site.node));
545
- const symbols = typeSites.length === 0
546
- ? []
547
- : queries.symbolsAt(typeSites.map((site) => site.node.typeName));
548
- typeSites.forEach((site, at) => {
549
- const symbol = symbols[at];
550
- resolvedGenerics.set(site, symbol === UNANSWERED || symbol === undefined ? [] : genericsOf(symbol));
551
- });
552
- for (const site of pending.filter((one) => !isTypeReferenceNode(one.node))) {
553
- const resolved = queries.resolvedSignature(site.node);
554
- const declaration = resolved === UNANSWERED || resolved?.declaration === undefined
555
- ? undefined
556
- : project.declarationAt(resolved.declaration)?.node;
557
- const generic = declaration === undefined ? undefined : genericOf(declaration);
558
- resolvedGenerics.set(site, generic === undefined ? [] : [generic]);
559
- }
560
- sites.forEach((site, siteAt) => {
561
- for (const generic of resolvedGenerics.get(site) ?? []) {
562
- (carried.get(generic) ?? []).forEach((one, at) => {
563
- const key = `${String(siteAt)}:${String(at)}:${nodeKey(generic.getSourceFile(), generic)}`;
564
- if (read.has(key)) {
565
- return;
566
- }
567
- read.add(key);
568
- changed = readAt(site, generic, one) || changed;
569
- });
570
- }
571
- });
572
- }
573
- // A generic declared outside the project's own files carries what its own text indexes
574
- // by its type parameters and its receiver's. Only a call passing an argument the compiler
575
- // types as a literal can fix an index, so only such a call's signature is resolved.
576
- const outside = argumentCalls.filter((site) => !resolvedGenerics.has(site));
577
- const argumentNodes = argumentCalls.flatMap((site) => argumentsOf(site.node));
578
- const argumentTypes = queries.typesAt(argumentNodes);
579
- const literalArgument = new Set();
580
- argumentTypes.forEach((type, at) => {
581
- const node = argumentNodes[at];
582
- if (node !== undefined &&
583
- type !== UNANSWERED &&
584
- type !== undefined &&
585
- literalTypeNames(type, partsOf).length > 0) {
586
- literalArgument.add(node);
587
- }
588
- });
589
- const own = project.ownPaths();
590
- for (const site of outside) {
591
- if (!argumentsOf(site.node).some((one) => literalArgument.has(one))) {
592
- continue;
593
- }
594
- const resolved = queries.resolvedSignature(site.node);
595
- const handle = resolved === UNANSWERED || resolved === undefined ? undefined : resolved.declaration;
596
- if (handle === undefined || own.has(handle.path)) {
597
- continue;
598
- }
599
- const node = project.declarationAt(handle)?.node;
600
- const generic = node === undefined ? undefined : genericOf(node);
601
- if (generic === undefined) {
602
- continue;
603
- }
604
- for (const one of carriedIn(generic)) {
605
- readAt(site, generic, one);
606
- }
607
- }
608
- for (const site of argumentCalls) {
609
- if (argumentsOf(site.node).some((one) => literalArgument.has(one))) {
610
- accesses.push(...instantiatedAccesses(site));
611
- }
612
- }
613
- /**
614
- * The indexed accesses the generic signature a call instantiates holds, read through the
615
- * checker: the signature its receiver's instantiation gives, so an object type its
616
- * receiver fixes is concrete whatever declaration spells the receiver's type, a type
617
- * literal a generic function returns among them. An index naming a type parameter is the
618
- * literal type the call passes at each parameter of that type.
619
- */
620
- function instantiatedAccesses(site) {
621
- const resolved = queries.resolvedSignature(site.node);
622
- const generic = resolved === UNANSWERED || resolved === undefined ? undefined : queries.genericOf(resolved);
623
- if (resolved === UNANSWERED ||
624
- resolved === undefined ||
625
- generic === UNANSWERED ||
626
- generic === undefined) {
627
- return [];
628
- }
629
- const count = argumentsOf(site.node).length;
630
- const parameters = [];
631
- for (let at = 0; at < count; at += 1) {
632
- const type = queries.parameterType(generic, at);
633
- if (type !== UNANSWERED && type !== undefined) {
634
- parameters[at] = type;
635
- }
636
- }
637
- const result = queries.returnTypeOf(generic);
638
- const held = [
639
- ...parameters,
640
- ...(result === UNANSWERED || result === undefined ? [] : [result]),
641
- ];
642
- // A parameter of a callback and a type argument, a rest tuple's among them, hold the
643
- // accesses one level down: `fn: (payload: M[K]) => void` and `...[M[K]]`.
644
- const nested = held.flatMap((type) => [
645
- ...partsOf(type),
646
- ...partsOf(type).flatMap((part) => {
647
- const args = queries.typeArguments(part);
648
- const calls = queries.signaturesOf(part, SignatureKind.Call);
649
- return [
650
- ...(args === UNANSWERED ? [] : args),
651
- ...(calls === UNANSWERED ? [] : calls).flatMap((signature) => signature.parameters.flatMap((_parameter, index) => {
652
- const one = queries.parameterType(signature, index);
653
- return one === UNANSWERED || one === undefined ? [] : [one];
654
- })),
655
- ];
656
- }),
657
- ]);
658
- const found = [];
659
- for (const type of nested) {
660
- const access = queries.indexedAccessOf(type);
661
- if (access === UNANSWERED || access === undefined || access.object.isTypeParameter()) {
662
- continue;
663
- }
664
- const names = access.index.isTypeParameter()
665
- ? parameters.flatMap((parameter, at) => {
666
- if (!partsOf(parameter).some((part) => part.id === access.index.id)) {
379
+ function partsOf(type) {
380
+ const parts = queries.constituents(type);
381
+ return parts === UNANSWERED ? [type] : parts;
382
+ }
383
+ /** The generics one symbol declares, through the alias it may be. */
384
+ const genericsOf = (symbol) => {
385
+ const target = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : queries.aliased(symbol);
386
+ if (target === UNANSWERED) {
667
387
  return [];
668
388
  }
669
- const passed = queries.typeAtPosition(resolved, at);
670
- return passed === UNANSWERED ? [] : literalTypeNames(passed, partsOf);
671
- })
672
- : literalTypeNames(access.index, partsOf);
673
- if (names.length > 0) {
674
- found.push({
675
- file: site.file,
676
- from: site.from,
677
- at: site.node,
678
- object: access.object,
679
- index: names,
389
+ return target.declarations.flatMap((handle) => {
390
+ const node = project.declarationAt(handle)?.node;
391
+ const generic = node === undefined ? undefined : genericOf(node);
392
+ return generic === undefined ? [] : [generic];
393
+ });
394
+ };
395
+ /**
396
+ * The type a call's callee is read off: the receiver of a property access, the value an
397
+ * object binding pattern destructures the callee from, or the value whose member a
398
+ * variable holding the callee is initialized with.
399
+ */
400
+ const receiverTypeOf = (callee) => {
401
+ let operand;
402
+ if (isPropertyAccessExpression(callee)) {
403
+ const type = queries.typeAt(callee.expression);
404
+ operand = type === UNANSWERED ? undefined : type;
405
+ const args = operand === undefined ? [] : queries.typeArguments(operand);
406
+ if (operand !== undefined && args !== UNANSWERED && args.length > 0) {
407
+ return operand;
408
+ }
409
+ }
410
+ const name = isPropertyAccessExpression(callee) ? callee.name : callee;
411
+ const [named] = queries.symbolsAt([name]);
412
+ const symbol = named === UNANSWERED || named === undefined || (named.flags & SymbolFlags.Alias) === 0
413
+ ? named
414
+ : queries.aliased(named);
415
+ if (symbol === UNANSWERED || symbol === undefined) {
416
+ return operand;
417
+ }
418
+ for (const handle of symbol.declarations) {
419
+ const node = project.declarationAt(handle)?.node;
420
+ const holder = node !== undefined &&
421
+ isBindingElement(node) &&
422
+ isObjectBindingPattern(node.parent) &&
423
+ isVariableDeclaration(node.parent.parent)
424
+ ? node.parent.parent.initializer
425
+ : node !== undefined &&
426
+ isVariableDeclaration(node) &&
427
+ node.initializer !== undefined &&
428
+ isPropertyAccessExpression(node.initializer)
429
+ ? node.initializer.expression
430
+ : undefined;
431
+ const type = holder === undefined ? undefined : queries.typeAt(holder);
432
+ if (type !== UNANSWERED && type !== undefined) {
433
+ return type;
434
+ }
435
+ }
436
+ return operand;
437
+ };
438
+ /**
439
+ * The instantiation of `owner` a receiver's type is, or the first one among the base
440
+ * types it extends, breadth first: a subclass fixes its base's type parameters, and
441
+ * `this` stands for its class.
442
+ */
443
+ const instantiationOf = (type, owner) => {
444
+ const seen = new Set();
445
+ for (let queue = [type]; queue.length > 0;) {
446
+ const next = [];
447
+ for (const at of queue) {
448
+ if (seen.has(at.id)) {
449
+ continue;
450
+ }
451
+ seen.add(at.id);
452
+ const target = at.isTypeReference() ? queries.targetOf(at) : at;
453
+ const symbol = target === UNANSWERED ? undefined : queries.symbolOfType(target);
454
+ const declares = symbol !== undefined &&
455
+ symbol !== UNANSWERED &&
456
+ symbol.declarations.some((handle) => project.declarationAt(handle)?.node === owner);
457
+ if (declares) {
458
+ return at;
459
+ }
460
+ const constraint = at.isTypeParameter() ? queries.constraintOf(at) : undefined;
461
+ const bases = at.isTypeParameter() ? [] : queries.baseTypes(at);
462
+ next.push(...(bases === UNANSWERED ? [] : bases));
463
+ if (constraint !== undefined && constraint !== UNANSWERED) {
464
+ next.push(constraint);
465
+ }
466
+ }
467
+ queue = next;
468
+ }
469
+ return type;
470
+ };
471
+ /** What one site fixes for one side of a carried access. */
472
+ const fixedAt = (site, side, generic) => {
473
+ if (side.kind === "written") {
474
+ return { kind: "node", node: side.node };
475
+ }
476
+ const { node } = site;
477
+ if (side.kind === "receiver") {
478
+ const callee = isCallExpression(node) ? node.expression : undefined;
479
+ const owner = receiverOf(generic);
480
+ const typed = callee === undefined ? undefined : receiverTypeOf(callee);
481
+ const receiver = typed === undefined || owner === undefined ? typed : instantiationOf(typed, owner);
482
+ if (receiver === undefined) {
483
+ return undefined;
484
+ }
485
+ const args = queries.typeArguments(receiver);
486
+ const type = args === UNANSWERED ? undefined : args[side.index];
487
+ return type === undefined ? undefined : { kind: "type", type };
488
+ }
489
+ if (side.kind === "argument") {
490
+ const argument = isTypeReferenceNode(node) ? undefined : node.arguments?.[side.index];
491
+ const type = argument === undefined ? undefined : queries.typeAt(argument);
492
+ return type === UNANSWERED || type === undefined ? undefined : { kind: "type", type };
493
+ }
494
+ const written = node.typeArguments?.[side.index];
495
+ if (written !== undefined) {
496
+ const passed = namedParameter(written, site.enclosing);
497
+ const mapped = passed === undefined ? mappedConstraint(written) : undefined;
498
+ return passed !== undefined
499
+ ? { kind: "passed", generic: passed.generic, index: passed.index }
500
+ : { kind: "node", node: mapped ?? written };
501
+ }
502
+ if (isTypeReferenceNode(node)) {
503
+ return undefined;
504
+ }
505
+ const at = parametersOf(generic).findIndex((parameter) => {
506
+ const type = parameter.type === undefined ? undefined : unwrappedType(parameter.type);
507
+ return (type !== undefined &&
508
+ isTypeReferenceNode(type) &&
509
+ isIdentifier(type.typeName) &&
510
+ parameterAt(generic, type.typeName.text) === side.index);
511
+ });
512
+ if (at < 0) {
513
+ return undefined;
514
+ }
515
+ const resolved = queries.resolvedSignature(node);
516
+ if (resolved === UNANSWERED || resolved === undefined) {
517
+ return undefined;
518
+ }
519
+ const type = queries.typeAtPosition(resolved, at);
520
+ return type === UNANSWERED ? undefined : { kind: "type", type };
521
+ };
522
+ /** Reads one carried access at one site: an access where both sides are fixed, a carry where one is passed on. */
523
+ const readAt = (site, generic, one) => {
524
+ const object = fixedAt(site, one.object, generic);
525
+ const index = object === undefined ? undefined : fixedAt(site, one.index, generic);
526
+ if (object === undefined || index === undefined) {
527
+ return false;
528
+ }
529
+ const passed = [object, index].find((fixed) => fixed.kind === "passed");
530
+ if (passed !== undefined) {
531
+ const side = (fixed) => fixed.kind === "passed"
532
+ ? fixed.generic === passed.generic
533
+ ? { kind: "parameter", index: fixed.index }
534
+ : undefined
535
+ : fixed.kind === "node"
536
+ ? { kind: "written", node: fixed.node }
537
+ : undefined;
538
+ const objectSide = side(object);
539
+ const indexSide = side(index);
540
+ return objectSide !== undefined && indexSide !== undefined
541
+ ? carry(passed.generic, { object: objectSide, index: indexSide })
542
+ : false;
543
+ }
544
+ const objectAt = concrete(object);
545
+ const indexAt = concrete(index);
546
+ if (objectAt === undefined || indexAt === undefined) {
547
+ return false;
548
+ }
549
+ accesses.push({
550
+ file: site.file,
551
+ from: site.from,
552
+ at: site.node,
553
+ object: objectAt,
554
+ index: "kind" in indexAt ? (literalNames(indexAt) ?? indexAt) : indexAt,
555
+ });
556
+ return false;
557
+ };
558
+ // A site passing a type parameter on carries the access to its own generic, so the
559
+ // sites are read again until no generic gains an access.
560
+ const resolvedGenerics = new Map();
561
+ const read = new Set();
562
+ for (let changed = true; changed;) {
563
+ changed = false;
564
+ const pending = sites.filter((site) => names.has(site.name) && !resolvedGenerics.has(site));
565
+ const typeSites = pending.filter((site) => isTypeReferenceNode(site.node));
566
+ const symbols = typeSites.length === 0
567
+ ? []
568
+ : queries.symbolsAt(typeSites.map((site) => site.node.typeName));
569
+ typeSites.forEach((site, at) => {
570
+ const symbol = symbols[at];
571
+ if (symbol === UNANSWERED) {
572
+ holdSite(site);
573
+ }
574
+ resolvedGenerics.set(site, symbol === UNANSWERED || symbol === undefined ? [] : genericsOf(symbol));
575
+ });
576
+ for (const site of pending.filter((one) => !isTypeReferenceNode(one.node))) {
577
+ const resolved = queries.resolvedSignature(site.node);
578
+ if (resolved === UNANSWERED) {
579
+ holdSite(site);
580
+ }
581
+ const declaration = resolved === UNANSWERED || resolved?.declaration === undefined
582
+ ? undefined
583
+ : project.declarationAt(resolved.declaration)?.node;
584
+ const generic = declaration === undefined ? undefined : genericOf(declaration);
585
+ resolvedGenerics.set(site, generic === undefined ? [] : [generic]);
586
+ }
587
+ sites.forEach((site, siteAt) => {
588
+ for (const generic of resolvedGenerics.get(site) ?? []) {
589
+ (carried.get(generic) ?? []).forEach((one, at) => {
590
+ const key = `${String(siteAt)}:${String(at)}:${nodeKey(generic.getSourceFile(), generic)}`;
591
+ if (read.has(key)) {
592
+ return;
593
+ }
594
+ read.add(key);
595
+ changed = readAt(site, generic, one) || changed;
596
+ });
597
+ }
598
+ });
599
+ }
600
+ // A generic declared outside the project's own files carries what its own text indexes
601
+ // by its type parameters and its receiver's. Only a call passing an argument the compiler
602
+ // types as a literal can fix an index, so only such a call's signature is resolved.
603
+ const outside = argumentCalls.filter((site) => !resolvedGenerics.has(site));
604
+ const argumentNodes = argumentCalls.flatMap((site) => argumentsOf(site.node));
605
+ const argumentTypes = queries.typesAt(argumentNodes);
606
+ const literalArgument = new Set();
607
+ argumentTypes.forEach((type, at) => {
608
+ const node = argumentNodes[at];
609
+ if (node !== undefined &&
610
+ type !== UNANSWERED &&
611
+ type !== undefined &&
612
+ literalTypeNames(type, partsOf).length > 0) {
613
+ literalArgument.add(node);
614
+ }
680
615
  });
681
- }
682
- }
683
- return found;
684
- }
685
- const typeNodes = accesses.flatMap((one) => [
686
- ...("kind" in one.object ? [one.object] : []),
687
- ...(!Array.isArray(one.index) && "kind" in one.index ? [one.index] : []),
688
- ]);
689
- const typeOf = new Map();
690
- queries.typesAt(typeNodes).forEach((answer, index) => {
691
- const node = typeNodes[index];
692
- if (answer !== UNANSWERED && answer !== undefined && node !== undefined) {
693
- typeOf.set(node, answer);
694
- }
695
- });
696
- const asType = (side) => "kind" in side ? typeOf.get(side) : side;
697
- const found = [];
698
- for (const access of accesses) {
699
- const type = asType(access.object);
700
- const indexType = Array.isArray(access.index)
701
- ? undefined
702
- : asType(access.index);
703
- const accessed = Array.isArray(access.index)
704
- ? access.index
705
- : indexType === undefined
706
- ? undefined
707
- : literalTypeNames(indexType, partsOf);
708
- if (type === undefined || accessed === undefined) {
709
- continue;
710
- }
711
- const position = renderPosition(access.file, root, access.at.getStart());
712
- for (const part of partsOf(type)) {
713
- const properties = queries.propertiesOf(part);
714
- if (properties === UNANSWERED) {
715
- continue;
716
- }
717
- for (const property of properties) {
718
- if (!accessed.includes(property.escapedName)) {
719
- continue;
616
+ const own = project.ownPaths();
617
+ for (const site of outside) {
618
+ if (!argumentsOf(site.node).some((one) => literalArgument.has(one))) {
619
+ continue;
620
+ }
621
+ const resolved = queries.resolvedSignature(site.node);
622
+ const handle = resolved === UNANSWERED || resolved === undefined ? undefined : resolved.declaration;
623
+ if (handle === undefined || own.has(handle.path)) {
624
+ continue;
625
+ }
626
+ const node = project.declarationAt(handle)?.node;
627
+ const generic = node === undefined ? undefined : genericOf(node);
628
+ if (generic === undefined) {
629
+ continue;
630
+ }
631
+ for (const one of carriedIn(generic)) {
632
+ readAt(site, generic, one);
633
+ }
720
634
  }
721
- for (const to of chains.declarationsOf(property)) {
722
- found.push({
723
- from: access.from,
724
- to,
725
- position,
726
- use: "read",
727
- resolution: "batch",
728
- test: testFiles.has(position.path),
729
- });
635
+ for (const site of argumentCalls) {
636
+ if (argumentsOf(site.node).some((one) => literalArgument.has(one))) {
637
+ accesses.push(...instantiatedAccesses(site));
638
+ }
730
639
  }
731
- }
732
- }
733
- }
734
- return found;
735
- }
640
+ /**
641
+ * The indexed accesses the generic signature a call instantiates holds, read through the
642
+ * checker: the signature its receiver's instantiation gives, so an object type its
643
+ * receiver fixes is concrete whatever declaration spells the receiver's type, a type
644
+ * literal a generic function returns among them. An index naming a type parameter is the
645
+ * literal type the call passes at each parameter of that type.
646
+ */
647
+ function instantiatedAccesses(site) {
648
+ const resolved = queries.resolvedSignature(site.node);
649
+ const generic = resolved === UNANSWERED || resolved === undefined
650
+ ? undefined
651
+ : queries.genericOf(resolved);
652
+ if (resolved === UNANSWERED ||
653
+ resolved === undefined ||
654
+ generic === UNANSWERED ||
655
+ generic === undefined) {
656
+ return [];
657
+ }
658
+ const count = argumentsOf(site.node).length;
659
+ const parameters = [];
660
+ for (let at = 0; at < count; at += 1) {
661
+ const type = queries.parameterType(generic, at);
662
+ if (type !== UNANSWERED && type !== undefined) {
663
+ parameters[at] = type;
664
+ }
665
+ }
666
+ const result = queries.returnTypeOf(generic);
667
+ const held = [
668
+ ...parameters,
669
+ ...(result === UNANSWERED || result === undefined ? [] : [result]),
670
+ ];
671
+ // A parameter of a callback and a type argument, a rest tuple's among them, hold the
672
+ // accesses one level down: `fn: (payload: M[K]) => void` and `...[M[K]]`.
673
+ const nested = held.flatMap((type) => [
674
+ ...partsOf(type),
675
+ ...partsOf(type).flatMap((part) => {
676
+ const args = queries.typeArguments(part);
677
+ const calls = queries.signaturesOf(part, SignatureKind.Call);
678
+ return [
679
+ ...(args === UNANSWERED ? [] : args),
680
+ ...(calls === UNANSWERED ? [] : calls).flatMap((signature) => signature.parameters.flatMap((_parameter, index) => {
681
+ const one = queries.parameterType(signature, index);
682
+ return one === UNANSWERED || one === undefined ? [] : [one];
683
+ })),
684
+ ];
685
+ }),
686
+ ]);
687
+ const found = [];
688
+ for (const type of nested) {
689
+ const access = queries.indexedAccessOf(type);
690
+ if (access === UNANSWERED || access === undefined || access.object.isTypeParameter()) {
691
+ continue;
692
+ }
693
+ const names = access.index.isTypeParameter()
694
+ ? parameters.flatMap((parameter, at) => {
695
+ if (!partsOf(parameter).some((part) => part.id === access.index.id)) {
696
+ return [];
697
+ }
698
+ const passed = queries.typeAtPosition(resolved, at);
699
+ return passed === UNANSWERED ? [] : literalTypeNames(passed, partsOf);
700
+ })
701
+ : literalTypeNames(access.index, partsOf);
702
+ if (names.length > 0) {
703
+ found.push({
704
+ file: site.file,
705
+ from: site.from,
706
+ at: site.node,
707
+ object: access.object,
708
+ index: names,
709
+ });
710
+ }
711
+ }
712
+ return found;
713
+ }
714
+ const typeNodes = accesses.flatMap((one) => [
715
+ ...("kind" in one.object ? [one.object] : []),
716
+ ...(!Array.isArray(one.index) && "kind" in one.index ? [one.index] : []),
717
+ ]);
718
+ const typeOf = new Map();
719
+ queries.typesAt(typeNodes).forEach((answer, index) => {
720
+ const node = typeNodes[index];
721
+ if (answer !== undefined && node !== undefined) {
722
+ typeOf.set(node, answer);
723
+ }
724
+ });
725
+ const asType = (side) => "kind" in side ? typeOf.get(side) : side;
726
+ for (const access of accesses) {
727
+ const type = asType(access.object);
728
+ const indexType = Array.isArray(access.index)
729
+ ? undefined
730
+ : asType(access.index);
731
+ const accessed = Array.isArray(access.index)
732
+ ? access.index
733
+ : indexType === undefined || indexType === UNANSWERED
734
+ ? undefined
735
+ : literalTypeNames(indexType, partsOf);
736
+ if (type === UNANSWERED || indexType === UNANSWERED) {
737
+ found.push({
738
+ kind: "held",
739
+ from: access.from,
740
+ ...(accessed === undefined ? {} : { names: accessed }),
741
+ ...(type === UNANSWERED || type === undefined
742
+ ? accessed === undefined
743
+ ? { members: true }
744
+ : {}
745
+ : { types: partsOf(type) }),
746
+ });
747
+ continue;
748
+ }
749
+ if (type === undefined || accessed === undefined) {
750
+ continue;
751
+ }
752
+ for (const part of partsOf(type)) {
753
+ const properties = queries.propertiesOf(part);
754
+ if (properties === UNANSWERED) {
755
+ found.push({ kind: "held", from: access.from, names: accessed });
756
+ continue;
757
+ }
758
+ for (const property of properties) {
759
+ if (!accessed.includes(property.escapedName)) {
760
+ continue;
761
+ }
762
+ for (const to of chains.declarationsOf(property)) {
763
+ found.push({
764
+ kind: "named",
765
+ from: access.from,
766
+ to,
767
+ file: access.file,
768
+ at: access.at,
769
+ use: "read",
770
+ via: "batch",
771
+ });
772
+ }
773
+ }
774
+ }
775
+ }
776
+ return found;
777
+ },
778
+ };
779
+ },
780
+ };
736
781
  /** The arguments a call or a `new` expression passes, none for a type reference. */
737
782
  function argumentsOf(node) {
738
783
  return isTypeReferenceNode(node) ? [] : (node.arguments ?? []);