@cplieger/deadset-ts 8.0.0-dev.2 → 8.0.0-dev.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/THIRD_PARTY_NOTICES.md +27 -0
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/alias-chain.js +35 -9
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +274 -496
- package/dist/src/augmentations.js +64 -3
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/bindings.js +382 -0
- package/dist/src/commands/held.js +499 -0
- package/dist/src/commands/read.js +161 -0
- package/dist/src/commands/shell.js +169 -0
- package/dist/src/commands/tools.js +1847 -0
- package/dist/src/component-files.js +3 -2
- package/dist/src/component-layer.js +4 -2
- package/dist/src/config.js +6 -82
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/consumers.js +10 -38
- package/dist/src/contextual-uses.js +57 -26
- package/dist/src/contract/config.generated.js +561 -0
- package/dist/src/contract/contract.generated.js +1370 -0
- package/dist/src/contract/report.generated.js +866 -0
- package/dist/src/dependencies.js +165 -122
- package/dist/src/destructuring.js +1 -19
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +181 -168
- package/dist/src/entries/build-scripts.js +222 -0
- package/dist/src/{configuration-files.js → entries/configuration-files.js} +52 -256
- package/dist/src/{convention-rows.js → entries/convention-rows.js} +170 -30
- package/dist/src/entries/conventions.js +740 -0
- package/dist/src/{html-entries.js → entries/html-entries.js} +12 -42
- package/dist/src/{mdx-entries.js → entries/mdx-entries.js} +5 -28
- package/dist/src/entries/run-directories.js +77 -0
- package/dist/src/entries/sources.js +144 -0
- package/dist/src/entries/static-module.js +670 -0
- package/dist/src/entries/test-runners.js +650 -0
- package/dist/src/entries/workflow-steps.js +345 -0
- package/dist/src/exemptions/argument-places.js +131 -0
- package/dist/src/exemptions/calls.js +338 -0
- package/dist/src/exemptions/classes.js +13 -0
- package/dist/src/{decorator.js → exemptions/decorator.js} +19 -30
- package/dist/src/exemptions/detectors.js +48 -0
- package/dist/src/{enum-group.js → exemptions/enum-group.js} +9 -16
- package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +7 -11
- package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +76 -54
- package/dist/src/exemptions/generated-file.js +241 -0
- package/dist/src/{injection-container.js → exemptions/injection-container.js} +20 -26
- package/dist/src/exemptions/installed-globals.js +173 -0
- package/dist/src/{interface-satisfaction.js → exemptions/interface-satisfaction.js} +52 -121
- package/dist/src/exemptions/lifecycle.js +150 -0
- package/dist/src/{reflective-lookup.js → exemptions/reflective-lookup.js} +3 -3
- package/dist/src/exemptions/serialization-contract.js +1276 -0
- package/dist/src/exemptions/sites.js +182 -0
- package/dist/src/{template-field.js → exemptions/template-field.js} +13 -23
- package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
- package/dist/src/exposure.js +400 -0
- package/dist/src/extensions.js +37 -0
- package/dist/src/facts.js +38 -0
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +3 -26
- package/dist/src/findings/completion.js +109 -56
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +27 -41
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +3 -2
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +16 -13
- package/dist/src/findings/non-code-artifacts.js +17 -12
- package/dist/src/findings/reads-and-writes.js +86 -27
- package/dist/src/findings/self-check.js +44 -30
- package/dist/src/findings/unused-declarations.js +29 -45
- package/dist/src/findings/visibility-narrowing.js +23 -31
- package/dist/src/findings-pass.js +13 -21
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +67 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +39 -26
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/index/values.js +124 -0
- package/dist/src/index.js +2 -2
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/intra-function-parts.js +63 -43
- package/dist/src/inventory/commonjs.js +507 -0
- package/dist/src/inventory/enumerate.js +167 -0
- package/dist/src/inventory/syntax.js +236 -0
- package/dist/src/inventory.js +323 -724
- package/dist/src/javascript-file.js +48 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +126 -107
- package/dist/src/kinds.js +3 -366
- package/dist/src/{components.js → liveness/components.js} +2 -3
- package/dist/src/{graph.js → liveness/graph.js} +52 -28
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/load/classify.js +289 -0
- package/dist/src/load/diagnostics.js +87 -0
- package/dist/src/load/discover.js +285 -0
- package/dist/src/{emit-map.js → load/emit-map.js} +1 -1
- package/dist/src/load/expected-module.js +136 -0
- package/dist/src/load/load.js +27 -0
- package/dist/src/load/module-lookup.js +37 -0
- package/dist/src/load/package-scope.js +56 -0
- package/dist/src/load/plan.js +313 -0
- package/dist/src/load/specifiers.js +113 -0
- package/dist/src/load/synthesize.js +152 -0
- package/dist/src/load/test-globals.js +207 -0
- package/dist/src/load/test-joins.js +82 -0
- package/dist/src/{workspace-manifests.js → load/workspace-manifests.js} +4 -4
- package/dist/src/{workspace-programs.js → load/workspace-programs.js} +89 -66
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +24 -66
- package/dist/src/{workspace-run.js → load/workspace-run.js} +7 -7
- package/dist/src/{workspace.js → load/workspace.js} +62 -124
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +82 -16
- package/dist/src/platform-calls.js +78 -0
- package/dist/src/platform-symbols.js +134 -0
- package/dist/src/position.js +43 -1
- package/dist/src/print.js +11 -79
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +95 -0
- package/dist/src/ref.js +12 -20
- package/dist/src/references/read-rule.js +1 -0
- package/dist/src/references/read-rules.js +48 -0
- package/dist/src/references/sink.js +237 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +117 -1074
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +57 -41
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/reporters/sarif.js +16 -10
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +246 -730
- package/dist/src/{configured-declarations.js → roots/configured-declarations.js} +22 -24
- package/dist/src/{entry-point-gaps.js → roots/entry-point-gaps.js} +6 -15
- package/dist/src/roots/manifest.js +216 -0
- package/dist/src/roots/published-reach.js +412 -0
- package/dist/src/roots/root-set.js +487 -0
- package/dist/src/roots/unseen-loads.js +236 -0
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -343
- package/dist/src/scope.js +38 -115
- package/dist/src/session.js +37 -29
- package/dist/src/source-files.js +6 -10
- package/dist/src/stores.js +4 -2
- package/dist/src/suppress-file.js +25 -28
- package/dist/src/suppress-inline.js +36 -83
- package/dist/src/suppress.js +50 -71
- package/dist/src/symbol-keys.js +92 -88
- package/dist/src/tests/file-class.js +76 -0
- package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
- package/dist/src/{test-support.js → tests/support.js} +5 -12
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +298 -0
- package/dist/src/text/yaml.js +684 -0
- package/dist/src/type-errors.js +11 -232
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/vendor/unbash/ansi-c.js +132 -0
- package/dist/src/vendor/unbash/arithmetic-parts.js +37 -0
- package/dist/src/vendor/unbash/arithmetic.js +591 -0
- package/dist/src/vendor/unbash/assignment.js +131 -0
- package/dist/src/vendor/unbash/chars.js +48 -0
- package/dist/src/vendor/unbash/command.js +41 -0
- package/dist/src/vendor/unbash/expansion.js +80 -0
- package/dist/src/vendor/unbash/glob.js +268 -0
- package/dist/src/vendor/unbash/heredoc.js +27 -0
- package/dist/src/vendor/unbash/internal-types.js +1 -0
- package/dist/src/vendor/unbash/lexer.js +4274 -0
- package/dist/src/vendor/unbash/limits.js +3 -0
- package/dist/src/vendor/unbash/parser.js +1366 -0
- package/dist/src/vendor/unbash/parts.js +57 -0
- package/dist/src/vendor/unbash/substitution-context.js +8 -0
- package/dist/src/vendor/unbash/traversal.js +212 -0
- package/dist/src/vendor/unbash/types.js +1 -0
- package/dist/src/vendor/unbash/word-value.js +61 -0
- package/dist/src/vendor/unbash/word.js +40 -0
- package/dist/src/verbs/analyze.js +194 -127
- package/dist/src/verbs/describe.js +13 -18
- package/dist/src/verbs/explain.js +85 -139
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +6 -4
- package/dist/src/verbs/print-retained.js +30 -6
- package/dist/src/verbs/print-roots.js +4 -2
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +14 -3
- package/jsr.json +2 -1
- package/package.json +14 -8
- package/src/alias-chain.ts +40 -11
- package/src/amd.ts +130 -0
- package/src/analysis.ts +426 -733
- package/src/augmentations.ts +93 -4
- package/src/automatic-types.ts +12 -30
- package/src/bindings.ts +460 -0
- package/src/commands/held.ts +632 -0
- package/src/commands/read.ts +242 -0
- package/src/commands/shell.ts +228 -0
- package/src/commands/tools.ts +2382 -0
- package/src/component-files.ts +4 -2
- package/src/component-layer.ts +4 -2
- package/src/config.ts +31 -302
- package/src/conformance-record.ts +2 -2
- package/src/consumers.ts +16 -66
- package/src/contextual-uses.ts +66 -34
- package/src/contract/config.generated.ts +821 -0
- package/src/contract/contract.generated.ts +1453 -0
- package/src/contract/report.generated.ts +1241 -0
- package/src/dependencies.ts +223 -128
- package/src/destructuring.ts +1 -31
- package/src/edges.ts +41 -140
- package/src/element-access.ts +190 -187
- package/src/entries/build-scripts.ts +292 -0
- package/src/{configuration-files.ts → entries/configuration-files.ts} +68 -278
- package/src/{convention-rows.ts → entries/convention-rows.ts} +227 -31
- package/src/{conventions.ts → entries/conventions.ts} +367 -583
- package/src/{html-entries.ts → entries/html-entries.ts} +22 -48
- package/src/{mdx-entries.ts → entries/mdx-entries.ts} +7 -30
- package/src/entries/run-directories.ts +108 -0
- package/src/entries/sources.ts +266 -0
- package/src/entries/static-module.ts +858 -0
- package/src/entries/test-runners.ts +835 -0
- package/src/entries/workflow-steps.ts +432 -0
- package/src/exemptions/argument-places.ts +198 -0
- package/src/exemptions/calls.ts +483 -0
- package/src/exemptions/classes.ts +49 -0
- package/src/exemptions/decorator.ts +56 -0
- package/src/exemptions/detectors.ts +75 -0
- package/src/{enum-group.ts → exemptions/enum-group.ts} +10 -19
- package/src/{erased-stores.ts → exemptions/erased-stores.ts} +7 -14
- package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +85 -66
- package/src/exemptions/generated-file.ts +318 -0
- package/src/{injection-container.ts → exemptions/injection-container.ts} +29 -34
- package/src/exemptions/installed-globals.ts +255 -0
- package/src/{interface-satisfaction.ts → exemptions/interface-satisfaction.ts} +69 -160
- package/src/exemptions/lifecycle.ts +286 -0
- package/src/{reflective-lookup.ts → exemptions/reflective-lookup.ts} +5 -5
- package/src/exemptions/serialization-contract.ts +1729 -0
- package/src/exemptions/sites.ts +293 -0
- package/src/{template-field.ts → exemptions/template-field.ts} +22 -32
- package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
- package/src/exposure.ts +492 -0
- package/src/extensions.ts +45 -0
- package/src/facts.ts +46 -0
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +15 -25
- package/src/finding.ts +10 -10
- package/src/findings/boundary.ts +11 -0
- package/src/findings/completion.ts +133 -64
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +9 -10
- package/src/findings/emitters.ts +38 -52
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +4 -3
- package/src/findings/interfaces.ts +7 -9
- package/src/findings/intra-function.ts +16 -14
- package/src/findings/non-code-artifacts.ts +24 -15
- package/src/findings/reads-and-writes.ts +118 -41
- package/src/findings/self-check.ts +51 -38
- package/src/findings/unused-declarations.ts +31 -49
- package/src/findings/visibility-narrowing.ts +36 -40
- package/src/findings-pass.ts +15 -31
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +96 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +50 -26
- package/src/host.ts +12 -5
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/index/values.ts +170 -0
- package/src/index.ts +2 -2
- package/src/indexed-access.ts +570 -527
- package/src/intra-function-parts.ts +73 -44
- package/src/inventory/commonjs.ts +696 -0
- package/src/inventory/enumerate.ts +203 -0
- package/src/inventory/syntax.ts +311 -0
- package/src/inventory.ts +442 -895
- package/src/javascript-file.ts +54 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +133 -122
- package/src/kinds.ts +5 -413
- package/src/{components.ts → liveness/components.ts} +4 -6
- package/src/{graph.ts → liveness/graph.ts} +100 -35
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/load/classify.ts +433 -0
- package/src/load/diagnostics.ts +154 -0
- package/src/load/discover.ts +471 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +194 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/package-scope.ts +74 -0
- package/src/load/plan.ts +513 -0
- package/src/load/specifiers.ts +171 -0
- package/src/load/synthesize.ts +230 -0
- package/src/load/test-globals.ts +251 -0
- package/src/load/test-joins.ts +98 -0
- package/src/{workspace-manifests.ts → load/workspace-manifests.ts} +5 -5
- package/src/{workspace-programs.ts → load/workspace-programs.ts} +128 -85
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +41 -77
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/partial.ts +3 -2
- package/src/paths.ts +92 -16
- package/src/platform-calls.ts +119 -0
- package/src/platform-symbols.ts +189 -0
- package/src/position.ts +44 -1
- package/src/print.ts +12 -92
- package/src/product.ts +18 -0
- package/src/query.ts +201 -10
- package/src/ref.ts +12 -20
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +314 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +187 -1450
- package/src/report-schema.ts +68 -0
- package/src/report.ts +103 -226
- package/src/reporters/annotations.ts +22 -11
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -4
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +29 -16
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +284 -1115
- package/src/{configured-declarations.ts → roots/configured-declarations.ts} +26 -29
- package/src/{entry-point-gaps.ts → roots/entry-point-gaps.ts} +7 -16
- package/src/roots/manifest.ts +303 -0
- package/src/roots/published-reach.ts +486 -0
- package/src/{roots.ts → roots/root-set.ts} +286 -469
- package/src/roots/unseen-loads.ts +345 -0
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -388
- package/src/scope.ts +46 -138
- package/src/session.ts +87 -55
- package/src/source-files.ts +6 -11
- package/src/stores.ts +15 -3
- package/src/suppress-file.ts +24 -33
- package/src/suppress-inline.ts +55 -91
- package/src/suppress.ts +78 -79
- package/src/symbol-keys.ts +104 -107
- package/src/tests/file-class.ts +135 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +15 -21
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.ts +388 -0
- package/src/text/yaml.ts +747 -0
- package/src/type-errors.ts +18 -288
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/vendor/unbash/LICENSE +15 -0
- package/src/vendor/unbash/VENDORED.md +16 -0
- package/src/vendor/unbash/ansi-c.ts +132 -0
- package/src/vendor/unbash/arithmetic-parts.ts +53 -0
- package/src/vendor/unbash/arithmetic.ts +641 -0
- package/src/vendor/unbash/assignment.ts +163 -0
- package/src/vendor/unbash/chars.ts +48 -0
- package/src/vendor/unbash/command.ts +47 -0
- package/src/vendor/unbash/expansion.ts +144 -0
- package/src/vendor/unbash/glob.ts +284 -0
- package/src/vendor/unbash/heredoc.ts +34 -0
- package/src/vendor/unbash/internal-types.ts +94 -0
- package/src/vendor/unbash/lexer.ts +4352 -0
- package/src/vendor/unbash/limits.ts +3 -0
- package/src/vendor/unbash/parser.ts +1612 -0
- package/src/vendor/unbash/parts.ts +96 -0
- package/src/vendor/unbash/substitution-context.ts +20 -0
- package/src/vendor/unbash/traversal.ts +182 -0
- package/src/vendor/unbash/types.ts +660 -0
- package/src/vendor/unbash/word-value.ts +64 -0
- package/src/vendor/unbash/word.ts +50 -0
- package/src/verbs/analyze.ts +255 -164
- package/src/verbs/describe.ts +19 -17
- package/src/verbs/explain.ts +93 -169
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +36 -6
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/dist/src/base-url.js +0 -18
- package/dist/src/calls.js +0 -155
- package/dist/src/conventions.js +0 -937
- package/dist/src/discover.js +0 -468
- package/dist/src/entry-points.js +0 -529
- package/dist/src/exempt-classes.js +0 -75
- package/dist/src/exempt.js +0 -209
- package/dist/src/generated-file.js +0 -95
- package/dist/src/glob.js +0 -65
- package/dist/src/manifest.js +0 -165
- package/dist/src/matrix.js +0 -246
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/roots.js +0 -672
- package/dist/src/serialization-contract.js +0 -611
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/sweep.js +0 -224
- package/dist/src/test-joins.js +0 -101
- package/dist/src/value-flow.js +0 -74
- package/dist/src/version.js +0 -7
- package/dist/src/workflow-steps.js +0 -185
- package/src/base-url.ts +0 -24
- package/src/calls.ts +0 -225
- package/src/decorator.ts +0 -78
- package/src/discover.ts +0 -653
- package/src/entry-points.ts +0 -687
- package/src/exempt-classes.ts +0 -123
- package/src/exempt.ts +0 -323
- package/src/generated-file.ts +0 -111
- package/src/glob.ts +0 -67
- package/src/manifest.ts +0 -234
- package/src/matrix.ts +0 -423
- package/src/pnpm-workspace.ts +0 -135
- package/src/serialization-contract.ts +0 -849
- package/src/setup-failure.ts +0 -39
- package/src/sweep.ts +0 -347
- package/src/test-joins.ts +0 -120
- package/src/value-flow.ts +0 -104
- package/src/version.ts +0 -7
- package/src/workflow-steps.ts +0 -216
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The calls and decorators of one project that the configured classes read: what each
|
|
3
|
+
* call invokes, a call through a method of `Function.prototype` included, and which of the
|
|
4
|
+
* target's classes a call passes or a decorator is attached to. A class is passed by each
|
|
5
|
+
* value an argument can carry ({@link carriedValues}), named directly or through
|
|
6
|
+
* variables, and a decorator written as a call is a call.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
isArrayLiteralExpression,
|
|
11
|
+
isArrowFunction,
|
|
12
|
+
isBindingElement,
|
|
13
|
+
isCallExpression,
|
|
14
|
+
isDecorator,
|
|
15
|
+
isFunctionDeclaration,
|
|
16
|
+
isFunctionExpression,
|
|
17
|
+
isIdentifier,
|
|
18
|
+
isMethodDeclaration,
|
|
19
|
+
isParameterDeclaration,
|
|
20
|
+
isPropertyAccessExpression,
|
|
21
|
+
isPropertyAssignment,
|
|
22
|
+
isPropertyDeclaration,
|
|
23
|
+
isShorthandPropertyAssignment,
|
|
24
|
+
isSpreadElement,
|
|
25
|
+
isVariableDeclaration,
|
|
26
|
+
type CallExpression,
|
|
27
|
+
type Decorator,
|
|
28
|
+
type Expression,
|
|
29
|
+
type Node,
|
|
30
|
+
type ParameterDeclaration,
|
|
31
|
+
} from "@typescript/native/ast";
|
|
32
|
+
import {
|
|
33
|
+
ObjectFlags,
|
|
34
|
+
SymbolFlags,
|
|
35
|
+
type Symbol as TSSymbol,
|
|
36
|
+
type Type,
|
|
37
|
+
} from "@typescript/native/sync";
|
|
38
|
+
import { resolvedTargets, type AliasChains } from "../alias-chain.ts";
|
|
39
|
+
import { carriedValues, nameOf, type CarriedValue } from "../index/values.ts";
|
|
40
|
+
import type { Inventory } from "../inventory.ts";
|
|
41
|
+
import { must } from "../query.ts";
|
|
42
|
+
import type { ProjectView } from "../session.ts";
|
|
43
|
+
import { parenthesesRemoved, unwrapped } from "../unwrap.ts";
|
|
44
|
+
import type { ProjectSites } from "./sites.ts";
|
|
45
|
+
|
|
46
|
+
/** One class of the target a call passes or a decorator is attached to, and where. */
|
|
47
|
+
interface ClassUse {
|
|
48
|
+
/** The inventory's identifier of the class. */
|
|
49
|
+
readonly id: string;
|
|
50
|
+
readonly site: Node;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** The callee one decorator names: its expression, or the callee of the call it writes. */
|
|
54
|
+
export function decoratorCallee(decorator: Decorator): Node | undefined {
|
|
55
|
+
const expression = decorator.expression;
|
|
56
|
+
return nameOf(isCallExpression(expression) ? expression.expression : expression);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** How one decorator is spelled in a record: its expression, without a call's arguments. */
|
|
60
|
+
export function spelledDecorator(decorator: Decorator): string {
|
|
61
|
+
const expression = decorator.expression;
|
|
62
|
+
const named = isCallExpression(expression) ? expression.expression : expression;
|
|
63
|
+
return `@${named.getText().replace(/\s+/gu, "")}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The decorators written on one declaration. */
|
|
67
|
+
export function decoratorsOf(node: Node): readonly Decorator[] {
|
|
68
|
+
return ((node as { readonly modifiers?: readonly Node[] }).modifiers ?? []).filter(isDecorator);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The methods of `Function.prototype` that invoke a function with arguments of their own. */
|
|
72
|
+
const FUNCTION_METHODS: ReadonlySet<string> = new Set(["call", "apply", "bind"]);
|
|
73
|
+
|
|
74
|
+
/** The interfaces of the standard library that declare those methods for a function value. */
|
|
75
|
+
const FUNCTION_INTERFACES: ReadonlySet<string> = new Set([
|
|
76
|
+
"Function",
|
|
77
|
+
"CallableFunction",
|
|
78
|
+
"NewableFunction",
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* One call as the function it invokes sees it: the expression naming that function,
|
|
83
|
+
* and the argument each of its parameters binds, in order.
|
|
84
|
+
*/
|
|
85
|
+
export interface Invocation {
|
|
86
|
+
readonly callee: Expression;
|
|
87
|
+
readonly arguments: readonly Expression[];
|
|
88
|
+
/** Whether the call reaches the callee through a method of `Function.prototype`. */
|
|
89
|
+
readonly indirect: boolean;
|
|
90
|
+
/** Whether the last argument is a list the parameters bind the elements of, as `apply` takes. */
|
|
91
|
+
readonly listed: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A call through a method of `Function.prototype`: the method's name and the invocation. */
|
|
95
|
+
interface MethodForm {
|
|
96
|
+
readonly method: Node;
|
|
97
|
+
readonly invocation: Invocation;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The invocation a call writes through a method of `Function.prototype`, read from
|
|
102
|
+
* the syntax alone: `f.call(self, a)`, `f.apply(self, [a])`, `f.bind(self, a)`, which
|
|
103
|
+
* passes `a` though nothing calls the bound function there, because the bound function
|
|
104
|
+
* carries `a` to wherever it is called and that call is not followed, and
|
|
105
|
+
* `f.bind(self, a)(b)`, which passes `b` after it. An `apply` argument
|
|
106
|
+
* that is no array literal is the value of the first parameter, whose elements it
|
|
107
|
+
* carries. Whether the method is the prototype's is resolution's to say.
|
|
108
|
+
*/
|
|
109
|
+
function throughMethod(call: CallExpression): MethodForm | undefined {
|
|
110
|
+
const expression = parenthesesRemoved(call.expression);
|
|
111
|
+
if (isPropertyAccessExpression(expression) && FUNCTION_METHODS.has(expression.name.text)) {
|
|
112
|
+
const [, ...rest] = call.arguments;
|
|
113
|
+
let passed: readonly Expression[] = rest;
|
|
114
|
+
let listed = false;
|
|
115
|
+
if (expression.name.text === "apply") {
|
|
116
|
+
const list = rest[0] === undefined ? undefined : parenthesesRemoved(rest[0]);
|
|
117
|
+
listed = list !== undefined && !isArrayLiteralExpression(list);
|
|
118
|
+
passed = list === undefined ? [] : isArrayLiteralExpression(list) ? list.elements : [list];
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
method: expression.name,
|
|
122
|
+
invocation: { callee: expression.expression, arguments: passed, indirect: true, listed },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (isCallExpression(expression)) {
|
|
126
|
+
const bound = parenthesesRemoved(expression.expression);
|
|
127
|
+
if (isPropertyAccessExpression(bound) && bound.name.text === "bind") {
|
|
128
|
+
return {
|
|
129
|
+
method: bound.name,
|
|
130
|
+
invocation: {
|
|
131
|
+
callee: bound.expression,
|
|
132
|
+
arguments: [...expression.arguments.slice(1), ...call.arguments],
|
|
133
|
+
indirect: true,
|
|
134
|
+
listed: false,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** What each call of one project invokes. */
|
|
143
|
+
export interface Invocations {
|
|
144
|
+
/** The function one call invokes and the arguments its parameters bind. */
|
|
145
|
+
readonly of: (call: CallExpression) => Invocation;
|
|
146
|
+
/** The declaration the invoked function's name resolves to, every alias followed. */
|
|
147
|
+
readonly callee: (call: CallExpression) => TSSymbol | undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* What each of a project's calls invokes. A call through `call`, `apply` or `bind`
|
|
152
|
+
* invokes the function the method is read from only where the method is the standard
|
|
153
|
+
* library's: the program's own `call` and `Reflect.apply` pass nothing on.
|
|
154
|
+
*/
|
|
155
|
+
export function invocations<Brand>(
|
|
156
|
+
project: ProjectView<Brand>,
|
|
157
|
+
calls: readonly CallExpression[],
|
|
158
|
+
): Invocations {
|
|
159
|
+
const forms = new Map<CallExpression, MethodForm>();
|
|
160
|
+
for (const call of calls) {
|
|
161
|
+
const form = throughMethod(call);
|
|
162
|
+
if (form !== undefined) {
|
|
163
|
+
forms.set(call, form);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const callees = resolvedTargets(project, [
|
|
167
|
+
...calls.flatMap((call) => nameOf(call.expression) ?? []),
|
|
168
|
+
...[...forms.values()].flatMap((form) => {
|
|
169
|
+
const name = nameOf(form.invocation.callee);
|
|
170
|
+
return name === undefined ? [form.method] : [form.method, name];
|
|
171
|
+
}),
|
|
172
|
+
]);
|
|
173
|
+
const standard = (method: TSSymbol): boolean =>
|
|
174
|
+
method.declarations.some(
|
|
175
|
+
(handle) => project.program.getSourceFileMetadata(handle.path)?.isDefaultLibrary === true,
|
|
176
|
+
) && FUNCTION_INTERFACES.has(must(project.queries.parentOf(method))?.name ?? "");
|
|
177
|
+
const of = (call: CallExpression): Invocation => {
|
|
178
|
+
const form = forms.get(call);
|
|
179
|
+
const method = form === undefined ? undefined : callees.get(form.method);
|
|
180
|
+
return form !== undefined && method !== undefined && standard(method)
|
|
181
|
+
? form.invocation
|
|
182
|
+
: { callee: call.expression, arguments: call.arguments, indirect: false, listed: false };
|
|
183
|
+
};
|
|
184
|
+
return {
|
|
185
|
+
of,
|
|
186
|
+
callee: (call) => {
|
|
187
|
+
const name = nameOf(of(call).callee);
|
|
188
|
+
return name === undefined ? undefined : callees.get(name);
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** The declaration a shorthand property's value resolves to, an import followed to what it names. */
|
|
194
|
+
function shorthandTarget<Brand>(project: ProjectView<Brand>, node: Node): TSSymbol | undefined {
|
|
195
|
+
const value = must(project.shorthandValueAt(project.handle(node)));
|
|
196
|
+
if (value === undefined || (value.flags & SymbolFlags.Alias) === 0) {
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
const aliased = must(project.queries.aliased(value));
|
|
200
|
+
return aliased.declarations.length === 0 ? undefined : aliased;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* What one carried value holds: the declaration its name resolves to, and the classes and
|
|
205
|
+
* functions the checker's type at it holds ({@link constructedAt}). `known` holds names
|
|
206
|
+
* already resolved in a batch.
|
|
207
|
+
*/
|
|
208
|
+
function heldBy<Brand>(
|
|
209
|
+
project: ProjectView<Brand>,
|
|
210
|
+
value: CarriedValue,
|
|
211
|
+
known?: ReadonlyMap<Node, TSSymbol>,
|
|
212
|
+
): { readonly named: TSSymbol | undefined; readonly typed: readonly TSSymbol[] } {
|
|
213
|
+
const { name } = value;
|
|
214
|
+
if (name === undefined) {
|
|
215
|
+
return { named: undefined, typed: constructedAt(project, value.site) };
|
|
216
|
+
}
|
|
217
|
+
if (value.shorthand) {
|
|
218
|
+
return { named: shorthandTarget(project, name), typed: [] };
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
named: known?.get(name) ?? resolvedTargets(project, [name]).get(name),
|
|
222
|
+
typed: constructedAt(project, value.site),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** The classes of the target each call passes, by the call, as {@link classesCarried} reads its invocation's arguments. */
|
|
227
|
+
export function classesPassed<Brand>(
|
|
228
|
+
project: ProjectView<Brand>,
|
|
229
|
+
held: Inventory,
|
|
230
|
+
chains: AliasChains,
|
|
231
|
+
sites: Pick<ProjectSites, "calls" | "invocations">,
|
|
232
|
+
calls: readonly CallExpression[],
|
|
233
|
+
): ReadonlyMap<CallExpression, readonly ClassUse[]> {
|
|
234
|
+
const carried = classesCarried(
|
|
235
|
+
project,
|
|
236
|
+
held,
|
|
237
|
+
chains,
|
|
238
|
+
sites,
|
|
239
|
+
calls.map((call) => sites.invocations.of(call).arguments),
|
|
240
|
+
);
|
|
241
|
+
return new Map(calls.map((call, at) => [call, carried[at] ?? []]));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The classes of the target each group of written values carries: each class a value
|
|
246
|
+
* can carry holds as a constructor, as the checker types it. A name whose type erases
|
|
247
|
+
* the class is followed through what its declaration binds ({@link boundTo}) and, for a
|
|
248
|
+
* parameter, through what each call of its function passes at its place, so a class
|
|
249
|
+
* handed to a wrapper is passed. A class is used at each value that carries it. A class
|
|
250
|
+
* of another program is carried by nothing.
|
|
251
|
+
*/
|
|
252
|
+
export function classesCarried<Brand>(
|
|
253
|
+
project: ProjectView<Brand>,
|
|
254
|
+
held: Inventory,
|
|
255
|
+
chains: AliasChains,
|
|
256
|
+
sites: Pick<ProjectSites, "calls" | "invocations">,
|
|
257
|
+
groups: readonly (readonly Expression[])[],
|
|
258
|
+
): readonly (readonly ClassUse[])[] {
|
|
259
|
+
const { invocations: invoked } = sites;
|
|
260
|
+
const passed = groups.map((written) => written.flatMap(carriedValues));
|
|
261
|
+
const targets = resolvedTargets(
|
|
262
|
+
project,
|
|
263
|
+
passed.flat().flatMap((value) => (value.shorthand ? [] : (value.name ?? []))),
|
|
264
|
+
);
|
|
265
|
+
const classes = new Set(
|
|
266
|
+
held.symbols.filter((symbol) => symbol.kind === "class").map((symbol) => symbol.id),
|
|
267
|
+
);
|
|
268
|
+
let calling: CallsOf | undefined;
|
|
269
|
+
const callsOfFunction = (symbol: TSSymbol): readonly CallExpression[] => {
|
|
270
|
+
calling ??= callsByDeclaration(
|
|
271
|
+
project,
|
|
272
|
+
sites.calls.map((one) => one.call),
|
|
273
|
+
invoked,
|
|
274
|
+
);
|
|
275
|
+
return symbol.declarations.flatMap((handle) => calling?.get(keyOf(handle)) ?? []);
|
|
276
|
+
};
|
|
277
|
+
const classesOf = (value: CarriedValue, seen: Set<number>): readonly string[] => {
|
|
278
|
+
const { named, typed } = heldBy(project, value, targets);
|
|
279
|
+
return [
|
|
280
|
+
...typed.flatMap((symbol) => chains.declarationsOf(symbol).filter((id) => classes.has(id))),
|
|
281
|
+
...(named === undefined ? [] : reached(named, seen)),
|
|
282
|
+
];
|
|
283
|
+
};
|
|
284
|
+
const reached = (symbol: TSSymbol, seen: Set<number>): readonly string[] => {
|
|
285
|
+
const ids = chains.declarationsOf(symbol).filter((id) => classes.has(id));
|
|
286
|
+
if (ids.length > 0 || seen.has(symbol.id)) {
|
|
287
|
+
return ids;
|
|
288
|
+
}
|
|
289
|
+
seen.add(symbol.id);
|
|
290
|
+
const through = (written: Expression): readonly string[] =>
|
|
291
|
+
carriedValues(written).flatMap((value) => classesOf(value, seen));
|
|
292
|
+
return symbol.declarations.flatMap((handle) => {
|
|
293
|
+
const node = project.declarationAt(handle)?.node;
|
|
294
|
+
if (node === undefined) {
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
if (isParameterDeclaration(node)) {
|
|
298
|
+
return argumentsAt(project, node, callsOfFunction, invoked).flatMap(through);
|
|
299
|
+
}
|
|
300
|
+
const bound = boundTo(project, node);
|
|
301
|
+
return [
|
|
302
|
+
...(bound.written === undefined ? [] : through(bound.written)),
|
|
303
|
+
...bound.symbols.flatMap((next) => reached(next, seen)),
|
|
304
|
+
];
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
return passed.map((values) => {
|
|
308
|
+
const uses = new Map<string, ClassUse>();
|
|
309
|
+
for (const value of values) {
|
|
310
|
+
for (const id of classesOf(value, new Set())) {
|
|
311
|
+
uses.set(`${id}\u0000${String(value.site.pos)}`, { id, site: value.site });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return [...uses.values()];
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The symbols of the classes and functions the checker's type at one expression holds as
|
|
320
|
+
* values, each member of a union: the static side of a class or a function is an
|
|
321
|
+
* anonymous type its declaration's symbol names, which an instance's type is not.
|
|
322
|
+
*/
|
|
323
|
+
function constructedAt<Brand>(project: ProjectView<Brand>, written: Node): readonly TSSymbol[] {
|
|
324
|
+
return heldIn(project, must(project.queries.typeAt(unwrapped(written))));
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** The classes and functions one type holds as values, as {@link constructedAt} reads them. */
|
|
328
|
+
function heldIn<Brand>(project: ProjectView<Brand>, type: Type | undefined): readonly TSSymbol[] {
|
|
329
|
+
const { queries } = project;
|
|
330
|
+
return (type === undefined ? [] : must(queries.constituents(type))).flatMap((part) => {
|
|
331
|
+
if (!part.isObjectType() || (part.objectFlags & ObjectFlags.Anonymous) === 0) {
|
|
332
|
+
return [];
|
|
333
|
+
}
|
|
334
|
+
return must(queries.symbolOfType(part)) ?? [];
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** What one declaration binds its name to: the value it writes, or the symbols it copies. */
|
|
339
|
+
interface Bound {
|
|
340
|
+
readonly written?: Expression;
|
|
341
|
+
readonly symbols: readonly TSSymbol[];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* What a variable, a property or a destructured binding binds its name to: a variable's
|
|
346
|
+
* or a property's initializer, the value a shorthand property names, or the property of
|
|
347
|
+
* the destructured value a binding element reads.
|
|
348
|
+
*/
|
|
349
|
+
function boundTo<Brand>(project: ProjectView<Brand>, node: Node): Bound {
|
|
350
|
+
if (isShorthandPropertyAssignment(node)) {
|
|
351
|
+
const value = shorthandTarget(project, node);
|
|
352
|
+
return { symbols: value === undefined ? [] : [value] };
|
|
353
|
+
}
|
|
354
|
+
if (isBindingElement(node)) {
|
|
355
|
+
const key = node.propertyName ?? node.name;
|
|
356
|
+
if (key === undefined || !isIdentifier(key)) {
|
|
357
|
+
return { symbols: [] };
|
|
358
|
+
}
|
|
359
|
+
const source = must(project.queries.typeAt(node.parent));
|
|
360
|
+
const property =
|
|
361
|
+
source === undefined
|
|
362
|
+
? undefined
|
|
363
|
+
: must(project.queries.propertiesOf(source)).find((one) => one.name === key.text);
|
|
364
|
+
return { symbols: property === undefined ? [] : [property] };
|
|
365
|
+
}
|
|
366
|
+
const written = initializerOf(node);
|
|
367
|
+
return written === undefined ? { symbols: [] } : { written, symbols: [] };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** The value a variable or a property declaration binds its name to, where it writes one. */
|
|
371
|
+
function initializerOf(node: Node): Expression | undefined {
|
|
372
|
+
return isVariableDeclaration(node) || isPropertyAssignment(node) || isPropertyDeclaration(node)
|
|
373
|
+
? node.initializer
|
|
374
|
+
: undefined;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** The project's calls, by each declaration their callee resolves to. */
|
|
378
|
+
type CallsOf = ReadonlyMap<string, readonly CallExpression[]>;
|
|
379
|
+
|
|
380
|
+
/** The key of one declaration a symbol names, the same for each symbol the checker makes of it. */
|
|
381
|
+
function keyOf(handle: { readonly path: string; readonly index: number }): string {
|
|
382
|
+
return `${handle.path}#${String(handle.index)}`;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Every call of the project by the declarations its invoked function can be: the function
|
|
387
|
+
* or the method it names, or else each function the checker's type at it holds, wherever
|
|
388
|
+
* that value came from. A name is also followed through what its binding copies
|
|
389
|
+
* ({@link boundTo}), which is how a call reaches a function an annotation erases.
|
|
390
|
+
* Only the target's own declarations are keys, as only its own functions are asked for.
|
|
391
|
+
*/
|
|
392
|
+
function callsByDeclaration<Brand>(
|
|
393
|
+
project: ProjectView<Brand>,
|
|
394
|
+
calls: readonly CallExpression[],
|
|
395
|
+
invoked: Invocations,
|
|
396
|
+
): CallsOf {
|
|
397
|
+
const own = project.ownPaths();
|
|
398
|
+
const named = new Map<number, readonly string[]>();
|
|
399
|
+
const keysOf = (symbol: TSSymbol, seen: Set<number>): readonly string[] => {
|
|
400
|
+
const known = named.get(symbol.id);
|
|
401
|
+
if (known !== undefined || seen.has(symbol.id)) {
|
|
402
|
+
return known ?? [];
|
|
403
|
+
}
|
|
404
|
+
seen.add(symbol.id);
|
|
405
|
+
const keys = symbol.declarations
|
|
406
|
+
.filter((handle) => own.has(handle.path))
|
|
407
|
+
.flatMap((handle) => {
|
|
408
|
+
const node = project.declarationAt(handle)?.node;
|
|
409
|
+
const bound = node === undefined ? { symbols: [] } : boundTo(project, node);
|
|
410
|
+
const copied = [
|
|
411
|
+
...bound.symbols,
|
|
412
|
+
...(bound.written === undefined ? [] : carriedValues(bound.written)).flatMap((value) => {
|
|
413
|
+
const { named: next, typed } = heldBy(project, value);
|
|
414
|
+
return [...typed, ...(next === undefined ? [] : [next])];
|
|
415
|
+
}),
|
|
416
|
+
];
|
|
417
|
+
return [keyOf(handle), ...copied.flatMap((next) => keysOf(next, seen))];
|
|
418
|
+
});
|
|
419
|
+
named.set(symbol.id, keys);
|
|
420
|
+
return keys;
|
|
421
|
+
};
|
|
422
|
+
const declared = (call: CallExpression): boolean =>
|
|
423
|
+
((invoked.callee(call)?.flags ?? 0) & (SymbolFlags.Function | SymbolFlags.Method)) !== 0;
|
|
424
|
+
const typed = calls.filter((call) => !declared(call));
|
|
425
|
+
const types = new Map(
|
|
426
|
+
project.queries
|
|
427
|
+
.typesAt(typed.map((call) => unwrapped(invoked.of(call).callee)))
|
|
428
|
+
.map((type, index) => [typed[index], must(type)]),
|
|
429
|
+
);
|
|
430
|
+
const found = new Map<string, CallExpression[]>();
|
|
431
|
+
for (const call of calls) {
|
|
432
|
+
const callee = invoked.callee(call);
|
|
433
|
+
const held = [
|
|
434
|
+
...(callee === undefined ? [] : [callee]),
|
|
435
|
+
...(types.has(call) ? heldIn(project, types.get(call)) : []),
|
|
436
|
+
];
|
|
437
|
+
for (const key of new Set(held.flatMap((symbol) => keysOf(symbol, new Set())))) {
|
|
438
|
+
found.set(key, [...(found.get(key) ?? []), call]);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return found;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* What each call of the function a parameter belongs to passes at the parameter's place,
|
|
446
|
+
* every argument from there on for a rest parameter, as the call's invocation binds them.
|
|
447
|
+
* The function is the one the checker's type at its own declaration names, so a function
|
|
448
|
+
* with no name of its own is called too. A `this` parameter takes no place, and an
|
|
449
|
+
* argument at or after a spread argument passes at every place the spread's length can
|
|
450
|
+
* move it to.
|
|
451
|
+
*/
|
|
452
|
+
function argumentsAt<Brand>(
|
|
453
|
+
project: ProjectView<Brand>,
|
|
454
|
+
parameter: ParameterDeclaration,
|
|
455
|
+
callsOfFunction: (symbol: TSSymbol) => readonly CallExpression[],
|
|
456
|
+
invoked: Invocations,
|
|
457
|
+
): readonly Expression[] {
|
|
458
|
+
const owner = parameter.parent;
|
|
459
|
+
if (
|
|
460
|
+
!isFunctionDeclaration(owner) &&
|
|
461
|
+
!isMethodDeclaration(owner) &&
|
|
462
|
+
!isArrowFunction(owner) &&
|
|
463
|
+
!isFunctionExpression(owner)
|
|
464
|
+
) {
|
|
465
|
+
return [];
|
|
466
|
+
}
|
|
467
|
+
const calls = new Set(constructedAt(project, owner).flatMap(callsOfFunction));
|
|
468
|
+
const [first] = owner.parameters;
|
|
469
|
+
const erased = first !== undefined && isIdentifier(first.name) && first.name.text === "this";
|
|
470
|
+
const place = owner.parameters.indexOf(parameter) - (erased ? 1 : 0);
|
|
471
|
+
const rest = parameter.dotDotDotToken !== undefined;
|
|
472
|
+
return [...calls].flatMap((call) => {
|
|
473
|
+
let spreads = 0;
|
|
474
|
+
return invoked.of(call).arguments.flatMap((argument, index) => {
|
|
475
|
+
const spread = isSpreadElement(argument);
|
|
476
|
+
const earliest = index - spreads;
|
|
477
|
+
spreads += spread ? 1 : 0;
|
|
478
|
+
const open = spread || earliest < index;
|
|
479
|
+
const lands = open ? rest || earliest <= place : rest ? index >= place : index === place;
|
|
480
|
+
return lands ? [spread ? argument.expression : argument] : [];
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { EXEMPTION_CLASSES, type Language } from "../contract/contract.generated.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether a class may retain a member declared with the `private` modifier, and one
|
|
5
|
+
* declared with a `#private` name. The modifier is a compile-time constraint only, so
|
|
6
|
+
* such a member stays reachable by its name at run time; a private name cannot be
|
|
7
|
+
* named from outside its class body at all.
|
|
8
|
+
*/
|
|
9
|
+
export interface TypeScriptVisibility {
|
|
10
|
+
readonly private: boolean;
|
|
11
|
+
readonly privateName: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type Row = (typeof EXEMPTION_CLASSES)[number];
|
|
15
|
+
|
|
16
|
+
/** The name of a class of the vocabulary. */
|
|
17
|
+
export type ExemptionClass = Row["class"];
|
|
18
|
+
|
|
19
|
+
/** The classes of the rows that list one language. */
|
|
20
|
+
type ClassesOf<R, L extends Language> = R extends {
|
|
21
|
+
readonly class: infer C;
|
|
22
|
+
readonly languages: readonly (infer Listed)[];
|
|
23
|
+
}
|
|
24
|
+
? L extends Listed
|
|
25
|
+
? C
|
|
26
|
+
: never
|
|
27
|
+
: never;
|
|
28
|
+
|
|
29
|
+
/** The name of a class that runs on TypeScript, the only classes this analyzer records. */
|
|
30
|
+
export type TSExemptionClass = ClassesOf<Row, "ts">;
|
|
31
|
+
|
|
32
|
+
function runsOnTS(row: Row): row is Extract<Row, { readonly class: TSExemptionClass }> {
|
|
33
|
+
const languages: readonly Language[] = row.languages;
|
|
34
|
+
return languages.includes("ts");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const TS_ROWS = EXEMPTION_CLASSES.filter(runsOnTS);
|
|
38
|
+
|
|
39
|
+
/** The classes that run on TypeScript, in the vocabulary's order. */
|
|
40
|
+
export const TS_EXEMPTION_CLASSES: readonly TSExemptionClass[] = TS_ROWS.map((row) => row.class);
|
|
41
|
+
|
|
42
|
+
const VISIBILITY: ReadonlyMap<TSExemptionClass, TypeScriptVisibility> = new Map(
|
|
43
|
+
TS_ROWS.map((row) => [row.class, row.typescriptVisibility]),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/** Which private members one class that runs on TypeScript may retain. */
|
|
47
|
+
export function typescriptVisibilityOf(exemptionClass: TSExemptionClass): TypeScriptVisibility {
|
|
48
|
+
return VISIBILITY.get(exemptionClass) ?? { private: false, privateName: false };
|
|
49
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decorator class: a member a decorator expression is attached to, and every member
|
|
3
|
+
* of a class one is attached to but the member a parameter property declares, is held
|
|
4
|
+
* back, because the decorator may reach the member by its name. The rule is read from
|
|
5
|
+
* the tree alone. The decorator's own use of what it decorates is the reference pass's
|
|
6
|
+
* `decorator` reference; this class records only that the declaration is held back,
|
|
7
|
+
* static members of a decorated class included.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { decoratorsOf, spelledDecorator } from "./calls.ts";
|
|
11
|
+
import type { DetectorInput, Evidence } from "./lifecycle.ts";
|
|
12
|
+
import { isParameterProperty, type InventorySymbol, type SymbolKind } from "../inventory.ts";
|
|
13
|
+
import { renderPosition } from "../position.ts";
|
|
14
|
+
|
|
15
|
+
/** The kinds of declaration a decorator is attached to and holds back. */
|
|
16
|
+
const DECORATED: ReadonlySet<SymbolKind> = new Set(["class", "method", "class-member"]);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The decorator detector: each decorated member, and each member of a decorated
|
|
20
|
+
* class, recorded at the decorator.
|
|
21
|
+
*/
|
|
22
|
+
export function decorator<Brand>(input: DetectorInput<Brand>): readonly Evidence[] {
|
|
23
|
+
const { held, sites, targetRoot } = input;
|
|
24
|
+
const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
|
|
25
|
+
const membersOf = new Map<string, InventorySymbol[]>();
|
|
26
|
+
for (const symbol of held.symbols) {
|
|
27
|
+
if (symbol.kind === "method" || symbol.kind === "class-member") {
|
|
28
|
+
const members = membersOf.get(symbol.parent);
|
|
29
|
+
if (members === undefined) {
|
|
30
|
+
membersOf.set(symbol.parent, [symbol]);
|
|
31
|
+
} else {
|
|
32
|
+
members.push(symbol);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// A decorated class's container passes each constructor argument, which keeps the
|
|
37
|
+
// parameter, not the member a parameter property stores it in. A parameter property's
|
|
38
|
+
// decorator is attached to its parameter, and to no member.
|
|
39
|
+
return sites.decorated.flatMap(({ id, node }) => {
|
|
40
|
+
const symbol = byId.get(id);
|
|
41
|
+
if (symbol === undefined || !DECORATED.has(symbol.kind) || isParameterProperty(node)) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const members =
|
|
45
|
+
symbol.kind === "class"
|
|
46
|
+
? (membersOf.get(symbol.id) ?? []).filter(
|
|
47
|
+
(member) => !sites.parameterProperties.has(member.id),
|
|
48
|
+
)
|
|
49
|
+
: [symbol];
|
|
50
|
+
return decoratorsOf(node).flatMap((attached) => {
|
|
51
|
+
const detail = `decorated by ${spelledDecorator(attached)}`;
|
|
52
|
+
const site = renderPosition(node.getSourceFile(), targetRoot, attached.getStart());
|
|
53
|
+
return members.map((member) => ({ id: member.id, detail, site }));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exemption classes this analyzer detects, each by its detector, and what their
|
|
3
|
+
* detection reads of the run once for every project. A class without a detector here
|
|
4
|
+
* retains nothing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ResolvedConfig } from "../config.ts";
|
|
8
|
+
import type { Host } from "../host.ts";
|
|
9
|
+
import type { Inventory } from "../inventory.ts";
|
|
10
|
+
import type { ProjectView } from "../session.ts";
|
|
11
|
+
import type { TSExemptionClass } from "./classes.ts";
|
|
12
|
+
import { decorator } from "./decorator.ts";
|
|
13
|
+
import { enumGroup } from "./enum-group.ts";
|
|
14
|
+
import { frameworkLifecycle } from "./framework-lifecycle.ts";
|
|
15
|
+
import { generatedFile, type GeneratedFact } from "./generated-file.ts";
|
|
16
|
+
import { injectionContainer } from "./injection-container.ts";
|
|
17
|
+
import { interfaceSatisfaction } from "./interface-satisfaction.ts";
|
|
18
|
+
import { detect, type Detector, type Detectors, type Exemption } from "./lifecycle.ts";
|
|
19
|
+
import { reflectiveLookup } from "./reflective-lookup.ts";
|
|
20
|
+
import { serializationContract } from "./serialization-contract.ts";
|
|
21
|
+
import { readTemplates, templateField } from "./template-field.ts";
|
|
22
|
+
|
|
23
|
+
export const DETECTORS: Detectors = new Map<TSExemptionClass, Detector>([
|
|
24
|
+
["interface-satisfaction", interfaceSatisfaction],
|
|
25
|
+
["enum-group", enumGroup],
|
|
26
|
+
["generated-file", generatedFile],
|
|
27
|
+
["template-field", templateField],
|
|
28
|
+
["reflective-lookup", reflectiveLookup],
|
|
29
|
+
["decorator", decorator],
|
|
30
|
+
["injection-container", injectionContainer],
|
|
31
|
+
["framework-lifecycle", frameworkLifecycle],
|
|
32
|
+
["serialization-contract", serializationContract],
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/** One project, while its view is open, as detection reads it. */
|
|
36
|
+
interface DetectedProject<Brand> {
|
|
37
|
+
readonly project: ProjectView<Brand>;
|
|
38
|
+
readonly held: Inventory;
|
|
39
|
+
/** The project's generated files, by path below the target root, each with what decided it. */
|
|
40
|
+
readonly generated: ReadonlyMap<string, GeneratedFact>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The detection of one run: what every project's detectors read of the run, read once,
|
|
45
|
+
* and a function that runs them over one project. A switched-off class reads nothing,
|
|
46
|
+
* its templates included, and generated files are held only where the configuration
|
|
47
|
+
* leaves them out of the analysis.
|
|
48
|
+
*/
|
|
49
|
+
export function detection(
|
|
50
|
+
host: Host,
|
|
51
|
+
targetRoot: string,
|
|
52
|
+
config: ResolvedConfig,
|
|
53
|
+
consumers: readonly string[],
|
|
54
|
+
detectors: Detectors,
|
|
55
|
+
): <Brand>(one: DetectedProject<Brand>) => readonly Exemption[] {
|
|
56
|
+
const disabled = new Set(config.exemptions.disabled);
|
|
57
|
+
const templates = disabled.has("template-field")
|
|
58
|
+
? { delimiters: config.analysis.template_delimiters, files: [] }
|
|
59
|
+
: readTemplates(host, targetRoot, config.analysis);
|
|
60
|
+
const included = config.analysis.generated_files === "include";
|
|
61
|
+
return ({ project, held, generated }) =>
|
|
62
|
+
detect(
|
|
63
|
+
{
|
|
64
|
+
project,
|
|
65
|
+
held,
|
|
66
|
+
targetRoot,
|
|
67
|
+
templates,
|
|
68
|
+
ts: config.ts,
|
|
69
|
+
consumers,
|
|
70
|
+
...(included ? {} : { generated }),
|
|
71
|
+
},
|
|
72
|
+
detectors,
|
|
73
|
+
disabled,
|
|
74
|
+
);
|
|
75
|
+
}
|