carrick 0.3.122 → 0.3.124

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.
@@ -13,29 +13,26 @@
13
13
  * | `member` | property or element access | the name or the argument | the access |
14
14
  * | `compare` | `=== !== == != < > <= >= instanceof in` | the left operand | the left operand |
15
15
  * | `ident` | an identifier in expression position | itself | itself |
16
- * | `impl` | a `call` or `member` whose declaration has no body | an implementation of it | nothing |
17
16
  * | `decl` | a `call` or `member` on a union of declaring types | one of those declarations | nothing |
18
17
  * | `jsx` | a JSX opening or self-closing element's tag | the tag's name | the tag |
19
18
  *
20
- * An `impl` row (version 2, carrick#2430) follows the `call` or `member` row
21
- * it belongs to and has its span: one per declaration that go-to-implementation
22
- * names for that row's declaration, when that declaration is an interface's
23
- * or type literal's method, a function-typed property signature, an abstract
24
- * member, or a function-typed class property or parameter with no
25
- * initializer. Its `decl_*` name the implementing declaration. Past
26
- * {@link IMPL_CAP} the row is one, with {@link FLAG_TOO_MANY} and nothing else.
27
- *
28
19
  * A `decl` row (version 3, carrick#2448) follows the `call` or `member` row
29
- * it belongs to, before its `impl` rows, and has its span: one per
20
+ * it belongs to and has its span: one per
30
21
  * declaration of the member when the receiver is a union whose constituents
31
22
  * declare it at more than one place (`(pick ? a : b).get()` with two
32
23
  * classes). That row names only the first of them. Each constituent's
33
24
  * declaration is its own member's, or, for a member with none (a mapped
34
- * type's), the declaration of the constituent type itself. Sorted and capped
35
- * as `impl` rows are; `type_text` is `null`.
25
+ * type's), the declaration of the constituent type itself. Sorted by file and
26
+ * position; past {@link DECL_CAP} the row is one, with {@link FLAG_TOO_MANY}
27
+ * and nothing else. `type_text` is `null`.
28
+ *
29
+ * Version 4 (carrick#2561) dropped the `impl` rows of versions 2 and 3: the
30
+ * go-to-implementation search behind them was most of the walk's time, and
31
+ * nothing read them. Implementations are asked for where discovery stops
32
+ * (carrick#2432).
36
33
  *
37
- * A row's key is `(start, end, kind)`, except that several `impl` rows, and
38
- * several `decl` rows, share one: TypeScript `getStart()`/`getEnd()` of the
34
+ * A row's key is `(start, end, kind)`, except that several `decl` rows share
35
+ * one: TypeScript `getStart()`/`getEnd()` of the
39
36
  * node, in UTF-16 code units from the start of the file, which is what the scanner's `sidecar_position`
40
37
  * gives for an SWC span. Every path written is relative to the scanned repo's
41
38
  * root, with `/` separators.
@@ -59,7 +56,7 @@ import { WriteGuard, installedPackageDirectory, isDeclarationFileName, moduleExp
59
56
  import { attributedDeclaration, installedPackageOf } from './declaration-attribution.js';
60
57
  import { typeToStringAsDeclared } from './print-type.js';
61
58
  /** The format's version, written in the header. */
62
- export const POSITION_ROWS_VERSION = 3;
59
+ export const POSITION_ROWS_VERSION = 4;
63
60
  /** The type is `any`, declared or inferred. Never set with the error type. */
64
61
  export const FLAG_ANY = 1;
65
62
  /** The type is the compiler's error type: something did not resolve. */
@@ -68,7 +65,7 @@ export const FLAG_ERROR_TYPE = 2;
68
65
  export const FLAG_CHECKER_THREW = 4;
69
66
  /** The declaration is outside the checkout, not the default library and in no installed package. */
70
67
  export const FLAG_DECLARATION_UNNAMED = 8;
71
- /** An `impl` or `decl` row standing for more than {@link IMPL_CAP} rows of its kind; it names none. */
68
+ /** A `decl` row standing for more than {@link DECL_CAP} rows; it names none. */
72
69
  export const FLAG_TOO_MANY = 16;
73
70
  /**
74
71
  * The declaration is published under the row's symbol name: its module
@@ -79,8 +76,8 @@ export const FLAG_TOO_MANY = 16;
79
76
  export const FLAG_PUBLISHED = 32;
80
77
  /** The declaration is what its module exports as `export =` or `default` (carrick#2508). */
81
78
  export const FLAG_MODULE_DEFAULT = 64;
82
- /** The most `impl` rows one declaration lists, and the most `decl` rows one position lists. */
83
- export const IMPL_CAP = 16;
79
+ /** The most `decl` rows one position lists. */
80
+ export const DECL_CAP = 16;
84
81
  const COMPARE_OPERATORS = new Set([
85
82
  ts.SyntaxKind.EqualsEqualsEqualsToken,
86
83
  ts.SyntaxKind.ExclamationEqualsEqualsToken,
@@ -113,7 +110,6 @@ export function writePositionRows(options) {
113
110
  resolved: 0,
114
111
  any: 0,
115
112
  error_type: 0,
116
- impl: 0,
117
113
  jsx: 0,
118
114
  bytes: 0,
119
115
  load_ms: 0,
@@ -164,15 +160,15 @@ export function writePositionRows(options) {
164
160
  read.set(index, sourceFile?.getFilePath());
165
161
  }
166
162
  // Built once, after every file the group adds. The language service's
167
- // program is the one walked, so go-to-implementation answers over it
168
- // and no second program is built.
163
+ // program is the one walked, as it was when go-to-implementation answered
164
+ // over it (carrick#2430, dropped in #2561): its rows are pinned.
169
165
  const service = project.getLanguageService().compilerObject;
170
166
  const program = service.getProgram();
171
167
  if (!program)
172
168
  throw new Error('the language service built no program');
173
169
  counts.load_ms += performance.now() - loadStarted;
174
170
  const walkStarted = performance.now();
175
- const walker = new PositionWalker(program, service, paths, loader);
171
+ const walker = new PositionWalker(program, paths, loader);
176
172
  const missed = [];
177
173
  for (const index of indexes) {
178
174
  if (!read.has(index))
@@ -192,8 +188,6 @@ export function writePositionRows(options) {
192
188
  counts.any += 1;
193
189
  if (row[8] & FLAG_ERROR_TYPE)
194
190
  counts.error_type += 1;
195
- if (row[0] === 'impl')
196
- counts.impl += 1;
197
191
  if (row[0] === 'jsx')
198
192
  counts.jsx += 1;
199
193
  }
@@ -248,6 +242,10 @@ export function writePositionRows(options) {
248
242
  counts.load_ms = Math.round(counts.load_ms);
249
243
  return counts;
250
244
  }
245
+ /** What a declaration's file is written as in a row's `decl_file`: `lib:<name>` for the default library, else `PathNames`'. */
246
+ export function declaringFileName(program, paths, file) {
247
+ return program.isSourceFileDefaultLibrary(file) ? `lib:${path.basename(file.fileName)}` : paths.declaringFile(file.fileName);
248
+ }
251
249
  /** A file's line, with its keys in the pinned order. */
252
250
  function fileLine(line) {
253
251
  return JSON.stringify(line);
@@ -259,7 +257,7 @@ function fileLine(line) {
259
257
  * default library, `pkg:<name>/<path>` for an installed package outside the
260
258
  * root, and nothing otherwise.
261
259
  */
262
- class PathNames {
260
+ export class PathNames {
263
261
  roots;
264
262
  packageNames = new Map();
265
263
  /**
@@ -386,23 +384,13 @@ function toSlashes(p) {
386
384
  /** The rows of one program's files. */
387
385
  class PositionWalker {
388
386
  program;
389
- service;
390
387
  paths;
391
388
  loader;
392
389
  checker;
393
- /**
394
- * Each bodyless declaration's implementations, as `impl` rows with no span
395
- * yet (`start` and `end` are 0): go-to-implementation searches the whole
396
- * program, so it is asked once per declaration, never per call.
397
- */
398
- implementations = new Map();
399
- /** The same for a member whose symbol has no declaration, keyed by symbol. */
400
- undeclaredImplementations = new Map();
401
390
  /** Each module's exports by name, and its `export =` and `default` targets, aliases followed. */
402
391
  moduleExports = new Map();
403
- constructor(program, service, paths, loader) {
392
+ constructor(program, paths, loader) {
404
393
  this.program = program;
405
- this.service = service;
406
394
  this.paths = paths;
407
395
  this.loader = loader;
408
396
  this.checker = program.getTypeChecker();
@@ -417,8 +405,6 @@ class PositionWalker {
417
405
  if ((kind === 'call' || kind === 'member') && !(row[8] & FLAG_CHECKER_THREW)) {
418
406
  for (const decl of this.declRows(kind, node))
419
407
  rows.push(['decl', row[1], row[2], ...tail(decl)]);
420
- for (const impl of this.implRows(kind, node))
421
- rows.push(['impl', row[1], row[2], ...tail(impl)]);
422
408
  }
423
409
  }
424
410
  if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
@@ -484,104 +470,11 @@ class PositionWalker {
484
470
  }
485
471
  /** How rows name a position in a program file: its file, 1-based line and package. */
486
472
  located(declaringFile, position) {
487
- const declFile = this.program.isSourceFileDefaultLibrary(declaringFile)
488
- ? `lib:${path.basename(declaringFile.fileName)}`
489
- : this.paths.declaringFile(declaringFile.fileName);
473
+ const declFile = declaringFileName(this.program, this.paths, declaringFile);
490
474
  const declLine = declaringFile.getLineAndCharacterOfPosition(position).line + 1;
491
475
  const declPackage = this.paths.declaringPackage(declaringFile.fileName, (file) => this.loader.packageOf(file));
492
476
  return { declFile, declLine, declPackage };
493
477
  }
494
- /**
495
- * The `impl` rows of a `call` or `member` row (spans unset): none unless the
496
- * row's declaration has no body of its own ({@link isBodyless}). Its own
497
- * search, apart from the row's: a throw here writes one `impl` row flagged
498
- * {@link FLAG_CHECKER_THREW} and leaves the row as it was.
499
- */
500
- implRows(kind, node) {
501
- let declaration;
502
- try {
503
- const symbol = this.symbolOf(kind, node);
504
- if (symbol && (symbol.declarations ?? []).length === 0)
505
- return this.undeclaredImplRows(kind, node, symbol);
506
- declaration = symbol ? attributedDeclaration(() => this.program, symbol.declarations ?? []) : undefined;
507
- if (!declaration || !isBodyless(declaration, this.checker))
508
- return [];
509
- }
510
- catch {
511
- return [];
512
- }
513
- let rows = this.implementations.get(declaration);
514
- if (!rows) {
515
- try {
516
- rows = this.implementationsOf(declaration);
517
- }
518
- catch {
519
- rows = [['impl', 0, 0, null, null, null, null, null, FLAG_CHECKER_THREW]];
520
- }
521
- this.implementations.set(declaration, rows);
522
- }
523
- return rows;
524
- }
525
- /**
526
- * The `impl` rows of a member whose symbol has no declaration (a mapped
527
- * type's member): there is no declaration name to ask at, so the answer is
528
- * asked at the use site's name, once per symbol. Same shape as
529
- * {@link implementationsOf}; nothing of the symbol's own is filtered out.
530
- */
531
- undeclaredImplRows(kind, node, symbol) {
532
- const cached = this.undeclaredImplementations.get(symbol);
533
- if (cached)
534
- return cached;
535
- let rows;
536
- try {
537
- const named = nameNodeOf(kind, node);
538
- rows = named ? this.implementationRows(named, []) : [];
539
- }
540
- catch {
541
- rows = [['impl', 0, 0, null, null, null, null, null, FLAG_CHECKER_THREW]];
542
- }
543
- this.undeclaredImplementations.set(symbol, rows);
544
- return rows;
545
- }
546
- /**
547
- * What go-to-implementation names for `declaration`, asked at its name:
548
- * every declaration the language service answers, except the symbol's own
549
- * (an overloaded method answers with its other signatures), in file and
550
- * position order.
551
- */
552
- implementationsOf(declaration) {
553
- const name = ts.getNameOfDeclaration(declaration) ?? declaration;
554
- const own = (this.checker.getSymbolAtLocation(name) ?? declaration.symbol)
555
- ?.declarations ?? [declaration];
556
- return this.implementationRows(name, own);
557
- }
558
- /** Go-to-implementation at `name`, minus results inside `own`, as sorted `impl` rows. */
559
- implementationRows(name, own) {
560
- const declaringFile = name.getSourceFile();
561
- const found = new Map();
562
- for (const location of this.service.getImplementationAtPosition(declaringFile.fileName, name.getStart(declaringFile)) ?? []) {
563
- const file = this.program.getSourceFile(location.fileName);
564
- if (!file)
565
- continue;
566
- const start = location.textSpan.start;
567
- if (own.some((d) => d.getSourceFile() === file && d.pos <= start && start < d.end))
568
- continue;
569
- found.set(`${location.fileName}\u0000${start}`, { file, start });
570
- }
571
- if (found.size === 0)
572
- return [];
573
- if (found.size > IMPL_CAP)
574
- return [['impl', 0, 0, null, null, null, null, null, FLAG_TOO_MANY]];
575
- const rows = [...found.values()].map(({ file, start }) => {
576
- const { declFile, declLine, declPackage } = this.located(file, start);
577
- const named = nodeStartingAt(file, start);
578
- const symbol = named ? this.checker.getSymbolAtLocation(named) : undefined;
579
- const flags = declFile === null ? FLAG_DECLARATION_UNNAMED : 0;
580
- return { start, row: ['impl', 0, 0, symbol ? ts.symbolName(symbol) : null, declFile, declLine, declPackage, null, flags] };
581
- });
582
- rows.sort((a, b) => compareNames(a.row[4], b.row[4]) || a.start - b.start);
583
- return rows.map(({ row }) => row);
584
- }
585
478
  /**
586
479
  * The declaration a member with none is attributed to (carrick#2448): the
587
480
  * mapped type node, or the alias naming it, of the receiver's type. A
@@ -709,7 +602,7 @@ class PositionWalker {
709
602
  }
710
603
  if (found.size < 2)
711
604
  return [];
712
- if (found.size > IMPL_CAP)
605
+ if (found.size > DECL_CAP)
713
606
  return [['decl', 0, 0, null, null, null, null, null, FLAG_TOO_MANY]];
714
607
  const rows = [...found].map((declaration) => {
715
608
  const file = declaration.getSourceFile();
@@ -858,40 +751,6 @@ function compareNames(a, b) {
858
751
  return -1;
859
752
  return a < b ? -1 : 1;
860
753
  }
861
- /**
862
- * Whether a declaration has no body for a call to run: an interface's or type
863
- * literal's method, a function-typed property signature, an abstract method or
864
- * property, or a function-typed class property or parameter with no
865
- * initializer. Its implementations are what go-to-implementation names.
866
- */
867
- function isBodyless(declaration, checker) {
868
- const functionTyped = () => checker.getTypeAtLocation(declaration).getNonNullableType().getCallSignatures().length > 0;
869
- if (ts.isMethodSignature(declaration))
870
- return true;
871
- if (ts.isPropertySignature(declaration))
872
- return functionTyped();
873
- if (ts.isMethodDeclaration(declaration) || ts.isPropertyDeclaration(declaration)) {
874
- if (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Abstract)
875
- return true;
876
- return ts.isPropertyDeclaration(declaration) && !declaration.initializer && functionTyped();
877
- }
878
- if (ts.isParameter(declaration))
879
- return !declaration.initializer && functionTyped();
880
- return false;
881
- }
882
- /** The innermost node of `file` that starts at `position` (an implementation's name). */
883
- function nodeStartingAt(file, position) {
884
- let found;
885
- const visit = (node) => {
886
- if (position < node.getStart(file) || position >= node.getEnd())
887
- return;
888
- if (node.getStart(file) === position)
889
- found = node;
890
- ts.forEachChild(node, visit);
891
- };
892
- ts.forEachChild(file, visit);
893
- return found;
894
- }
895
754
  /** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
896
755
  export function kindOf(node) {
897
756
  if (ts.isCallExpression(node) || ts.isNewExpression(node) || ts.isTaggedTemplateExpression(node)) {
@@ -918,7 +777,6 @@ function nameNodeOf(kind, node) {
918
777
  return referenceName(node.left);
919
778
  case 'ident':
920
779
  return node;
921
- case 'impl':
922
780
  case 'decl':
923
781
  return undefined;
924
782
  case 'jsx':
@@ -115,6 +115,12 @@ export const MACHINERY_MEMBER_INDICATORS = new Set([
115
115
  'clone',
116
116
  'json',
117
117
  'statusText',
118
+ // FROZEN (old path, 2026-10-21): the reply-object half below only (the
119
+ // fetch half above and the threshold stay). Deletions and safety fixes
120
+ // only. Fixes for wrong or missing types go in labelled reply objects
121
+ // (carrick#2472 C4) and the language library rows (carrick#2445). Delete
122
+ // when a delete-and-replay on the four repos moves no row wrongly, after
123
+ // carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
118
124
  // Node http ServerResponse / reply-object surface
119
125
  'statusCode',
120
126
  'statusMessage',
@@ -1628,6 +1634,8 @@ export class TypeInferrer {
1628
1634
  branches = [
1629
1635
  ...returnedBranches,
1630
1636
  ...statementSends
1637
+ // FROZEN (old path, 2026-10-21): the `statementEndsFunctionPath` test
1638
+ // here; see that method.
1631
1639
  .filter((s) => s.send === locatedBranch || this.statementEndsFunctionPath(s.statement, func))
1632
1640
  .map((s) => s.send),
1633
1641
  ];
@@ -1702,6 +1710,10 @@ export class TypeInferrer {
1702
1710
  }
1703
1711
  return sends;
1704
1712
  }
1713
+ // FROZEN (old path, 2026-10-21): this method and its one use. Deletions and
1714
+ // safety fixes only. Fixes for wrong or missing types go in send versus
1715
+ // setter, read from the framework label (carrick#2472 C4). Delete when C4 is
1716
+ // released (carrick#2472, C6).
1705
1717
  /**
1706
1718
  * True when nothing in `func` runs after `statement`: a bare `return;`
1707
1719
  * follows it, or it is the last statement of its block and the statement
@@ -1748,6 +1760,11 @@ export class TypeInferrer {
1748
1760
  return false;
1749
1761
  }
1750
1762
  }
1763
+ // FROZEN (old path, 2026-10-21): this method's structural test (the whole
1764
+ // method is the upper bound). Deletions and safety fixes only. Fixes for
1765
+ // wrong or missing types go in labelled sends (carrick#2472 C4). Delete when
1766
+ // a delete-and-replay on the four repos moves no row wrongly, after
1767
+ // carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
1751
1768
  /**
1752
1769
  * True when `candidate` is a response send: a call or `new` whose result is
1753
1770
  * transport machinery (by the extraction config's verified rule or the
@@ -5288,6 +5305,10 @@ export class TypeInferrer {
5288
5305
  }
5289
5306
  return flattened;
5290
5307
  }
5308
+ // FROZEN (old path, 2026-10-21): this method only. Deletions and safety fixes
5309
+ // only. Fixes for wrong or missing types go in labelled sends (carrick#2472
5310
+ // C4). Delete when a delete-and-replay on the four repos moves no row
5311
+ // wrongly, after carrick-cloud#2379 and carrick#2445 (carrick#2472, C6).
5291
5312
  /**
5292
5313
  * The callees these returned expressions PROVE are response serialisers.
5293
5314
  *
@@ -411,6 +411,18 @@ export interface Yields {
411
411
  export: string;
412
412
  side: string;
413
413
  }
414
+ /**
415
+ * Where a route handler's request holds its path and method (#2474 R0): the
416
+ * handler's parameter `param` is the request, and each of the others is a
417
+ * member path read off it, relative to that parameter (`['path']`,
418
+ * `['raw', 'url']`). At least one of `path` or `url`.
419
+ */
420
+ export interface RequestAt {
421
+ param: number;
422
+ path?: string[];
423
+ url?: string[];
424
+ method?: string[];
425
+ }
414
426
  /** The closed role list; the role alone picks the checks a claim needs. */
415
427
  export type LibraryRole = 'http_client' | 'graphql_client' | 'broker' | 'in_process_bus' | 'socket' | 'server_framework' | 'none';
416
428
  /** What an `op` claim does on the wire. */
@@ -467,6 +479,15 @@ export type LibraryClaim = {
467
479
  key_labels?: Record<string, KeyLabel>;
468
480
  name_scope?: NameScope;
469
481
  picker?: string;
482
+ /**
483
+ * Route fields (#2474 R0): the member runs the function here and hands
484
+ * it, as `yields` says, an instance bound under `name`. Comes with
485
+ * `yields`.
486
+ */
487
+ runs?: ClaimSlot;
488
+ yields?: Yields;
489
+ /** Route fields (#2474 R0): the instance handed here is served under `name`. */
490
+ mounts?: ClaimSlot;
470
491
  } | {
471
492
  kind: 'op';
472
493
  op: LibraryOp;
@@ -502,6 +523,8 @@ export type LibraryClaim = {
502
523
  runs?: RunsSlot;
503
524
  /** Label v2, `receive` only: what the handler is handed. */
504
525
  yields?: Yields;
526
+ /** Route fields (#2474 R0), `receive` only: where the handler's request holds its path and method. */
527
+ request?: RequestAt;
505
528
  } | {
506
529
  /** A name the library emits itself, spelled in one of `member`'s parameters. */
507
530
  kind: 'reserved';
@@ -538,6 +561,8 @@ export interface LibraryCheck {
538
561
  export interface VerifyLibraryClaimsRequest extends BaseRequest {
539
562
  action: 'verify_library_claims';
540
563
  from_dir: string;
564
+ /** The scanned repo's root, as on `init`: a verdict's `declarations` are named from it, as position rows are. */
565
+ scan_root?: string;
541
566
  checks: LibraryCheck[];
542
567
  /** Checks not reached in time come back `unchecked` with reason `budget`; null or absent is the default. */
543
568
  budget_ms?: number | null;
@@ -765,8 +790,6 @@ export interface PositionRowsCounts {
765
790
  any: number;
766
791
  /** Rows whose type is the compiler's error type (flag 2). */
767
792
  error_type: number;
768
- /** `impl` rows: implementations of a bodyless declaration, or one past the cap (carrick#2430). */
769
- impl: number;
770
793
  /** `jsx` rows: one per JSX opening or self-closing element. */
771
794
  jsx: number;
772
795
  /** The rows file's size on disk, compressed. */
@@ -827,6 +850,11 @@ export interface RetypeCheckResponse extends BaseResponse {
827
850
  outcomes?: RetypeOutcome[];
828
851
  errors?: string[];
829
852
  }
853
+ /** Where a verified op's member is declared on its receiver, as a position row's `decl_file` and `decl_line`. */
854
+ export interface SemanticsDeclaration {
855
+ decl_file: string;
856
+ decl_line: number;
857
+ }
830
858
  /**
831
859
  * The verdict on one `(claim_id, receiver)` pair.
832
860
  *
@@ -840,6 +868,8 @@ export interface SemanticsResult {
840
868
  verdict: 'verified' | 'failed' | 'unchecked';
841
869
  /** Absent exactly when verified. */
842
870
  reason?: string;
871
+ /** Verified op verdicts only: the declarations its member resolves to on the receiver. */
872
+ declarations?: SemanticsDeclaration[];
843
873
  }
844
874
  /** How one package of the request resolved, for logs. */
845
875
  export interface SemanticsModule {