@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.10
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/README.md +1 -1
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +245 -309
- package/dist/src/augmentations.js +124 -18
- package/dist/src/automatic-types.js +170 -0
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +9 -9
- package/dist/src/config.js +6 -81
- package/dist/src/configuration-files.js +78 -188
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +146 -1
- package/dist/src/conventions.js +136 -70
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +280 -62
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +9 -18
- package/dist/src/entry-points.js +106 -21
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +225 -63
- package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
- package/dist/src/findings/emitters.js +6 -6
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +53 -19
- package/dist/src/findings/non-code-artifacts.js +71 -39
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +57 -28
- package/dist/src/findings/visibility-narrowing.js +40 -18
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +18 -4
- package/dist/src/git-ignore.js +99 -0
- package/dist/src/graph.js +9 -4
- package/dist/src/html-entries.js +6 -33
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +341 -69
- package/dist/src/inventory.js +54 -29
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +88 -61
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/manifest.js +49 -54
- package/dist/src/matrix.js +94 -13
- package/dist/src/mdx-entries.js +174 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +41 -10
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -78
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +11 -0
- package/dist/src/references.js +52 -31
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +59 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +245 -727
- package/dist/src/roots.js +374 -116
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -342
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +649 -29
- package/dist/src/session.js +41 -29
- package/dist/src/source-files.js +40 -15
- package/dist/src/suppress-file.js +34 -29
- package/dist/src/suppress-inline.js +21 -4
- package/dist/src/suppress.js +40 -56
- package/dist/src/sweep.js +32 -17
- package/dist/src/symbol-keys.js +105 -6
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +119 -18
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +27 -221
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +181 -125
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +14 -9
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +4 -2
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/dist/src/workflow-steps.js +128 -33
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +333 -457
- package/src/augmentations.ts +152 -23
- package/src/automatic-types.ts +228 -0
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +9 -10
- package/src/config.ts +31 -299
- package/src/configuration-files.ts +100 -191
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +2 -2
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +186 -1
- package/src/conventions.ts +166 -90
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +356 -78
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +11 -20
- package/src/entry-points.ts +124 -28
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +24 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +297 -77
- package/src/findings/dependencies-and-module-machinery.ts +90 -31
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +8 -8
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +64 -20
- package/src/findings/non-code-artifacts.ts +88 -50
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +65 -28
- package/src/findings/visibility-narrowing.ts +51 -19
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +23 -4
- package/src/git-ignore.ts +122 -0
- package/src/graph.ts +11 -3
- package/src/host.ts +17 -14
- package/src/html-entries.ts +6 -33
- package/src/import-bindings.ts +128 -0
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +152 -30
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +449 -85
- package/src/inventory.ts +73 -27
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +127 -80
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +154 -18
- package/src/mdx-entries.ts +218 -0
- package/src/module-declarations.ts +11 -13
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +51 -12
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -91
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +13 -0
- package/src/references.ts +62 -32
- package/src/report-schema.ts +68 -0
- package/src/report.ts +105 -215
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +283 -1111
- package/src/roots.ts +490 -137
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -387
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +818 -26
- package/src/session.ts +77 -54
- package/src/source-files.ts +67 -17
- package/src/suppress-file.ts +36 -34
- package/src/suppress-inline.ts +20 -3
- package/src/suppress.ts +52 -63
- package/src/sweep.ts +53 -18
- package/src/symbol-keys.ts +131 -7
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +155 -18
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +35 -273
- package/src/type-parameter-reads.ts +227 -0
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +245 -161
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +15 -13
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +149 -34
- package/dist/src/discover.js +0 -347
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/discover.ts +0 -510
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
package/src/type-errors.ts
CHANGED
|
@@ -1,41 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What
|
|
3
|
-
*
|
|
4
|
-
* is a type error: the function, method or file-level statement holding it is skipped,
|
|
5
|
-
* which only ever withholds a finding. `projectErrors` decides, before any of these, which
|
|
6
|
-
* errors the compiler places in no file leave the project unloaded.
|
|
2
|
+
* What a type error in a project's own file does: the function, method or file-level statement
|
|
3
|
+
* holding it is skipped, which only ever withholds a finding.
|
|
7
4
|
*/
|
|
8
5
|
|
|
9
6
|
import {
|
|
10
7
|
isClassDeclaration,
|
|
11
|
-
|
|
8
|
+
isFunctionLikeDeclaration,
|
|
12
9
|
isPropertyAccessExpression,
|
|
13
|
-
isStringLiteral,
|
|
14
10
|
type Node,
|
|
15
11
|
type PropertyAccessExpression,
|
|
16
12
|
type SourceFile,
|
|
17
13
|
} from "@typescript/native/ast";
|
|
18
14
|
import type { Symbol as TSSymbol, Type } from "@typescript/native/sync";
|
|
19
|
-
import { packageOfSpecifier } from "./dependencies.ts";
|
|
20
|
-
import type { LocatedDiagnostic } from "./discover.ts";
|
|
21
|
-
import type { Host } from "./host.ts";
|
|
22
15
|
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
23
|
-
import {
|
|
16
|
+
import { relativePath } from "./paths.ts";
|
|
24
17
|
import { isAnswered } from "./query.ts";
|
|
25
18
|
import type { TypeErrorSkip } from "./report.ts";
|
|
26
|
-
import type {
|
|
27
|
-
import type { SetupFailure } from "./setup-failure.ts";
|
|
28
|
-
|
|
29
|
-
/** The compiler's codes for an import whose module it cannot find. */
|
|
30
|
-
const MODULE_NOT_FOUND: ReadonlySet<number> = new Set([2307, 2792]);
|
|
31
|
-
|
|
32
|
-
const MANIFEST = "package.json";
|
|
33
|
-
const MANIFEST_SECTIONS = [
|
|
34
|
-
"dependencies",
|
|
35
|
-
"devDependencies",
|
|
36
|
-
"peerDependencies",
|
|
37
|
-
"optionalDependencies",
|
|
38
|
-
] as const;
|
|
19
|
+
import type { ProjectView } from "./session.ts";
|
|
39
20
|
|
|
40
21
|
/** The lines of one file a skip withholds every finding on. */
|
|
41
22
|
export interface SkippedUnit {
|
|
@@ -57,205 +38,34 @@ export function insideSkipped(
|
|
|
57
38
|
}
|
|
58
39
|
|
|
59
40
|
/** One type error of an own file, with the unit it skips. */
|
|
60
|
-
interface Skip {
|
|
41
|
+
export interface Skip {
|
|
61
42
|
readonly record: TypeErrorSkip;
|
|
62
43
|
readonly file: SourceFile;
|
|
63
44
|
readonly unit: Node;
|
|
64
45
|
}
|
|
65
46
|
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
readonly host: Host;
|
|
77
|
-
/** The target root, absolute. */
|
|
78
|
-
readonly targetRoot: string;
|
|
79
|
-
/** The project configuration's `paths` mapping. */
|
|
80
|
-
readonly paths: PathsMapping;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** One configuration's `paths` mapping, its targets read against `base`. */
|
|
84
|
-
interface PathsMapping {
|
|
85
|
-
readonly base: string;
|
|
86
|
-
readonly entries: readonly { readonly key: string; readonly targets: readonly string[] }[];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** The string literal holding one offset, innermost first, a doc comment's included. */
|
|
90
|
-
function literalAt(file: SourceFile, offset: number): string | undefined {
|
|
91
|
-
let found: string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* The unit a type error at one offset skips: the innermost function holding it, else the
|
|
49
|
+
* class member holding it where a top-level class does, else the top-level statement.
|
|
50
|
+
*/
|
|
51
|
+
function unitAt(file: SourceFile, offset: number): Node | undefined {
|
|
52
|
+
const statement = file.statements.find((one) => one.pos <= offset && offset < one.end);
|
|
53
|
+
if (statement === undefined) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
let innermost: Node | undefined;
|
|
92
57
|
const visit = (node: Node): void => {
|
|
93
|
-
if (offset < node.
|
|
58
|
+
if (offset < node.getStart() || offset >= node.end) {
|
|
94
59
|
return;
|
|
95
60
|
}
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
// `forEachChild` does not enter a doc comment, where a JavaScript file imports types.
|
|
100
|
-
for (const doc of (node as { readonly jsDoc?: readonly Node[] }).jsDoc ?? []) {
|
|
101
|
-
visit(doc);
|
|
61
|
+
if (isFunctionLikeDeclaration(node)) {
|
|
62
|
+
innermost = node;
|
|
102
63
|
}
|
|
103
64
|
node.forEachChild(visit);
|
|
104
65
|
};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* What one `paths` key's `*` matches in one specifier, `""` for a key with no `*` that
|
|
111
|
-
* is the specifier, or `undefined` where the key does not match it.
|
|
112
|
-
*/
|
|
113
|
-
function mappedBy(key: string, specifier: string): string | undefined {
|
|
114
|
-
const star = key.indexOf("*");
|
|
115
|
-
if (star < 0) {
|
|
116
|
-
return key === specifier ? "" : undefined;
|
|
117
|
-
}
|
|
118
|
-
const prefix = key.slice(0, star);
|
|
119
|
-
const suffix = key.slice(star + 1);
|
|
120
|
-
return specifier.length >= prefix.length + suffix.length &&
|
|
121
|
-
specifier.startsWith(prefix) &&
|
|
122
|
-
specifier.endsWith(suffix)
|
|
123
|
-
? specifier.slice(prefix.length, specifier.length - suffix.length)
|
|
124
|
-
: undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* One `paths` target, its first `*` replaced by `star` taken literally, as the compiler does:
|
|
129
|
-
* https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/module/resolver.go#L1282
|
|
130
|
-
*/
|
|
131
|
-
function substituted(target: string, star: string): string {
|
|
132
|
-
const at = target.indexOf("*");
|
|
133
|
-
return at < 0 ? target : target.slice(0, at) + star + target.slice(at + 1);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Whether a path names something on disk, as written or with any extension; under
|
|
138
|
-
* `fileOnly`, only a file counts.
|
|
139
|
-
*/
|
|
140
|
-
function provided(host: Host, path: string, fileOnly: boolean): boolean {
|
|
141
|
-
const counts = (at: string): boolean =>
|
|
142
|
-
fileOnly ? host.kindOf(at) === "file" : host.kindOf(at) !== "absent";
|
|
143
|
-
if (counts(path)) {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
const dir = dirnamePath(path);
|
|
147
|
-
const stem = `${path.slice(dir.length + 1)}.`;
|
|
148
|
-
try {
|
|
149
|
-
return host
|
|
150
|
-
.readDirectory(dir)
|
|
151
|
-
.some((entry) => entry.name.startsWith(stem) && counts(joinPath(dir, entry.name)));
|
|
152
|
-
} catch {
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** The manifest at or above `dir`, no higher than the target root, that declares `name`. */
|
|
158
|
-
function declaringManifest(
|
|
159
|
-
host: Host,
|
|
160
|
-
dir: string,
|
|
161
|
-
targetRoot: string,
|
|
162
|
-
name: string,
|
|
163
|
-
): string | undefined {
|
|
164
|
-
for (let at = dir; ; at = dirnamePath(at)) {
|
|
165
|
-
const path = joinPath(at, MANIFEST);
|
|
166
|
-
let manifest: unknown;
|
|
167
|
-
try {
|
|
168
|
-
manifest = JSON.parse(host.readFile(path)) as unknown;
|
|
169
|
-
} catch {
|
|
170
|
-
manifest = undefined;
|
|
171
|
-
}
|
|
172
|
-
if (typeof manifest === "object" && manifest !== null) {
|
|
173
|
-
const sections = manifest as Record<string, unknown>;
|
|
174
|
-
for (const section of MANIFEST_SECTIONS) {
|
|
175
|
-
const declared = sections[section];
|
|
176
|
-
if (typeof declared === "object" && declared !== null && Object.hasOwn(declared, name)) {
|
|
177
|
-
return path;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
// The target root is not below itself, so the walk stops after reading its manifest.
|
|
182
|
-
if (relativePath(targetRoot, at) === undefined) {
|
|
183
|
-
return undefined;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Whether a package is installed for an importer in `dir`: in the `node_modules` of
|
|
190
|
-
* `dir` or of any ancestor, above the target root too, as Node and the compiler look.
|
|
191
|
-
*/
|
|
192
|
-
function installed(host: Host, dir: string, name: string): boolean {
|
|
193
|
-
for (let at = dir; ; at = dirnamePath(at)) {
|
|
194
|
-
if (host.kindOf(joinPath(at, "node_modules", name)) === "directory") {
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
if (dirnamePath(at) === at) {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Why one import that the compiler cannot resolve names a module the project expects,
|
|
205
|
-
* with the fix, or undefined where the module is one the project does not expect.
|
|
206
|
-
*/
|
|
207
|
-
function expectedModule(
|
|
208
|
-
context: ErrorContext,
|
|
209
|
-
importer: string,
|
|
210
|
-
specifier: string,
|
|
211
|
-
): string | undefined {
|
|
212
|
-
const { host, targetRoot } = context;
|
|
213
|
-
const fix = "run the generator or the build that writes it";
|
|
214
|
-
if (specifier.startsWith(".") || isAbsolutePath(specifier)) {
|
|
215
|
-
return provided(host, resolvePath(dirnamePath(importer), specifier), false)
|
|
216
|
-
? undefined
|
|
217
|
-
: `which names no file of the target: ${fix}`;
|
|
218
|
-
}
|
|
219
|
-
if (specifier.startsWith("#")) {
|
|
220
|
-
return `which the manifest's imports map to no file: ${fix}`;
|
|
221
|
-
}
|
|
222
|
-
const mapped = context.paths.entries.flatMap(({ key, targets }) => {
|
|
223
|
-
const star = mappedBy(key, specifier);
|
|
224
|
-
return star === undefined
|
|
225
|
-
? []
|
|
226
|
-
: targets.map((target) => resolvePath(context.paths.base, substituted(target, star)));
|
|
227
|
-
});
|
|
228
|
-
const name = packageOfSpecifier(specifier);
|
|
229
|
-
// The compiler looks up in `node_modules` what `paths` does not resolve, so the error
|
|
230
|
-
// of an installed package's import is the import's own, a subpath its exports omit:
|
|
231
|
-
// https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/module/resolver.go#L548-L575
|
|
232
|
-
if (name !== undefined && installed(host, dirnamePath(importer), name)) {
|
|
233
|
-
return undefined;
|
|
234
|
-
}
|
|
235
|
-
if (mapped.length > 0) {
|
|
236
|
-
return mapped.some((path) => provided(host, path, true))
|
|
237
|
-
? undefined
|
|
238
|
-
: `which the compiler configuration's paths map to no file: ${fix}`;
|
|
239
|
-
}
|
|
240
|
-
const manifest =
|
|
241
|
-
name === undefined
|
|
242
|
-
? undefined
|
|
243
|
-
: declaringManifest(host, dirnamePath(importer), targetRoot, name);
|
|
244
|
-
if (manifest === undefined) {
|
|
245
|
-
return undefined;
|
|
246
|
-
}
|
|
247
|
-
const where = relativePath(targetRoot, manifest) ?? manifest;
|
|
248
|
-
return `a dependency ${where} declares that is not installed: run the package manager's install`;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* The unit a type error at one offset skips: the class member holding it where a
|
|
253
|
-
* top-level class does, else the top-level statement.
|
|
254
|
-
*/
|
|
255
|
-
function unitAt(file: SourceFile, offset: number): Node | undefined {
|
|
256
|
-
const statement = file.statements.find((one) => one.pos <= offset && offset < one.end);
|
|
257
|
-
if (statement === undefined || !isClassDeclaration(statement)) {
|
|
258
|
-
return statement;
|
|
66
|
+
visit(statement);
|
|
67
|
+
if (innermost !== undefined || !isClassDeclaration(statement)) {
|
|
68
|
+
return innermost ?? statement;
|
|
259
69
|
}
|
|
260
70
|
return statement.members.find((one) => one.pos <= offset && offset < one.end) ?? statement;
|
|
261
71
|
}
|
|
@@ -266,48 +76,19 @@ function messageOf(text: string, targetRoot: string): string {
|
|
|
266
76
|
return line === "" ? "a type error" : line;
|
|
267
77
|
}
|
|
268
78
|
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
for (const { diagnostic, at } of errors) {
|
|
283
|
-
const file = diagnostic.fileName === undefined ? undefined : files.get(diagnostic.fileName);
|
|
284
|
-
if (file === undefined || diagnostic.reportsUnnecessary === true) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
const path = relativePath(context.targetRoot, file.fileName) ?? file.fileName;
|
|
288
|
-
const line = at?.line ?? 1;
|
|
289
|
-
const specifier = MODULE_NOT_FOUND.has(diagnostic.code)
|
|
290
|
-
? literalAt(file, diagnostic.pos)
|
|
291
|
-
: undefined;
|
|
292
|
-
const expected =
|
|
293
|
-
specifier === undefined ? undefined : expectedModule(context, file.fileName, specifier);
|
|
294
|
-
if (specifier !== undefined && expected !== undefined) {
|
|
295
|
-
missing.push({
|
|
296
|
-
setupClass: "missing-module",
|
|
297
|
-
detail: `${path}:${String(line)}: ${path} imports ${JSON.stringify(specifier)}, ${expected}`,
|
|
298
|
-
});
|
|
299
|
-
continue;
|
|
300
|
-
}
|
|
301
|
-
const unit = unitAt(file, diagnostic.pos);
|
|
302
|
-
if (unit !== undefined) {
|
|
303
|
-
skips.push({
|
|
304
|
-
record: { path, line, message: messageOf(diagnostic.text, context.targetRoot) },
|
|
305
|
-
file,
|
|
306
|
-
unit,
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return { missing, skips };
|
|
79
|
+
/** The skip one type error at `offset` of an own file makes, the root spelled relative to itself. */
|
|
80
|
+
export function skipAt(
|
|
81
|
+
file: SourceFile,
|
|
82
|
+
offset: number,
|
|
83
|
+
line: number,
|
|
84
|
+
text: string,
|
|
85
|
+
targetRoot: string,
|
|
86
|
+
): Skip | undefined {
|
|
87
|
+
const unit = unitAt(file, offset);
|
|
88
|
+
const path = relativePath(targetRoot, file.fileName) ?? file.fileName;
|
|
89
|
+
return unit === undefined
|
|
90
|
+
? undefined
|
|
91
|
+
: { record: { path, line, message: messageOf(text, targetRoot) }, file, unit };
|
|
311
92
|
}
|
|
312
93
|
|
|
313
94
|
/** The lines each skip withholds, below the target root. */
|
|
@@ -379,22 +160,3 @@ export function skippedRoots<Brand>(
|
|
|
379
160
|
}
|
|
380
161
|
return [...kept];
|
|
381
162
|
}
|
|
382
|
-
|
|
383
|
-
/** One compiler configuration's `paths` mapping, empty where it cannot be read. */
|
|
384
|
-
export function pathsOf(engine: Engine, configFile: string): PathsMapping {
|
|
385
|
-
const base = dirnamePath(configFile);
|
|
386
|
-
try {
|
|
387
|
-
const { options } = engine.parseConfigFile(configFile);
|
|
388
|
-
const { paths } = options;
|
|
389
|
-
// The parse answers `pathsBasePath`, the directory of the configuration that writes
|
|
390
|
-
// `paths` (an extended one included), though the client's option type omits it:
|
|
391
|
-
// https://github.com/microsoft/typescript-go/blob/89d5d5b2849a0db0957065889ca58536fa6d2e4a/internal/tsoptions/tsconfigparsing.go#L1120
|
|
392
|
-
const declaredAt = (options as { readonly pathsBasePath?: unknown }).pathsBasePath;
|
|
393
|
-
return {
|
|
394
|
-
base: typeof declaredAt === "string" ? declaredAt : base,
|
|
395
|
-
entries: Object.entries(paths ?? {}).map(([key, targets]) => ({ key, targets })),
|
|
396
|
-
};
|
|
397
|
-
} catch {
|
|
398
|
-
return { base, entries: [] };
|
|
399
|
-
}
|
|
400
|
-
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The members a read through a value typed by a type parameter reaches: `value.kind`,
|
|
3
|
+
* where `value` is of a type parameter `T` of a generic function, reads `kind` on each
|
|
4
|
+
* object type no class declares that a call of the function passes for `T`, because the
|
|
5
|
+
* call's value is what the read reads, whatever member of the constraint the name resolves
|
|
6
|
+
* to. A class's members a constraint requires are interface satisfaction's.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
isBinaryExpression,
|
|
11
|
+
isCallExpression,
|
|
12
|
+
isIdentifier,
|
|
13
|
+
isNewExpression,
|
|
14
|
+
isPropertyAccessExpression,
|
|
15
|
+
isTypeParameterDeclaration,
|
|
16
|
+
SyntaxKind,
|
|
17
|
+
type CallExpression,
|
|
18
|
+
type NewExpression,
|
|
19
|
+
type Node,
|
|
20
|
+
type PropertyAccessExpression,
|
|
21
|
+
type SourceFile,
|
|
22
|
+
} from "@typescript/native/ast";
|
|
23
|
+
import { SymbolFlags, type Type } from "@typescript/native/sync";
|
|
24
|
+
import type { AliasChains } from "./alias-chain.ts";
|
|
25
|
+
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
26
|
+
import { renderPosition } from "./position.ts";
|
|
27
|
+
import { isAnswered } from "./query.ts";
|
|
28
|
+
import type { Reference } from "./references.ts";
|
|
29
|
+
import type { ProjectView } from "./session.ts";
|
|
30
|
+
|
|
31
|
+
/** How deep a parameter's type is matched against the type a call passes: `readonly T[]` is one level. */
|
|
32
|
+
const MATCH_DEPTH = 3;
|
|
33
|
+
|
|
34
|
+
/** One read of a member through a value whose type is a type parameter. */
|
|
35
|
+
interface Read {
|
|
36
|
+
readonly file: SourceFile;
|
|
37
|
+
readonly from: string;
|
|
38
|
+
readonly access: PropertyAccessExpression;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A node that declares parameters, as a generic function does. */
|
|
42
|
+
type Generic = Node & {
|
|
43
|
+
readonly parameters?: readonly Node[];
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** The name a call reaches one generic by: its own, or that of what holds it. */
|
|
47
|
+
function nameOf(generic: Generic): string | undefined {
|
|
48
|
+
const own = (generic as { readonly name?: Node }).name;
|
|
49
|
+
const holder = (generic.parent as { readonly name?: Node } | undefined)?.name;
|
|
50
|
+
const name = own ?? holder;
|
|
51
|
+
return name !== undefined && isIdentifier(name) ? name.text : undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Whether a property access is the target of a plain assignment, which writes the member. */
|
|
55
|
+
function assignedTo(access: PropertyAccessExpression): boolean {
|
|
56
|
+
const parent = access.parent;
|
|
57
|
+
return (
|
|
58
|
+
isBinaryExpression(parent) &&
|
|
59
|
+
parent.operatorToken.kind === SyntaxKind.EqualsToken &&
|
|
60
|
+
parent.left === access
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The references the reads through type-parameter-typed values of one project's own files
|
|
66
|
+
* make to the members of the types its calls pass, each made by the declaration holding
|
|
67
|
+
* the read.
|
|
68
|
+
*/
|
|
69
|
+
export function typeParameterReads<Brand>(
|
|
70
|
+
project: ProjectView<Brand>,
|
|
71
|
+
held: Inventory,
|
|
72
|
+
chains: AliasChains,
|
|
73
|
+
root: string,
|
|
74
|
+
testFiles: ReadonlySet<string>,
|
|
75
|
+
): readonly Reference[] {
|
|
76
|
+
const queries = project.queries;
|
|
77
|
+
const reads: Read[] = [];
|
|
78
|
+
const calls: (CallExpression | NewExpression)[] = [];
|
|
79
|
+
for (const file of project.ownSourceFiles()) {
|
|
80
|
+
const fileId = held.declarations.get(nodeKey(file, file));
|
|
81
|
+
if (fileId === undefined) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const visit = (node: Node, from: string, generic: boolean): void => {
|
|
85
|
+
const holder = held.declarations.get(nodeKey(file, node)) ?? from;
|
|
86
|
+
const inside =
|
|
87
|
+
generic ||
|
|
88
|
+
(node as { readonly typeParameters?: readonly Node[] }).typeParameters !== undefined;
|
|
89
|
+
if (
|
|
90
|
+
inside &&
|
|
91
|
+
isPropertyAccessExpression(node) &&
|
|
92
|
+
isIdentifier(node.name) &&
|
|
93
|
+
!assignedTo(node)
|
|
94
|
+
) {
|
|
95
|
+
reads.push({ file, from: holder, access: node });
|
|
96
|
+
}
|
|
97
|
+
if (isCallExpression(node) || isNewExpression(node)) {
|
|
98
|
+
calls.push(node);
|
|
99
|
+
}
|
|
100
|
+
node.forEachChild((child) => {
|
|
101
|
+
visit(child, holder, inside);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
file.forEachChild((child) => {
|
|
105
|
+
visit(child, fileId, false);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (reads.length === 0) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// The reads whose receiver the checker types as a type parameter, by the generic declaring it.
|
|
113
|
+
const receivers = queries.typesAt(reads.map((read) => read.access.expression));
|
|
114
|
+
const byGeneric = new Map<Generic, { read: Read; parameter: Type }[]>();
|
|
115
|
+
reads.forEach((read, at) => {
|
|
116
|
+
const type = receivers[at];
|
|
117
|
+
if (type === undefined || !isAnswered(type) || !type.isTypeParameter()) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const symbol = queries.symbolOfType(type);
|
|
121
|
+
const declaration =
|
|
122
|
+
symbol === undefined || !isAnswered(symbol)
|
|
123
|
+
? undefined
|
|
124
|
+
: symbol.declarations
|
|
125
|
+
.map((handle) => project.declarationAt(handle)?.node)
|
|
126
|
+
.find((node) => node !== undefined && isTypeParameterDeclaration(node));
|
|
127
|
+
const generic = declaration?.parent as Generic | undefined;
|
|
128
|
+
if (generic !== undefined) {
|
|
129
|
+
byGeneric.set(generic, [...(byGeneric.get(generic) ?? []), { read, parameter: type }]);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
if (byGeneric.size === 0) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** The types `instantiated` holds where `declared` holds `parameter`, matched part for part. */
|
|
137
|
+
const passedFor = (
|
|
138
|
+
parameter: Type,
|
|
139
|
+
declared: Type,
|
|
140
|
+
instantiated: Type,
|
|
141
|
+
depth: number,
|
|
142
|
+
): readonly Type[] => {
|
|
143
|
+
if (declared.id === parameter.id) {
|
|
144
|
+
return [instantiated];
|
|
145
|
+
}
|
|
146
|
+
if (depth >= MATCH_DEPTH) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
const a = queries.typeArguments(declared);
|
|
150
|
+
const b = queries.typeArguments(instantiated);
|
|
151
|
+
if (!isAnswered(a) || !isAnswered(b) || a.length !== b.length) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
return a.flatMap((one, at) => {
|
|
155
|
+
const other = b[at];
|
|
156
|
+
return other === undefined ? [] : passedFor(parameter, one, other, depth + 1);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Only a call spelling a generic's name is resolved: one request each.
|
|
161
|
+
const spelled = new Set([...byGeneric.keys()].flatMap((generic) => nameOf(generic) ?? []));
|
|
162
|
+
const found = new Map<string, Reference>();
|
|
163
|
+
for (const call of calls) {
|
|
164
|
+
const callee = call.expression;
|
|
165
|
+
const written = isIdentifier(callee)
|
|
166
|
+
? callee.text
|
|
167
|
+
: isPropertyAccessExpression(callee) && isIdentifier(callee.name)
|
|
168
|
+
? callee.name.text
|
|
169
|
+
: undefined;
|
|
170
|
+
if (written === undefined || !spelled.has(written)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const resolved = queries.resolvedSignature(call);
|
|
174
|
+
if (resolved === undefined || !isAnswered(resolved) || resolved.declaration === undefined) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const generic = project.declarationAt(resolved.declaration)?.node as Generic | undefined;
|
|
178
|
+
const pending = generic === undefined ? undefined : byGeneric.get(generic);
|
|
179
|
+
const declared = pending === undefined ? undefined : queries.genericOf(resolved);
|
|
180
|
+
if (pending === undefined || declared === undefined || !isAnswered(declared)) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const count = (generic?.parameters ?? []).length;
|
|
184
|
+
for (const { read, parameter } of pending) {
|
|
185
|
+
const passed: Type[] = [];
|
|
186
|
+
for (let at = 0; at < count; at += 1) {
|
|
187
|
+
const own = queries.parameterType(declared, at);
|
|
188
|
+
const given = queries.typeAtPosition(resolved, at);
|
|
189
|
+
if (own !== undefined && isAnswered(own) && isAnswered(given)) {
|
|
190
|
+
passed.push(...passedFor(parameter, own, given, 0));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const name = read.access.name.text;
|
|
194
|
+
const position = renderPosition(read.file, root, read.access.name.getStart());
|
|
195
|
+
for (const type of passed) {
|
|
196
|
+
const parts = type.isUnionType() ? queries.constituents(type) : [type];
|
|
197
|
+
for (const part of isAnswered(parts) ? parts : []) {
|
|
198
|
+
const owner = queries.symbolOfType(part);
|
|
199
|
+
if (
|
|
200
|
+
!isAnswered(owner) ||
|
|
201
|
+
owner === undefined ||
|
|
202
|
+
(owner.flags & SymbolFlags.Class) !== 0
|
|
203
|
+
) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const properties = queries.propertiesOf(part);
|
|
207
|
+
for (const property of isAnswered(properties) ? properties : []) {
|
|
208
|
+
if (property.name !== name) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
for (const to of chains.declarationsOf(property)) {
|
|
212
|
+
found.set(`${read.from}\u0000${to}\u0000${String(read.access.name.pos)}`, {
|
|
213
|
+
from: read.from,
|
|
214
|
+
to,
|
|
215
|
+
position,
|
|
216
|
+
use: "read",
|
|
217
|
+
resolution: "batch",
|
|
218
|
+
test: testFiles.has(position.path),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return [...found.values()];
|
|
227
|
+
}
|