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