carrick 0.3.117 → 0.3.119
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carrick",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.119",
|
|
4
4
|
"carrickCache": {
|
|
5
5
|
"cache_version": 22,
|
|
6
6
|
"intent_cache_version": 2
|
|
@@ -113,11 +113,11 @@
|
|
|
113
113
|
"zod": "^3.23.0"
|
|
114
114
|
},
|
|
115
115
|
"optionalDependencies": {
|
|
116
|
-
"@carrick-tools/cli-darwin-arm64": "0.3.
|
|
117
|
-
"@carrick-tools/cli-darwin-x64": "0.3.
|
|
118
|
-
"@carrick-tools/cli-linux-arm64": "0.3.
|
|
119
|
-
"@carrick-tools/cli-linux-x64": "0.3.
|
|
120
|
-
"@carrick-tools/cli-win32-x64": "0.3.
|
|
116
|
+
"@carrick-tools/cli-darwin-arm64": "0.3.119",
|
|
117
|
+
"@carrick-tools/cli-darwin-x64": "0.3.119",
|
|
118
|
+
"@carrick-tools/cli-linux-arm64": "0.3.119",
|
|
119
|
+
"@carrick-tools/cli-linux-x64": "0.3.119",
|
|
120
|
+
"@carrick-tools/cli-win32-x64": "0.3.119"
|
|
121
121
|
},
|
|
122
122
|
"devDependencies": {
|
|
123
123
|
"@types/node": "^24.13.3"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "carrick",
|
|
3
3
|
"description": "Claude Code plugin for Carrick, which indexes TypeScript codebases across service and repository boundaries. After each edit it adds the file's routes, calls, and cross-service type mismatches to the session, and it registers Carrick's language server. It pairs with the Carrick MCP server, which lets agents search functions by intent rather than name.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.119",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Carrick",
|
|
7
7
|
"email": "hello@carrick.tools"
|
|
@@ -89,6 +89,20 @@ const RUNTIME_TYPE_PACKAGES = new Set([
|
|
|
89
89
|
* package's, never the runtime module.
|
|
90
90
|
*/
|
|
91
91
|
const RUNTIME_MODULE = /^node:/;
|
|
92
|
+
/**
|
|
93
|
+
* A key for the language's own library (carrick#2445): `lib:<lib file>`
|
|
94
|
+
* (`lib:lib.dom.d.ts`) names the globals that default-library file declares.
|
|
95
|
+
* Its installed version is the compiler's, so the store keys it
|
|
96
|
+
* `lib:lib.dom.d.ts@<typescript version>`. A global has no specifier: the
|
|
97
|
+
* probe reads it as `globalThis.<name>`, and its home is the default-library
|
|
98
|
+
* file set (`DEFAULT_LIBRARY_HOME`).
|
|
99
|
+
*/
|
|
100
|
+
const DEFAULT_LIBRARY = /^lib:/;
|
|
101
|
+
/**
|
|
102
|
+
* The owner a home names for every default-library file. No package name
|
|
103
|
+
* holds a `:`, so only a `lib:` key's home ever holds it.
|
|
104
|
+
*/
|
|
105
|
+
const DEFAULT_LIBRARY_HOME = 'lib:';
|
|
92
106
|
/** A dependency range that names the service's own source, not a registry release. */
|
|
93
107
|
const LOCAL_RANGE = /^(workspace|file|link|portal):/;
|
|
94
108
|
/** A resolved module lands on TypeScript: a declaration file or source. */
|
|
@@ -283,7 +297,7 @@ export class LibraryClaimsVerifier {
|
|
|
283
297
|
}
|
|
284
298
|
const importLines = importKeys.map((key, i) => {
|
|
285
299
|
const [pkg, name] = JSON.parse(key);
|
|
286
|
-
return
|
|
300
|
+
return probeLine(pkg, name, `__carrick_e${i}`);
|
|
287
301
|
});
|
|
288
302
|
// One statement per receiver a message check's maker or scope claim
|
|
289
303
|
// builds, after the imports: that receiver built with no argument and no
|
|
@@ -322,7 +336,8 @@ export class LibraryClaimsVerifier {
|
|
|
322
336
|
const exportReads = new Map();
|
|
323
337
|
const httpReceivers = new Map();
|
|
324
338
|
const judged = new Map();
|
|
325
|
-
|
|
339
|
+
/** The probe's import of the check's export, or its read of a global (`probeLine`). */
|
|
340
|
+
const statementOf = (check) => file.statements[importIndex.get(exportKey(check))];
|
|
326
341
|
/** The probe's no-argument build of `receiver` on the check's export, resolved. */
|
|
327
342
|
const atDefaults = (check, receiver) => {
|
|
328
343
|
const index = builtIndex.get(builtKey(check, receiver));
|
|
@@ -333,7 +348,9 @@ export class LibraryClaimsVerifier {
|
|
|
333
348
|
const key = `${runtimeModules}\u0000${check.package}`;
|
|
334
349
|
let moduleRead = moduleReads.get(key);
|
|
335
350
|
if (!moduleRead) {
|
|
336
|
-
moduleRead =
|
|
351
|
+
moduleRead = DEFAULT_LIBRARY.test(check.package)
|
|
352
|
+
? reader.readDefaultLibrary(check.package)
|
|
353
|
+
: reader.readModule(check.package, statementOf(check), runtimeModules);
|
|
337
354
|
moduleReads.set(key, moduleRead);
|
|
338
355
|
}
|
|
339
356
|
return moduleRead;
|
|
@@ -342,7 +359,9 @@ export class LibraryClaimsVerifier {
|
|
|
342
359
|
const key = exportKey(check);
|
|
343
360
|
let exportRead = exportReads.get(key);
|
|
344
361
|
if (!exportRead) {
|
|
345
|
-
exportRead =
|
|
362
|
+
exportRead = DEFAULT_LIBRARY.test(check.package)
|
|
363
|
+
? reader.readGlobal(check.package, statementOf(check))
|
|
364
|
+
: reader.readExport(statementOf(check));
|
|
346
365
|
exportReads.set(key, exportRead);
|
|
347
366
|
}
|
|
348
367
|
return exportRead;
|
|
@@ -534,15 +553,25 @@ export class LibraryClaimsVerifier {
|
|
|
534
553
|
return { surfaces: [], surface_sha256: fullSurfaceSha256([]) };
|
|
535
554
|
// Each specifier twice: as a namespace, which holds its named exports, and
|
|
536
555
|
// as a default import, which is how a module that exports a value whole
|
|
537
|
-
// (`export =`) is imported as `default`.
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
556
|
+
// (`export =`) is imported as `default`. A `lib:` key imports nothing: its
|
|
557
|
+
// globals are read from the file it names.
|
|
558
|
+
const imports = [];
|
|
559
|
+
const importAt = packages.map((pkg, i) => {
|
|
560
|
+
if (DEFAULT_LIBRARY.test(pkg))
|
|
561
|
+
return undefined;
|
|
562
|
+
const at = imports.length;
|
|
563
|
+
imports.push(`import * as __carrick_ns${i} from ${JSON.stringify(pkg)};`, importLine(pkg, 'default', `__carrick_d${i}`));
|
|
564
|
+
return at;
|
|
545
565
|
});
|
|
566
|
+
const probeOf = (file, i) => {
|
|
567
|
+
const at = importAt[i];
|
|
568
|
+
return at === undefined
|
|
569
|
+
? { defaultLibrary: packages[i] }
|
|
570
|
+
: {
|
|
571
|
+
namespace: file.statements[at],
|
|
572
|
+
defaultImport: file.statements[at + 1],
|
|
573
|
+
};
|
|
574
|
+
};
|
|
546
575
|
const makers = this.readProbe(fromDir, imports, (_checker, reader, file) => packages.map((pkg, i) => reader.makersOf(pkg, probeOf(file, i), only[pkg])));
|
|
547
576
|
const importLines = [];
|
|
548
577
|
const buildLines = [];
|
|
@@ -550,7 +579,7 @@ export class LibraryClaimsVerifier {
|
|
|
550
579
|
packages.forEach((pkg, i) => {
|
|
551
580
|
for (const { export: name, receivers } of makers[i]) {
|
|
552
581
|
const local = `__carrick_e${importLines.length}`;
|
|
553
|
-
importLines.push(
|
|
582
|
+
importLines.push(probeLine(pkg, name, local));
|
|
554
583
|
for (const receiver of receivers) {
|
|
555
584
|
const expression = receiverExpression(local, receiver);
|
|
556
585
|
if (expression === undefined)
|
|
@@ -744,6 +773,18 @@ function typeSymbols(type) {
|
|
|
744
773
|
}
|
|
745
774
|
return symbols;
|
|
746
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* The probe's read of one export, bound to `local`: its import, or for a
|
|
778
|
+
* `lib:` key the global itself, as `globalThis` holds it. A block-scoped
|
|
779
|
+
* global (`declare const`) is not on `globalThis`; no default-library file
|
|
780
|
+
* declares one.
|
|
781
|
+
*/
|
|
782
|
+
function probeLine(pkg, name, local) {
|
|
783
|
+
if (!DEFAULT_LIBRARY.test(pkg))
|
|
784
|
+
return importLine(pkg, name, local);
|
|
785
|
+
const access = IDENTIFIER.test(name) ? `.${name}` : `[${JSON.stringify(name)}]`;
|
|
786
|
+
return `const ${local} = globalThis${access};`;
|
|
787
|
+
}
|
|
747
788
|
/** The probe's import of one export, bound to `local`. */
|
|
748
789
|
function importLine(pkg, name, local) {
|
|
749
790
|
const specifier = JSON.stringify(pkg);
|
|
@@ -943,6 +984,54 @@ class DeclarationReader {
|
|
|
943
984
|
?.redirectInfo?.redirectTarget;
|
|
944
985
|
return target === file && this.isInstalledFile(file.fileName);
|
|
945
986
|
}
|
|
987
|
+
/**
|
|
988
|
+
* A `lib:` key: the default-library file it names, as the program's own
|
|
989
|
+
* `lib` setting loads it, read by the compiler that built the program. A
|
|
990
|
+
* file the program does not load is no module reason: each global answers
|
|
991
|
+
* `export_missing` (`readGlobal`).
|
|
992
|
+
*/
|
|
993
|
+
readDefaultLibrary(pkg) {
|
|
994
|
+
const entry = { package: pkg, installed_version: ts.version };
|
|
995
|
+
const file = this.defaultLibraryFile(pkg);
|
|
996
|
+
if (file)
|
|
997
|
+
entry.resolved_file = file.fileName;
|
|
998
|
+
return { entry };
|
|
999
|
+
}
|
|
1000
|
+
/** The default-library file a `lib:` key names, when the program loads it. */
|
|
1001
|
+
defaultLibraryFile(pkg) {
|
|
1002
|
+
const name = pkg.slice('lib:'.length);
|
|
1003
|
+
return this.program
|
|
1004
|
+
.getSourceFiles()
|
|
1005
|
+
.find(file => this.program.isSourceFileDefaultLibrary(file) && path.basename(file.fileName) === name);
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* `T` for a global (`probeLine`'s `globalThis` read): the type the probe's
|
|
1009
|
+
* local gets, and the global it names. The global must be a value the
|
|
1010
|
+
* key's lib file declares; one only another file declares (the runtime's
|
|
1011
|
+
* types, the service) is missing from it.
|
|
1012
|
+
*/
|
|
1013
|
+
readGlobal(pkg, statement) {
|
|
1014
|
+
const declaration = statement && ts.isVariableStatement(statement) ? statement.declarationList.declarations[0] : undefined;
|
|
1015
|
+
const access = declaration?.initializer;
|
|
1016
|
+
const name = access && ts.isPropertyAccessExpression(access)
|
|
1017
|
+
? access.name
|
|
1018
|
+
: access && ts.isElementAccessExpression(access)
|
|
1019
|
+
? access.argumentExpression
|
|
1020
|
+
: undefined;
|
|
1021
|
+
const target = name ? this.checker.getSymbolAtLocation(name) : undefined;
|
|
1022
|
+
const file = this.defaultLibraryFile(pkg);
|
|
1023
|
+
if (!declaration ||
|
|
1024
|
+
!target ||
|
|
1025
|
+
!file ||
|
|
1026
|
+
!(target.flags & ts.SymbolFlags.Value) ||
|
|
1027
|
+
!(target.declarations ?? []).some(each => each.getSourceFile() === file)) {
|
|
1028
|
+
return { reason: 'export_missing' };
|
|
1029
|
+
}
|
|
1030
|
+
const type = this.checker.getTypeAtLocation(declaration.name);
|
|
1031
|
+
if (this.isOpenTop(type))
|
|
1032
|
+
return { reason: 'export_untyped' };
|
|
1033
|
+
return { value: { type, target } };
|
|
1034
|
+
}
|
|
946
1035
|
/** `T`: the type the probe's import gets, and the declaration it names. */
|
|
947
1036
|
readExport(declaration) {
|
|
948
1037
|
const clause = declaration.importClause;
|
|
@@ -1038,6 +1127,9 @@ class DeclarationReader {
|
|
|
1038
1127
|
* sub-object's, along a claim's `path`).
|
|
1039
1128
|
*/
|
|
1040
1129
|
homeOf(pkg, symbols, base) {
|
|
1130
|
+
// A global's home is the default-library file set, and only that.
|
|
1131
|
+
if (DEFAULT_LIBRARY.test(pkg))
|
|
1132
|
+
return base ?? new Set([DEFAULT_LIBRARY_HOME]);
|
|
1041
1133
|
const named = packageNameOf(pkg);
|
|
1042
1134
|
const home = new Set(base ?? [named, typesPackageOf(named)]);
|
|
1043
1135
|
const runtime = RUNTIME_MODULE.test(pkg) || RUNTIME_TYPE_PACKAGES.has(named) || RUNTIME_TYPE_PACKAGES.has(typesPackageOf(named));
|
|
@@ -1061,6 +1153,8 @@ class DeclarationReader {
|
|
|
1061
1153
|
return installed === undefined ? undefined : installed.packageName ?? installed.directoryName;
|
|
1062
1154
|
}
|
|
1063
1155
|
isOwnFile(file, home) {
|
|
1156
|
+
if (this.program.isSourceFileDefaultLibrary(file))
|
|
1157
|
+
return home.has(DEFAULT_LIBRARY_HOME);
|
|
1064
1158
|
const owner = this.packageOf(file);
|
|
1065
1159
|
return owner !== undefined && home.has(owner);
|
|
1066
1160
|
}
|
|
@@ -2231,7 +2325,7 @@ class DeclarationReader {
|
|
|
2231
2325
|
this.setPackage(pkg);
|
|
2232
2326
|
// Runtime modules on: a `node:` specifier is listed from the runtime's
|
|
2233
2327
|
// types package, as the message roles read it.
|
|
2234
|
-
const moduleRead = this.readModule(pkg, probe.namespace, true);
|
|
2328
|
+
const moduleRead = 'defaultLibrary' in probe ? this.readDefaultLibrary(pkg) : this.readModule(pkg, probe.namespace, true);
|
|
2235
2329
|
const surface = { package: pkg, truncated: 0, exports: [] };
|
|
2236
2330
|
if (moduleRead.entry.resolved_file)
|
|
2237
2331
|
surface.resolved_file = moduleRead.entry.resolved_file;
|
|
@@ -2294,7 +2388,7 @@ class DeclarationReader {
|
|
|
2294
2388
|
*/
|
|
2295
2389
|
makersOf(pkg, probe, only) {
|
|
2296
2390
|
this.setPackage(pkg);
|
|
2297
|
-
if (this.readModule(pkg, probe.namespace, true).reason)
|
|
2391
|
+
if ('namespace' in probe && this.readModule(pkg, probe.namespace, true).reason)
|
|
2298
2392
|
return [];
|
|
2299
2393
|
return (this.listedExports(probe, only) ?? [])
|
|
2300
2394
|
.filter(({ type }) => !this.isOpenTop(type))
|
|
@@ -2314,6 +2408,8 @@ class DeclarationReader {
|
|
|
2314
2408
|
* resolves to no module symbol.
|
|
2315
2409
|
*/
|
|
2316
2410
|
listedExports(probe, only) {
|
|
2411
|
+
if ('defaultLibrary' in probe)
|
|
2412
|
+
return this.listedGlobals(probe.defaultLibrary, only);
|
|
2317
2413
|
const moduleSymbol = this.checker.getSymbolAtLocation(probe.namespace.moduleSpecifier);
|
|
2318
2414
|
if (!moduleSymbol)
|
|
2319
2415
|
return undefined;
|
|
@@ -2339,6 +2435,23 @@ class DeclarationReader {
|
|
|
2339
2435
|
}
|
|
2340
2436
|
return listed.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
2341
2437
|
}
|
|
2438
|
+
/**
|
|
2439
|
+
* The value globals a `lib:` key's file declares (every one, or only those
|
|
2440
|
+
* `only` names), sorted by name, each with its declared type: none when the
|
|
2441
|
+
* program's `lib` setting does not load the file.
|
|
2442
|
+
*/
|
|
2443
|
+
listedGlobals(pkg, only) {
|
|
2444
|
+
const file = this.defaultLibraryFile(pkg);
|
|
2445
|
+
if (!file)
|
|
2446
|
+
return [];
|
|
2447
|
+
return this.checker
|
|
2448
|
+
.getSymbolsInScope(this.probe, ts.SymbolFlags.Value)
|
|
2449
|
+
.filter(symbol => (symbol.flags & ts.SymbolFlags.Prototype) === 0 &&
|
|
2450
|
+
(only === undefined || only.includes(symbol.getName())) &&
|
|
2451
|
+
(symbol.declarations ?? []).some(declaration => declaration.getSourceFile() === file))
|
|
2452
|
+
.map(symbol => ({ name: symbol.getName(), target: symbol, type: this.checker.getTypeOfSymbol(symbol) }))
|
|
2453
|
+
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
2454
|
+
}
|
|
2342
2455
|
/**
|
|
2343
2456
|
* The ways an export makes a receiver, in listing order: calling it
|
|
2344
2457
|
* (`instance:()`), constructing it (`instance:new`), then calling or
|
|
@@ -2896,16 +3009,21 @@ class DeclarationReader {
|
|
|
2896
3009
|
}
|
|
2897
3010
|
/**
|
|
2898
3011
|
* What a factory builds says nothing: the type itself, or any branch of a
|
|
2899
|
-
* conditional return type, does.
|
|
3012
|
+
* conditional return type, does. A conditional whose branches lead back to
|
|
3013
|
+
* itself (a recursive alias) is read once: `seen` holds the conditionals
|
|
3014
|
+
* on the way down, and one met again says nothing new.
|
|
2900
3015
|
*/
|
|
2901
|
-
returnSaysNothing(type) {
|
|
3016
|
+
returnSaysNothing(type, seen = new Set()) {
|
|
2902
3017
|
if (this.saysNothing(type))
|
|
2903
3018
|
return true;
|
|
2904
3019
|
return this.parts(type).some(part => {
|
|
2905
3020
|
if (!(part.flags & ts.TypeFlags.Conditional))
|
|
2906
3021
|
return false;
|
|
2907
3022
|
const node = part.root.node;
|
|
2908
|
-
|
|
3023
|
+
if (seen.has(node))
|
|
3024
|
+
return false;
|
|
3025
|
+
seen.add(node);
|
|
3026
|
+
return [node.trueType, node.falseType].some(branch => this.returnSaysNothing(this.checker.getTypeFromTypeNode(branch), seen));
|
|
2909
3027
|
});
|
|
2910
3028
|
}
|
|
2911
3029
|
/** `any` (an unresolved type included) or `unknown`. */
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Per-position compiler rows for a service (carrick#2405, move 2 of the fact
|
|
3
3
|
* layer). Record-only: nothing in a scan reads the file yet.
|
|
4
4
|
*
|
|
5
|
-
* For every source file the scanner sends, one row for each position of
|
|
5
|
+
* For every source file the scanner sends, one row for each position of six
|
|
6
6
|
* kinds, each holding what the compiler knows there: the symbol, the
|
|
7
7
|
* declaration it is attributed to (file and line), the package that declares
|
|
8
8
|
* it and the type text.
|
|
@@ -13,11 +13,22 @@
|
|
|
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
|
+
* | `jsx` | a JSX opening or self-closing element's tag | the tag's name | the tag |
|
|
16
18
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* An `impl` row (version 2, carrick#2430) follows the `call` or `member` row
|
|
20
|
+
* it belongs to and has its span: one per declaration that go-to-implementation
|
|
21
|
+
* names for that row's declaration, when that declaration is an interface's
|
|
22
|
+
* or type literal's method, a function-typed property signature, an abstract
|
|
23
|
+
* member, or a function-typed class property or parameter with no
|
|
24
|
+
* initializer. Its `decl_*` name the implementing declaration. Past
|
|
25
|
+
* {@link IMPL_CAP} the row is one, with {@link FLAG_TOO_MANY} and nothing else.
|
|
26
|
+
*
|
|
27
|
+
* A row's key is `(start, end, kind)`, except that several `impl` rows share
|
|
28
|
+
* one: TypeScript `getStart()`/`getEnd()` of the node, in UTF-16 code units
|
|
29
|
+
* from the start of the file, which is what the scanner's `sidecar_position`
|
|
30
|
+
* gives for an SWC span. Every path written is relative to the scanned repo's
|
|
31
|
+
* root, with `/` separators.
|
|
21
32
|
*
|
|
22
33
|
* Each file is read in the program of the project that owns it
|
|
23
34
|
* (`ProjectLoader.projectKeyFor`, carrick#1604), one owner's program at a
|
|
@@ -33,7 +44,7 @@ import { ts } from 'ts-morph';
|
|
|
33
44
|
import type { ProjectLoader } from './project-loader.js';
|
|
34
45
|
import type { PositionRowsCounts } from './types.js';
|
|
35
46
|
/** The format's version, written in the header. */
|
|
36
|
-
export declare const POSITION_ROWS_VERSION =
|
|
47
|
+
export declare const POSITION_ROWS_VERSION = 2;
|
|
37
48
|
/** The type is `any`, declared or inferred. Never set with the error type. */
|
|
38
49
|
export declare const FLAG_ANY = 1;
|
|
39
50
|
/** The type is the compiler's error type: something did not resolve. */
|
|
@@ -42,7 +53,22 @@ export declare const FLAG_ERROR_TYPE = 2;
|
|
|
42
53
|
export declare const FLAG_CHECKER_THREW = 4;
|
|
43
54
|
/** The declaration is outside the checkout, not the default library and in no installed package. */
|
|
44
55
|
export declare const FLAG_DECLARATION_UNNAMED = 8;
|
|
45
|
-
|
|
56
|
+
/** An `impl` row standing for more implementations than {@link IMPL_CAP}; it names none. */
|
|
57
|
+
export declare const FLAG_TOO_MANY = 16;
|
|
58
|
+
/**
|
|
59
|
+
* The declaration is published under the row's symbol name: its module
|
|
60
|
+
* exports it by that name, or it is a global (a script's top-level
|
|
61
|
+
* declaration, or one in `declare global`). A member of a class, interface,
|
|
62
|
+
* type literal or object literal never is (carrick#2508).
|
|
63
|
+
*/
|
|
64
|
+
export declare const FLAG_PUBLISHED = 32;
|
|
65
|
+
/** The declaration is what its module exports as `export =` or `default` (carrick#2508). */
|
|
66
|
+
export declare const FLAG_MODULE_DEFAULT = 64;
|
|
67
|
+
/** The most implementations one declaration's `impl` rows list. */
|
|
68
|
+
export declare const IMPL_CAP = 16;
|
|
69
|
+
/** The kinds {@link kindOf} gives a node; `impl` and `jsx` rows are written beside them. */
|
|
70
|
+
export type NodeKind = 'call' | 'member' | 'compare' | 'ident';
|
|
71
|
+
export type PositionKind = NodeKind | 'impl' | 'jsx';
|
|
46
72
|
/** `[kind, start, end, symbol, decl_file, decl_line, decl_package, type_text, flags]` */
|
|
47
73
|
export type PositionRow = [
|
|
48
74
|
PositionKind,
|
|
@@ -75,4 +101,4 @@ export interface PositionRowsOptions {
|
|
|
75
101
|
*/
|
|
76
102
|
export declare function writePositionRows(options: PositionRowsOptions): PositionRowsCounts;
|
|
77
103
|
/** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
|
|
78
|
-
export declare function kindOf(node: ts.Node):
|
|
104
|
+
export declare function kindOf(node: ts.Node): NodeKind | undefined;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Per-position compiler rows for a service (carrick#2405, move 2 of the fact
|
|
3
3
|
* layer). Record-only: nothing in a scan reads the file yet.
|
|
4
4
|
*
|
|
5
|
-
* For every source file the scanner sends, one row for each position of
|
|
5
|
+
* For every source file the scanner sends, one row for each position of six
|
|
6
6
|
* kinds, each holding what the compiler knows there: the symbol, the
|
|
7
7
|
* declaration it is attributed to (file and line), the package that declares
|
|
8
8
|
* it and the type text.
|
|
@@ -13,11 +13,22 @@
|
|
|
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
|
+
* | `jsx` | a JSX opening or self-closing element's tag | the tag's name | the tag |
|
|
16
18
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* An `impl` row (version 2, carrick#2430) follows the `call` or `member` row
|
|
20
|
+
* it belongs to and has its span: one per declaration that go-to-implementation
|
|
21
|
+
* names for that row's declaration, when that declaration is an interface's
|
|
22
|
+
* or type literal's method, a function-typed property signature, an abstract
|
|
23
|
+
* member, or a function-typed class property or parameter with no
|
|
24
|
+
* initializer. Its `decl_*` name the implementing declaration. Past
|
|
25
|
+
* {@link IMPL_CAP} the row is one, with {@link FLAG_TOO_MANY} and nothing else.
|
|
26
|
+
*
|
|
27
|
+
* A row's key is `(start, end, kind)`, except that several `impl` rows share
|
|
28
|
+
* one: TypeScript `getStart()`/`getEnd()` of the node, in UTF-16 code units
|
|
29
|
+
* from the start of the file, which is what the scanner's `sidecar_position`
|
|
30
|
+
* gives for an SWC span. Every path written is relative to the scanned repo's
|
|
31
|
+
* root, with `/` separators.
|
|
21
32
|
*
|
|
22
33
|
* Each file is read in the program of the project that owns it
|
|
23
34
|
* (`ProjectLoader.projectKeyFor`, carrick#1604), one owner's program at a
|
|
@@ -38,7 +49,7 @@ import { WriteGuard, installedPackageDirectory, isDeclarationFileName, moduleExp
|
|
|
38
49
|
import { attributedDeclaration, installedPackageOf } from './declaration-attribution.js';
|
|
39
50
|
import { typeToStringAsDeclared } from './print-type.js';
|
|
40
51
|
/** The format's version, written in the header. */
|
|
41
|
-
export const POSITION_ROWS_VERSION =
|
|
52
|
+
export const POSITION_ROWS_VERSION = 2;
|
|
42
53
|
/** The type is `any`, declared or inferred. Never set with the error type. */
|
|
43
54
|
export const FLAG_ANY = 1;
|
|
44
55
|
/** The type is the compiler's error type: something did not resolve. */
|
|
@@ -47,6 +58,19 @@ export const FLAG_ERROR_TYPE = 2;
|
|
|
47
58
|
export const FLAG_CHECKER_THREW = 4;
|
|
48
59
|
/** The declaration is outside the checkout, not the default library and in no installed package. */
|
|
49
60
|
export const FLAG_DECLARATION_UNNAMED = 8;
|
|
61
|
+
/** An `impl` row standing for more implementations than {@link IMPL_CAP}; it names none. */
|
|
62
|
+
export const FLAG_TOO_MANY = 16;
|
|
63
|
+
/**
|
|
64
|
+
* The declaration is published under the row's symbol name: its module
|
|
65
|
+
* exports it by that name, or it is a global (a script's top-level
|
|
66
|
+
* declaration, or one in `declare global`). A member of a class, interface,
|
|
67
|
+
* type literal or object literal never is (carrick#2508).
|
|
68
|
+
*/
|
|
69
|
+
export const FLAG_PUBLISHED = 32;
|
|
70
|
+
/** The declaration is what its module exports as `export =` or `default` (carrick#2508). */
|
|
71
|
+
export const FLAG_MODULE_DEFAULT = 64;
|
|
72
|
+
/** The most implementations one declaration's `impl` rows list. */
|
|
73
|
+
export const IMPL_CAP = 16;
|
|
50
74
|
const COMPARE_OPERATORS = new Set([
|
|
51
75
|
ts.SyntaxKind.EqualsEqualsEqualsToken,
|
|
52
76
|
ts.SyntaxKind.ExclamationEqualsEqualsToken,
|
|
@@ -79,6 +103,8 @@ export function writePositionRows(options) {
|
|
|
79
103
|
resolved: 0,
|
|
80
104
|
any: 0,
|
|
81
105
|
error_type: 0,
|
|
106
|
+
impl: 0,
|
|
107
|
+
jsx: 0,
|
|
82
108
|
bytes: 0,
|
|
83
109
|
load_ms: 0,
|
|
84
110
|
walk_ms: 0,
|
|
@@ -127,11 +153,16 @@ export function writePositionRows(options) {
|
|
|
127
153
|
}
|
|
128
154
|
read.set(index, sourceFile?.getFilePath());
|
|
129
155
|
}
|
|
130
|
-
// Built once, after every file the group adds.
|
|
131
|
-
|
|
156
|
+
// Built once, after every file the group adds. The language service's
|
|
157
|
+
// program is the one walked, so go-to-implementation answers over it
|
|
158
|
+
// and no second program is built.
|
|
159
|
+
const service = project.getLanguageService().compilerObject;
|
|
160
|
+
const program = service.getProgram();
|
|
161
|
+
if (!program)
|
|
162
|
+
throw new Error('the language service built no program');
|
|
132
163
|
counts.load_ms += performance.now() - loadStarted;
|
|
133
164
|
const walkStarted = performance.now();
|
|
134
|
-
const walker = new PositionWalker(program, paths, loader);
|
|
165
|
+
const walker = new PositionWalker(program, service, paths, loader);
|
|
135
166
|
const missed = [];
|
|
136
167
|
for (const index of indexes) {
|
|
137
168
|
if (!read.has(index))
|
|
@@ -151,22 +182,23 @@ export function writePositionRows(options) {
|
|
|
151
182
|
counts.any += 1;
|
|
152
183
|
if (row[8] & FLAG_ERROR_TYPE)
|
|
153
184
|
counts.error_type += 1;
|
|
185
|
+
if (row[0] === 'impl')
|
|
186
|
+
counts.impl += 1;
|
|
187
|
+
if (row[0] === 'jsx')
|
|
188
|
+
counts.jsx += 1;
|
|
154
189
|
}
|
|
155
190
|
lines[index] = fileLine({ f: paths.relative(files[index]), h: hashes.get(index), r: rows });
|
|
156
191
|
options.onFile?.();
|
|
157
192
|
}
|
|
158
193
|
counts.walk_ms += performance.now() - walkStarted;
|
|
159
|
-
return missed;
|
|
194
|
+
return { missed, program };
|
|
160
195
|
};
|
|
161
196
|
const hashes = new Map();
|
|
162
197
|
for (const [key, indexes] of groups) {
|
|
163
198
|
const project = loader.getProjectFor(key);
|
|
164
|
-
const missed = walkGroup(project, indexes, hashes);
|
|
199
|
+
const { missed, program } = walkGroup(project, indexes, hashes);
|
|
165
200
|
if (missed.length > 0) {
|
|
166
|
-
const declarations =
|
|
167
|
-
.getProgram()
|
|
168
|
-
.compilerObject.getRootFileNames()
|
|
169
|
-
.filter(isDeclarationFileName);
|
|
201
|
+
const declarations = program.getRootFileNames().filter(isDeclarationFileName);
|
|
170
202
|
untaken.set(key, { indexes: missed, declarations });
|
|
171
203
|
}
|
|
172
204
|
// One owner's program at a time.
|
|
@@ -184,7 +216,7 @@ export function writePositionRows(options) {
|
|
|
184
216
|
}
|
|
185
217
|
}
|
|
186
218
|
counts.load_ms += performance.now() - loadStarted;
|
|
187
|
-
for (const index of walkGroup(project, indexes, hashes)) {
|
|
219
|
+
for (const index of walkGroup(project, indexes, hashes).missed) {
|
|
188
220
|
lines[index] = fileLine({ f: paths.relative(files[index]), h: hashes.get(index), skip: 'not_in_program', r: [] });
|
|
189
221
|
options.onFile?.();
|
|
190
222
|
}
|
|
@@ -344,11 +376,23 @@ function toSlashes(p) {
|
|
|
344
376
|
/** The rows of one program's files. */
|
|
345
377
|
class PositionWalker {
|
|
346
378
|
program;
|
|
379
|
+
service;
|
|
347
380
|
paths;
|
|
348
381
|
loader;
|
|
349
382
|
checker;
|
|
350
|
-
|
|
383
|
+
/**
|
|
384
|
+
* Each bodyless declaration's implementations, as `impl` rows with no span
|
|
385
|
+
* yet (`start` and `end` are 0): go-to-implementation searches the whole
|
|
386
|
+
* program, so it is asked once per declaration, never per call.
|
|
387
|
+
*/
|
|
388
|
+
implementations = new Map();
|
|
389
|
+
/** The same for a member whose symbol has no declaration, keyed by symbol. */
|
|
390
|
+
undeclaredImplementations = new Map();
|
|
391
|
+
/** Each module's exports by name, and its `export =` and `default` targets, aliases followed. */
|
|
392
|
+
moduleExports = new Map();
|
|
393
|
+
constructor(program, service, paths, loader) {
|
|
351
394
|
this.program = program;
|
|
395
|
+
this.service = service;
|
|
352
396
|
this.paths = paths;
|
|
353
397
|
this.loader = loader;
|
|
354
398
|
this.checker = program.getTypeChecker();
|
|
@@ -357,8 +401,17 @@ class PositionWalker {
|
|
|
357
401
|
const rows = [];
|
|
358
402
|
const visit = (node) => {
|
|
359
403
|
const kind = kindOf(node);
|
|
360
|
-
if (kind)
|
|
361
|
-
|
|
404
|
+
if (kind) {
|
|
405
|
+
const row = this.row(kind, node, sourceFile);
|
|
406
|
+
rows.push(row);
|
|
407
|
+
if ((kind === 'call' || kind === 'member') && !(row[8] & FLAG_CHECKER_THREW)) {
|
|
408
|
+
for (const impl of this.implRows(kind, node))
|
|
409
|
+
rows.push(['impl', row[1], row[2], ...tail(impl)]);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
|
|
413
|
+
rows.push(this.row('jsx', node.tagName, sourceFile));
|
|
414
|
+
}
|
|
362
415
|
ts.forEachChild(node, visit);
|
|
363
416
|
};
|
|
364
417
|
ts.forEachChild(sourceFile, visit);
|
|
@@ -370,7 +423,7 @@ class PositionWalker {
|
|
|
370
423
|
try {
|
|
371
424
|
const symbol = this.symbolOf(kind, node);
|
|
372
425
|
const typed = kind === 'compare' ? node.left : node;
|
|
373
|
-
const type = this.checker.getTypeAtLocation(typed);
|
|
426
|
+
const type = (kind === 'jsx' && this.intrinsicProps(node)) || this.checker.getTypeAtLocation(typed);
|
|
374
427
|
let flags = 0;
|
|
375
428
|
if (type.flags & ts.TypeFlags.Any) {
|
|
376
429
|
flags |= type.intrinsicName === 'error' ? FLAG_ERROR_TYPE : FLAG_ANY;
|
|
@@ -382,6 +435,9 @@ class PositionWalker {
|
|
|
382
435
|
this.ownerDeclaration(kind, node, symbol);
|
|
383
436
|
if (!declaration)
|
|
384
437
|
return [kind, start, end, ts.symbolName(symbol), null, null, null, typeText, flags];
|
|
438
|
+
// A declaration named through its owner's type is a member.
|
|
439
|
+
if ((symbol.declarations ?? []).includes(declaration))
|
|
440
|
+
flags |= this.publication(symbol, declaration);
|
|
385
441
|
// A file's own module symbol is named by the compiler as the quoted
|
|
386
442
|
// absolute file name (`"/<checkout>/pkg/index"`). Name it as `decl_file`
|
|
387
443
|
// names the file. A symbol that merely lives in a file (CommonJS
|
|
@@ -389,13 +445,9 @@ class PositionWalker {
|
|
|
389
445
|
const symbolName = ts.symbolName(symbol);
|
|
390
446
|
const isModule = declaration.kind === ts.SyntaxKind.SourceFile && symbolName.startsWith('"');
|
|
391
447
|
const declaringFile = declaration.getSourceFile();
|
|
392
|
-
const declFile = this.
|
|
393
|
-
? `lib:${path.basename(declaringFile.fileName)}`
|
|
394
|
-
: this.paths.declaringFile(declaringFile.fileName);
|
|
448
|
+
const { declFile, declLine, declPackage } = this.located(declaringFile, declaration.getStart(declaringFile));
|
|
395
449
|
if (declFile === null)
|
|
396
450
|
flags |= FLAG_DECLARATION_UNNAMED;
|
|
397
|
-
const declLine = declaringFile.getLineAndCharacterOfPosition(declaration.getStart(declaringFile)).line + 1;
|
|
398
|
-
const declPackage = this.paths.declaringPackage(declaringFile.fileName, (file) => this.loader.packageOf(file));
|
|
399
451
|
const name = isModule ? declFile : symbolName;
|
|
400
452
|
return [kind, start, end, name, declFile, declLine, declPackage, typeText, flags];
|
|
401
453
|
}
|
|
@@ -403,6 +455,121 @@ class PositionWalker {
|
|
|
403
455
|
return [kind, start, end, null, null, null, null, null, FLAG_CHECKER_THREW];
|
|
404
456
|
}
|
|
405
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* An intrinsic element's type: the attributes `JSX.IntrinsicElements`
|
|
460
|
+
* gives its tag. The compiler types an intrinsic tag name itself as the
|
|
461
|
+
* error type, though the name resolves. A tag is intrinsic when it is an
|
|
462
|
+
* identifier that starts with a lowercase letter or holds a `-`, as the
|
|
463
|
+
* language reads it.
|
|
464
|
+
*/
|
|
465
|
+
intrinsicProps(tag) {
|
|
466
|
+
if (!ts.isIdentifier(tag) || !/^[a-z]|-/.test(tag.text))
|
|
467
|
+
return undefined;
|
|
468
|
+
const element = tag.parent;
|
|
469
|
+
if (!ts.isJsxOpeningElement(element) && !ts.isJsxSelfClosingElement(element))
|
|
470
|
+
return undefined;
|
|
471
|
+
return this.checker.getContextualType(element.attributes);
|
|
472
|
+
}
|
|
473
|
+
/** How rows name a position in a program file: its file, 1-based line and package. */
|
|
474
|
+
located(declaringFile, position) {
|
|
475
|
+
const declFile = this.program.isSourceFileDefaultLibrary(declaringFile)
|
|
476
|
+
? `lib:${path.basename(declaringFile.fileName)}`
|
|
477
|
+
: this.paths.declaringFile(declaringFile.fileName);
|
|
478
|
+
const declLine = declaringFile.getLineAndCharacterOfPosition(position).line + 1;
|
|
479
|
+
const declPackage = this.paths.declaringPackage(declaringFile.fileName, (file) => this.loader.packageOf(file));
|
|
480
|
+
return { declFile, declLine, declPackage };
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* The `impl` rows of a `call` or `member` row (spans unset): none unless the
|
|
484
|
+
* row's declaration has no body of its own ({@link isBodyless}). Its own
|
|
485
|
+
* search, apart from the row's: a throw here writes one `impl` row flagged
|
|
486
|
+
* {@link FLAG_CHECKER_THREW} and leaves the row as it was.
|
|
487
|
+
*/
|
|
488
|
+
implRows(kind, node) {
|
|
489
|
+
let declaration;
|
|
490
|
+
try {
|
|
491
|
+
const symbol = this.symbolOf(kind, node);
|
|
492
|
+
if (symbol && (symbol.declarations ?? []).length === 0)
|
|
493
|
+
return this.undeclaredImplRows(kind, node, symbol);
|
|
494
|
+
declaration = symbol ? attributedDeclaration(() => this.program, symbol.declarations ?? []) : undefined;
|
|
495
|
+
if (!declaration || !isBodyless(declaration, this.checker))
|
|
496
|
+
return [];
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
return [];
|
|
500
|
+
}
|
|
501
|
+
let rows = this.implementations.get(declaration);
|
|
502
|
+
if (!rows) {
|
|
503
|
+
try {
|
|
504
|
+
rows = this.implementationsOf(declaration);
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
rows = [['impl', 0, 0, null, null, null, null, null, FLAG_CHECKER_THREW]];
|
|
508
|
+
}
|
|
509
|
+
this.implementations.set(declaration, rows);
|
|
510
|
+
}
|
|
511
|
+
return rows;
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* The `impl` rows of a member whose symbol has no declaration (a mapped
|
|
515
|
+
* type's member): there is no declaration name to ask at, so the answer is
|
|
516
|
+
* asked at the use site's name, once per symbol. Same shape as
|
|
517
|
+
* {@link implementationsOf}; nothing of the symbol's own is filtered out.
|
|
518
|
+
*/
|
|
519
|
+
undeclaredImplRows(kind, node, symbol) {
|
|
520
|
+
const cached = this.undeclaredImplementations.get(symbol);
|
|
521
|
+
if (cached)
|
|
522
|
+
return cached;
|
|
523
|
+
let rows;
|
|
524
|
+
try {
|
|
525
|
+
const named = nameNodeOf(kind, node);
|
|
526
|
+
rows = named ? this.implementationRows(named, []) : [];
|
|
527
|
+
}
|
|
528
|
+
catch {
|
|
529
|
+
rows = [['impl', 0, 0, null, null, null, null, null, FLAG_CHECKER_THREW]];
|
|
530
|
+
}
|
|
531
|
+
this.undeclaredImplementations.set(symbol, rows);
|
|
532
|
+
return rows;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* What go-to-implementation names for `declaration`, asked at its name:
|
|
536
|
+
* every declaration the language service answers, except the symbol's own
|
|
537
|
+
* (an overloaded method answers with its other signatures), in file and
|
|
538
|
+
* position order.
|
|
539
|
+
*/
|
|
540
|
+
implementationsOf(declaration) {
|
|
541
|
+
const name = ts.getNameOfDeclaration(declaration) ?? declaration;
|
|
542
|
+
const own = (this.checker.getSymbolAtLocation(name) ?? declaration.symbol)
|
|
543
|
+
?.declarations ?? [declaration];
|
|
544
|
+
return this.implementationRows(name, own);
|
|
545
|
+
}
|
|
546
|
+
/** Go-to-implementation at `name`, minus results inside `own`, as sorted `impl` rows. */
|
|
547
|
+
implementationRows(name, own) {
|
|
548
|
+
const declaringFile = name.getSourceFile();
|
|
549
|
+
const found = new Map();
|
|
550
|
+
for (const location of this.service.getImplementationAtPosition(declaringFile.fileName, name.getStart(declaringFile)) ?? []) {
|
|
551
|
+
const file = this.program.getSourceFile(location.fileName);
|
|
552
|
+
if (!file)
|
|
553
|
+
continue;
|
|
554
|
+
const start = location.textSpan.start;
|
|
555
|
+
if (own.some((d) => d.getSourceFile() === file && d.pos <= start && start < d.end))
|
|
556
|
+
continue;
|
|
557
|
+
found.set(`${location.fileName}\u0000${start}`, { file, start });
|
|
558
|
+
}
|
|
559
|
+
if (found.size === 0)
|
|
560
|
+
return [];
|
|
561
|
+
if (found.size > IMPL_CAP)
|
|
562
|
+
return [['impl', 0, 0, null, null, null, null, null, FLAG_TOO_MANY]];
|
|
563
|
+
const rows = [...found.values()].map(({ file, start }) => {
|
|
564
|
+
const { declFile, declLine, declPackage } = this.located(file, start);
|
|
565
|
+
const named = nodeStartingAt(file, start);
|
|
566
|
+
const symbol = named ? this.checker.getSymbolAtLocation(named) : undefined;
|
|
567
|
+
const flags = declFile === null ? FLAG_DECLARATION_UNNAMED : 0;
|
|
568
|
+
return { start, row: ['impl', 0, 0, symbol ? ts.symbolName(symbol) : null, declFile, declLine, declPackage, null, flags] };
|
|
569
|
+
});
|
|
570
|
+
rows.sort((a, b) => compareNames(a.row[4], b.row[4]) || a.start - b.start);
|
|
571
|
+
return rows.map(({ row }) => row);
|
|
572
|
+
}
|
|
406
573
|
/**
|
|
407
574
|
* The declaration a member with none is attributed to (carrick#2448): the
|
|
408
575
|
* mapped type node, or the alias naming it, of the receiver's type. A
|
|
@@ -427,6 +594,68 @@ class PositionWalker {
|
|
|
427
594
|
const owner = holders[0].getSymbol() ?? holders[0].aliasSymbol;
|
|
428
595
|
return attributedDeclaration(() => this.program, owner?.declarations ?? []);
|
|
429
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* How `declaration`'s module publishes `symbol`: {@link FLAG_PUBLISHED}
|
|
599
|
+
* when under the symbol's own name (or as a global), and
|
|
600
|
+
* {@link FLAG_MODULE_DEFAULT} when as the module's `export =` or `default`.
|
|
601
|
+
* The module is the nearest ambient `declare module "…"` or the file.
|
|
602
|
+
*/
|
|
603
|
+
publication(symbol, declaration) {
|
|
604
|
+
let container = declaration.parent;
|
|
605
|
+
while (container && !ts.isSourceFile(container) && !ts.isModuleBlock(container)) {
|
|
606
|
+
// Anything but a statement list between the declaration and its module
|
|
607
|
+
// (a class, interface, type or object literal, a function body) makes
|
|
608
|
+
// it a member or a local.
|
|
609
|
+
if (!ts.isVariableDeclarationList(container) && !ts.isVariableStatement(container))
|
|
610
|
+
return 0;
|
|
611
|
+
container = container.parent;
|
|
612
|
+
}
|
|
613
|
+
if (!container)
|
|
614
|
+
return 0;
|
|
615
|
+
const block = ts.isModuleBlock(container) ? container.parent : undefined;
|
|
616
|
+
if (block && block.flags & ts.NodeFlags.GlobalAugmentation)
|
|
617
|
+
return FLAG_PUBLISHED;
|
|
618
|
+
// A namespace's member is published when the module its file is publishes it.
|
|
619
|
+
if (block && !ts.isStringLiteral(block.name))
|
|
620
|
+
return this.publishedByExports(symbol, declaration);
|
|
621
|
+
const moduleSymbol = this.checker.getSymbolAtLocation(block ? block.name : container);
|
|
622
|
+
if (moduleSymbol)
|
|
623
|
+
return this.publishedIn(moduleSymbol, symbol);
|
|
624
|
+
// A script's top-level declaration is a global.
|
|
625
|
+
return ts.isSourceFile(container) && !ts.isExternalModule(container) ? FLAG_PUBLISHED : 0;
|
|
626
|
+
}
|
|
627
|
+
/** A namespace member, found among the exports of the module it sits in. */
|
|
628
|
+
publishedByExports(symbol, declaration) {
|
|
629
|
+
for (let node = declaration.parent; node; node = node.parent) {
|
|
630
|
+
if (ts.isModuleDeclaration(node) && ts.isStringLiteral(node.name)) {
|
|
631
|
+
const moduleSymbol = this.checker.getSymbolAtLocation(node.name);
|
|
632
|
+
return moduleSymbol ? this.publishedIn(moduleSymbol, symbol) : 0;
|
|
633
|
+
}
|
|
634
|
+
if (ts.isSourceFile(node)) {
|
|
635
|
+
const moduleSymbol = this.checker.getSymbolAtLocation(node);
|
|
636
|
+
return moduleSymbol ? this.publishedIn(moduleSymbol, symbol) & FLAG_PUBLISHED : 0;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return 0;
|
|
640
|
+
}
|
|
641
|
+
publishedIn(moduleSymbol, symbol) {
|
|
642
|
+
let exports = this.moduleExports.get(moduleSymbol);
|
|
643
|
+
if (!exports) {
|
|
644
|
+
const resolve = (s) => s.flags & ts.SymbolFlags.Alias ? this.checker.getAliasedSymbol(s) : s;
|
|
645
|
+
const byName = new Map();
|
|
646
|
+
for (const exported of this.checker.getExportsOfModule(moduleSymbol)) {
|
|
647
|
+
byName.set(exported.getName(), resolve(exported));
|
|
648
|
+
}
|
|
649
|
+
const wholes = [ts.InternalSymbolName.ExportEquals, ts.InternalSymbolName.Default]
|
|
650
|
+
.map((key) => moduleSymbol.exports?.get(key))
|
|
651
|
+
.filter((own) => own !== undefined)
|
|
652
|
+
.map(resolve);
|
|
653
|
+
exports = { byName, wholes };
|
|
654
|
+
this.moduleExports.set(moduleSymbol, exports);
|
|
655
|
+
}
|
|
656
|
+
return ((exports.byName.get(symbol.getName()) === symbol ? FLAG_PUBLISHED : 0) |
|
|
657
|
+
(exports.wholes.includes(symbol) ? FLAG_MODULE_DEFAULT : 0));
|
|
658
|
+
}
|
|
430
659
|
/** The row's symbol, following aliases to a target that has a declaration. */
|
|
431
660
|
symbolOf(kind, node) {
|
|
432
661
|
let symbol;
|
|
@@ -550,6 +779,54 @@ class PositionWalker {
|
|
|
550
779
|
return this.host;
|
|
551
780
|
}
|
|
552
781
|
}
|
|
782
|
+
/** A row's fields after its kind and span. */
|
|
783
|
+
function tail(row) {
|
|
784
|
+
return [row[3], row[4], row[5], row[6], row[7], row[8]];
|
|
785
|
+
}
|
|
786
|
+
/** Order two `decl_file` names, `null` last. */
|
|
787
|
+
function compareNames(a, b) {
|
|
788
|
+
if (a === b)
|
|
789
|
+
return 0;
|
|
790
|
+
if (a === null)
|
|
791
|
+
return 1;
|
|
792
|
+
if (b === null)
|
|
793
|
+
return -1;
|
|
794
|
+
return a < b ? -1 : 1;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Whether a declaration has no body for a call to run: an interface's or type
|
|
798
|
+
* literal's method, a function-typed property signature, an abstract method or
|
|
799
|
+
* property, or a function-typed class property or parameter with no
|
|
800
|
+
* initializer. Its implementations are what go-to-implementation names.
|
|
801
|
+
*/
|
|
802
|
+
function isBodyless(declaration, checker) {
|
|
803
|
+
const functionTyped = () => checker.getTypeAtLocation(declaration).getNonNullableType().getCallSignatures().length > 0;
|
|
804
|
+
if (ts.isMethodSignature(declaration))
|
|
805
|
+
return true;
|
|
806
|
+
if (ts.isPropertySignature(declaration))
|
|
807
|
+
return functionTyped();
|
|
808
|
+
if (ts.isMethodDeclaration(declaration) || ts.isPropertyDeclaration(declaration)) {
|
|
809
|
+
if (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Abstract)
|
|
810
|
+
return true;
|
|
811
|
+
return ts.isPropertyDeclaration(declaration) && !declaration.initializer && functionTyped();
|
|
812
|
+
}
|
|
813
|
+
if (ts.isParameter(declaration))
|
|
814
|
+
return !declaration.initializer && functionTyped();
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
/** The innermost node of `file` that starts at `position` (an implementation's name). */
|
|
818
|
+
function nodeStartingAt(file, position) {
|
|
819
|
+
let found;
|
|
820
|
+
const visit = (node) => {
|
|
821
|
+
if (position < node.getStart(file) || position >= node.getEnd())
|
|
822
|
+
return;
|
|
823
|
+
if (node.getStart(file) === position)
|
|
824
|
+
found = node;
|
|
825
|
+
ts.forEachChild(node, visit);
|
|
826
|
+
};
|
|
827
|
+
ts.forEachChild(file, visit);
|
|
828
|
+
return found;
|
|
829
|
+
}
|
|
553
830
|
/** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
|
|
554
831
|
export function kindOf(node) {
|
|
555
832
|
if (ts.isCallExpression(node) || ts.isNewExpression(node) || ts.isTaggedTemplateExpression(node)) {
|
|
@@ -576,6 +853,12 @@ function nameNodeOf(kind, node) {
|
|
|
576
853
|
return referenceName(node.left);
|
|
577
854
|
case 'ident':
|
|
578
855
|
return node;
|
|
856
|
+
case 'impl':
|
|
857
|
+
return undefined;
|
|
858
|
+
case 'jsx':
|
|
859
|
+
// The tag: `<Badge>`, `<ui.Button>`, `<this.Panel>`; a namespaced
|
|
860
|
+
// name (`<svg:path>`) names nothing.
|
|
861
|
+
return ts.isJsxNamespacedName(node) ? undefined : (referenceName(node) ?? node);
|
|
579
862
|
}
|
|
580
863
|
}
|
|
581
864
|
/**
|
|
@@ -765,6 +765,10 @@ export interface PositionRowsCounts {
|
|
|
765
765
|
any: number;
|
|
766
766
|
/** Rows whose type is the compiler's error type (flag 2). */
|
|
767
767
|
error_type: number;
|
|
768
|
+
/** `impl` rows: implementations of a bodyless declaration, or one past the cap (carrick#2430). */
|
|
769
|
+
impl: number;
|
|
770
|
+
/** `jsx` rows: one per JSX opening or self-closing element. */
|
|
771
|
+
jsx: number;
|
|
768
772
|
/** The rows file's size on disk, compressed. */
|
|
769
773
|
bytes: number;
|
|
770
774
|
/** Time spent building programs and reading files into them. */
|