@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.
- 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 +2 -7
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +269 -432
- 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/calls.js +279 -43
- 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 +1 -1
- 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 +1369 -0
- package/dist/src/contract/report.generated.js +866 -0
- package/dist/src/dependencies.js +164 -121
- 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/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +1 -63
- package/dist/src/exempt.js +21 -66
- 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/framework-lifecycle.js +46 -19
- 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/generated-file.js +174 -28
- 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.js +2 -2
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/injection-container.js +2 -2
- package/dist/src/interface-satisfaction.js +1 -1
- 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 +47 -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/serialization-contract.js +601 -22
- 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/template-field.js +9 -19
- 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 +7 -5
- 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 +2 -9
- package/src/amd.ts +130 -0
- package/src/analysis.ts +424 -650
- package/src/augmentations.ts +93 -4
- package/src/automatic-types.ts +12 -30
- package/src/bindings.ts +460 -0
- package/src/calls.ts +338 -46
- 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 +1 -1
- 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 +1452 -0
- package/src/contract/report.generated.ts +1241 -0
- package/src/dependencies.ts +222 -127
- 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/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +1 -75
- package/src/exempt.ts +70 -98
- 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/framework-lifecycle.ts +47 -19
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +96 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +242 -35
- 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.ts +2 -2
- package/src/indexed-access.ts +570 -527
- package/src/injection-container.ts +2 -2
- package/src/interface-satisfaction.ts +2 -2
- 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 +92 -0
- 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} +27 -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/serialization-contract.ts +769 -23
- 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/template-field.ts +15 -25
- 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 +7 -5
- 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/conventions.js +0 -937
- package/dist/src/discover.js +0 -468
- package/dist/src/entry-points.js +0 -529
- 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/setup-failure.js +0 -23
- package/dist/src/sweep.js +0 -224
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/dist/src/workflow-steps.js +0 -185
- package/src/base-url.ts +0 -24
- package/src/discover.ts +0 -653
- package/src/entry-points.ts +0 -687
- 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/setup-failure.ts +0 -39
- package/src/sweep.ts +0 -347
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
- package/src/workflow-steps.ts +0 -216
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const FILE_NEVER_IMPORTED = "DS1502";
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
3
2
|
/** The subject kind of a finding about a source file, from the finding schema's vocabulary. */
|
|
4
3
|
const FILE_SUBJECT = "file";
|
|
5
4
|
/** A file's subject starts at its first character. */
|
|
@@ -11,8 +10,8 @@ const FILE_START = 1;
|
|
|
11
10
|
* derived matrix is never complete.
|
|
12
11
|
*/
|
|
13
12
|
function completeMatrix(config) {
|
|
14
|
-
return (config.analysis.
|
|
15
|
-
config.analysis.configurations.some((entry) =>
|
|
13
|
+
return (config.analysis.matrix.complete &&
|
|
14
|
+
config.analysis.configurations.some((entry) => "project" in entry));
|
|
16
15
|
}
|
|
17
16
|
/** One finding about a whole file. */
|
|
18
17
|
function fileFinding(code, file, message) {
|
|
@@ -40,7 +39,7 @@ function fileNeverBuilt({ config, swept, files }) {
|
|
|
40
39
|
const message = `no project of the build matrix includes this file (${swept.matrix.configurations.join(", ")})`;
|
|
41
40
|
return files.tree
|
|
42
41
|
.filter((file) => !built.has(file.path))
|
|
43
|
-
.map((file) => fileFinding(
|
|
42
|
+
.map((file) => fileFinding(CODE.fileNeverBuilt, file, message));
|
|
44
43
|
}
|
|
45
44
|
/**
|
|
46
45
|
* The paths of the files a reference from a live declaration of another file reaches, a
|
|
@@ -125,14 +124,20 @@ function fileNeverImported(input) {
|
|
|
125
124
|
const file = { path: symbol.position.path, ref: symbol.ref, endLine: symbol.endLine };
|
|
126
125
|
return referenced.has(symbol.position.path)
|
|
127
126
|
? {
|
|
128
|
-
...fileFinding(
|
|
127
|
+
...fileFinding(CODE.fileNeverImported, file, "only code that is itself dead imports this file, and no root names it or a declaration in it"),
|
|
129
128
|
unreachableOnly: true,
|
|
130
129
|
}
|
|
131
|
-
: fileFinding(
|
|
130
|
+
: fileFinding(CODE.fileNeverImported, file, "no import reaches this file and no root names it or a declaration in it");
|
|
132
131
|
});
|
|
133
132
|
}
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
133
|
+
/**
|
|
134
|
+
* The findings of the files family, `DS1600` to `DS1699`, each at the class `possible` in a
|
|
135
|
+
* package a command of which may run a file whose path no word of it writes, since that
|
|
136
|
+
* command may run the file (confidence.35).
|
|
137
|
+
*/
|
|
138
|
+
export const nonCodeArtifacts = (input) => {
|
|
139
|
+
const unwritten = [...(input.files.unwrittenRuns ?? [])];
|
|
140
|
+
return [...fileNeverBuilt(input), ...fileNeverImported(input)].map((finding) => unwritten.some((dir) => dir === "." || finding.position.path === dir || finding.position.path.startsWith(`${dir}/`))
|
|
141
|
+
? { ...finding, reachabilityClass: "possible" }
|
|
142
|
+
: finding);
|
|
143
|
+
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
2
|
+
import { WORDS } from "../inventory.js";
|
|
1
3
|
import { positionKey } from "../position.js";
|
|
2
4
|
import { declarationKey } from "../ref.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
import { reachabilityClasses } from "./completion.js";
|
|
6
|
+
import { bytewise } from "../text/compare.js";
|
|
7
|
+
/** The containers whose type parameters `DS1006` reports: a function, a class method and a type alias. */
|
|
8
|
+
const SIGNATURES = new Set(["function", "method", "type"]);
|
|
5
9
|
function indexed(input) {
|
|
6
10
|
const { matrix, sweep } = input.swept;
|
|
7
11
|
return {
|
|
@@ -68,14 +72,16 @@ function parametersOnlyNamedBy(run, namedBy, member) {
|
|
|
68
72
|
.map((symbol) => symbol.name);
|
|
69
73
|
}
|
|
70
74
|
/** What one write-only finding says, the declarations its deletion takes with it included. */
|
|
71
|
-
function writeOnlyMessage(run,
|
|
75
|
+
function writeOnlyMessage(run, written, subject) {
|
|
76
|
+
const { symbol, held } = subject;
|
|
72
77
|
const writes = held.writes.length === 1 ? "once" : `at ${String(held.writes.length)} positions`;
|
|
73
78
|
const read = run.input.stores.readInTests.has(symbol.id)
|
|
74
79
|
? "read only from test files"
|
|
75
80
|
: "never read";
|
|
76
81
|
const cascade = [
|
|
77
|
-
...parametersOnlyNamedBy(run, namedBy, symbol).map((name) => `type parameter ${name}`),
|
|
78
|
-
...[...held.alone].sort().map((name) => `import ${name}`),
|
|
82
|
+
...parametersOnlyNamedBy(run, written.namedBy, symbol).map((name) => `type parameter ${name}`),
|
|
83
|
+
...[...held.alone].sort(bytewise).map((name) => `import ${name}`),
|
|
84
|
+
...(written.named.get(symbol.id) ?? []),
|
|
79
85
|
];
|
|
80
86
|
const word = SUBJECT_WORDS[symbol.kind] ?? "member";
|
|
81
87
|
const message = `${word} ${symbol.name} is written ${writes} and ${read}`;
|
|
@@ -84,16 +90,34 @@ function writeOnlyMessage(run, namedBy, symbol, held) {
|
|
|
84
90
|
: `${message}, and deleting it with its writes deletes ${cascade.join(" and ")} too`;
|
|
85
91
|
}
|
|
86
92
|
/**
|
|
87
|
-
*
|
|
88
|
-
*
|
|
93
|
+
* The kinds of declaration that fall with a write-only subject: a declaration of its own.
|
|
94
|
+
* A member falls with its container, and a type parameter is the type-parameter clause's.
|
|
95
|
+
*/
|
|
96
|
+
const FALLING = new Set([
|
|
97
|
+
"function",
|
|
98
|
+
"class",
|
|
99
|
+
"interface",
|
|
100
|
+
"type",
|
|
101
|
+
"enum",
|
|
102
|
+
"namespace",
|
|
103
|
+
"variable",
|
|
104
|
+
]);
|
|
105
|
+
const WRITTEN = new WeakMap();
|
|
106
|
+
/**
|
|
107
|
+
* `DS1301`'s subjects: a module-level variable, a type member or a class member the
|
|
108
|
+
* counted references store into and never read.
|
|
89
109
|
*
|
|
90
110
|
* The subject is a live declaration: a store is a reference, so the sweep holds a
|
|
91
111
|
* written one live and its candidates are never this kind's. Every reference that is
|
|
92
112
|
* not a store reads, and so does an exemption record naming the declaration.
|
|
93
113
|
*/
|
|
94
|
-
function
|
|
114
|
+
function writtenOf(run) {
|
|
115
|
+
const known = WRITTEN.get(run.input);
|
|
116
|
+
if (known !== undefined) {
|
|
117
|
+
return known;
|
|
118
|
+
}
|
|
95
119
|
const { stores } = run.input;
|
|
96
|
-
const { matrix } = run.input.swept;
|
|
120
|
+
const { matrix, sweep } = run.input.swept;
|
|
97
121
|
const uses = new Map();
|
|
98
122
|
for (const reference of stores.references) {
|
|
99
123
|
let held = uses.get(reference.to);
|
|
@@ -122,22 +146,49 @@ function writeOnlySymbols(run) {
|
|
|
122
146
|
namedBy.set(reference.to, (namedBy.get(reference.to) ?? new Set()).add(reference.from));
|
|
123
147
|
}
|
|
124
148
|
}
|
|
125
|
-
const
|
|
126
|
-
for (const symbol of matrix.union.symbols) {
|
|
149
|
+
const subjects = matrix.union.symbols.flatMap((symbol) => {
|
|
127
150
|
const held = uses.get(symbol.id);
|
|
128
|
-
|
|
151
|
+
return held === undefined ||
|
|
129
152
|
held.writes.length === 0 ||
|
|
130
153
|
held.reads > 0 ||
|
|
131
154
|
!writable(run, symbol) ||
|
|
132
155
|
stores.exempt.has(symbol.id) ||
|
|
133
|
-
run.candidates.has(symbol.id)
|
|
156
|
+
run.candidates.has(symbol.id)
|
|
157
|
+
? []
|
|
158
|
+
: [{ symbol, held }];
|
|
159
|
+
});
|
|
160
|
+
const subjectIds = new Set(subjects.map((subject) => subject.symbol.id));
|
|
161
|
+
const classOf = reachabilityClasses(run.input);
|
|
162
|
+
const named = new Map();
|
|
163
|
+
const falls = new Set();
|
|
164
|
+
for (const symbol of matrix.union.symbols) {
|
|
165
|
+
const chains = sweep.carried.get(symbol.id);
|
|
166
|
+
if (chains === undefined ||
|
|
167
|
+
subjectIds.has(symbol.id) ||
|
|
168
|
+
!FALLING.has(symbol.kind) ||
|
|
169
|
+
classOf(symbol.id) !== "certain") {
|
|
134
170
|
continue;
|
|
135
171
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
172
|
+
const innermost = chains.map((chain) => chain.find((id) => subjectIds.has(id)));
|
|
173
|
+
if (innermost.includes(undefined)) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
falls.add(symbol.id);
|
|
177
|
+
for (const subject of new Set(innermost)) {
|
|
178
|
+
const name = `${WORDS[symbol.kind]} ${symbol.name}`;
|
|
179
|
+
named.set(subject ?? "", [...(named.get(subject ?? "") ?? []), name]);
|
|
180
|
+
}
|
|
139
181
|
}
|
|
140
|
-
|
|
182
|
+
const written = { subjects, namedBy, named, falls };
|
|
183
|
+
WRITTEN.set(run.input, written);
|
|
184
|
+
return written;
|
|
185
|
+
}
|
|
186
|
+
/** `DS1301`: each subject, named with every position written and what falls with it. */
|
|
187
|
+
function writeOnlySymbols(run) {
|
|
188
|
+
const written = writtenOf(run);
|
|
189
|
+
return written.subjects.map((subject) => findingAbout(subject.symbol, CODE.writeOnlySymbol, writeOnlyMessage(run, written, subject), {
|
|
190
|
+
writePositions: subject.held.writes,
|
|
191
|
+
}));
|
|
141
192
|
}
|
|
142
193
|
/**
|
|
143
194
|
* The candidates no reference of any file names whose container is not itself dead, of
|
|
@@ -157,23 +208,24 @@ function unnamed(run, kind) {
|
|
|
157
208
|
});
|
|
158
209
|
}
|
|
159
210
|
/**
|
|
160
|
-
* `
|
|
211
|
+
* `DS1005`: a member of an enum declaration that nothing names. A member an enum-group
|
|
161
212
|
* record holds back is no candidate, so a member a conversion can produce never reaches
|
|
162
213
|
* this kind.
|
|
163
214
|
*/
|
|
164
215
|
function unusedEnumMembers(run) {
|
|
165
|
-
return unnamed(run, "enum-member").map((symbol) => findingAbout(symbol,
|
|
216
|
+
return unnamed(run, "enum-member").map((symbol) => findingAbout(symbol, CODE.unusedEnumMember, `enum member ${symbol.name} is named nowhere`));
|
|
166
217
|
}
|
|
167
218
|
/**
|
|
168
|
-
* `
|
|
169
|
-
* declaration's signature nor its body names.
|
|
170
|
-
* a
|
|
171
|
-
*
|
|
219
|
+
* `DS1006`: a type parameter of a function, a class method or a type alias that neither
|
|
220
|
+
* the declaration's signature nor its body names. An alias names no type of its own, so
|
|
221
|
+
* a parameter its type never names makes no two instantiations distinct. One of a class,
|
|
222
|
+
* an interface or a method signature is never reported: a class or an interface merges
|
|
223
|
+
* only with declarations of the same parameters.
|
|
172
224
|
*/
|
|
173
225
|
function unusedTypeParameters(run) {
|
|
174
226
|
return unnamed(run, "type-parameter")
|
|
175
227
|
.filter((symbol) => SIGNATURES.has(run.symbols.get(symbol.parent)?.kind ?? ""))
|
|
176
|
-
.map((symbol) => findingAbout(symbol,
|
|
228
|
+
.map((symbol) => findingAbout(symbol, CODE.unusedTypeParameter, `type parameter ${symbol.name} is named in neither its signature nor its body`));
|
|
177
229
|
}
|
|
178
230
|
/**
|
|
179
231
|
* The keys of the declarations the write-only kind reports, whatever the
|
|
@@ -181,11 +233,18 @@ function unusedTypeParameters(run) {
|
|
|
181
233
|
* supersedes narrowing it.
|
|
182
234
|
*/
|
|
183
235
|
export function writeOnlyDeclarations(input) {
|
|
184
|
-
return new Set(
|
|
236
|
+
return new Set(writtenOf(indexed(input)).subjects.map(({ symbol }) => declarationKey(symbol.ref, symbol.position.path)));
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* The declarations that fall with a write-only subject, whatever the severity: a finding
|
|
240
|
+
* about one would name code its subject's deletion already deletes.
|
|
241
|
+
*/
|
|
242
|
+
export function fallingWithWrites(input) {
|
|
243
|
+
return writtenOf(indexed(input)).falls;
|
|
185
244
|
}
|
|
186
245
|
/**
|
|
187
|
-
* The findings of the reads-and-writes
|
|
188
|
-
* each code's in site order.
|
|
246
|
+
* The findings of the reads-and-writes kinds, `DS1301`, `DS1005` and `DS1006`, in that
|
|
247
|
+
* order and each code's in site order.
|
|
189
248
|
*/
|
|
190
249
|
export const readsAndWrites = (input) => {
|
|
191
250
|
const run = indexed(input);
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { CODE, REMEDIES } from "../contract/contract.generated.js";
|
|
1
2
|
import { REPOSITORY_DOCUMENT } from "../config.js";
|
|
2
3
|
import { FIXED_SEVERITY_CODES } from "../kinds.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const UNMATCHED_ROOT = "DS1704";
|
|
6
|
-
/** The code a configured declaration that names nothing is reported under. */
|
|
7
|
-
const UNMATCHED_DECLARATION = "DS1706";
|
|
4
|
+
import { names, subjectOfCode, } from "../suppress.js";
|
|
5
|
+
import { bytewise } from "../text/compare.js";
|
|
8
6
|
/** The subject kind of a finding about a configured declaration. */
|
|
9
7
|
const DECLARATION_SUBJECT = "configured-declaration";
|
|
10
8
|
/** The subject kind of a finding about a suppression record. */
|
|
@@ -26,6 +24,39 @@ export function fileRefsOf(symbols) {
|
|
|
26
24
|
.map((symbol) => [symbol.position.path, symbol.ref]));
|
|
27
25
|
return (path) => byPath.get(path);
|
|
28
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Why a record of `mechanism` naming `codes` can match nothing, one clause per subject whose
|
|
29
|
+
* findings kinds.json's remedies table lets no record of that mechanism name, in subject order.
|
|
30
|
+
*/
|
|
31
|
+
function unnameable(codes, mechanism) {
|
|
32
|
+
return Object.keys(REMEDIES).flatMap((subject) => {
|
|
33
|
+
const named = codes.filter((code) => subjectOfCode(code) === subject);
|
|
34
|
+
if (named.length === 0 || names(subject, mechanism)) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const { names: what, suppressions } = REMEDIES[subject];
|
|
38
|
+
const reports = `${spelled(named)} ${named.length === 1 ? "reports" : "each report"} ${what}`;
|
|
39
|
+
if (suppressions.length > 0) {
|
|
40
|
+
const records = suppressions.map((one) => articled(WRITTEN[one]));
|
|
41
|
+
return [`${reports}, which only ${spelledOr(records)} can name`];
|
|
42
|
+
}
|
|
43
|
+
const settable = named.filter((code) => !FIXED_SEVERITY_CODES.includes(code));
|
|
44
|
+
const remedy = settable.length === 0
|
|
45
|
+
? "fix what the finding names instead"
|
|
46
|
+
: `fix what the finding names or set the severity of ${spelled(settable)} instead`;
|
|
47
|
+
return [`${reports}, which no suppression can name; ${remedy}`];
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/** A record's written name with its indefinite article. */
|
|
51
|
+
function articled(name) {
|
|
52
|
+
return `${/^[aeiou]/u.test(name) ? "an" : "a"} ${name}`;
|
|
53
|
+
}
|
|
54
|
+
/** A list as a sentence names alternatives. */
|
|
55
|
+
function spelledOr(items) {
|
|
56
|
+
return items.length < 2
|
|
57
|
+
? items.join("")
|
|
58
|
+
: `${items.slice(0, -1).join(", ")} or ${items.at(-1) ?? ""}`;
|
|
59
|
+
}
|
|
29
60
|
/** A list of codes as a sentence names them. */
|
|
30
61
|
function spelled(codes) {
|
|
31
62
|
if (codes.length < 2) {
|
|
@@ -63,7 +94,7 @@ function suppressionRef(symbol, path, files) {
|
|
|
63
94
|
}
|
|
64
95
|
/** One finding per refused suppression: the grammar's reason refusal and its scope refusal. */
|
|
65
96
|
function refusedSuppressions(refusals, files) {
|
|
66
|
-
return refusals.map((refused) => aboutSuppression(refused.reported, suppressionRef(refused.symbol, refused.path, files), refused.site, `${WRITTEN[refused.mechanism]} for ${refused.code} ${refused.reported ===
|
|
97
|
+
return refusals.map((refused) => aboutSuppression(refused.reported, suppressionRef(refused.symbol, refused.path, files), refused.site, `${WRITTEN[refused.mechanism]} for ${refused.code} ${refused.reported === CODE.suppressionWithoutReason
|
|
67
98
|
? "carries no reason"
|
|
68
99
|
: "names a symbol and no path"}`, { mechanism: refused.mechanism, entry: writtenEntry(refused) }));
|
|
69
100
|
}
|
|
@@ -83,36 +114,19 @@ export function staleSuppressions(stale, files) {
|
|
|
83
114
|
bySite.set(key, held);
|
|
84
115
|
}
|
|
85
116
|
return [...bySite.values()].map(({ first, records }) => {
|
|
86
|
-
const codes = [...new Set(records.map((one) => one.record.code))].sort();
|
|
117
|
+
const codes = [...new Set(records.map((one) => one.record.code))].sort(bytewise);
|
|
87
118
|
const reports = [...new Set(records.flatMap((one) => one.reports))]
|
|
88
119
|
.filter((code) => !codes.includes(code))
|
|
89
|
-
.sort();
|
|
90
|
-
const rows = codes.filter(isUnsuppressibleCode);
|
|
91
|
-
const dependencies = first.mechanism === "inline" ? codes.filter(isDependencyCode) : [];
|
|
92
|
-
const settable = rows.filter((code) => !FIXED_SEVERITY_CODES.includes(code));
|
|
93
|
-
const remedy = settable.length === 0
|
|
94
|
-
? "fix what the finding names instead"
|
|
95
|
-
: `fix what the finding names or set the severity of ${spelled(settable)} instead`;
|
|
120
|
+
.sort(bytewise);
|
|
96
121
|
const declared = reports.length === 0 ? "" : `its declaration reports ${spelled(reports)}`;
|
|
97
|
-
const because =
|
|
98
|
-
...(rows.length > 0
|
|
99
|
-
? [
|
|
100
|
-
`${spelled(rows)} ${rows.length === 1 ? "reports a row" : "report rows"} of a document, which no suppression can name; ${remedy}`,
|
|
101
|
-
]
|
|
102
|
-
: []),
|
|
103
|
-
...(dependencies.length > 0
|
|
104
|
-
? [
|
|
105
|
-
`${spelled(dependencies)} ${dependencies.length === 1 ? "reports a dependency row" : "report dependency rows"}, which only an ignore entry or a baseline row can name`,
|
|
106
|
-
]
|
|
107
|
-
: []),
|
|
108
|
-
];
|
|
122
|
+
const because = unnameable(codes, first.mechanism);
|
|
109
123
|
const instead = because.length > 0
|
|
110
124
|
? `, because ${because.join("; ")}${declared === "" ? "" : `; ${declared}`}`
|
|
111
125
|
: declared === ""
|
|
112
126
|
? ""
|
|
113
127
|
: `, and ${declared}`;
|
|
114
128
|
return {
|
|
115
|
-
code:
|
|
129
|
+
code: CODE.staleSuppression,
|
|
116
130
|
mechanism: first.mechanism,
|
|
117
131
|
entry: {
|
|
118
132
|
code: first.code,
|
|
@@ -141,7 +155,7 @@ function documentPosition(document) {
|
|
|
141
155
|
*/
|
|
142
156
|
function unmatchedDeclarationFindings(unmatched) {
|
|
143
157
|
return unmatched.map((one) => ({
|
|
144
|
-
code:
|
|
158
|
+
code: CODE.unmatchedConfiguredDeclaration,
|
|
145
159
|
position: documentPosition(one.document),
|
|
146
160
|
symbol: { ref: one.ref, kind: DECLARATION_SUBJECT, name: one.ref, sizeLines: 1 },
|
|
147
161
|
message: `${one.key} entry names no declaration in any project`,
|
|
@@ -157,7 +171,7 @@ function unmatchedDeclarationFindings(unmatched) {
|
|
|
157
171
|
export function unmatchedRootFindings(sources, document) {
|
|
158
172
|
const position = documentPosition(document);
|
|
159
173
|
return sources.map((source) => ({
|
|
160
|
-
code:
|
|
174
|
+
code: CODE.unmatchedRoot,
|
|
161
175
|
position,
|
|
162
176
|
symbol: { ref: source, kind: ROOT_SUBJECT, name: source, sizeLines: 1 },
|
|
163
177
|
message: source.includes("*") || source.includes("?")
|
|
@@ -166,7 +180,7 @@ export function unmatchedRootFindings(sources, document) {
|
|
|
166
180
|
}));
|
|
167
181
|
}
|
|
168
182
|
/**
|
|
169
|
-
* The findings of the
|
|
183
|
+
* The findings of the configuration family, `DS9000` to `DS9099`: a suppression refused for
|
|
170
184
|
* carrying no reason or naming no path, and a configured root or declaration that named
|
|
171
185
|
* nothing. A
|
|
172
186
|
* suppression that matched nothing is a record of the report's stale suppressions, which
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
2
|
+
import { WORDS } from "../inventory.js";
|
|
1
3
|
import { unreachableExports } from "./visibility-narrowing.js";
|
|
2
4
|
/** The kinds of declaration that are members of a class, an interface, a type or an enum. */
|
|
3
5
|
const MEMBERS = new Set([
|
|
@@ -7,34 +9,16 @@ const MEMBERS = new Set([
|
|
|
7
9
|
"enum-member",
|
|
8
10
|
]);
|
|
9
11
|
/**
|
|
10
|
-
* The kinds of declaration no code of this
|
|
12
|
+
* The kinds of declaration no code of this emitter reports: an interface and a method an
|
|
11
13
|
* interface declares are the interface kinds' subjects, and a type parameter is the
|
|
12
14
|
* subject of the unused-type-parameter kind or of none.
|
|
13
15
|
*/
|
|
14
|
-
const
|
|
16
|
+
const NOT_REPORTED_HERE = new Set([
|
|
15
17
|
"file",
|
|
16
18
|
"interface",
|
|
17
19
|
"interface-method",
|
|
18
20
|
"type-parameter",
|
|
19
21
|
]);
|
|
20
|
-
/** The reader's word for each kind of declaration a finding of the family names. */
|
|
21
|
-
const WORDS = {
|
|
22
|
-
file: "file",
|
|
23
|
-
function: "function",
|
|
24
|
-
class: "class",
|
|
25
|
-
interface: "interface",
|
|
26
|
-
type: "type alias",
|
|
27
|
-
enum: "enum",
|
|
28
|
-
namespace: "namespace",
|
|
29
|
-
variable: "variable",
|
|
30
|
-
"export-alias": "re-export",
|
|
31
|
-
method: "method",
|
|
32
|
-
"class-member": "class member",
|
|
33
|
-
"interface-method": "interface method",
|
|
34
|
-
"type-member": "type member",
|
|
35
|
-
"enum-member": "enum member",
|
|
36
|
-
"type-parameter": "type parameter",
|
|
37
|
-
};
|
|
38
22
|
/** What the test-of-dead-code kind says, which states the rule it applies. */
|
|
39
23
|
const TEST_OF_DEAD_CODE = "every target symbol this test references is reported dead";
|
|
40
24
|
/**
|
|
@@ -48,9 +32,9 @@ const TEST_OF_DEAD_CODE = "every target symbol this test references is reported
|
|
|
48
32
|
function codeOf(judged) {
|
|
49
33
|
const { candidate, symbol } = judged;
|
|
50
34
|
if (candidate.testOfDeadCode) {
|
|
51
|
-
return
|
|
35
|
+
return CODE.testOfDeadCode;
|
|
52
36
|
}
|
|
53
|
-
if ((judged.test && !candidate.unreferencedTest) ||
|
|
37
|
+
if ((judged.test && !candidate.unreferencedTest) || NOT_REPORTED_HERE.has(symbol.kind)) {
|
|
54
38
|
return undefined;
|
|
55
39
|
}
|
|
56
40
|
const referenced = candidate.productionRefs + candidate.testRefs > 0;
|
|
@@ -61,30 +45,30 @@ function codeOf(judged) {
|
|
|
61
45
|
return unreferencedCode(symbol);
|
|
62
46
|
}
|
|
63
47
|
if (candidate.heldByTests === candidate.id) {
|
|
64
|
-
return
|
|
48
|
+
return CODE.testOnlyUse;
|
|
65
49
|
}
|
|
66
50
|
if (candidate.productionRefs === 0 && judged.deprecated) {
|
|
67
|
-
return
|
|
51
|
+
return CODE.deprecatedUnused;
|
|
68
52
|
}
|
|
69
53
|
if (candidate.testRefs > 0 &&
|
|
70
54
|
(candidate.productionRefs === 0 ||
|
|
71
55
|
(candidate.relation === "reachability" && candidate.keptByTests))) {
|
|
72
|
-
return
|
|
56
|
+
return CODE.testOnlyUse;
|
|
73
57
|
}
|
|
74
58
|
if (MEMBERS.has(symbol.kind)) {
|
|
75
|
-
return
|
|
59
|
+
return CODE.unusedMember;
|
|
76
60
|
}
|
|
77
61
|
if (!symbol.exported) {
|
|
78
|
-
return
|
|
62
|
+
return CODE.unusedUnexported;
|
|
79
63
|
}
|
|
80
|
-
return judged.unreachable ? undefined :
|
|
64
|
+
return judged.unreachable ? undefined : CODE.unusedExported;
|
|
81
65
|
}
|
|
82
66
|
/** The code a declaration nothing references carries, by its shape and its visibility. */
|
|
83
67
|
function unreferencedCode(symbol) {
|
|
84
68
|
if (MEMBERS.has(symbol.kind)) {
|
|
85
|
-
return
|
|
69
|
+
return CODE.unusedMember;
|
|
86
70
|
}
|
|
87
|
-
return symbol.exported ?
|
|
71
|
+
return symbol.exported ? CODE.unusedExported : CODE.unusedUnexported;
|
|
88
72
|
}
|
|
89
73
|
/** The visibility a member's message names, where it narrows the member's reach. */
|
|
90
74
|
function visibilityWord(symbol) {
|
|
@@ -107,16 +91,16 @@ function messageOf(code, judged) {
|
|
|
107
91
|
const { candidate, symbol } = judged;
|
|
108
92
|
const word = WORDS[symbol.kind];
|
|
109
93
|
switch (code) {
|
|
110
|
-
case
|
|
94
|
+
case CODE.testOfDeadCode:
|
|
111
95
|
return TEST_OF_DEAD_CODE;
|
|
112
|
-
case
|
|
96
|
+
case CODE.deprecatedUnused:
|
|
113
97
|
return `deprecated ${word} has no production reference`;
|
|
114
|
-
case
|
|
98
|
+
case CODE.testOnlyUse:
|
|
115
99
|
return candidate.productionRefs === 0
|
|
116
100
|
? `${word} is referenced only from test files and never from production code`
|
|
117
101
|
: `${word} is referenced only from test files and from declarations that are themselves dead`;
|
|
118
102
|
}
|
|
119
|
-
const subject = code ===
|
|
103
|
+
const subject = code === CODE.unusedMember
|
|
120
104
|
? `${visibilityWord(symbol)}${word}`
|
|
121
105
|
: symbol.kind === "export-alias"
|
|
122
106
|
? word
|
|
@@ -124,7 +108,7 @@ function messageOf(code, judged) {
|
|
|
124
108
|
if (candidate.relation === "reachability") {
|
|
125
109
|
return `${subject} is referenced only from declarations that are themselves dead`;
|
|
126
110
|
}
|
|
127
|
-
if (code ===
|
|
111
|
+
if (code === CODE.unusedExported) {
|
|
128
112
|
return `${subject} has no reference in the target and none from any loaded consumer`;
|
|
129
113
|
}
|
|
130
114
|
return `${subject} has no reference in the target`;
|
|
@@ -166,22 +150,22 @@ function brandMembers(input) {
|
|
|
166
150
|
}
|
|
167
151
|
/** The kinds of member a brand is: a data member of a type or of a class. */
|
|
168
152
|
const BRANDS = new Set(["type-member", "class-member"]);
|
|
169
|
-
/** The findings of the unused-
|
|
153
|
+
/** The findings of the unused-declaration kinds, `DS1001` to `DS1004` and `DS1102` to `DS1104`. */
|
|
170
154
|
export const unusedDeclarations = (input) => [
|
|
171
|
-
...
|
|
155
|
+
...emitterFindings(input, false),
|
|
172
156
|
...unusedImports(input),
|
|
173
157
|
];
|
|
174
158
|
/**
|
|
175
|
-
* The import bindings their file never reads
|
|
176
|
-
*
|
|
159
|
+
* The import bindings their file never reads. A generated file's are the generator's to
|
|
160
|
+
* remove unless the run includes generated files.
|
|
177
161
|
*/
|
|
178
162
|
function unusedImports(input) {
|
|
179
|
-
const generated = input.config.analysis.
|
|
163
|
+
const generated = input.config.analysis.generated_files === "include" ? undefined : input.files.generated;
|
|
180
164
|
return input.intraFunction.imports.flatMap((binding) => binding.used || generated?.has(binding.position.path) === true
|
|
181
165
|
? []
|
|
182
166
|
: [
|
|
183
167
|
{
|
|
184
|
-
code:
|
|
168
|
+
code: CODE.unusedImport,
|
|
185
169
|
position: binding.position,
|
|
186
170
|
symbol: { ref: binding.ref, kind: "import", name: binding.name, sizeLines: 1 },
|
|
187
171
|
message: `import binding ${binding.name} is never read in its file`,
|
|
@@ -193,9 +177,9 @@ function unusedImports(input) {
|
|
|
193
177
|
* a record on such a member is matched against, since the report carries the container's
|
|
194
178
|
* finding rather than one of the member's own.
|
|
195
179
|
*/
|
|
196
|
-
export const fallenMembers = (input) =>
|
|
197
|
-
/**
|
|
198
|
-
function
|
|
180
|
+
export const fallenMembers = (input) => emitterFindings(input, true);
|
|
181
|
+
/** This emitter's findings, or, where `fallen` is set, its dead containers' members' alone. */
|
|
182
|
+
function emitterFindings(input, fallen) {
|
|
199
183
|
const union = input.swept.matrix.union;
|
|
200
184
|
const dead = new Set(input.swept.sweep.candidates.map((candidate) => candidate.id));
|
|
201
185
|
const unreachable = unreachableExports(input);
|
|
@@ -220,7 +204,7 @@ function familyFindings(input, fallen) {
|
|
|
220
204
|
};
|
|
221
205
|
const code = codeOf(judged);
|
|
222
206
|
if (code === undefined ||
|
|
223
|
-
(code ===
|
|
207
|
+
(code === CODE.unusedMember && (brands ??= brandMembers(input)).has(symbol.id))) {
|
|
224
208
|
continue;
|
|
225
209
|
}
|
|
226
210
|
const endLine = Math.max(symbol.position.line, symbol.endLine);
|
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CODE } from "../contract/contract.generated.js";
|
|
2
|
+
import { OUTSIDE } from "../liveness/graph.js";
|
|
3
|
+
import { WORDS } from "../inventory.js";
|
|
2
4
|
import { declarationKey } from "../ref.js";
|
|
3
|
-
import { runnerCalls } from "../sweep.js";
|
|
5
|
+
import { runnerCalls } from "../liveness/sweep.js";
|
|
6
|
+
import { exposedTypes, exposes } from "../exposure.js";
|
|
4
7
|
import { writeOnlyDeclarations } from "./reads-and-writes.js";
|
|
5
|
-
const UNNECESSARY_EXPORT = "DS1101";
|
|
6
|
-
const UNREACHABLE_EXPORT = "DS1103";
|
|
7
|
-
const REDUNDANT_EXPORT_KEYWORD = "DS1104";
|
|
8
8
|
/**
|
|
9
9
|
* The root kinds by which a consumer importing the package reaches a declaration. A
|
|
10
10
|
* declaration or a file rooted by any other kind is entered by something that names it
|
|
11
11
|
* from outside the reference graph: a runtime, a tool, a framework or the configuration.
|
|
12
12
|
*/
|
|
13
13
|
const PUBLISHING = new Set(["manifest-entry", "published-api"]);
|
|
14
|
-
/** The reader's word for each kind of declaration this family reports. */
|
|
15
|
-
const WORDS = {
|
|
16
|
-
function: "function",
|
|
17
|
-
class: "class",
|
|
18
|
-
interface: "interface",
|
|
19
|
-
type: "type alias",
|
|
20
|
-
enum: "enum",
|
|
21
|
-
namespace: "namespace",
|
|
22
|
-
variable: "variable",
|
|
23
|
-
"export-alias": "re-export",
|
|
24
|
-
};
|
|
25
14
|
function publishes(kinds) {
|
|
26
15
|
return [...(kinds ?? [])].some((kind) => PUBLISHING.has(kind));
|
|
27
16
|
}
|
|
@@ -104,13 +93,10 @@ function topLevelExport(facts, at, symbol) {
|
|
|
104
93
|
const parent = facts.graph.parent[at] ?? OUTSIDE;
|
|
105
94
|
return symbol.exported && parent !== OUTSIDE && facts.graph.symbols[parent]?.kind === "file";
|
|
106
95
|
}
|
|
107
|
-
function wordOf(symbol) {
|
|
108
|
-
return WORDS[symbol.kind] ?? symbol.kind;
|
|
109
|
-
}
|
|
110
96
|
/** The visibility each narrowing code names, which only the narrowing codes carry. */
|
|
111
97
|
const NARROWER = {
|
|
112
|
-
[
|
|
113
|
-
[
|
|
98
|
+
[CODE.redundantExportKeyword]: "file",
|
|
99
|
+
[CODE.unnecessaryExport]: "package",
|
|
114
100
|
};
|
|
115
101
|
function findingOf(symbol, code, message) {
|
|
116
102
|
const { path, line, column } = symbol.position;
|
|
@@ -147,18 +133,18 @@ function narrowing(facts, at, symbol, world) {
|
|
|
147
133
|
(facts.named[at] ?? 0) === 0) {
|
|
148
134
|
return undefined;
|
|
149
135
|
}
|
|
150
|
-
const word =
|
|
136
|
+
const word = WORDS[symbol.kind];
|
|
151
137
|
switch (facts.reach[at]) {
|
|
152
138
|
case "file":
|
|
153
139
|
if (facts.published[file] === true && !closed) {
|
|
154
140
|
return undefined;
|
|
155
141
|
}
|
|
156
|
-
return findingOf(symbol,
|
|
142
|
+
return findingOf(symbol, CODE.redundantExportKeyword, `exported ${word} is referenced only inside the file that declares it`);
|
|
157
143
|
case "package":
|
|
158
|
-
if (!closed || !publishes(facts.rootedBy[at])) {
|
|
144
|
+
if (!closed || !publishes(facts.rootedBy[at]) || exposes(world.exposed, symbol)) {
|
|
159
145
|
return undefined;
|
|
160
146
|
}
|
|
161
|
-
return findingOf(symbol,
|
|
147
|
+
return findingOf(symbol, CODE.unnecessaryExport, `exported ${word} is referenced only inside the package that declares it`);
|
|
162
148
|
default:
|
|
163
149
|
return undefined;
|
|
164
150
|
}
|
|
@@ -183,10 +169,10 @@ function unreachable(facts, at, symbol, candidate, input) {
|
|
|
183
169
|
const found = candidate.relation === "reachability"
|
|
184
170
|
? "is referenced only from declarations that are themselves dead"
|
|
185
171
|
: "has no reference in the target";
|
|
186
|
-
return findingOf(symbol,
|
|
172
|
+
return findingOf(symbol, CODE.unreachableExport, `exported ${WORDS[symbol.kind]} ${found}, and nothing outside can import the file that declares it`);
|
|
187
173
|
}
|
|
188
174
|
/**
|
|
189
|
-
* The visibility-narrowing
|
|
175
|
+
* The visibility-narrowing kinds over one run: `DS1501`, `DS1101` and `DS1503`. A finding
|
|
190
176
|
* about a symbol a declared cross-language edge names is reported here like any other, and
|
|
191
177
|
* the edge's evaluation then holds it pending, because the edge stands for a reference from
|
|
192
178
|
* outside the file and the package whose use is decided in the other language. A
|
|
@@ -195,10 +181,16 @@ function unreachable(facts, at, symbol, candidate, input) {
|
|
|
195
181
|
export function narrowings(input) {
|
|
196
182
|
const facts = factsOf(input.swept.matrix.union);
|
|
197
183
|
const { declared, loaded } = input.boundary.consumers;
|
|
198
|
-
const closed = input.config.
|
|
199
|
-
const world = {
|
|
184
|
+
const closed = input.config.consumers.complete && declared.every((id) => loaded.includes(id));
|
|
185
|
+
const world = {
|
|
186
|
+
closed,
|
|
187
|
+
writeOnly: writeOnlyDeclarations(input),
|
|
188
|
+
exposed: closed
|
|
189
|
+
? exposedTypes(facts.graph, facts.reach.map((reach) => reach === "outside"), input.boundary.carriers)
|
|
190
|
+
: new Set(),
|
|
191
|
+
};
|
|
200
192
|
const candidates = new Map(input.swept.sweep.candidates.map((candidate) => [candidate.id, candidate]));
|
|
201
|
-
const generated = input.config.analysis.
|
|
193
|
+
const generated = input.config.analysis.generated_files === "include" ? undefined : input.files.generated;
|
|
202
194
|
const findings = [];
|
|
203
195
|
facts.graph.symbols.forEach((symbol, at) => {
|
|
204
196
|
if (generated?.has(symbol.position.path) === true) {
|
|
@@ -234,5 +226,5 @@ export function unreachableExports(input) {
|
|
|
234
226
|
}
|
|
235
227
|
return found;
|
|
236
228
|
}
|
|
237
|
-
/** The findings of the visibility-narrowing
|
|
229
|
+
/** The findings of the visibility-narrowing kinds, `DS1501`, `DS1101` and `DS1503`. */
|
|
238
230
|
export const visibilityNarrowing = narrowings;
|