@cplieger/deadset-ts 8.0.0-dev.2 → 8.0.0-dev.20

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 (402) hide show
  1. package/README.md +3 -1
  2. package/THIRD_PARTY_NOTICES.md +27 -0
  3. package/dist/bin/mapper-connection.js +2 -1
  4. package/dist/bin/node-host.js +24 -5
  5. package/dist/src/alias-chain.js +2 -7
  6. package/dist/src/amd.js +81 -0
  7. package/dist/src/analysis.js +269 -432
  8. package/dist/src/augmentations.js +64 -3
  9. package/dist/src/automatic-types.js +10 -29
  10. package/dist/src/bindings.js +382 -0
  11. package/dist/src/calls.js +279 -43
  12. package/dist/src/commands/held.js +499 -0
  13. package/dist/src/commands/read.js +161 -0
  14. package/dist/src/commands/shell.js +169 -0
  15. package/dist/src/commands/tools.js +1847 -0
  16. package/dist/src/component-files.js +3 -2
  17. package/dist/src/component-layer.js +4 -2
  18. package/dist/src/config.js +6 -82
  19. package/dist/src/conformance-record.js +1 -1
  20. package/dist/src/consumers.js +10 -38
  21. package/dist/src/contextual-uses.js +57 -26
  22. package/dist/src/contract/config.generated.js +561 -0
  23. package/dist/src/contract/contract.generated.js +1369 -0
  24. package/dist/src/contract/report.generated.js +866 -0
  25. package/dist/src/dependencies.js +164 -121
  26. package/dist/src/destructuring.js +1 -19
  27. package/dist/src/edges.js +38 -122
  28. package/dist/src/element-access.js +181 -168
  29. package/dist/src/entries/build-scripts.js +222 -0
  30. package/dist/src/{configuration-files.js → entries/configuration-files.js} +52 -256
  31. package/dist/src/{convention-rows.js → entries/convention-rows.js} +170 -30
  32. package/dist/src/entries/conventions.js +740 -0
  33. package/dist/src/{html-entries.js → entries/html-entries.js} +12 -42
  34. package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +5 -28
  35. package/dist/src/entries/run-directories.js +77 -0
  36. package/dist/src/entries/sources.js +144 -0
  37. package/dist/src/entries/static-module.js +670 -0
  38. package/dist/src/entries/test-runners.js +650 -0
  39. package/dist/src/entries/workflow-steps.js +345 -0
  40. package/dist/src/enum-group.js +3 -10
  41. package/dist/src/erased-stores.js +4 -6
  42. package/dist/src/exempt-classes.js +1 -63
  43. package/dist/src/exempt.js +21 -66
  44. package/dist/src/exposure.js +400 -0
  45. package/dist/src/extensions.js +37 -0
  46. package/dist/src/facts.js +38 -0
  47. package/dist/src/failure.js +36 -0
  48. package/dist/src/file-facts.js +3 -26
  49. package/dist/src/findings/completion.js +109 -56
  50. package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
  51. package/dist/src/findings/emitters.js +27 -41
  52. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  53. package/dist/src/{in-operands.js → findings/in-operands.js} +3 -2
  54. package/dist/src/findings/interfaces.js +4 -5
  55. package/dist/src/findings/intra-function.js +16 -13
  56. package/dist/src/findings/non-code-artifacts.js +17 -12
  57. package/dist/src/findings/reads-and-writes.js +86 -27
  58. package/dist/src/findings/self-check.js +44 -30
  59. package/dist/src/findings/unused-declarations.js +29 -45
  60. package/dist/src/findings/visibility-narrowing.js +23 -31
  61. package/dist/src/findings-pass.js +13 -21
  62. package/dist/src/framework-lifecycle.js +46 -19
  63. package/dist/src/fs/document.js +52 -0
  64. package/dist/src/fs/manifest.js +67 -0
  65. package/dist/src/fs/tree.js +88 -0
  66. package/dist/src/generated-file.js +174 -28
  67. package/dist/src/git-ignore.js +39 -26
  68. package/dist/src/import-bindings.js +73 -28
  69. package/dist/src/index/sites.js +557 -0
  70. package/dist/src/index.js +2 -2
  71. package/dist/src/indexed-access.js +523 -478
  72. package/dist/src/injection-container.js +2 -2
  73. package/dist/src/interface-satisfaction.js +1 -1
  74. package/dist/src/intra-function-parts.js +63 -43
  75. package/dist/src/inventory/commonjs.js +507 -0
  76. package/dist/src/inventory/enumerate.js +167 -0
  77. package/dist/src/inventory/syntax.js +236 -0
  78. package/dist/src/inventory.js +323 -724
  79. package/dist/src/javascript-file.js +48 -0
  80. package/dist/src/json-document.js +198 -67
  81. package/dist/src/keyof-comparisons.js +126 -107
  82. package/dist/src/kinds.js +3 -366
  83. package/dist/src/{components.js → liveness/components.js} +2 -3
  84. package/dist/src/{graph.js → liveness/graph.js} +52 -28
  85. package/dist/src/liveness/matrix.js +99 -0
  86. package/dist/src/liveness/sweep.js +558 -0
  87. package/dist/src/load/classify.js +289 -0
  88. package/dist/src/load/diagnostics.js +87 -0
  89. package/dist/src/load/discover.js +285 -0
  90. package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
  91. package/dist/src/load/expected-module.js +136 -0
  92. package/dist/src/load/load.js +27 -0
  93. package/dist/src/load/module-lookup.js +37 -0
  94. package/dist/src/load/package-scope.js +56 -0
  95. package/dist/src/load/plan.js +313 -0
  96. package/dist/src/load/specifiers.js +113 -0
  97. package/dist/src/load/synthesize.js +152 -0
  98. package/dist/src/load/test-globals.js +207 -0
  99. package/dist/src/load/test-joins.js +82 -0
  100. package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
  101. package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
  102. package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +24 -66
  103. package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
  104. package/dist/src/{workspace.js → load/workspace.js} +62 -124
  105. package/dist/src/module-declarations.js +70 -85
  106. package/dist/src/overrides.js +134 -112
  107. package/dist/src/partial.js +2 -1
  108. package/dist/src/paths.js +82 -16
  109. package/dist/src/platform-calls.js +78 -0
  110. package/dist/src/platform-symbols.js +134 -0
  111. package/dist/src/position.js +43 -1
  112. package/dist/src/print.js +11 -79
  113. package/dist/src/product.js +13 -0
  114. package/dist/src/query.js +47 -0
  115. package/dist/src/ref.js +12 -20
  116. package/dist/src/references/read-rule.js +1 -0
  117. package/dist/src/references/read-rules.js +48 -0
  118. package/dist/src/references/sink.js +237 -0
  119. package/dist/src/references/sources.js +450 -0
  120. package/dist/src/references.js +117 -1074
  121. package/dist/src/report-schema.js +51 -0
  122. package/dist/src/report.js +57 -41
  123. package/dist/src/reporters/annotations.js +15 -8
  124. package/dist/src/reporters/sarif.js +16 -10
  125. package/dist/src/reporters/template.js +4 -19
  126. package/dist/src/resolve.js +246 -730
  127. package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +22 -24
  128. package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +6 -15
  129. package/dist/src/roots/manifest.js +216 -0
  130. package/dist/src/roots/published-reach.js +412 -0
  131. package/dist/src/roots/root-set.js +487 -0
  132. package/dist/src/roots/unseen-loads.js +236 -0
  133. package/dist/src/run.js +66 -77
  134. package/dist/src/schema.js +320 -343
  135. package/dist/src/scope.js +38 -115
  136. package/dist/src/serialization-contract.js +601 -22
  137. package/dist/src/session.js +37 -29
  138. package/dist/src/source-files.js +6 -10
  139. package/dist/src/stores.js +4 -2
  140. package/dist/src/suppress-file.js +25 -28
  141. package/dist/src/suppress-inline.js +36 -83
  142. package/dist/src/suppress.js +50 -71
  143. package/dist/src/symbol-keys.js +92 -88
  144. package/dist/src/template-field.js +9 -19
  145. package/dist/src/tests/file-class.js +76 -0
  146. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  147. package/dist/src/{test-support.js → tests/support.js} +5 -12
  148. package/dist/src/text/compare.js +26 -0
  149. package/dist/src/text/glob.js +194 -0
  150. package/dist/src/text/named-files.js +298 -0
  151. package/dist/src/text/yaml.js +684 -0
  152. package/dist/src/type-errors.js +11 -232
  153. package/dist/src/type-parameter-reads.js +178 -139
  154. package/dist/src/unimplemented-callees.js +262 -242
  155. package/dist/src/unwrap.js +23 -0
  156. package/dist/src/usage.js +10 -0
  157. package/dist/src/vendor/unbash/ansi-c.js +132 -0
  158. package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
  159. package/dist/src/vendor/unbash/arithmetic.js +591 -0
  160. package/dist/src/vendor/unbash/assignment.js +131 -0
  161. package/dist/src/vendor/unbash/chars.js +48 -0
  162. package/dist/src/vendor/unbash/command.js +41 -0
  163. package/dist/src/vendor/unbash/expansion.js +80 -0
  164. package/dist/src/vendor/unbash/glob.js +268 -0
  165. package/dist/src/vendor/unbash/heredoc.js +27 -0
  166. package/dist/src/vendor/unbash/internal-types.js +1 -0
  167. package/dist/src/vendor/unbash/lexer.js +4274 -0
  168. package/dist/src/vendor/unbash/limits.js +3 -0
  169. package/dist/src/vendor/unbash/parser.js +1366 -0
  170. package/dist/src/vendor/unbash/parts.js +57 -0
  171. package/dist/src/vendor/unbash/substitution-context.js +8 -0
  172. package/dist/src/vendor/unbash/traversal.js +212 -0
  173. package/dist/src/vendor/unbash/types.js +1 -0
  174. package/dist/src/vendor/unbash/word-value.js +61 -0
  175. package/dist/src/vendor/unbash/word.js +40 -0
  176. package/dist/src/verbs/analyze.js +194 -127
  177. package/dist/src/verbs/describe.js +13 -18
  178. package/dist/src/verbs/explain.js +85 -139
  179. package/dist/src/verbs/print-config.js +2 -1
  180. package/dist/src/verbs/print-projects.js +6 -4
  181. package/dist/src/verbs/print-retained.js +7 -5
  182. package/dist/src/verbs/print-roots.js +4 -2
  183. package/dist/src/verbs/verb.js +1 -5
  184. package/dist/src/verbs/version.js +14 -3
  185. package/jsr.json +2 -1
  186. package/package.json +14 -8
  187. package/src/alias-chain.ts +2 -9
  188. package/src/amd.ts +130 -0
  189. package/src/analysis.ts +424 -650
  190. package/src/augmentations.ts +93 -4
  191. package/src/automatic-types.ts +12 -30
  192. package/src/bindings.ts +460 -0
  193. package/src/calls.ts +338 -46
  194. package/src/commands/held.ts +632 -0
  195. package/src/commands/read.ts +242 -0
  196. package/src/commands/shell.ts +228 -0
  197. package/src/commands/tools.ts +2382 -0
  198. package/src/component-files.ts +4 -2
  199. package/src/component-layer.ts +4 -2
  200. package/src/config.ts +31 -302
  201. package/src/conformance-record.ts +1 -1
  202. package/src/consumers.ts +16 -66
  203. package/src/contextual-uses.ts +66 -34
  204. package/src/contract/config.generated.ts +821 -0
  205. package/src/contract/contract.generated.ts +1452 -0
  206. package/src/contract/report.generated.ts +1241 -0
  207. package/src/dependencies.ts +222 -127
  208. package/src/destructuring.ts +1 -31
  209. package/src/edges.ts +41 -140
  210. package/src/element-access.ts +190 -187
  211. package/src/entries/build-scripts.ts +292 -0
  212. package/src/{configuration-files.ts → entries/configuration-files.ts} +68 -278
  213. package/src/{convention-rows.ts → entries/convention-rows.ts} +227 -31
  214. package/src/{conventions.ts → entries/conventions.ts} +367 -583
  215. package/src/{html-entries.ts → entries/html-entries.ts} +22 -48
  216. package/src/{mdx-entries.ts → entries/mdx-entries.ts} +7 -30
  217. package/src/entries/run-directories.ts +108 -0
  218. package/src/entries/sources.ts +266 -0
  219. package/src/entries/static-module.ts +858 -0
  220. package/src/entries/test-runners.ts +835 -0
  221. package/src/entries/workflow-steps.ts +432 -0
  222. package/src/enum-group.ts +2 -11
  223. package/src/erased-stores.ts +3 -7
  224. package/src/exempt-classes.ts +1 -75
  225. package/src/exempt.ts +70 -98
  226. package/src/exposure.ts +492 -0
  227. package/src/extensions.ts +45 -0
  228. package/src/facts.ts +46 -0
  229. package/src/failure.ts +63 -0
  230. package/src/file-facts.ts +15 -25
  231. package/src/finding.ts +10 -10
  232. package/src/findings/boundary.ts +11 -0
  233. package/src/findings/completion.ts +133 -64
  234. package/src/findings/dependencies-and-module-machinery.ts +4 -7
  235. package/src/findings/emitter.ts +9 -10
  236. package/src/findings/emitters.ts +38 -52
  237. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  238. package/src/{in-operands.ts → findings/in-operands.ts} +4 -3
  239. package/src/findings/interfaces.ts +7 -9
  240. package/src/findings/intra-function.ts +16 -14
  241. package/src/findings/non-code-artifacts.ts +24 -15
  242. package/src/findings/reads-and-writes.ts +118 -41
  243. package/src/findings/self-check.ts +51 -38
  244. package/src/findings/unused-declarations.ts +31 -49
  245. package/src/findings/visibility-narrowing.ts +36 -40
  246. package/src/findings-pass.ts +15 -31
  247. package/src/framework-lifecycle.ts +47 -19
  248. package/src/fs/document.ts +70 -0
  249. package/src/fs/manifest.ts +96 -0
  250. package/src/fs/tree.ts +110 -0
  251. package/src/generated-file.ts +242 -35
  252. package/src/git-ignore.ts +50 -26
  253. package/src/host.ts +12 -5
  254. package/src/import-bindings.ts +98 -29
  255. package/src/index/sites.ts +781 -0
  256. package/src/index.ts +2 -2
  257. package/src/indexed-access.ts +570 -527
  258. package/src/injection-container.ts +2 -2
  259. package/src/interface-satisfaction.ts +2 -2
  260. package/src/intra-function-parts.ts +73 -44
  261. package/src/inventory/commonjs.ts +696 -0
  262. package/src/inventory/enumerate.ts +203 -0
  263. package/src/inventory/syntax.ts +311 -0
  264. package/src/inventory.ts +442 -895
  265. package/src/javascript-file.ts +54 -0
  266. package/src/json-document.ts +241 -76
  267. package/src/keyof-comparisons.ts +133 -122
  268. package/src/kinds.ts +5 -413
  269. package/src/{components.ts → liveness/components.ts} +4 -6
  270. package/src/{graph.ts → liveness/graph.ts} +100 -35
  271. package/src/liveness/matrix.ts +162 -0
  272. package/src/liveness/sweep.ts +855 -0
  273. package/src/load/classify.ts +433 -0
  274. package/src/load/diagnostics.ts +154 -0
  275. package/src/load/discover.ts +471 -0
  276. package/src/{emit-map.ts → load/emit-map.ts} +1 -1
  277. package/src/load/expected-module.ts +194 -0
  278. package/src/load/load.ts +73 -0
  279. package/src/load/module-lookup.ts +50 -0
  280. package/src/load/package-scope.ts +74 -0
  281. package/src/load/plan.ts +513 -0
  282. package/src/load/specifiers.ts +171 -0
  283. package/src/load/synthesize.ts +230 -0
  284. package/src/load/test-globals.ts +251 -0
  285. package/src/load/test-joins.ts +98 -0
  286. package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
  287. package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
  288. package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +41 -77
  289. package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
  290. package/src/{workspace.ts → load/workspace.ts} +90 -134
  291. package/src/module-declarations.ts +74 -98
  292. package/src/overrides.ts +162 -134
  293. package/src/partial.ts +3 -2
  294. package/src/paths.ts +92 -16
  295. package/src/platform-calls.ts +119 -0
  296. package/src/platform-symbols.ts +189 -0
  297. package/src/position.ts +44 -1
  298. package/src/print.ts +12 -92
  299. package/src/product.ts +18 -0
  300. package/src/query.ts +92 -0
  301. package/src/ref.ts +12 -20
  302. package/src/references/read-rule.ts +60 -0
  303. package/src/references/read-rules.ts +55 -0
  304. package/src/references/sink.ts +314 -0
  305. package/src/references/sources.ts +632 -0
  306. package/src/references.ts +187 -1450
  307. package/src/report-schema.ts +68 -0
  308. package/src/report.ts +103 -226
  309. package/src/reporters/annotations.ts +22 -11
  310. package/src/reporters/json.ts +1 -1
  311. package/src/reporters/reporter.ts +2 -4
  312. package/src/reporters/reporters.ts +1 -1
  313. package/src/reporters/sarif.ts +29 -16
  314. package/src/reporters/template.ts +4 -20
  315. package/src/reporters/text.ts +3 -3
  316. package/src/resolve.ts +284 -1115
  317. package/src/{configured-declarations.ts → roots/configured-declarations.ts} +27 -29
  318. package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +7 -16
  319. package/src/roots/manifest.ts +303 -0
  320. package/src/roots/published-reach.ts +486 -0
  321. package/src/{roots.ts → roots/root-set.ts} +286 -469
  322. package/src/roots/unseen-loads.ts +345 -0
  323. package/src/run.ts +75 -91
  324. package/src/schema.ts +391 -388
  325. package/src/scope.ts +46 -138
  326. package/src/serialization-contract.ts +769 -23
  327. package/src/session.ts +87 -55
  328. package/src/source-files.ts +6 -11
  329. package/src/stores.ts +15 -3
  330. package/src/suppress-file.ts +24 -33
  331. package/src/suppress-inline.ts +55 -91
  332. package/src/suppress.ts +78 -79
  333. package/src/symbol-keys.ts +104 -107
  334. package/src/template-field.ts +15 -25
  335. package/src/tests/file-class.ts +135 -0
  336. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  337. package/src/{test-support.ts → tests/support.ts} +15 -21
  338. package/src/text/compare.ts +27 -0
  339. package/src/text/glob.ts +210 -0
  340. package/src/text/named-files.ts +388 -0
  341. package/src/text/yaml.ts +747 -0
  342. package/src/type-errors.ts +18 -288
  343. package/src/type-parameter-reads.ts +187 -158
  344. package/src/unimplemented-callees.ts +297 -280
  345. package/src/unwrap.ts +34 -0
  346. package/src/usage.ts +10 -0
  347. package/src/vendor/unbash/LICENSE +15 -0
  348. package/src/vendor/unbash/VENDORED.md +16 -0
  349. package/src/vendor/unbash/ansi-c.ts +132 -0
  350. package/src/vendor/unbash/arithmetic-parts.ts +53 -0
  351. package/src/vendor/unbash/arithmetic.ts +641 -0
  352. package/src/vendor/unbash/assignment.ts +163 -0
  353. package/src/vendor/unbash/chars.ts +48 -0
  354. package/src/vendor/unbash/command.ts +47 -0
  355. package/src/vendor/unbash/expansion.ts +144 -0
  356. package/src/vendor/unbash/glob.ts +284 -0
  357. package/src/vendor/unbash/heredoc.ts +34 -0
  358. package/src/vendor/unbash/internal-types.ts +94 -0
  359. package/src/vendor/unbash/lexer.ts +4352 -0
  360. package/src/vendor/unbash/limits.ts +3 -0
  361. package/src/vendor/unbash/parser.ts +1612 -0
  362. package/src/vendor/unbash/parts.ts +96 -0
  363. package/src/vendor/unbash/substitution-context.ts +20 -0
  364. package/src/vendor/unbash/traversal.ts +182 -0
  365. package/src/vendor/unbash/types.ts +660 -0
  366. package/src/vendor/unbash/word-value.ts +64 -0
  367. package/src/vendor/unbash/word.ts +50 -0
  368. package/src/verbs/analyze.ts +255 -164
  369. package/src/verbs/describe.ts +19 -17
  370. package/src/verbs/explain.ts +93 -169
  371. package/src/verbs/print-config.ts +2 -1
  372. package/src/verbs/print-projects.ts +6 -4
  373. package/src/verbs/print-retained.ts +7 -5
  374. package/src/verbs/print-roots.ts +4 -2
  375. package/src/verbs/verb.ts +2 -8
  376. package/src/verbs/version.ts +15 -3
  377. package/dist/src/base-url.js +0 -18
  378. package/dist/src/conventions.js +0 -937
  379. package/dist/src/discover.js +0 -468
  380. package/dist/src/entry-points.js +0 -529
  381. package/dist/src/glob.js +0 -65
  382. package/dist/src/manifest.js +0 -165
  383. package/dist/src/matrix.js +0 -246
  384. package/dist/src/pnpm-workspace.js +0 -120
  385. package/dist/src/roots.js +0 -672
  386. package/dist/src/setup-failure.js +0 -23
  387. package/dist/src/sweep.js +0 -224
  388. package/dist/src/test-joins.js +0 -101
  389. package/dist/src/version.js +0 -7
  390. package/dist/src/workflow-steps.js +0 -185
  391. package/src/base-url.ts +0 -24
  392. package/src/discover.ts +0 -653
  393. package/src/entry-points.ts +0 -687
  394. package/src/glob.ts +0 -67
  395. package/src/manifest.ts +0 -234
  396. package/src/matrix.ts +0 -423
  397. package/src/pnpm-workspace.ts +0 -135
  398. package/src/setup-failure.ts +0 -39
  399. package/src/sweep.ts +0 -347
  400. package/src/test-joins.ts +0 -120
  401. package/src/version.ts +0 -7
  402. package/src/workflow-steps.ts +0 -216
package/src/references.ts CHANGED
@@ -1,95 +1,32 @@
1
- import {
2
- isArrayLiteralExpression,
3
- isAsExpression,
4
- isBinaryExpression,
5
- isCallExpression,
6
- isDecorator,
7
- isDeleteExpression,
8
- isElementAccessExpression,
9
- isExportDeclaration,
10
- isExpressionStatement,
11
- isExternalModuleReference,
12
- isForInStatement,
13
- isForOfStatement,
14
- isForStatement,
15
- isIdentifier,
16
- isImportDeclaration,
17
- isImportEqualsDeclaration,
18
- isInterfaceDeclaration,
19
- isMetaProperty,
20
- isModuleBlock,
21
- isModuleDeclaration,
22
- isNewExpression,
23
- isNamespaceExport,
24
- isNoSubstitutionTemplateLiteral,
25
- isObjectLiteralExpression,
26
- isParenthesizedExpression,
27
- isPostfixUnaryExpression,
28
- isPrefixUnaryExpression,
29
- isPropertyAccessExpression,
30
- isPropertyAssignment,
31
- isShorthandPropertyAssignment,
32
- isSpreadAssignment,
33
- isSpreadElement,
34
- isStringLiteral,
35
- isVoidExpression,
36
- SyntaxKind,
37
- type Node,
38
- type ParameterDeclaration,
39
- type SourceFile,
40
- } from "@typescript/native/ast";
1
+ /**
2
+ * Every use one side's files make of the target's declarations: one walk and one batched
3
+ * resolution per file, never a reference query per symbol, whose answers would depend on the
4
+ * order the symbols are asked in. A reference carries its use, its position, the declaration
5
+ * enclosing it and whether a test file made it; it carries no mode: which references a run
6
+ * counts is the sweep's reading of this set.
7
+ */
8
+
9
+ import { SyntaxKind, type Node } from "@typescript/native/ast";
41
10
  import type { Symbol as TSSymbol } from "@typescript/native/sync";
42
11
  import { aliasChains, type ChainTarget } from "./alias-chain.ts";
43
- import { contextualUses, type FlowAt, type LiteralAt } from "./contextual-uses.ts";
44
- import { destructuring, propertyReadsOf, type PropertyRead } from "./destructuring.ts";
45
- import { specifierCandidates } from "./configuration-files.ts";
46
- import { namespaceUses } from "./namespace-objects.ts";
47
- import { globExpression } from "./glob.ts";
48
- import {
49
- declarationsByName,
50
- isParameterProperty,
51
- nodeKey,
52
- type Inventory,
53
- type InventorySymbol,
54
- } from "./inventory.ts";
55
- import { ambientGlobalInterfaces, augmentationReferences } from "./augmentations.ts";
56
- import { elementAccessReferences } from "./element-access.ts";
57
- import { indexedAccessReferences } from "./indexed-access.ts";
58
- import { keyofComparisonReferences } from "./keyof-comparisons.ts";
59
- import { unimplementedCalleeReads } from "./unimplemented-callees.ts";
60
- import { moduleDeclarationReferences } from "./module-declarations.ts";
61
- import { overrideReferences } from "./overrides.ts";
62
- import { dirnamePath, queriedModulePath, relativePath, resolvePath } from "./paths.ts";
63
- import { byPosition, isComponentFile, renderPosition, type Position } from "./position.ts";
12
+ import { augmentationReferences, mergedGlobalInterfaces } from "./augmentations.ts";
13
+ import { contextualUses } from "./contextual-uses.ts";
14
+ import { destructuring } from "./destructuring.ts";
15
+ import type { RunDirectories } from "./entries/run-directories.ts";
16
+ import { bindingsOf } from "./import-bindings.ts";
17
+ import { sitesOf } from "./index/sites.ts";
18
+ import { nodeKey, type Inventory, type InventorySymbol } from "./inventory.ts";
19
+ import { relativePath } from "./paths.ts";
20
+ import type { Position } from "./position.ts";
64
21
  import { DEFAULT_BATCH_CAP, propertyTables, UNANSWERED, type Answer } from "./query.ts";
22
+ import { heldBy, READ_RULES } from "./references/read-rules.ts";
23
+ import { Sink } from "./references/sink.ts";
24
+ import { emitFile, prepared, type Pass } from "./references/sources.ts";
65
25
  import type { ProjectView } from "./session.ts";
66
- import { typeParameterReads } from "./type-parameter-reads.ts";
26
+ import { sourceFilesOf, type SourceFiles } from "./source-files.ts";
27
+ import { bytewise } from "./text/compare.ts";
28
+ import type { TestFile } from "./tests/file-class.ts";
67
29
  import { typeQueryAliases } from "./type-query-alias.ts";
68
- import {
69
- computedKeyNames,
70
- iterationReferences,
71
- keyNameOf,
72
- keyedUses,
73
- type KeyedAccess,
74
- } from "./symbol-keys.ts";
75
- import { handedOn, valuesOf } from "./value-flow.ts";
76
-
77
- /**
78
- * Every use one project's own files make of the project's own declarations.
79
- *
80
- * The pass is one local walk per file and one batched resolution per file, never a
81
- * query per symbol: a per-symbol reference query is the shape whose answers depend on
82
- * the order the symbols are asked about, so no answer here depends on that order. The
83
- * tree is on this side of the client boundary and the resolution is not, which is why a
84
- * file's name nodes are gathered whole and handed over together.
85
- *
86
- * What the pass records and what it does not: a reference carries its use, its
87
- * position, the declaration that encloses it and the classification of the file that
88
- * made it, and every place that imports one of the project's own modules is an
89
- * evaluation of that module's file. It carries no mode. Which of the references a run
90
- * counts is the reading the sweep makes of this set, so the mode is one value the run
91
- * decides once and this pass never sees it.
92
- */
93
30
 
94
31
  /**
95
32
  * How one reference uses the declaration it names. An `evaluation` names a file: the
@@ -98,17 +35,17 @@ import { handedOn, valuesOf } from "./value-flow.ts";
98
35
  * `decorator` names the declaration a decorator expression is attached to, which the
99
36
  * decorator receives when the class is defined.
100
37
  */
101
- type Use = "read" | "write" | "evaluation" | "decorator";
38
+ export type Use = "read" | "write" | "evaluation" | "decorator";
102
39
 
103
40
  /**
104
- * Which path answered for one reference: `batch`, a file's batched lookup; `resolved-symbol`, the
105
- * per-node lookup for its residue; `shorthand`, the value `{ a }` reads; `alias`, a step along an
106
- * import or export chain; `syntax`, the tree alone (a decorator's target, a component's markup);
107
- * `destructured`, the property a pattern names; `contextual`, the member a literal property writes
108
- * or a value's position reads; `override`, the override a base call dispatches to; `keyed`, the
109
- * member a symbol key selects; `by-name`, each declaration so spelled, where the checker failed.
41
+ * Which path answered for one reference, provenance only: `batch`, a file's batched lookup;
42
+ * `resolved-symbol`, the per-node lookup for its residue; `shorthand`, the value `{ a }` reads;
43
+ * `alias`, a step along an import or export chain; `syntax`, the tree alone; `destructured`,
44
+ * the property a pattern names; `contextual`, the member a literal property writes or a
45
+ * value's position reads; `override`, the override a base call dispatches to; `keyed`, the
46
+ * member a key selects; `by-name`, each declaration so spelled, where the checker failed.
110
47
  */
111
- type Resolution =
48
+ export type Resolution =
112
49
  | "batch"
113
50
  | "resolved-symbol"
114
51
  | "shorthand"
@@ -135,8 +72,13 @@ export interface Reference {
135
72
  readonly position: Position;
136
73
  readonly use: Use;
137
74
  readonly resolution: Resolution;
138
- /** Whether the file that made the reference is classified as a test file. */
75
+ /** Whether a test file made the reference (`tests/file-class.ts`). */
139
76
  readonly test: boolean;
77
+ /**
78
+ * Present where only the name answered, the checker having failed (program.33): the
79
+ * reference may be a use of any declaration so spelled, so the run holds what it names.
80
+ */
81
+ readonly guessed?: true;
140
82
  /**
141
83
  * The identifier of the consumer whose file made the reference, absent for one the
142
84
  * target made. A consumer's declarations are no declarations of the run, so such a
@@ -152,6 +94,13 @@ export interface Reference {
152
94
  * declaration with its writes deletes too.
153
95
  */
154
96
  readonly alone?: readonly string[];
97
+ /**
98
+ * The declarations whose deletion with their writes deletes the referencing name,
99
+ * innermost first: `from` where the name is in its own annotation, then each declaration
100
+ * a write of the same file stores into whose stored value holds the name. Absent where
101
+ * there is none, and on a consumer's reference.
102
+ */
103
+ readonly carriers?: readonly string[];
155
104
  }
156
105
 
157
106
  /** The consumer one reference pass reads the files of, beside the target it names. */
@@ -162,68 +111,24 @@ export interface ConsumerSide {
162
111
  readonly root: string;
163
112
  }
164
113
 
165
- /** One rule that classified files as test files, and how many files it matched. */
166
- export interface TestFileRule {
167
- /** The rule's name, as a report's own rule list spells one. */
168
- readonly rule: string;
169
- readonly matched: number;
170
- }
171
-
172
- /** What one project's reference pass cost at the client boundary. */
173
- export interface ReferenceCost {
174
- /**
175
- * The name nodes the batches asked about, summed over the files. It is the size of
176
- * the question rather than a cost of its own, and it is reported so that the number
177
- * of batches is readable against it.
178
- */
179
- readonly batched: number;
180
- /**
181
- * Batched lookups: per file, the name-node count divided by the batch cap, rounded
182
- * up. It is what keeps the pass's cost off the identifier count.
183
- */
184
- readonly fileBatches: number;
185
- /** Per-node lookups for the name nodes a batch left unresolved. */
186
- readonly residueFallbacks: number;
187
- /** Per-node lookups for a shorthand property assignment's value. */
188
- readonly shorthandLookups: number;
189
- /** Per-symbol lookups that step from one alias to what it names, one per alias. */
190
- readonly aliasSteps: number;
191
- /**
192
- * Batched type lookups for the object patterns: per file, the patterns and the values
193
- * assigned to destructuring targets, divided by the batch cap, rounded up.
194
- */
195
- readonly patternBatches: number;
196
- /**
197
- * Per-type lookups for the object patterns: one property table per distinct type a
198
- * pattern destructures, and each step a nested assignment target takes into its value.
199
- */
200
- readonly patternLookups: number;
114
+ /** One side's references, in position order. */
115
+ export interface References {
116
+ readonly references: readonly Reference[];
201
117
  /**
202
- * Batched lookups for the contextual uses: the contextual types of the object literals
203
- * and of the values that carry a member, and the values' types, each per capped run.
118
+ * The declarations a read rule's unanswered question could have made live, and those
119
+ * making such a read, each once, ascending: the run holds them (program.33).
204
120
  */
205
- readonly contextualBatches: number;
206
- /** Per-type lookups for the contextual uses: each distinct type's constituents, properties and symbol. */
207
- readonly contextualLookups: number;
121
+ readonly held: readonly string[];
208
122
  /**
209
- * The requests the passes after the walk make: the bases overrides are read against,
210
- * element accesses, key comparisons, instantiated signatures, values handed to
211
- * unimplemented callees and the modules a `declare module` names. They are counted
212
- * where the caller measures the client, through `requestCount`, and are 0 otherwise.
123
+ * Each load by a computed path a production file of the target writes whose files the
124
+ * analysis cannot name (confidence.26), in file then source order; none for a consumer.
213
125
  */
214
- readonly typedLookups: number;
215
- }
216
-
217
- /** One project's references, in position order, and what reading them cost. */
218
- export interface References {
219
- readonly references: readonly Reference[];
126
+ readonly unseenLoads: readonly Position[];
220
127
  /**
221
- * Every file the rules classified as a test file, by its path below the target root,
222
- * ascending. A declaration such a file holds is a test declaration, by the same
223
- * classification that marks the references the file makes.
128
+ * The files, and the declarations an import binds there, that only a bundler's retry of a
129
+ * specifier Node leaves unresolved names (program.38), each once, ascending.
224
130
  */
225
- readonly testFilePaths: readonly string[];
226
- readonly cost: ReferenceCost;
131
+ readonly possiblyLoaded: readonly string[];
227
132
  }
228
133
 
229
134
  /** How one run resolves references. */
@@ -234,598 +139,19 @@ export interface ReferenceOptions {
234
139
  * makes one answer proportional to that file. Absent takes {@link DEFAULT_BATCH_CAP}.
235
140
  */
236
141
  readonly batchCap?: number;
237
- /**
238
- * Glob patterns, relative to the target root, naming the files that are test files.
239
- * Each pattern is a rule of its own, which {@link testFileRulesOf} counts.
240
- */
241
- readonly testFiles: readonly string[];
242
- /** The files, absolute, that are test files beside those the patterns name. */
243
- readonly testFileNames?: ReadonlySet<string>;
142
+ /** Which of the side's files are test files. */
143
+ readonly testFile: TestFile;
244
144
  /**
245
145
  * The consumer whose files the pass walks instead of the target's. Its files are the
246
146
  * program's own files below its root and not below the target root, each position is
247
- * rendered against its root and the test patterns read the path below it, and only a
248
- * reference to a declaration of `held`, the target's, is recorded.
147
+ * rendered against its root, and only a reference to a declaration of `held`, the
148
+ * target's, is recorded.
249
149
  */
250
150
  readonly consumer?: ConsumerSide;
251
- /** The client's running request count, where the caller measures the client. */
252
- readonly requestCount?: () => number;
253
- }
254
-
255
- /**
256
- * The operators whose left side one assignment stores into. A compound assignment is
257
- * among them: where its value is discarded it reads its target only to write the result
258
- * back, so nothing else receives what it read.
259
- */
260
- const ASSIGNMENTS: ReadonlySet<SyntaxKind> = new Set([
261
- SyntaxKind.EqualsToken,
262
- SyntaxKind.PlusEqualsToken,
263
- SyntaxKind.MinusEqualsToken,
264
- SyntaxKind.AsteriskEqualsToken,
265
- SyntaxKind.AsteriskAsteriskEqualsToken,
266
- SyntaxKind.SlashEqualsToken,
267
- SyntaxKind.PercentEqualsToken,
268
- SyntaxKind.LessThanLessThanEqualsToken,
269
- SyntaxKind.GreaterThanGreaterThanEqualsToken,
270
- SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
271
- SyntaxKind.AmpersandEqualsToken,
272
- SyntaxKind.BarEqualsToken,
273
- SyntaxKind.BarBarEqualsToken,
274
- SyntaxKind.AmpersandAmpersandEqualsToken,
275
- SyntaxKind.QuestionQuestionEqualsToken,
276
- SyntaxKind.CaretEqualsToken,
277
- ]);
278
-
279
- /**
280
- * The assignments that read their target to decide whether to store at all, so the read
281
- * carries information out whatever becomes of the result.
282
- */
283
- const LOGICAL_ASSIGNMENTS: ReadonlySet<SyntaxKind> = new Set([
284
- SyntaxKind.BarBarEqualsToken,
285
- SyntaxKind.AmpersandAmpersandEqualsToken,
286
- SyntaxKind.QuestionQuestionEqualsToken,
287
- ]);
288
-
289
- /**
290
- * Whether the value of one store expression is discarded: it is a statement of its own, a
291
- * clause of a `for` loop, the operand of `void`, or the left operand of a comma.
292
- */
293
- function valueDiscarded(node: Node): boolean {
294
- let child = node;
295
- let parent = node.parent;
296
- while (isParenthesizedExpression(parent)) {
297
- child = parent;
298
- parent = parent.parent;
299
- }
300
- if (isExpressionStatement(parent) || isVoidExpression(parent)) {
301
- return true;
302
- }
303
- if (isForStatement(parent)) {
304
- return parent.initializer === child || parent.incrementor === child;
305
- }
306
- return (
307
- isBinaryExpression(parent) &&
308
- parent.operatorToken.kind === SyntaxKind.CommaToken &&
309
- parent.left === child
310
- );
311
- }
312
-
313
- /** Whether one node is an identifier or a private name, the two forms a name takes. */
314
- function isName(node: Node): boolean {
315
- return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier;
316
- }
317
-
318
- /** The expression one node is, through however many parentheses are written around it. */
319
- function unparenthesized(node: Node): Node {
320
- let held = node;
321
- while (isParenthesizedExpression(held)) {
322
- held = held.expression;
323
- }
324
- return held;
325
- }
326
-
327
- /**
328
- * Records the name one store writes into.
329
- *
330
- * A property access writes the member it names. An element access writes the collection
331
- * it stores into, since a collection a module only stores into holds nothing anything
332
- * reads, and joins `accesses` for the member a unique-symbol key selects. A destructuring
333
- * target writes each name it binds, through its spreads and nested patterns.
334
- */
335
- function markWrite(target: Node, writes: Set<number>, accesses: Set<Node>): void {
336
- const held = unparenthesized(target);
337
- if (isName(held)) {
338
- writes.add(held.pos);
339
- return;
340
- }
341
- if (isPropertyAccessExpression(held)) {
342
- if (isName(held.name)) {
343
- writes.add(held.name.pos);
344
- }
345
- return;
346
- }
347
- if (isElementAccessExpression(held)) {
348
- accesses.add(held);
349
- // A store through a member's value lands in storage every holder of the value
350
- // reads, so it reads the member; a variable only ever stored into stays written.
351
- if (!isPropertyAccessExpression(unparenthesized(held.expression))) {
352
- markWrite(held.expression, writes, accesses);
353
- }
354
- return;
355
- }
356
- if (isArrayLiteralExpression(held)) {
357
- for (const element of held.elements) {
358
- markWrite(element, writes, accesses);
359
- }
360
- return;
361
- }
362
- if (isObjectLiteralExpression(held)) {
363
- for (const property of held.properties) {
364
- if (isPropertyAssignment(property)) {
365
- markWrite(property.initializer, writes, accesses);
366
- continue;
367
- }
368
- if (isShorthandPropertyAssignment(property)) {
369
- markWrite(property.name, writes, accesses);
370
- continue;
371
- }
372
- if (isSpreadAssignment(property)) {
373
- markWrite(property.expression, writes, accesses);
374
- }
375
- }
376
- return;
377
- }
378
- if (isSpreadElement(held)) {
379
- markWrite(held.expression, writes, accesses);
380
- }
381
- }
382
-
383
- /**
384
- * Records every store one node performs: an assignment, a compound assignment, an
385
- * increment or a decrement, a `delete`, and an iteration that assigns to a name declared
386
- * elsewhere. An iteration that declares its own name stores into nothing the inventory
387
- * holds. A store that reads its target as well records it in `reads`: a logical
388
- * assignment, and a compound assignment, an increment or a decrement whose value is used.
389
- */
390
- function markStores(
391
- node: Node,
392
- writes: Set<number>,
393
- reads: Set<number>,
394
- accesses: { readonly written: Set<Node>; readonly read: Set<Node> },
395
- ): void {
396
- if (isBinaryExpression(node)) {
397
- const operator = node.operatorToken.kind;
398
- if (ASSIGNMENTS.has(operator)) {
399
- markWrite(node.left, writes, accesses.written);
400
- if (
401
- LOGICAL_ASSIGNMENTS.has(operator) ||
402
- (operator !== SyntaxKind.EqualsToken && !valueDiscarded(node))
403
- ) {
404
- markWrite(node.left, reads, accesses.read);
405
- }
406
- }
407
- return;
408
- }
409
- if (isPrefixUnaryExpression(node) || isPostfixUnaryExpression(node)) {
410
- if (
411
- node.operator === SyntaxKind.PlusPlusToken ||
412
- node.operator === SyntaxKind.MinusMinusToken
413
- ) {
414
- markWrite(node.operand, writes, accesses.written);
415
- if (!valueDiscarded(node)) {
416
- markWrite(node.operand, reads, accesses.read);
417
- }
418
- }
419
- return;
420
- }
421
- if (isDeleteExpression(node)) {
422
- markWrite(node.expression, writes, accesses.written);
423
- return;
424
- }
425
- if (isForInStatement(node) || isForOfStatement(node)) {
426
- if (node.initializer.kind !== SyntaxKind.VariableDeclarationList) {
427
- markWrite(node.initializer, writes, accesses.written);
428
- }
429
- }
430
- }
431
-
432
- /** One name node the walk found, and what the walk knows about it. */
433
- interface Site {
434
- /** The name node, whose position the reference carries. */
435
- readonly node: Node;
436
- /**
437
- * The shorthand property assignment this name belongs to, where it belongs to one.
438
- * Its value is the declaration the name reads, while the name itself declares the
439
- * property the literal carries.
440
- */
441
- readonly shorthand: Node | undefined;
442
- readonly from: string;
443
- readonly use: Use;
444
- }
445
-
446
- /**
447
- * One re-export the walk found, which names what it carries forward without a name
448
- * that is a use: an export specifier, a namespace export, or a bare star re-export.
449
- */
450
- interface Link {
451
- /**
452
- * The node whose symbol says what the re-export carries: the alias a specifier or a
453
- * namespace export declares, or the module a bare star re-export names.
454
- */
455
- readonly node: Node;
456
- /** Where the reference is written: the name a specifier re-exports, or the module specifier. */
457
- readonly at: Node;
458
- /** The re-export's own declaration, or for a bare star the declaration enclosing it. */
459
- readonly from: string;
460
- /** Whether the node's symbol is an alias, which is followed one link along. */
461
- readonly alias: boolean;
462
- }
463
-
464
- /** One place the walk found that evaluates a module, and the declaration that holds it. */
465
- interface Evaluation {
466
- /** The module specifier, whose symbol is the module evaluated. */
467
- readonly node: Node;
468
- readonly from: string;
469
- }
470
-
471
- /**
472
- * One module URL a source file builds: `new URL(<literal>, import.meta.url)` or
473
- * `import.meta.resolve(<literal>)`, the literal relative. A runtime starts the file it
474
- * names as a module, so it is an import of that file for its effects.
475
- */
476
- interface ModuleURL {
477
- readonly specifier: string;
478
- /** The `URL` the constructor names, which must name no declaration of the target. */
479
- readonly constructor: Node | undefined;
480
- readonly at: Node;
481
- }
482
-
483
- /** One use of a module namespace object that may read every export of the module. */
484
- interface WholeRead {
485
- /** The module specifier, whose symbol is the module read. */
486
- readonly specifier: Node;
487
- /** The use, whose position the reference carries. */
488
- readonly at: Node;
489
- readonly from: string;
490
- }
491
-
492
- /** One decorator expression, and the declaration it is attached to. */
493
- interface Decorated {
494
- readonly decorator: Node;
495
- readonly declaration: string;
496
- }
497
-
498
- /** One parameter property, which its constructor writes, and the declaration that writes it. */
499
- interface ParameterWrite {
500
- readonly name: Node;
501
- readonly from: string;
502
- readonly member: string;
503
- }
504
-
505
- /** One property a pattern reads, and the declaration the pattern is written inside. */
506
- interface Read extends PropertyRead {
507
- readonly from: string;
508
- }
509
-
510
- /** Everything one file's walk found to resolve, and the decorators it needs no resolution for. */
511
- interface FileSites {
512
- readonly uses: readonly Site[];
513
- readonly literals: readonly LiteralAt[];
514
- readonly flows: readonly FlowAt[];
515
- readonly reads: readonly Read[];
516
- readonly links: readonly Link[];
517
- readonly evaluations: readonly Evaluation[];
518
- readonly wholes: readonly WholeRead[];
519
- readonly urls: readonly ModuleURL[];
520
- readonly decorated: readonly Decorated[];
521
- readonly parameterWrites: readonly ParameterWrite[];
522
- readonly keyed: readonly KeyedAccess[];
523
- }
524
-
525
- /** Whether one node is `import.meta.url`. */
526
- function isImportMetaURL(node: Node): boolean {
527
- const at = unparenthesized(node);
528
- return (
529
- isPropertyAccessExpression(at) &&
530
- isMetaProperty(at.expression) &&
531
- at.expression.keywordToken === SyntaxKind.ImportKeyword &&
532
- at.name.text === "url"
533
- );
534
- }
535
-
536
- /** The module URL one node builds, where it builds one. */
537
- function moduleURLOf(node: Node): ModuleURL | undefined {
538
- const relative = (argument: Node | undefined): string | undefined => {
539
- const at = argument === undefined ? undefined : unparenthesized(argument);
540
- return at !== undefined &&
541
- (isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at)) &&
542
- /^\.\.?\//u.test(at.text)
543
- ? at.text
544
- : undefined;
545
- };
546
- if (isNewExpression(node) && isIdentifier(node.expression) && node.expression.text === "URL") {
547
- const [reference, base] = node.arguments ?? [];
548
- const specifier = relative(reference);
549
- return specifier !== undefined && base !== undefined && isImportMetaURL(base)
550
- ? { specifier, constructor: node.expression, at: node }
551
- : undefined;
552
- }
553
- if (isCallExpression(node)) {
554
- const callee = unparenthesized(node.expression);
555
- const specifier = relative(node.arguments[0]);
556
- if (
557
- specifier !== undefined &&
558
- isPropertyAccessExpression(callee) &&
559
- isMetaProperty(callee.expression) &&
560
- callee.expression.keywordToken === SyntaxKind.ImportKeyword &&
561
- callee.name.text === "resolve"
562
- ) {
563
- return { specifier, constructor: undefined, at: node };
564
- }
565
- }
566
- return undefined;
567
- }
568
-
569
- /** Whether one node is a string literal or a template literal with no substitution. */
570
- function isLiteralText(node: Node | undefined): node is Node {
571
- return node !== undefined && (isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node));
572
- }
573
-
574
- /**
575
- * The module specifier of the module one node evaluates, if it evaluates one: an import
576
- * declaration, an import-equals declaration of an external module, an export
577
- * declaration that names a module, and an `import()` call whose argument is literal
578
- * text. A type-only form evaluates nothing. An `import()` of a computed argument names
579
- * no module this pass can know.
580
- */
581
- function evaluatedBy(node: Node): Node | undefined {
582
- if (isImportDeclaration(node)) {
583
- return node.importClause?.phaseModifier === SyntaxKind.TypeKeyword
584
- ? undefined
585
- : node.moduleSpecifier;
586
- }
587
- if (isExportDeclaration(node)) {
588
- return node.isTypeOnly ? undefined : node.moduleSpecifier;
589
- }
590
- if (isImportEqualsDeclaration(node)) {
591
- const reference = node.moduleReference;
592
- return !node.isTypeOnly && isExternalModuleReference(reference)
593
- ? reference.expression
594
- : undefined;
595
- }
596
- if (isCallExpression(node) && node.expression.kind === SyntaxKind.ImportKeyword) {
597
- const [argument] = node.arguments;
598
- return isLiteralText(argument) ? argument : undefined;
599
- }
600
- return undefined;
601
- }
602
-
603
- /**
604
- * The own file a module specifier with a query evaluates ({@link queriedModulePath}): the
605
- * checker resolves the whole specifier to whatever module declaration matches it.
606
- */
607
- function queriedFile(
608
- file: SourceFile,
609
- specifier: Node,
610
- fileIdByName: ReadonlyMap<string, string>,
611
- ): string | undefined {
612
- if (!isStringLiteral(specifier) && !isNoSubstitutionTemplateLiteral(specifier)) {
613
- return undefined;
614
- }
615
- const path = queriedModulePath(specifier.text);
616
- return path === undefined
617
- ? undefined
618
- : fileIdByName.get(resolvePath(dirnamePath(file.fileName), path));
619
- }
620
-
621
- /**
622
- * The re-exports one node writes, if it is an export declaration. A specifier and a
623
- * namespace export are declarations whose names are where they are written, so no use is
624
- * read from them; each references what it carries forward instead. A bare star declares
625
- * nothing, so the declaration enclosing it, at the top level the file, references the
626
- * module it names.
627
- */
628
- function linksOf(
629
- file: SourceFile,
630
- node: Node,
631
- declarations: ReadonlyMap<string, string>,
632
- enclosing: string,
633
- ): Link[] {
634
- if (!isExportDeclaration(node)) {
635
- return [];
636
- }
637
- const clause = node.exportClause;
638
- const specifier = node.moduleSpecifier;
639
- if (clause === undefined) {
640
- return specifier === undefined
641
- ? []
642
- : [{ node: specifier, at: specifier, from: enclosing, alias: false }];
643
- }
644
- if (isNamespaceExport(clause)) {
645
- const from = declarations.get(nodeKey(file, clause));
646
- return from === undefined
647
- ? []
648
- : [{ node: clause.name, at: specifier ?? clause.name, from, alias: true }];
649
- }
650
- const found: Link[] = [];
651
- for (const element of clause.elements) {
652
- const from = declarations.get(nodeKey(file, element));
653
- if (from !== undefined) {
654
- found.push({
655
- node: element.name,
656
- at: element.propertyName ?? element.name,
657
- from,
658
- alias: true,
659
- });
660
- }
661
- }
662
- return found;
663
- }
664
-
665
- /**
666
- * Whether one object literal is a destructuring target, written on the left of an
667
- * assignment or as a `for` loop's initializer, through the literals and spreads it nests in.
668
- */
669
- function isAssignmentPattern(node: Node): boolean {
670
- let child = node;
671
- let parent = node.parent;
672
- while (
673
- isParenthesizedExpression(parent) ||
674
- isArrayLiteralExpression(parent) ||
675
- isObjectLiteralExpression(parent) ||
676
- isSpreadElement(parent) ||
677
- isSpreadAssignment(parent) ||
678
- (isPropertyAssignment(parent) && parent.initializer === child)
679
- ) {
680
- child = parent;
681
- parent = parent.parent;
682
- }
683
- if (isBinaryExpression(parent)) {
684
- return parent.operatorToken.kind === SyntaxKind.EqualsToken && parent.left === child;
685
- }
686
- return (isForInStatement(parent) || isForOfStatement(parent)) && parent.initializer === child;
687
- }
688
-
689
- /**
690
- * Every name node one file holds that is a use rather than a declaration, in source
691
- * order, each with its enclosing declaration and its use, and every re-export the file
692
- * writes. One walk answers all of it, because a second would have to agree with this one
693
- * about which nodes are names. A declaration's own declared name is left out, which is
694
- * what makes a declaration referenced only from its own site an unreferenced one, and so
695
- * is every node of `unread`, a subtree that is no use.
696
- */
697
- function sitesOf(
698
- file: SourceFile,
699
- declarations: ReadonlyMap<string, string>,
700
- fileId: string,
701
- unread: ReadonlySet<Node>,
702
- ): FileSites {
703
- const found: Site[] = [];
704
- const reads: Read[] = [];
705
- const links: Link[] = [];
706
- const evaluations: Evaluation[] = [];
707
- const wholes: WholeRead[] = [];
708
- const urls: ModuleURL[] = [];
709
- const decorated: Decorated[] = [];
710
- const parameterWrites: ParameterWrite[] = [];
711
- const namespaces = namespaceUses(file);
712
- const literals: LiteralAt[] = [];
713
- const flows: FlowAt[] = [];
714
- const writes = new Set<number>();
715
- const alsoRead = new Set<number>();
716
- const accesses = { written: new Set<Node>(), read: new Set<Node>() };
717
- const keyed: KeyedAccess[] = [];
718
- const declared = new Set<number>();
719
- const shorthands = new Map<number, Node>();
720
- let enclosing = fileId;
721
-
722
- const markNames = (node: Node): void => {
723
- if (isShorthandPropertyAssignment(node)) {
724
- shorthands.set(node.name.pos, node);
725
- return;
726
- }
727
- // A property access carries the member it names where a declaration carries its
728
- // declared name, and it is the one form of the grammar that does.
729
- if (isPropertyAccessExpression(node)) {
730
- return;
731
- }
732
- const named = node as { readonly name?: Node; readonly propertyName?: Node };
733
- for (const name of [named.name, named.propertyName]) {
734
- if (name !== undefined && isName(name)) {
735
- declared.add(name.pos);
736
- }
737
- }
738
- };
739
-
740
- const visit = (node: Node): void => {
741
- if (unread.has(node)) {
742
- return;
743
- }
744
- const namespace = namespaces.get(node);
745
- if (namespace?.kind === "whole") {
746
- wholes.push({ specifier: namespace.specifier, at: node, from: enclosing });
747
- } else if (namespace?.kind === "indexed") {
748
- found.push({ node: namespace.literal, shorthand: undefined, from: enclosing, use: "read" });
749
- }
750
- if (isName(node)) {
751
- if (!declared.has(node.pos)) {
752
- const site = { node, shorthand: shorthands.get(node.pos), from: enclosing };
753
- if (writes.has(node.pos)) {
754
- found.push({ ...site, use: "write" });
755
- }
756
- if (!writes.has(node.pos) || alsoRead.has(node.pos)) {
757
- found.push({ ...site, use: "read" });
758
- }
759
- }
760
- return;
761
- }
762
- markNames(node);
763
- markStores(node, writes, alsoRead, accesses);
764
- const key = isElementAccessExpression(node) ? keyNameOf(node.argumentExpression) : undefined;
765
- if (key !== undefined && isElementAccessExpression(node)) {
766
- const written = accesses.written.has(node);
767
- keyed.push({
768
- access: node,
769
- key,
770
- from: enclosing,
771
- use: written ? (accesses.read.has(node) ? "both" : "write") : "read",
772
- });
773
- }
774
- if (isObjectLiteralExpression(node) && !isAssignmentPattern(node)) {
775
- literals.push({ literal: node, from: enclosing });
776
- }
777
- // An assertion needs only comparability, so no member of its operand is read by it.
778
- for (const written of isAsExpression(node) ? [] : handedOn(node)) {
779
- flows.push(...valuesOf(written).map((value) => ({ value, from: enclosing })));
780
- }
781
- for (const read of propertyReadsOf(node)) {
782
- reads.push({ ...read, from: enclosing });
783
- }
784
- links.push(...linksOf(file, node, declarations, enclosing));
785
- const evaluated = evaluatedBy(node);
786
- if (evaluated !== undefined) {
787
- evaluations.push({ node: evaluated, from: enclosing });
788
- }
789
- const url = moduleURLOf(node);
790
- if (url !== undefined) {
791
- urls.push(url);
792
- }
793
- const held = declarations.get(nodeKey(file, node));
794
- const outer = enclosing;
795
- // A parameter property's annotation, initializer and decorators stay with the
796
- // parameter when the member goes, so they are written inside the constructor's class.
797
- if (held !== undefined && isParameterProperty(node)) {
798
- parameterWrites.push({
799
- name: (node as ParameterDeclaration).name,
800
- from: outer,
801
- member: held,
802
- });
803
- } else if (held !== undefined) {
804
- enclosing = held;
805
- for (const modifier of (node as { readonly modifiers?: readonly Node[] }).modifiers ?? []) {
806
- if (isDecorator(modifier)) {
807
- decorated.push({ decorator: modifier, declaration: held });
808
- }
809
- }
810
- }
811
- node.forEachChild(visit);
812
- enclosing = outer;
813
- };
814
-
815
- file.forEachChild(visit);
816
- return {
817
- uses: found,
818
- literals,
819
- flows,
820
- reads,
821
- links,
822
- evaluations,
823
- wholes,
824
- urls,
825
- decorated,
826
- parameterWrites,
827
- keyed,
828
- };
151
+ /** The directories a thread's path and a glob's root are read against. */
152
+ readonly directories: RunDirectories;
153
+ /** Whether the program is one the run formed to resolve as Node does (program.36). */
154
+ readonly formedForNode?: boolean;
829
155
  }
830
156
 
831
157
  /** The declaration kinds of a property an object literal declares. */
@@ -837,203 +163,13 @@ const LITERAL_PROPERTY_KINDS: ReadonlySet<SyntaxKind> = new Set([
837
163
  SyntaxKind.SetAccessor,
838
164
  ]);
839
165
 
840
- /** The rule a file the analysis classified as test-support code is counted under. */
841
- const TEST_SUPPORT_RULE = "test-support";
842
-
843
- /**
844
- * The name one test-file rule takes. A rule's name is a word list the report's own
845
- * vocabulary spells, which cannot carry a pattern, so a rule is named by the place of
846
- * its pattern in the configured list.
847
- */
848
- function patternRule(index: number): string {
849
- return `test-file-pattern-${String(index + 1)}`;
850
- }
851
-
852
- /**
853
- * The rules over the test files of several projects at once, ordered by rule: a file two
854
- * projects hold is one file, so each rule counts the distinct paths it matches.
855
- */
856
- export function testFileRulesOf(
857
- testFiles: readonly string[],
858
- paths: readonly string[],
859
- supportFiles: readonly string[] = [],
860
- ): readonly TestFileRule[] {
861
- const distinct = [...new Set(paths)];
862
- const support = new Set(supportFiles).size;
863
- return [
864
- ...testFiles.map((pattern, index) => {
865
- const expression = globExpression(pattern);
866
- return {
867
- rule: patternRule(index),
868
- matched: distinct.filter((path) => expression.test(path)).length,
869
- };
870
- }),
871
- ...(support === 0 ? [] : [{ rule: TEST_SUPPORT_RULE, matched: support }]),
872
- ].sort((a, b) => compare(a.rule, b.rule));
873
- }
874
-
875
- const importedCache = new WeakMap<
876
- SourceFile,
877
- ReadonlyMap<string, readonly (string | undefined)[]>
878
- >();
879
-
880
- /**
881
- * Each local name one file's imports bind, mapped to the names it imports: the
882
- * exported name of a named import, `default` for a default import, and `undefined` for
883
- * a namespace import or an import assignment, which binds a whole module.
884
- */
885
- function importedNames(file: SourceFile): ReadonlyMap<string, readonly (string | undefined)[]> {
886
- const known = importedCache.get(file);
887
- if (known !== undefined) {
888
- return known;
889
- }
890
- const names = new Map<string, (string | undefined)[]>();
891
- const bind = (local: string, imported: string | undefined): void => {
892
- names.set(local, [...(names.get(local) ?? []), imported]);
893
- };
894
- for (const statement of file.statements) {
895
- if (isImportEqualsDeclaration(statement)) {
896
- bind(statement.name.text, undefined);
897
- continue;
898
- }
899
- const clause = isImportDeclaration(statement) ? statement.importClause : undefined;
900
- if (clause === undefined) {
901
- continue;
902
- }
903
- if (clause.name !== undefined) {
904
- bind(clause.name.text, "default");
905
- }
906
- const bindings = clause.namedBindings;
907
- if (bindings === undefined) {
908
- continue;
909
- }
910
- if (bindings.kind === SyntaxKind.NamespaceImport) {
911
- bind(bindings.name.text, undefined);
912
- } else {
913
- for (const element of bindings.elements) {
914
- bind(element.name.text, element.propertyName?.text ?? element.name.text);
915
- }
916
- }
917
- }
918
- importedCache.set(file, names);
919
- return names;
920
- }
921
-
922
- /** The name node of every import binding one file's top level declares. */
923
- function importBindings(file: SourceFile): readonly { node: Node }[] {
924
- const found: { node: Node }[] = [];
925
- for (const statement of file.statements) {
926
- if (isImportEqualsDeclaration(statement)) {
927
- found.push({ node: statement.name });
928
- continue;
929
- }
930
- const clause = isImportDeclaration(statement) ? statement.importClause : undefined;
931
- if (clause === undefined) {
932
- continue;
933
- }
934
- if (clause.name !== undefined) {
935
- found.push({ node: clause.name });
936
- }
937
- const bindings = clause.namedBindings;
938
- if (bindings === undefined) {
939
- continue;
940
- }
941
- if (bindings.kind === SyntaxKind.NamespaceImport) {
942
- found.push({ node: bindings.name });
943
- } else {
944
- found.push(...bindings.elements.map((element) => ({ node: element.name })));
945
- }
946
- }
947
- return found;
948
- }
949
-
950
- /** The value one assignment stores through the name it writes, where it is a plain assignment. */
951
- function storedValue(name: Node): Node | undefined {
952
- let target: Node = name;
953
- if (isPropertyAccessExpression(name.parent) && name.parent.name === name) {
954
- target = name.parent;
955
- }
956
- while (isParenthesizedExpression(target.parent)) {
957
- target = target.parent;
958
- }
959
- const parent = target.parent;
960
- return isBinaryExpression(parent) &&
961
- parent.operatorToken.kind === SyntaxKind.EqualsToken &&
962
- parent.left === target
963
- ? parent.right
964
- : undefined;
965
- }
966
-
967
- /**
968
- * The import bindings of one file that only the values written into one declaration
969
- * use, for each write into it. A binding is named by an identifier that spells its
970
- * local name and is not the member a property access names.
971
- */
972
- function aloneIn(
973
- file: SourceFile,
974
- uses: readonly Site[],
975
- writes: readonly { index: number; value: Node }[],
976
- found: readonly Reference[],
977
- ): readonly { index: number; names: readonly string[] }[] {
978
- const bindings = new Set(importBindings(file).map((binding) => binding.node.getText()));
979
- if (bindings.size === 0) {
980
- return [];
981
- }
982
- const occurrences = new Map<string, Node[]>();
983
- for (const site of uses) {
984
- const text = site.node.getText();
985
- const parent = site.node.parent;
986
- if (!bindings.has(text) || (isPropertyAccessExpression(parent) && parent.name === site.node)) {
987
- continue;
988
- }
989
- if (!occurrences.get(text)?.includes(site.node)) {
990
- occurrences.set(text, [...(occurrences.get(text) ?? []), site.node]);
991
- }
992
- }
993
- const inside = (node: Node, value: Node): boolean =>
994
- node.pos >= value.pos && node.end <= value.end;
995
- const result: { index: number; names: readonly string[] }[] = [];
996
- for (const write of writes) {
997
- const to = found[write.index]?.to;
998
- const sameTarget = writes.filter((one) => found[one.index]?.to === to);
999
- const names = [...occurrences]
1000
- .filter(
1001
- ([, nodes]) =>
1002
- nodes.some((node) => inside(node, write.value)) &&
1003
- nodes.every((node) => sameTarget.some((one) => inside(node, one.value))),
1004
- )
1005
- .map(([name]) => name)
1006
- .sort(compare);
1007
- if (names.length > 0) {
1008
- result.push({ index: write.index, names });
1009
- }
1010
- }
1011
- return result;
1012
- }
1013
-
1014
- /** Two strings ordered bytewise, which is the order every set of a run is read in. */
1015
- function compare(a: string, b: string): number {
1016
- if (a === b) {
1017
- return 0;
1018
- }
1019
- return a < b ? -1 : 1;
1020
- }
1021
-
1022
166
  /**
1023
- * Every reference one project's own files make to the project's own declarations, in
1024
- * position order, and the files the test-file rules classified.
1025
- *
1026
- * `held` is the inventory of the same project, whose declaration map is what a resolved
1027
- * symbol is looked up in; `targetRoot` is the absolute path every position is rendered
1028
- * against, which is the root the inventory was read against. A pass over a consumer
1029
- * walks the consumer's files instead and renders against its root, as
1030
- * {@link ReferenceOptions.consumer} states.
1031
- *
1032
- * The order is by the referencing position, then by the declaration named, the
1033
- * declaration referencing it and the use. That is a total order because one name
1034
- * references one declaration once from one declaration, while a chain of aliases reaches
1035
- * several declarations at one position and a bare star re-export's specifier both reads
1036
- * and evaluates the module it names.
167
+ * Every reference one side's files make to the target's declarations. `held` is the
168
+ * target's inventory over the files the project holds, where a resolved symbol is looked up;
169
+ * positions render against `targetRoot`, or a consumer's root as
170
+ * {@link ReferenceOptions.consumer} states. The order is by position, then the declaration
171
+ * named, the referencing declaration and the use: an alias chain or a bare star re-export
172
+ * names several at one position, and one name references one declaration once.
1037
173
  */
1038
174
  export function references<Brand>(
1039
175
  project: ProjectView<Brand>,
@@ -1043,19 +179,9 @@ export function references<Brand>(
1043
179
  ): References {
1044
180
  const cap = options.batchCap ?? DEFAULT_BATCH_CAP;
1045
181
  const declarations = held.declarations;
1046
- const patterns = options.testFiles.map(globExpression);
1047
- const found: Reference[] = [];
1048
- const tests: string[] = [];
1049
182
  const chains = aliasChains(project, held);
1050
183
  const tables = propertyTables(project.queries);
1051
- const destructured = destructuring(project, cap, tables);
1052
- let batched = 0;
1053
- let fileBatches = 0;
1054
- let residueFallbacks = 0;
1055
- let shorthandLookups = 0;
1056
-
1057
184
  const consumer = options.consumer;
1058
- const root = consumer?.root ?? targetRoot;
1059
185
  const files =
1060
186
  consumer === undefined
1061
187
  ? project.ownSourceFiles()
@@ -1070,6 +196,10 @@ export function references<Brand>(
1070
196
  const fileIds = new Set(
1071
197
  held.symbols.filter((symbol) => symbol.kind === "file").map((symbol) => symbol.id),
1072
198
  );
199
+ const declaredAt = (node: Node): string | undefined =>
200
+ declarations.get(nodeKey(node.getSourceFile(), node));
201
+ const sink = new Sink(consumer?.root ?? targetRoot, options.testFile, consumer, declaredAt);
202
+
1073
203
  let moduleBodies: ReadonlyMap<string, readonly InventorySymbol[]> | undefined;
1074
204
  /**
1075
205
  * The declarations one module holds directly: a file's top level, or the body of a
@@ -1079,21 +209,20 @@ export function references<Brand>(
1079
209
  if (moduleBodies === undefined) {
1080
210
  const byModule = new Map<string, InventorySymbol[]>();
1081
211
  for (const symbol of held.symbols) {
1082
- if (!fileIds.has(symbol.parent) && !held.modules.has(symbol.parent)) {
1083
- continue;
1084
- }
1085
- const siblings = byModule.get(symbol.parent);
1086
- if (siblings === undefined) {
1087
- byModule.set(symbol.parent, [symbol]);
1088
- } else {
1089
- siblings.push(symbol);
212
+ if (fileIds.has(symbol.parent) || held.modules.has(symbol.parent)) {
213
+ byModule.set(symbol.parent, [...(byModule.get(symbol.parent) ?? []), symbol]);
1090
214
  }
1091
215
  }
1092
216
  moduleBodies = byModule;
1093
217
  }
1094
218
  return moduleBodies.get(moduleId) ?? [];
1095
219
  };
1096
-
220
+ const exportedBy = (ids: Iterable<string>): readonly ChainTarget[] =>
221
+ [...ids].flatMap((id) =>
222
+ declaredIn(id)
223
+ .filter((symbol) => symbol.exported)
224
+ .map((symbol) => ({ id: symbol.id, stepped: false, guessed: true })),
225
+ );
1097
226
  const exportsCache = new Map<number, readonly ChainTarget[]>();
1098
227
  /**
1099
228
  * Every declaration one module exports, each re-export walked along its chain. A module
@@ -1101,11 +230,7 @@ export function references<Brand>(
1101
230
  */
1102
231
  const exportsRead = (module: Answer<TSSymbol>): readonly ChainTarget[] => {
1103
232
  if (module === UNANSWERED) {
1104
- return [...fileIds].flatMap((id) =>
1105
- declaredIn(id)
1106
- .filter((symbol) => symbol.exported)
1107
- .map((symbol) => ({ id: symbol.id, stepped: false, guessed: true })),
1108
- );
233
+ return exportedBy(fileIds);
1109
234
  }
1110
235
  const known = exportsCache.get(module.id);
1111
236
  if (known !== undefined) {
@@ -1114,56 +239,37 @@ export function references<Brand>(
1114
239
  const table = project.queries.exportsOfModule(module);
1115
240
  const read =
1116
241
  table === UNANSWERED
1117
- ? chains.declarationsOf(module).flatMap((id) =>
1118
- declaredIn(id)
1119
- .filter((symbol) => symbol.exported)
1120
- .map((symbol) => ({ id: symbol.id, stepped: false, guessed: true })),
1121
- )
242
+ ? exportedBy(chains.declarationsOf(module))
1122
243
  : table.flatMap((exported) => chains.chainOf(exported));
1123
244
  exportsCache.set(module.id, read);
1124
245
  return read;
1125
246
  };
1126
247
 
1127
- /** The identifier of the declaration one node of this program is, where it is one. */
1128
- const declaredAt = (node: Node): string | undefined =>
1129
- declarations.get(nodeKey(node.getSourceFile(), node));
1130
-
1131
- /**
1132
- * The declarations one re-export carries forward: for an alias, the first link along
1133
- * its chain that the inventory declares, stepping over the links it does not, such as
1134
- * an import the re-export is written against; for a bare star, the module it names.
1135
- * Each later link is a declaration of its own that references the next, so one link
1136
- * per re-export is what lets a chain be followed one declaration at a time.
1137
- */
1138
- const carriedBy = (link: Link, symbol: TSSymbol): readonly ChainTarget[] =>
1139
- link.alias
1140
- ? chains.nextDeclared(symbol)
1141
- : chains.declarationsOf(symbol).map((id) => ({ id, stepped: false, guessed: false }));
1142
-
1143
248
  /**
1144
- * Every declaration bearing the name one node spells, or the name an import of its
1145
- * file binds that name to, which is what a reference the checker could not resolve is
1146
- * counted as: it may be a use of any of them, and a declaration it may use is kept
1147
- * live. A name a namespace import binds may be a use of any file.
249
+ * Every declaration bearing the name one node spells, or the name an import of its file
250
+ * binds that name to, which is what a reference the checker could not resolve is counted
251
+ * as (program.33): it may be a use of any of them. A name a namespace import binds may be
252
+ * a use of any file.
1148
253
  */
1149
- const named = (node: Node): readonly string[] => {
1150
- const byName = declarationsByName(held);
254
+ const named = (node: Node): readonly ChainTarget[] => {
1151
255
  const text = node.getText();
1152
- const imported = importedNames(node.getSourceFile()).get(text) ?? [];
256
+ const imported = bindingsOf(node.getSourceFile()).filter(
257
+ (binding) => binding.name.text === text,
258
+ );
1153
259
  return [
1154
260
  ...new Set([
1155
- ...(byName.get(text) ?? []),
1156
- ...imported.flatMap((name) =>
1157
- name === undefined ? [...fileIds] : (byName.get(name) ?? []),
261
+ ...(held.byName.get(text) ?? []),
262
+ ...imported.flatMap((binding) =>
263
+ binding.imported === undefined ? [...fileIds] : (held.byName.get(binding.imported) ?? []),
1158
264
  ),
1159
265
  ]),
1160
- ];
266
+ ].map((id) => ({ id, stepped: false, guessed: true }));
1161
267
  };
1162
268
 
1163
269
  // A type-query alias's own type names its export without using it; a use of the
1164
270
  // global is the use, so it names every link of the export's chain as well.
1165
271
  const aliases = project.ownSourceFiles().flatMap(typeQueryAliases);
1166
- const unread = new Set(aliases.map((alias) => alias.type));
272
+ const unread = new Set<Node>(aliases.map((alias) => alias.type));
1167
273
  const aliased = new Map<string, readonly ChainTarget[]>();
1168
274
  if (aliases.length > 0) {
1169
275
  const exported = project.symbolsAt(aliases.map((alias) => project.handle(alias.exported)));
@@ -1176,7 +282,7 @@ export function references<Brand>(
1176
282
  aliased.set(
1177
283
  id,
1178
284
  symbol === UNANSWERED
1179
- ? (declarationsByName(held).get(alias.name) ?? []).map((one) => ({
285
+ ? (held.byName.get(alias.name) ?? []).map((one) => ({
1180
286
  id: one,
1181
287
  stepped: true,
1182
288
  guessed: true,
@@ -1203,39 +309,34 @@ export function references<Brand>(
1203
309
  return all;
1204
310
  };
1205
311
 
1206
- /** The symbol one site resolves to, asking only the accessor the site's form names. */
1207
- const symbolFor = (
1208
- site: Site,
1209
- answered: ReadonlyMap<Node, Answer<TSSymbol>>,
1210
- ): Answer<TSSymbol | undefined> => {
1211
- if (site.shorthand !== undefined) {
1212
- shorthandLookups += 1;
1213
- return project.shorthandValueAt(project.handle(site.shorthand));
1214
- }
1215
- const batched = answered.get(site.node);
1216
- if (batched === UNANSWERED || typeof batched === "object") {
1217
- return batched;
1218
- }
1219
- residueFallbacks += 1;
1220
- return project.resolvedSymbolAt(project.handle(site.node));
1221
- };
1222
-
1223
- const walked = files.map((file) => {
312
+ const rules = READ_RULES.map((rule) => rule.start());
313
+ const sites = files.map((file) => {
1224
314
  const fileId = declaredAt(file) ?? "";
1225
- return { file, fileId, ...sitesOf(file, declarations, fileId, unread) };
315
+ return {
316
+ file,
317
+ fileId,
318
+ ...sitesOf(file, declarations, fileId, unread, (node, from) => {
319
+ for (const rule of rules) {
320
+ rule.visit(node, { file, from });
321
+ }
322
+ }),
323
+ };
1226
324
  });
1227
- const fileIdByName = new Map<string, string>(
1228
- walked.filter((one) => fileIds.has(one.fileId)).map((one) => [one.file.fileName, one.fileId]),
1229
- );
325
+ const loadable = sites
326
+ .filter((one) => fileIds.has(one.fileId))
327
+ .map(({ file, fileId }) => ({ file, fileId }));
328
+ const directories = options.directories;
329
+ const destructured = destructuring(project, cap, tables);
1230
330
  const contextual = contextualUses(
1231
331
  project,
1232
332
  held,
1233
333
  chains,
1234
- walked.flatMap((one) => one.literals),
1235
- walked.flatMap((one) => one.flows),
334
+ sites.flatMap((one) => one.literals),
335
+ sites.flatMap((one) => one.flows),
1236
336
  cap,
1237
337
  tables,
1238
338
  );
339
+
1239
340
  const writtenBy = new Map<string, readonly string[]>();
1240
341
  for (const write of contextual.writes) {
1241
342
  const property = write.at.parent;
@@ -1252,453 +353,89 @@ export function references<Brand>(
1252
353
  return known;
1253
354
  }
1254
355
  const found: ChainTarget[] = [];
1255
- if (writtenBy.size > 0) {
1256
- for (const handle of symbol.declarations) {
1257
- if (!LITERAL_PROPERTY_KINDS.has(handle.kind) || !ownPaths.has(handle.path)) {
1258
- continue;
1259
- }
1260
- const node = project.declarationAt(handle)?.node;
1261
- const ids =
1262
- node === undefined ? undefined : writtenBy.get(nodeKey(node.getSourceFile(), node));
1263
- for (const id of ids ?? []) {
1264
- if (!found.some((target) => target.id === id)) {
1265
- found.push({ id, stepped: true, guessed: false });
1266
- }
356
+ for (const handle of writtenBy.size === 0 ? [] : symbol.declarations) {
357
+ if (!LITERAL_PROPERTY_KINDS.has(handle.kind) || !ownPaths.has(handle.path)) {
358
+ continue;
359
+ }
360
+ const node = project.declarationAt(handle)?.node;
361
+ const ids =
362
+ node === undefined ? undefined : writtenBy.get(nodeKey(node.getSourceFile(), node));
363
+ for (const id of ids ?? []) {
364
+ if (!found.some((target) => target.id === id)) {
365
+ found.push({ id, stepped: true, guessed: false });
1267
366
  }
1268
367
  }
1269
368
  }
1270
369
  forwardedCache.set(symbol.id, found);
1271
370
  return found;
1272
371
  };
1273
- /** One resolved symbol's declarations along its chain, and the members its literal properties write. */
1274
- const targetsOf = (symbol: TSSymbol): readonly ChainTarget[] => {
1275
- const through = forwarded(symbol);
1276
- return through.length === 0 ? chains.chainOf(symbol) : [...chains.chainOf(symbol), ...through];
372
+
373
+ let ownFiles: SourceFiles | undefined;
374
+ const pass: Pass<Brand> = {
375
+ project,
376
+ held,
377
+ chains,
378
+ tables,
379
+ cap,
380
+ sink,
381
+ consumer: consumer !== undefined,
382
+ directories,
383
+ fileIds,
384
+ fileIdByName: new Map(loadable.map((one) => [one.file.fileName, one.fileId])),
385
+ loadableById: new Map(loadable.map((one) => [one.fileId, one.file])),
386
+ destructured,
387
+ unseenLoads: [],
388
+ formedForNode: options.formedForNode === true,
389
+ possiblyLoaded: new Set(),
390
+ declaredIn,
391
+ exportsRead,
392
+ named,
393
+ targetsOf: (symbol) => {
394
+ const through = forwarded(symbol);
395
+ return through.length === 0
396
+ ? chains.chainOf(symbol)
397
+ : [...chains.chainOf(symbol), ...through];
398
+ },
399
+ throughAliases,
400
+ ownFiles: () => (ownFiles ??= sourceFilesOf(project, targetRoot)),
1277
401
  };
1278
- const testFile = new Map<SourceFile, boolean>();
1279
- for (const { file } of walked) {
1280
- const path = renderPosition(file, root, 0).path;
1281
- const test =
1282
- patterns.some((pattern) => pattern.test(path)) ||
1283
- options.testFileNames?.has(file.fileName) === true;
1284
- testFile.set(file, test);
1285
- if (test) {
1286
- tests.push(path);
1287
- }
402
+
403
+ for (const site of contextual.writes) {
404
+ const file = site.at.getSourceFile();
405
+ sink.add(file, site.at, site.from, site.ids, "write", "contextual", site.at.parent);
1288
406
  }
1289
- /** Each write reference's index in `found`, by its file, with the value it stores. */
1290
- const valued = new Map<SourceFile, { index: number; value: Node }[]>();
1291
- const holdsValue = (file: SourceFile, value: Node | undefined): void => {
1292
- if (value !== undefined) {
1293
- valued.set(file, [...(valued.get(file) ?? []), { index: found.length - 1, value }]);
1294
- }
1295
- };
1296
- for (const [use, sites] of [
1297
- ["write", contextual.writes],
1298
- ["read", contextual.reads],
1299
- ] as const) {
1300
- for (const site of sites) {
1301
- const file = site.at.getSourceFile();
1302
- const position = renderPosition(file, root, site.at.getStart());
1303
- for (const id of site.ids) {
1304
- found.push({
1305
- from: site.from,
1306
- to: id,
1307
- position,
1308
- use,
1309
- resolution: "contextual",
1310
- test: testFile.get(file) === true,
1311
- });
1312
- if (use === "write") {
1313
- holdsValue(file, site.at.parent);
1314
- }
1315
- }
1316
- }
407
+ for (const site of contextual.reads) {
408
+ sink.add(site.at.getSourceFile(), site.at, site.from, site.ids, "read", "contextual");
1317
409
  }
1318
-
1319
- const keyNames = computedKeyNames(held.symbols);
1320
- for (const {
1321
- file,
1322
- fileId,
1323
- uses,
1324
- reads,
1325
- links,
1326
- evaluations,
1327
- wholes,
1328
- urls,
1329
- decorated,
1330
- parameterWrites,
1331
- keyed,
1332
- } of walked) {
1333
- const test = testFile.get(file) === true;
1334
- // A component file's markup may use any binding its blocks declare at the top level.
1335
- const markup = isComponentFile(file);
1336
- const bindings = markup ? importBindings(file) : [];
1337
-
1338
- // A decorator is written inside the declaration it is attached to and receives it,
1339
- // so the declaration is used where the decorator is written, whatever the
1340
- // decorator does with it.
1341
- for (const { decorator, declaration } of decorated) {
1342
- found.push({
1343
- from: declaration,
1344
- to: declaration,
1345
- position: renderPosition(file, root, decorator.getStart()),
1346
- use: "decorator",
1347
- resolution: "syntax",
1348
- test,
1349
- });
1350
- }
1351
-
1352
- // The constructor stores its argument in the member a parameter property declares.
1353
- for (const { name, from, member } of parameterWrites) {
1354
- found.push({
1355
- from,
1356
- to: member,
1357
- position: renderPosition(file, root, name.getStart()),
1358
- use: "write",
1359
- resolution: "syntax",
1360
- test,
1361
- });
1362
- }
1363
-
1364
- // One batch per capped run of the file's name nodes, the re-exports' nodes after
1365
- // the uses and the evaluated modules' specifiers last, each node once: a bare star
1366
- // re-export's specifier is a link and an evaluation both. A shorthand is left out
1367
- // of the batch: its name resolves to the property the literal declares, so the
1368
- // batch's answer for it would name something this project's inventory never holds.
1369
- const batching = [
1370
- ...new Set([
1371
- ...uses.filter((site) => site.shorthand === undefined).map((site) => site.node),
1372
- ...links.map((link) => link.node),
1373
- ...evaluations.map((evaluation) => evaluation.node),
1374
- ...wholes.map((whole) => whole.specifier),
1375
- ...urls.flatMap((url) => url.constructor ?? []),
1376
- ...bindings.map((binding) => binding.node),
1377
- ]),
1378
- ];
1379
- const answered = new Map<Node, Answer<TSSymbol>>();
1380
- batched += batching.length;
1381
- for (let from = 0; from < batching.length; from += cap) {
1382
- const run = batching.slice(from, from + cap);
1383
- const answers = project.symbolsAt(
1384
- run.map((node) => project.handle(node)),
1385
- cap,
1386
- );
1387
- fileBatches += 1;
1388
- run.forEach((node, index) => {
1389
- const symbol = answers[index];
1390
- if (symbol !== undefined) {
1391
- answered.set(node, symbol);
1392
- }
1393
- });
1394
- }
1395
-
1396
- for (const link of links) {
1397
- const symbol = answered.get(link.node);
1398
- if (symbol === undefined) {
1399
- continue;
1400
- }
1401
- const position = renderPosition(file, root, link.at.getStart());
1402
- const carried =
1403
- symbol === UNANSWERED
1404
- ? named(link.at).map((id) => ({ id, stepped: false, guessed: true }))
1405
- : carriedBy(link, symbol);
1406
- for (const target of carried) {
1407
- found.push({
1408
- from: link.from,
1409
- to: target.id,
1410
- position,
1411
- use: "read",
1412
- resolution: target.guessed ? "by-name" : link.alias ? "alias" : "batch",
1413
- test,
1414
- });
1415
- }
1416
- }
1417
-
1418
- for (const evaluation of evaluations) {
1419
- const queried = queriedFile(file, evaluation.node, fileIdByName);
1420
- if (queried !== undefined) {
1421
- found.push({
1422
- from: evaluation.from,
1423
- to: queried,
1424
- position: renderPosition(file, root, evaluation.node.getStart()),
1425
- use: "evaluation",
1426
- resolution: "syntax",
1427
- test,
1428
- });
1429
- }
1430
- const module = answered.get(evaluation.node);
1431
- if (module === undefined) {
1432
- continue;
1433
- }
1434
- const position = renderPosition(file, root, evaluation.node.getStart());
1435
- // A module the checker could not resolve may be any file of the target.
1436
- const evaluated = module === UNANSWERED ? [...fileIds] : chains.declarationsOf(module);
1437
- for (const id of evaluated) {
1438
- if (fileIds.has(id)) {
1439
- found.push({
1440
- from: evaluation.from,
1441
- to: id,
1442
- position,
1443
- use: "evaluation",
1444
- resolution: module === UNANSWERED ? "by-name" : "batch",
1445
- test,
1446
- });
1447
- }
1448
- }
1449
- }
1450
-
1451
- for (const url of urls) {
1452
- const constructor = url.constructor === undefined ? undefined : answered.get(url.constructor);
1453
- // An augmentation of the global `URL` merges into it, which keeps the
1454
- // library's declaration; a `URL` the target declares has no other.
1455
- if (
1456
- typeof constructor === "object" &&
1457
- constructor.declarations.length > 0 &&
1458
- constructor.declarations.every((handle) => ownPaths.has(handle.path))
1459
- ) {
1460
- continue;
1461
- }
1462
- const loaded = specifierCandidates(dirnamePath(file.fileName), url.specifier)
1463
- .map((path) => fileIdByName.get(path))
1464
- .find((id) => id !== undefined);
1465
- if (loaded !== undefined) {
1466
- found.push({
1467
- from: fileId,
1468
- to: loaded,
1469
- position: renderPosition(file, root, url.at.getStart()),
1470
- use: "evaluation",
1471
- resolution: "syntax",
1472
- test,
1473
- });
1474
- }
1475
- }
1476
-
1477
- for (const whole of wholes) {
1478
- const module = answered.get(whole.specifier);
1479
- if (module === undefined) {
1480
- continue;
1481
- }
1482
- const position = renderPosition(file, root, whole.at.getStart());
1483
- for (const target of exportsRead(module)) {
1484
- found.push({
1485
- from: whole.from,
1486
- to: target.id,
1487
- position,
1488
- use: "read",
1489
- resolution: target.guessed ? "by-name" : "batch",
1490
- test,
1491
- });
1492
- }
1493
- }
1494
-
1495
- if (markup) {
1496
- for (const symbol of declaredIn(fileId)) {
1497
- found.push({
1498
- from: fileId,
1499
- to: symbol.id,
1500
- position: symbol.position,
1501
- use: "read",
1502
- resolution: "syntax",
1503
- test,
1504
- });
1505
- }
1506
- for (const binding of bindings) {
1507
- const symbol = answered.get(binding.node);
1508
- if (symbol === undefined) {
1509
- continue;
1510
- }
1511
- const position = renderPosition(file, root, binding.node.getStart());
1512
- const chained =
1513
- symbol === UNANSWERED
1514
- ? named(binding.node).map((id) => ({ id, stepped: false, guessed: true }))
1515
- : chains.chainOf(symbol);
1516
- // The markup may name any export of a module a binding's chain reaches, whether a
1517
- // namespace import binds it or a named or default import of a namespace re-export.
1518
- const targets = chained.flatMap((target) => [
1519
- target,
1520
- ...declaredIn(target.id)
1521
- .filter((one) => one.exported)
1522
- .map((one) => ({ ...target, id: one.id })),
1523
- ]);
1524
- for (const target of targets) {
1525
- found.push({
1526
- from: fileId,
1527
- to: target.id,
1528
- position,
1529
- use: "read",
1530
- resolution: target.guessed ? "by-name" : target.stepped ? "alias" : "batch",
1531
- test,
1532
- });
1533
- }
1534
- }
1535
- }
1536
-
1537
- // A pattern names a property of the value it destructures, which no name node of
1538
- // the pattern resolves to: a shorthand's name is the local it binds.
1539
- destructured.resolve(reads).forEach((symbol, index) => {
1540
- const read = reads[index];
1541
- if (symbol === undefined || read === undefined) {
1542
- return;
1543
- }
1544
- const position = renderPosition(file, root, read.key.getStart());
1545
- const targets =
1546
- symbol === UNANSWERED
1547
- ? (declarationsByName(held).get(read.name) ?? []).map((id) => ({
1548
- id,
1549
- stepped: false,
1550
- guessed: true,
1551
- }))
1552
- : targetsOf(symbol);
1553
- for (const target of targets) {
1554
- found.push({
1555
- from: read.from,
1556
- to: target.id,
1557
- position,
1558
- use: "read",
1559
- resolution: target.guessed ? "by-name" : "destructured",
1560
- test,
1561
- });
1562
- }
1563
- });
1564
-
1565
- const resolvedKeys = keyed.flatMap((access) => {
1566
- const symbol =
1567
- isIdentifier(access.key) && keyNames.has(access.key.text)
1568
- ? answered.get(access.key)
1569
- : undefined;
1570
- return typeof symbol === "object" ? [{ access, symbol }] : [];
1571
- });
1572
- for (const { access, ids } of keyedUses(project, chains, resolvedKeys, tables, cap)) {
1573
- const position = renderPosition(file, root, access.access.argumentExpression.getStart());
1574
- for (const id of ids) {
1575
- for (const use of access.use === "both" ? (["read", "write"] as const) : [access.use]) {
1576
- found.push({ from: access.from, to: id, position, use, resolution: "keyed", test });
1577
- }
1578
- }
1579
- }
1580
-
1581
- for (const site of uses) {
1582
- const direct: Resolution =
1583
- site.shorthand !== undefined
1584
- ? "shorthand"
1585
- : typeof answered.get(site.node) === "object"
1586
- ? "batch"
1587
- : "resolved-symbol";
1588
- const symbol = symbolFor(site, answered);
1589
- if (symbol === undefined) {
1590
- continue;
1591
- }
1592
- const position = renderPosition(file, root, site.node.getStart());
1593
- // A re-export something imports is used and so is the declaration behind it, so
1594
- // the use names every link of the chain its name stands at the head of.
1595
- const targets = throughAliases(
1596
- symbol === UNANSWERED
1597
- ? named(site.node).map((id) => ({ id, stepped: false, guessed: true }))
1598
- : targetsOf(symbol),
1599
- );
1600
- for (const target of targets) {
1601
- found.push({
1602
- from: site.from,
1603
- to: target.id,
1604
- position,
1605
- use: site.use,
1606
- resolution: target.guessed ? "by-name" : target.stepped ? "alias" : direct,
1607
- test,
1608
- });
1609
- if (site.use === "write") {
1610
- holdsValue(file, storedValue(site.node));
1611
- }
1612
- }
1613
- }
410
+ for (const one of sites) {
411
+ emitFile(prepared(one, pass, loadable), pass);
1614
412
  }
1615
-
1616
- /** The ambient global interfaces of the walked files that merge into a declaration outside the project's own files. */
1617
- const libraryMerged = (scripts: ReadonlySet<string>): ReadonlySet<string> => {
1618
- const names = new Map<string, Node>();
1619
- const visit = (node: Node): void => {
1620
- if (isInterfaceDeclaration(node)) {
1621
- const id = declaredAt(node);
1622
- if (id !== undefined) {
1623
- names.set(id, node.name);
1624
- }
1625
- } else if (isModuleDeclaration(node) || isModuleBlock(node)) {
1626
- node.forEachChild(visit);
1627
- }
1628
- };
1629
- for (const { file } of walked) {
1630
- file.forEachChild(visit);
1631
- }
1632
- const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
1633
- const asked = [...names].filter(([id]) => global.has(id));
1634
- if (asked.length === 0) {
1635
- return new Set();
1636
- }
1637
- const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
1638
- return new Set(
1639
- asked.flatMap(([id], at) => {
1640
- const symbol = answers[at];
1641
- return typeof symbol === "object" &&
1642
- symbol.declarations.some((handle) => !ownPaths.has(handle.path))
1643
- ? [id]
1644
- : [];
1645
- }),
1646
- );
1647
- };
1648
-
1649
- const usesOf = new Map(walked.map((one) => [one.file, one.uses]));
1650
- for (const [file, writes] of valued) {
1651
- const uses = usesOf.get(file) ?? [];
1652
- for (const { index, names } of aloneIn(file, uses, writes, found)) {
1653
- const reference = found[index];
1654
- if (reference !== undefined) {
1655
- found[index] = { ...reference, alone: names };
413
+ const context = { project, held, chains };
414
+ for (const rule of rules) {
415
+ for (const read of rule.reads(context)) {
416
+ if (read.kind === "named") {
417
+ sink.add(
418
+ read.file,
419
+ read.at,
420
+ read.from,
421
+ [read.to],
422
+ read.use,
423
+ read.via,
424
+ undefined,
425
+ read.production,
426
+ );
427
+ } else {
428
+ sink.hold(heldBy(read, context));
1656
429
  }
1657
430
  }
1658
431
  }
1659
432
 
1660
- let typedLookups = 0;
1661
- if (consumer === undefined) {
1662
- const before = options.requestCount?.() ?? 0;
1663
- found.push(...overrideReferences(project, held, chains, new Set(tests)));
1664
- found.push(...indexedAccessReferences(project, held, chains, root, new Set(tests)));
1665
- found.push(...elementAccessReferences(project, held, chains, root, new Set(tests)));
1666
- found.push(...keyofComparisonReferences(project, held, chains, root, new Set(tests)));
1667
- found.push(...iterationReferences(project, held, chains, root, new Set(tests)));
1668
- found.push(...typeParameterReads(project, held, chains, root, new Set(tests)));
1669
- found.push(...unimplementedCalleeReads(project, held, chains, root, new Set(tests)));
1670
- found.push(...moduleDeclarationReferences(project, held, root, new Set(tests)));
1671
- typedLookups = (options.requestCount?.() ?? 0) - before;
1672
- }
1673
- const scripts = new Set(
1674
- walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId),
1675
- );
1676
- found.push(...augmentationReferences(held, found, scripts, libraryMerged(scripts)));
1677
- found.sort(
1678
- (a, b) =>
1679
- byPosition(a.position, b.position) ||
1680
- compare(a.to, b.to) ||
1681
- compare(a.from, b.from) ||
1682
- compare(a.use, b.use),
1683
- );
1684
-
433
+ const merges = mergedGlobalInterfaces(project, held, pass.ownFiles());
434
+ sink.extend(augmentationReferences(held, sink.made, merges.scripts, merges.interfaces));
435
+ const finished = sink.finish(new Map(sites.map((one) => [one.file, one.uses])));
1685
436
  return {
1686
- references:
1687
- consumer === undefined
1688
- ? found
1689
- : found.map((reference) => ({ ...reference, consumer: consumer.id })),
1690
- testFilePaths: tests.sort(compare),
1691
- cost: {
1692
- batched,
1693
- fileBatches,
1694
- residueFallbacks,
1695
- shorthandLookups,
1696
- aliasSteps: chains.steps,
1697
- patternBatches: destructured.cost.patternBatches,
1698
- patternLookups: destructured.cost.patternLookups,
1699
- contextualBatches: contextual.batches,
1700
- contextualLookups: contextual.lookups,
1701
- typedLookups,
1702
- },
437
+ ...finished,
438
+ unseenLoads: pass.unseenLoads,
439
+ possiblyLoaded: [...pass.possiblyLoaded].sort(bytewise),
1703
440
  };
1704
441
  }