carrick 0.3.125 → 0.3.127

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.
@@ -71,6 +71,17 @@ export declare const FLAG_TOO_MANY = 16;
71
71
  export declare const FLAG_PUBLISHED = 32;
72
72
  /** The declaration is what its module exports as `export =` or `default` (carrick#2508). */
73
73
  export declare const FLAG_MODULE_DEFAULT = 64;
74
+ /**
75
+ * A `call` row only: the call's declared return is the result of the
76
+ * function it is handed (`ReturnType<F>`, a bare type parameter the handed
77
+ * function returns, or an awaitable of one), so the library runs it and
78
+ * hands its result back (carrick#2634). Set when the call passes at least
79
+ * one argument at a function-typed parameter and every such parameter
80
+ * meets that return. The last free bit of the `u8`: the next flag widens
81
+ * the field and bumps the format. Pinned to `FLAG_RUNS_HANDED` in
82
+ * `src/position_rows.rs` by `position-rows-runs-handed.test.ts`.
83
+ */
84
+ export declare const FLAG_RUNS_HANDED = 128;
74
85
  /** The most `decl` rows one position lists. */
75
86
  export declare const DECL_CAP = 16;
76
87
  /** The kinds {@link kindOf} gives a node; `decl` and `jsx` rows are written beside them. */
@@ -109,14 +120,26 @@ export interface PositionRowsOptions {
109
120
  export declare function writePositionRows(options: PositionRowsOptions): PositionRowsCounts;
110
121
  /** What a declaration's file is written as in a row's `decl_file`: `lib:<name>` for the default library, else `PathNames`'. */
111
122
  export declare function declaringFileName(program: ts.Program, paths: PathNames, file: ts.SourceFile): string | null;
123
+ /** The installed package directory a resolved file lies in, and its name, as the module graph places it. */
124
+ export type PackageDirectoryOf = (fileName: string) => {
125
+ directory: string;
126
+ name: string;
127
+ } | undefined;
112
128
  /**
113
129
  * Names for the files rows point at, never an absolute path: relative to the
114
130
  * scanned repo's root when inside it (its real path too, so a root reached
115
131
  * through a link still contains its files), `lib:<name>` for the compiler's
116
132
  * default library, `pkg:<name>/<path>` for an installed package outside the
117
133
  * root, and nothing otherwise.
134
+ *
135
+ * An installed package outside the root is found by its path's last
136
+ * `node_modules` segment, else by `packageDirectoryOf`: the module graph's
137
+ * answer where the resolver serves a package from a cache whose path has no
138
+ * `node_modules` segment (carrick#2599). Either way the directory must hold a
139
+ * package.json.
118
140
  */
119
141
  export declare class PathNames {
142
+ private readonly packageDirectoryOf;
120
143
  private readonly roots;
121
144
  private readonly packageNames;
122
145
  /**
@@ -126,7 +149,7 @@ export declare class PathNames {
126
149
  */
127
150
  private readonly declaringFiles;
128
151
  private readonly declaringPackages;
129
- constructor(scanRoot: string);
152
+ constructor(scanRoot: string, packageDirectoryOf?: PackageDirectoryOf);
130
153
  /** The root `file` lies under, and its path from there; `undefined` outside every root. */
131
154
  private inside;
132
155
  /** A request file's name: relative to the root, which every file the scanner sends is inside. */
@@ -151,5 +174,100 @@ export declare class PathNames {
151
174
  */
152
175
  scrubType(text: string): string;
153
176
  }
177
+ /**
178
+ * The rows of one program's files. The `implementations` action
179
+ * (`implementations.ts`, carrick#2432) reads a call's symbol and names its
180
+ * answers through the same walker, so they join as the rows do.
181
+ */
182
+ export declare class PositionWalker {
183
+ private readonly program;
184
+ private readonly paths;
185
+ private readonly loader;
186
+ private readonly checker;
187
+ /** Each module's exports by name, and its `export =` and `default` targets, aliases followed. */
188
+ private readonly moduleExports;
189
+ constructor(program: ts.Program, paths: PathNames, loader: ProjectLoader);
190
+ rowsOf(sourceFile: ts.SourceFile): PositionRow[];
191
+ private row;
192
+ /**
193
+ * Whether a call's declared return is the result of the function it is
194
+ * handed (carrick#2634), read from the declared signature through public
195
+ * checker calls. See {@link FLAG_RUNS_HANDED}.
196
+ */
197
+ private runsHanded;
198
+ /** The call signatures of a parameter's type, a type parameter read through its constraint, `null` and `undefined` skipped. */
199
+ private callSignaturesOf;
200
+ /** The compiler's own awaited type differs from the type: `Promise<R>`, `PromiseLike<R>`. */
201
+ private isAwaitable;
202
+ /**
203
+ * An intrinsic element's type: the attributes `JSX.IntrinsicElements`
204
+ * gives its tag. The compiler types an intrinsic tag name itself as the
205
+ * error type, though the name resolves. A tag is intrinsic when it is an
206
+ * identifier that starts with a lowercase letter or holds a `-`, as the
207
+ * language reads it.
208
+ */
209
+ private intrinsicProps;
210
+ /** How rows name a position in a program file: its file, 1-based line and package. */
211
+ located(declaringFile: ts.SourceFile, position: number): {
212
+ declFile: string | null;
213
+ declLine: number;
214
+ declPackage: string | null;
215
+ };
216
+ /**
217
+ * The declaration a member with none is attributed to (carrick#2448): the
218
+ * mapped type node, or the alias naming it, of the receiver's type. A
219
+ * mapped type's members are made by the checker and have no declaration of
220
+ * their own. This is the owner `memberTypeWithoutDeclaration` reads such a
221
+ * member at (type-structural-expander.ts): the type's symbol, else its
222
+ * alias symbol. A union or intersection receiver contributes the one
223
+ * constituent that has the property; with several the row is left as it is.
224
+ */
225
+ private ownerDeclaration;
226
+ /** The declaration of a type's symbol, else of its alias symbol: a mapped type node, or the alias naming it. */
227
+ private typeDeclaration;
228
+ /**
229
+ * How `declaration`'s module publishes `symbol`: {@link FLAG_PUBLISHED}
230
+ * when under the symbol's own name (or as a global), and
231
+ * {@link FLAG_MODULE_DEFAULT} when as the module's `export =` or `default`.
232
+ * The module is the nearest ambient `declare module "…"` or the file.
233
+ */
234
+ private publication;
235
+ /** A namespace member, found among the exports of the module it sits in. */
236
+ private publishedByExports;
237
+ private publishedIn;
238
+ /** The type of the object a `member` or `call` row's name is read on, if the name is read on one. */
239
+ private receiverOf;
240
+ /**
241
+ * The `decl` rows of a `call` or `member` row (spans unset): one per
242
+ * declaration the receiver's union constituents give the member, when they
243
+ * give more than one (carrick#2448). A constituent's declaration is its
244
+ * member's ({@link attributedDeclaration}), or, for a member with none, the
245
+ * constituent type's own ({@link typeDeclaration}). Two instantiations of
246
+ * one generic class, or one class and `undefined`, give one, and no row.
247
+ * A throw writes one row flagged {@link FLAG_CHECKER_THREW}.
248
+ */
249
+ private declRows;
250
+ /** The row's symbol, following aliases to a target that has a declaration. */
251
+ symbolOf(kind: PositionKind, node: ts.Node): ts.Symbol | undefined;
252
+ /**
253
+ * In a TypeScript file `const { a } = require("m")` binds `a` as `any` with
254
+ * the binding as its only declaration (TypeScript resolves `require` itself
255
+ * only in JavaScript files), so a trace stops there. Name the export the
256
+ * call resolves to instead (carrick#2454): for the binding itself, and for
257
+ * `m.x` where `m` is a whole-module binding. `undefined` when the name is
258
+ * not such a binding, the specifier is not a literal or does not resolve,
259
+ * or the module has no such export.
260
+ */
261
+ private requiredExport;
262
+ /** The `require` binding a symbol is, when its only declaration is one. */
263
+ private requireBinding;
264
+ /** `"m"` of a call `require("m")` to the global `require`, else `undefined`. */
265
+ private requireSpecifier;
266
+ private exportOfRequired;
267
+ private host;
268
+ private resolutionHost;
269
+ }
270
+ /** Order two `decl_file` names, `null` last. */
271
+ export declare function compareNames(a: string | null, b: string | null): number;
154
272
  /** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
155
273
  export declare function kindOf(node: ts.Node): NodeKind | undefined;
@@ -53,7 +53,7 @@ import * as path from 'node:path';
53
53
  import * as zlib from 'node:zlib';
54
54
  import { ts } from 'ts-morph';
55
55
  import { WriteGuard, installedPackageDirectory, isDeclarationFileName, moduleExport } from './capture/index.js';
56
- import { attributedDeclaration, installedPackageOf } from './declaration-attribution.js';
56
+ import { attributedDeclaration, installedPackageOf, typeDeclaration } from './declaration-attribution.js';
57
57
  import { typeToStringAsDeclared } from './print-type.js';
58
58
  /** The format's version, written in the header. */
59
59
  export const POSITION_ROWS_VERSION = 4;
@@ -76,6 +76,17 @@ export const FLAG_TOO_MANY = 16;
76
76
  export const FLAG_PUBLISHED = 32;
77
77
  /** The declaration is what its module exports as `export =` or `default` (carrick#2508). */
78
78
  export const FLAG_MODULE_DEFAULT = 64;
79
+ /**
80
+ * A `call` row only: the call's declared return is the result of the
81
+ * function it is handed (`ReturnType<F>`, a bare type parameter the handed
82
+ * function returns, or an awaitable of one), so the library runs it and
83
+ * hands its result back (carrick#2634). Set when the call passes at least
84
+ * one argument at a function-typed parameter and every such parameter
85
+ * meets that return. The last free bit of the `u8`: the next flag widens
86
+ * the field and bumps the format. Pinned to `FLAG_RUNS_HANDED` in
87
+ * `src/position_rows.rs` by `position-rows-runs-handed.test.ts`.
88
+ */
89
+ export const FLAG_RUNS_HANDED = 128;
79
90
  /** The most `decl` rows one position lists. */
80
91
  export const DECL_CAP = 16;
81
92
  const COMPARE_OPERATORS = new Set([
@@ -102,7 +113,7 @@ export function writePositionRows(options) {
102
113
  const { loader, files, scanRoot, outPath, service } = options;
103
114
  const outDir = path.dirname(outPath);
104
115
  const guard = WriteGuard.of({ dirs: [outDir], protect: [loader.getRepoRoot(), scanRoot] });
105
- const paths = new PathNames(scanRoot);
116
+ const paths = new PathNames(scanRoot, (file) => loader.packageDirectoryOf(file));
106
117
  const lines = new Array(files.length);
107
118
  const counts = {
108
119
  files: files.length,
@@ -256,8 +267,15 @@ function fileLine(line) {
256
267
  * through a link still contains its files), `lib:<name>` for the compiler's
257
268
  * default library, `pkg:<name>/<path>` for an installed package outside the
258
269
  * root, and nothing otherwise.
270
+ *
271
+ * An installed package outside the root is found by its path's last
272
+ * `node_modules` segment, else by `packageDirectoryOf`: the module graph's
273
+ * answer where the resolver serves a package from a cache whose path has no
274
+ * `node_modules` segment (carrick#2599). Either way the directory must hold a
275
+ * package.json.
259
276
  */
260
277
  export class PathNames {
278
+ packageDirectoryOf;
261
279
  roots;
262
280
  packageNames = new Map();
263
281
  /**
@@ -267,7 +285,8 @@ export class PathNames {
267
285
  */
268
286
  declaringFiles = new Map();
269
287
  declaringPackages = new Map();
270
- constructor(scanRoot) {
288
+ constructor(scanRoot, packageDirectoryOf = () => undefined) {
289
+ this.packageDirectoryOf = packageDirectoryOf;
271
290
  const resolved = path.resolve(scanRoot);
272
291
  let real = resolved;
273
292
  try {
@@ -306,7 +325,7 @@ export class PathNames {
306
325
  const inside = this.inside(fileName);
307
326
  if (inside)
308
327
  return toSlashes(inside.rel);
309
- const installed = installedPackageDirectory(this.roots[0], fileName);
328
+ const installed = installedPackageDirectory(this.roots[0], fileName) ?? this.packageDirectoryOf(fileName);
310
329
  if (installed && fs.existsSync(path.join(installed.directory, 'package.json'))) {
311
330
  return `pkg:${installed.name}/${toSlashes(path.relative(installed.directory, fileName))}`;
312
331
  }
@@ -381,8 +400,12 @@ export class PathNames {
381
400
  function toSlashes(p) {
382
401
  return p.split(path.sep).join('/');
383
402
  }
384
- /** The rows of one program's files. */
385
- class PositionWalker {
403
+ /**
404
+ * The rows of one program's files. The `implementations` action
405
+ * (`implementations.ts`, carrick#2432) reads a call's symbol and names its
406
+ * answers through the same walker, so they join as the rows do.
407
+ */
408
+ export class PositionWalker {
386
409
  program;
387
410
  paths;
388
411
  loader;
@@ -426,6 +449,8 @@ class PositionWalker {
426
449
  if (type.flags & ts.TypeFlags.Any) {
427
450
  flags |= type.intrinsicName === 'error' ? FLAG_ERROR_TYPE : FLAG_ANY;
428
451
  }
452
+ if (kind === 'call' && this.runsHanded(node))
453
+ flags |= FLAG_RUNS_HANDED;
429
454
  const typeText = this.paths.scrubType(typeToStringAsDeclared(this.checker, type, undefined));
430
455
  if (!symbol)
431
456
  return [kind, start, end, null, null, null, null, typeText, flags];
@@ -453,6 +478,114 @@ class PositionWalker {
453
478
  return [kind, start, end, null, null, null, null, null, FLAG_CHECKER_THREW];
454
479
  }
455
480
  }
481
+ /**
482
+ * Whether a call's declared return is the result of the function it is
483
+ * handed (carrick#2634), read from the declared signature through public
484
+ * checker calls. See {@link FLAG_RUNS_HANDED}.
485
+ */
486
+ runsHanded(node) {
487
+ if (!ts.isCallExpression(node) && !ts.isNewExpression(node))
488
+ return false;
489
+ const args = node.arguments ?? [];
490
+ if (args.length === 0)
491
+ return false;
492
+ try {
493
+ const declaration = this.checker.getResolvedSignature(node)?.declaration;
494
+ if (!declaration || ts.isJSDocSignature(declaration))
495
+ return false;
496
+ const declared = this.checker.getSignatureFromDeclaration(declaration);
497
+ if (!declared)
498
+ return false;
499
+ const own = new Set(declared.getTypeParameters() ?? []);
500
+ const results = new Map();
501
+ const resultOf = (f) => {
502
+ let atom = results.get(f);
503
+ if (!atom)
504
+ results.set(f, (atom = {}));
505
+ return atom;
506
+ };
507
+ const atoms = (t, out, depth = 0) => {
508
+ if (depth > 8)
509
+ return out;
510
+ if (t.flags & ts.TypeFlags.TypeParameter) {
511
+ if (own.has(t))
512
+ out.add(t);
513
+ }
514
+ else if (t.flags & ts.TypeFlags.UnionOrIntersection) {
515
+ for (const member of t.types)
516
+ atoms(member, out, depth + 1);
517
+ }
518
+ else if (t.flags & ts.TypeFlags.Conditional) {
519
+ const f = resultOfConditional(t, own);
520
+ if (f)
521
+ out.add(resultOf(f));
522
+ }
523
+ else if (isReference(t) && this.isAwaitable(t)) {
524
+ for (const arg of this.checker.getTypeArguments(t))
525
+ atoms(arg, out, depth + 1);
526
+ }
527
+ return out;
528
+ };
529
+ const returned = atoms(this.checker.getReturnTypeOfSignature(declared), new Set());
530
+ if (returned.size === 0)
531
+ return false;
532
+ const parameters = declared.getParameters();
533
+ let handed = 0;
534
+ for (let i = 0; i < args.length; i++) {
535
+ const parameter = parameters[Math.min(i, parameters.length - 1)];
536
+ if (!parameter)
537
+ continue;
538
+ const isRest = parameter.valueDeclaration !== undefined &&
539
+ ts.isParameter(parameter.valueDeclaration) &&
540
+ parameter.valueDeclaration.dotDotDotToken !== undefined;
541
+ if (!isRest && i >= parameters.length)
542
+ continue;
543
+ let type = this.checker.getTypeOfSymbolAtLocation(parameter, declaration);
544
+ if (isRest) {
545
+ if (!isReference(type))
546
+ continue;
547
+ const [element] = this.checker.getTypeArguments(type);
548
+ if (!element)
549
+ continue;
550
+ type = element;
551
+ }
552
+ const signatures = this.callSignaturesOf(type);
553
+ if (signatures.length === 0)
554
+ continue;
555
+ handed++;
556
+ const mine = new Set();
557
+ if (type.flags & ts.TypeFlags.TypeParameter)
558
+ mine.add(resultOf(type));
559
+ else
560
+ for (const signature of signatures)
561
+ atoms(this.checker.getReturnTypeOfSignature(signature), mine);
562
+ if (![...mine].some((atom) => returned.has(atom)))
563
+ return false;
564
+ }
565
+ return handed > 0;
566
+ }
567
+ catch {
568
+ return false;
569
+ }
570
+ }
571
+ /** The call signatures of a parameter's type, a type parameter read through its constraint, `null` and `undefined` skipped. */
572
+ callSignaturesOf(type) {
573
+ let t = type;
574
+ if (t.flags & ts.TypeFlags.TypeParameter)
575
+ t = this.checker.getBaseConstraintOfType(t) ?? t;
576
+ if (t.flags & ts.TypeFlags.Union) {
577
+ const kept = t.types.filter((m) => !(m.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)));
578
+ if (kept.length !== 1)
579
+ return [];
580
+ t = kept[0];
581
+ }
582
+ return this.checker.getSignaturesOfType(t, ts.SignatureKind.Call);
583
+ }
584
+ /** The compiler's own awaited type differs from the type: `Promise<R>`, `PromiseLike<R>`. */
585
+ isAwaitable(t) {
586
+ const awaited = this.checker.getAwaitedType(t);
587
+ return awaited !== undefined && awaited !== t;
588
+ }
456
589
  /**
457
590
  * An intrinsic element's type: the attributes `JSX.IntrinsicElements`
458
591
  * gives its tag. The compiler types an intrinsic tag name itself as the
@@ -495,8 +628,7 @@ class PositionWalker {
495
628
  }
496
629
  /** The declaration of a type's symbol, else of its alias symbol: a mapped type node, or the alias naming it. */
497
630
  typeDeclaration(type) {
498
- const owner = type.getSymbol() ?? type.aliasSymbol;
499
- return attributedDeclaration(() => this.program, owner?.declarations ?? []);
631
+ return typeDeclaration(() => this.program, type);
500
632
  }
501
633
  /**
502
634
  * How `declaration`'s module publishes `symbol`: {@link FLAG_PUBLISHED}
@@ -742,7 +874,7 @@ function tail(row) {
742
874
  return [row[3], row[4], row[5], row[6], row[7], row[8]];
743
875
  }
744
876
  /** Order two `decl_file` names, `null` last. */
745
- function compareNames(a, b) {
877
+ export function compareNames(a, b) {
746
878
  if (a === b)
747
879
  return 0;
748
880
  if (a === null)
@@ -751,6 +883,28 @@ function compareNames(a, b) {
751
883
  return -1;
752
884
  return a < b ? -1 : 1;
753
885
  }
886
+ function isReference(t) {
887
+ return !!(t.flags & ts.TypeFlags.Object) && !!(t.objectFlags & ts.ObjectFlags.Reference);
888
+ }
889
+ /**
890
+ * The type parameter `F` of a conditional written `F extends (...) => infer R ? R : _`
891
+ * (`ReturnType<F>`): the result of `F`. `Parameters<F>` and the like infer
892
+ * elsewhere and give nothing.
893
+ */
894
+ function resultOfConditional(t, own) {
895
+ if (!(t.checkType.flags & ts.TypeFlags.TypeParameter) || !own.has(t.checkType))
896
+ return undefined;
897
+ const node = t.root.node;
898
+ if (!ts.isFunctionTypeNode(node.extendsType))
899
+ return undefined;
900
+ const returns = node.extendsType.type;
901
+ if (!ts.isInferTypeNode(returns))
902
+ return undefined;
903
+ const yielded = node.trueType;
904
+ if (!ts.isTypeReferenceNode(yielded) || !ts.isIdentifier(yielded.typeName))
905
+ return undefined;
906
+ return yielded.typeName.text === returns.typeParameter.name.text ? t.checkType : undefined;
907
+ }
754
908
  /** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
755
909
  export function kindOf(node) {
756
910
  if (ts.isCallExpression(node) || ts.isNewExpression(node) || ts.isTaggedTemplateExpression(node)) {
@@ -182,6 +182,21 @@ export declare class ProjectLoader {
182
182
  * through Deno, and for a file the workspace itself owns.
183
183
  */
184
184
  packageOf(filePath: string): string | undefined;
185
+ /**
186
+ * The installed package directory a resolved file lies in, and its name,
187
+ * when the module graph places it (carrick#2599). `undefined` for a project
188
+ * that did not resolve through Deno: there the path says it.
189
+ */
190
+ packageDirectoryOf(filePath: string): {
191
+ directory: string;
192
+ name: string;
193
+ } | undefined;
194
+ /**
195
+ * How the module graph resolves `name` from `from`, as the program did
196
+ * (carrick#2599). `undefined` for a project that did not resolve through
197
+ * Deno: there Node resolution is the program's.
198
+ */
199
+ resolveThroughGraph(name: string, from: string, options: import('typescript').CompilerOptions): import('typescript').ResolvedModuleFull | undefined;
185
200
  /**
186
201
  * Check if the project has been successfully initialized
187
202
  */
@@ -502,6 +502,22 @@ export class ProjectLoader {
502
502
  packageOf(filePath) {
503
503
  return this.denoProject?.packageOf(filePath);
504
504
  }
505
+ /**
506
+ * The installed package directory a resolved file lies in, and its name,
507
+ * when the module graph places it (carrick#2599). `undefined` for a project
508
+ * that did not resolve through Deno: there the path says it.
509
+ */
510
+ packageDirectoryOf(filePath) {
511
+ return this.denoProject?.packageDirectoryOf(filePath);
512
+ }
513
+ /**
514
+ * How the module graph resolves `name` from `from`, as the program did
515
+ * (carrick#2599). `undefined` for a project that did not resolve through
516
+ * Deno: there Node resolution is the program's.
517
+ */
518
+ resolveThroughGraph(name, from, options) {
519
+ return this.denoProject?.resolve(name, from, options);
520
+ }
505
521
  /**
506
522
  * Check if the project has been successfully initialized
507
523
  */
@@ -548,14 +548,8 @@ function bodyReadsOf(call) {
548
548
  const chained = bodyReadOn(top);
549
549
  if (chained)
550
550
  return checkedBodyReads([chained]);
551
- const declaration = top.getParent();
552
- if (!declaration ||
553
- !Node.isVariableDeclaration(declaration) ||
554
- declaration.getInitializer() !== top) {
555
- return [];
556
- }
557
- const name = declaration.getNameNode();
558
- if (!Node.isIdentifier(name))
551
+ const name = responseBinding(top);
552
+ if (!name)
559
553
  return [];
560
554
  const reads = [];
561
555
  let handedOn = false;
@@ -583,6 +577,92 @@ function bodyReadsOf(call) {
583
577
  }
584
578
  return checkedBodyReads(reads);
585
579
  }
580
+ /**
581
+ * The name the located call's response is bound to, when the source binds it
582
+ * in one of three ways: `const res = await call`, the first parameter of a
583
+ * `.then` callback (`call.then((res) => res.json())`), or an element of an
584
+ * array destructured off `Promise.all` (`const [a, b] = await Promise.all([call, ...])`).
585
+ */
586
+ function responseBinding(top) {
587
+ const declaration = top.getParent();
588
+ if (declaration && Node.isVariableDeclaration(declaration) && declaration.getInitializer() === top) {
589
+ const name = declaration.getNameNode();
590
+ return Node.isIdentifier(name) ? name : undefined;
591
+ }
592
+ return thenParameter(top) ?? promiseAllElement(top);
593
+ }
594
+ /** `top.then((res) => ...)`: the callback's first parameter. */
595
+ function thenParameter(top) {
596
+ const access = top.getParent();
597
+ if (!access || !Node.isPropertyAccessExpression(access) || access.getExpression() !== top) {
598
+ return undefined;
599
+ }
600
+ if (access.getName() !== 'then')
601
+ return undefined;
602
+ const then = access.getParent();
603
+ if (!then || !Node.isCallExpression(then) || then.getExpression() !== access)
604
+ return undefined;
605
+ const callback = then.getArguments()[0];
606
+ if (!callback || !(Node.isArrowFunction(callback) || Node.isFunctionExpression(callback))) {
607
+ return undefined;
608
+ }
609
+ const name = callback.getParameters()[0]?.getNameNode();
610
+ return name && Node.isIdentifier(name) ? name : undefined;
611
+ }
612
+ /**
613
+ * The name `top` is destructured to, when `top` is one element of the array
614
+ * literal handed to the language library's `Promise.all` and the call's
615
+ * awaited result is destructured to names:
616
+ * `const [a, b] = await Promise.all([call, other])`. An element after a
617
+ * spread has no position the destructuring can be read against.
618
+ */
619
+ function promiseAllElement(top) {
620
+ const array = top.getParent();
621
+ if (!array || !Node.isArrayLiteralExpression(array))
622
+ return undefined;
623
+ const elements = array.getElements();
624
+ const index = elements.indexOf(top);
625
+ if (index < 0 || elements.slice(0, index + 1).some((e) => Node.isSpreadElement(e))) {
626
+ return undefined;
627
+ }
628
+ const all = array.getParent();
629
+ if (!all ||
630
+ !Node.isCallExpression(all) ||
631
+ all.getArguments()[0] !== array ||
632
+ !isLibraryPromiseAll(all)) {
633
+ return undefined;
634
+ }
635
+ let result = all;
636
+ while (Node.isAwaitExpression(result.getParentOrThrow()) ||
637
+ Node.isParenthesizedExpression(result.getParentOrThrow())) {
638
+ result = result.getParentOrThrow();
639
+ }
640
+ const declaration = result.getParent();
641
+ if (!declaration || !Node.isVariableDeclaration(declaration) || declaration.getInitializer() !== result) {
642
+ return undefined;
643
+ }
644
+ const pattern = declaration.getNameNode();
645
+ if (!Node.isArrayBindingPattern(pattern))
646
+ return undefined;
647
+ const element = pattern.getElements()[index];
648
+ if (!element || !Node.isBindingElement(element) || element.getDotDotDotToken())
649
+ return undefined;
650
+ const name = element.getNameNode();
651
+ return Node.isIdentifier(name) ? name : undefined;
652
+ }
653
+ /** `Promise.all` as the language library declares it, not a lookalike. */
654
+ function isLibraryPromiseAll(call) {
655
+ const callee = call.getExpression();
656
+ if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== 'all')
657
+ return false;
658
+ const declaration = resolvedDeclaration(call);
659
+ if (!declaration)
660
+ return false;
661
+ return call
662
+ .getProject()
663
+ .getProgram()
664
+ .compilerObject.isSourceFileDefaultLibrary(declaration.getSourceFile());
665
+ }
586
666
  /**
587
667
  * Why these body reads cannot be retyped, or the reads to retype.
588
668
  *