@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
|
@@ -6,25 +6,28 @@
|
|
|
6
6
|
* the project handing such a parameter to a destination is one, to a fixpoint, and a
|
|
7
7
|
* function invoked through `call`, `apply` or `bind` is the callee.
|
|
8
8
|
*/
|
|
9
|
-
import { isArrayLiteralExpression, isArrowFunction, isAsExpression, isCallExpression, isClassDeclaration, isClassExpression, isClassStaticBlockDeclaration, isFunctionDeclaration, isFunctionExpression, isGetAccessorDeclaration, isIdentifier, isInterfaceDeclaration, isMethodDeclaration, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyDeclaration, isSatisfiesExpression, isSetAccessorDeclaration, isSpreadElement, isStringLiteral, isTypeLiteralNode, isVariableDeclaration, ModifierFlags, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
9
|
+
import { isArrayLiteralExpression, isArrowFunction, isAsExpression, isAwaitExpression, isCallExpression, isClassDeclaration, isClassExpression, isClassStaticBlockDeclaration, isElementAccessExpression, isExpressionStatement, isFunctionDeclaration, isFunctionExpression, isFunctionLikeDeclaration, isGetAccessorDeclaration, isIdentifier, isInterfaceDeclaration, isMethodDeclaration, isNonNullExpression, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isForInStatement, isPropertyAccessExpression, isPropertyDeclaration, isReturnStatement, isSatisfiesExpression, isSetAccessorDeclaration, isSourceFile, isSpreadAssignment, isSpreadElement, isStringLiteral, isTypeAssertion, isTypeLiteralNode, isVariableDeclaration, ModifierFlags, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
|
|
10
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
10
11
|
import { SignatureKind, SymbolFlags, TypeFlags, } from "@typescript/native/sync";
|
|
11
12
|
import { aliasChains } from "./alias-chain.js";
|
|
12
13
|
import { calleeName, resolvedTargets, valuesPassed } from "./calls.js";
|
|
13
14
|
import { classMembers, memberComponent, typesAt } from "./class-members.js";
|
|
14
|
-
import { names, namesADeclaration, resolveEntries, spelledEntry, } from "./configured-declarations.js";
|
|
15
|
+
import { names, namesADeclaration, resolveEntries, spelledEntry, } from "./roots/configured-declarations.js";
|
|
15
16
|
import { nodeKey } from "./inventory.js";
|
|
16
17
|
import { renderPosition } from "./position.js";
|
|
17
|
-
import { must } from "./query.js";
|
|
18
|
+
import { must, Unanswerable } from "./query.js";
|
|
18
19
|
import { erasedStores } from "./erased-stores.js";
|
|
19
20
|
import { checkedTypes, CONVERSION_MEMBERS, valueReach } from "./value-reach.js";
|
|
20
21
|
/** The serializer every run reads, whatever the configuration names. */
|
|
21
|
-
const JSON_STRINGIFY = {
|
|
22
|
+
const JSON_STRINGIFY = { global: "JSON.stringify" };
|
|
22
23
|
/** The methods a serializer and a formatter resolve by name on a value that left the analysis. */
|
|
23
24
|
const CONVERSIONS = new Set(["toJSON", "toString"]);
|
|
24
25
|
/** The method a serializer calls on a value that carries one, wherever the value goes. */
|
|
25
26
|
const SERIALIZED_CONVERSION = "toJSON";
|
|
26
27
|
/** The parameter types that keep nothing of the value they are given. */
|
|
27
28
|
const ERASED = TypeFlags.Any | TypeFlags.Unknown;
|
|
29
|
+
/** The standard library's promise types, whose value is their one type argument. */
|
|
30
|
+
const PROMISES = new Set(["Promise", "PromiseLike"]);
|
|
28
31
|
/** How deep a callee's result is searched for a type parameter: `Promise<Box<T>>` is two levels. */
|
|
29
32
|
const MENTION_DEPTH = 3;
|
|
30
33
|
/** The wider of two reaches, where either may be absent. */
|
|
@@ -86,11 +89,6 @@ const FUNCTION_INTERFACES = new Set([
|
|
|
86
89
|
"CallableFunction",
|
|
87
90
|
"NewableFunction",
|
|
88
91
|
]);
|
|
89
|
-
function unparenthesized(expression) {
|
|
90
|
-
return isParenthesizedExpression(expression)
|
|
91
|
-
? unparenthesized(expression.expression)
|
|
92
|
-
: expression;
|
|
93
|
-
}
|
|
94
92
|
/**
|
|
95
93
|
* The invocation a call writes through a method of `Function.prototype`, read from
|
|
96
94
|
* the syntax alone: `f.call(self, a)`, `f.apply(self, [a])`, `f.bind(self, a)`, which
|
|
@@ -99,12 +97,12 @@ function unparenthesized(expression) {
|
|
|
99
97
|
* carries. Whether the method is the prototype's is resolution's to say.
|
|
100
98
|
*/
|
|
101
99
|
function throughMethod(call) {
|
|
102
|
-
const expression =
|
|
100
|
+
const expression = parenthesesRemoved(call.expression);
|
|
103
101
|
if (isPropertyAccessExpression(expression) && FUNCTION_METHODS.has(expression.name.text)) {
|
|
104
102
|
const [, ...rest] = call.arguments;
|
|
105
103
|
let passed = rest;
|
|
106
104
|
if (expression.name.text === "apply") {
|
|
107
|
-
const list = rest[0] === undefined ? undefined :
|
|
105
|
+
const list = rest[0] === undefined ? undefined : parenthesesRemoved(rest[0]);
|
|
108
106
|
passed = list === undefined ? [] : isArrayLiteralExpression(list) ? list.elements : [list];
|
|
109
107
|
}
|
|
110
108
|
return {
|
|
@@ -113,7 +111,7 @@ function throughMethod(call) {
|
|
|
113
111
|
};
|
|
114
112
|
}
|
|
115
113
|
if (isCallExpression(expression)) {
|
|
116
|
-
const bound =
|
|
114
|
+
const bound = parenthesesRemoved(expression.expression);
|
|
117
115
|
if (isPropertyAccessExpression(bound) && bound.name.text === "bind") {
|
|
118
116
|
return {
|
|
119
117
|
method: bound.name,
|
|
@@ -135,9 +133,16 @@ function throughMethod(call) {
|
|
|
135
133
|
function walkProject(project, held) {
|
|
136
134
|
const sites = [];
|
|
137
135
|
const parameters = [];
|
|
136
|
+
const initialized = new Set();
|
|
138
137
|
for (const file of project.ownSourceFiles()) {
|
|
139
138
|
const visit = (node, self) => {
|
|
140
139
|
let inner = self;
|
|
140
|
+
if (isPropertyDeclaration(node) && node.initializer !== undefined) {
|
|
141
|
+
const id = held.declarations.get(nodeKey(file, node));
|
|
142
|
+
if (id !== undefined) {
|
|
143
|
+
initialized.add(id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
141
146
|
if (isClassDeclaration(node) || isClassExpression(node)) {
|
|
142
147
|
inner = held.declarations.get(nodeKey(file, node));
|
|
143
148
|
}
|
|
@@ -171,11 +176,33 @@ function walkProject(project, held) {
|
|
|
171
176
|
visit(child, undefined);
|
|
172
177
|
});
|
|
173
178
|
}
|
|
174
|
-
return { sites, parameters };
|
|
179
|
+
return { sites, parameters, initialized };
|
|
180
|
+
}
|
|
181
|
+
/** The calls of `Object` that read every own property of their argument by name. */
|
|
182
|
+
const ENUMERATIONS = new Set([
|
|
183
|
+
"keys",
|
|
184
|
+
"entries",
|
|
185
|
+
"values",
|
|
186
|
+
"getOwnPropertyNames",
|
|
187
|
+
"assign",
|
|
188
|
+
]);
|
|
189
|
+
/**
|
|
190
|
+
* Whether a parameter type keeps no member name of the value it is given beyond the
|
|
191
|
+
* properties it names: `unknown`, `any`, or an object type of a string index signature,
|
|
192
|
+
* under which every other member of the value is held.
|
|
193
|
+
*/
|
|
194
|
+
function keepsNoName(project, type) {
|
|
195
|
+
if ((type.flags & ERASED) !== 0) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if ((type.flags & TypeFlags.Object) === 0) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return must(project.queries.indexInfosOf(type)).some((info) => (info.keyType.flags & TypeFlags.String) !== 0);
|
|
175
202
|
}
|
|
176
203
|
/**
|
|
177
|
-
* The parameters that keep
|
|
178
|
-
*
|
|
204
|
+
* The parameters that keep no member name of the value they are given, as `keepsNoName`
|
|
205
|
+
* reads it, and a rest parameter whose elements keep none, each by its symbol.
|
|
179
206
|
*/
|
|
180
207
|
function erasedParameters(project, parameters) {
|
|
181
208
|
const types = typesAt(project, parameters.map((parameter) => parameter.name));
|
|
@@ -185,10 +212,10 @@ function erasedParameters(project, parameters) {
|
|
|
185
212
|
return false;
|
|
186
213
|
}
|
|
187
214
|
if (!parameter.rest) {
|
|
188
|
-
return (type
|
|
215
|
+
return keepsNoName(project, type);
|
|
189
216
|
}
|
|
190
217
|
const [element] = must(project.queries.typeArguments(type));
|
|
191
|
-
return element !== undefined && (element
|
|
218
|
+
return element !== undefined && keepsNoName(project, element);
|
|
192
219
|
});
|
|
193
220
|
const bySymbol = new Map();
|
|
194
221
|
project
|
|
@@ -202,6 +229,103 @@ function erasedParameters(project, parameters) {
|
|
|
202
229
|
});
|
|
203
230
|
return bySymbol;
|
|
204
231
|
}
|
|
232
|
+
/** Whether one node is a function, whose body runs only where something calls it. */
|
|
233
|
+
function isFunctionNode(node) {
|
|
234
|
+
return (isFunctionDeclaration(node) ||
|
|
235
|
+
isFunctionExpression(node) ||
|
|
236
|
+
isArrowFunction(node) ||
|
|
237
|
+
isMethodDeclaration(node) ||
|
|
238
|
+
isGetAccessorDeclaration(node) ||
|
|
239
|
+
isSetAccessorDeclaration(node));
|
|
240
|
+
}
|
|
241
|
+
/** The name a function nested in another is called by: its own, or the variable it initializes. */
|
|
242
|
+
function nestedName(fn) {
|
|
243
|
+
if (isFunctionDeclaration(fn)) {
|
|
244
|
+
return fn.name?.text;
|
|
245
|
+
}
|
|
246
|
+
let at = fn.parent;
|
|
247
|
+
while (isParenthesizedExpression(at) || isAsExpression(at) || isSatisfiesExpression(at)) {
|
|
248
|
+
at = at.parent;
|
|
249
|
+
}
|
|
250
|
+
return isVariableDeclaration(at) && isIdentifier(at.name) ? at.name.text : undefined;
|
|
251
|
+
}
|
|
252
|
+
/** The names one function's body writes as identifiers, other than the names its declarations bind. */
|
|
253
|
+
function namesWritten(fn) {
|
|
254
|
+
const found = new Set();
|
|
255
|
+
const visit = (node) => {
|
|
256
|
+
const declares = (isFunctionDeclaration(node.parent) || isVariableDeclaration(node.parent)) &&
|
|
257
|
+
node.parent.name === node;
|
|
258
|
+
if (isIdentifier(node) && !declares) {
|
|
259
|
+
found.add(node.text);
|
|
260
|
+
}
|
|
261
|
+
node.forEachChild(visit);
|
|
262
|
+
};
|
|
263
|
+
fn.forEachChild(visit);
|
|
264
|
+
return found;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* The erased parameters their own function enumerates: one an `Object.keys`, `entries`,
|
|
268
|
+
* `values`, `getOwnPropertyNames` or `assign` call takes, `Object` the standard library's,
|
|
269
|
+
* or a `for…in` statement walks, which reads every property of the value by its name. A
|
|
270
|
+
* nested function is read as the function's own only where its name is written in it, or
|
|
271
|
+
* it has none, a callback or a function called where it is written.
|
|
272
|
+
*/
|
|
273
|
+
function enumeratedParameters(project, erased) {
|
|
274
|
+
const asked = [];
|
|
275
|
+
for (const parameter of erased.values()) {
|
|
276
|
+
const fn = parameter.name.parent.parent;
|
|
277
|
+
const spelled = parameter.name.getText();
|
|
278
|
+
const written = namesWritten(fn);
|
|
279
|
+
const visit = (node) => {
|
|
280
|
+
if (isFunctionNode(node)) {
|
|
281
|
+
const name = nestedName(node);
|
|
282
|
+
if (name !== undefined && !written.has(name)) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const read = [];
|
|
287
|
+
let object;
|
|
288
|
+
if (isCallExpression(node)) {
|
|
289
|
+
const callee = node.expression;
|
|
290
|
+
if (isPropertyAccessExpression(callee) &&
|
|
291
|
+
isIdentifier(callee.expression) &&
|
|
292
|
+
callee.expression.text === "Object" &&
|
|
293
|
+
ENUMERATIONS.has(callee.name.text)) {
|
|
294
|
+
object = callee.expression;
|
|
295
|
+
// `Object.assign` reads its sources, every argument after the target.
|
|
296
|
+
read.push(...node.arguments.slice(...(callee.name.text === "assign" ? [1] : [0, 1])));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (isForInStatement(node)) {
|
|
300
|
+
read.push(node.expression);
|
|
301
|
+
}
|
|
302
|
+
for (const value of read.map(evaluated)) {
|
|
303
|
+
if (isIdentifier(value) && value.text === spelled) {
|
|
304
|
+
asked.push({ node: value, parameter, object });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
node.forEachChild(visit);
|
|
308
|
+
};
|
|
309
|
+
fn.forEachChild(visit);
|
|
310
|
+
}
|
|
311
|
+
const symbols = project.symbolsAt(asked.map((one) => project.handle(one.node))).map(must);
|
|
312
|
+
const objects = asked.flatMap((one) => (one.object === undefined ? [] : [one.object]));
|
|
313
|
+
const own = project.ownPaths();
|
|
314
|
+
const standard = new Set(project
|
|
315
|
+
.symbolsAt(objects.map((node) => project.handle(node)))
|
|
316
|
+
.map(must)
|
|
317
|
+
.flatMap((symbol, at) => symbol?.declarations.every((handle) => !own.has(handle.path)) === true ? [objects[at]] : []));
|
|
318
|
+
return [
|
|
319
|
+
...new Set(asked.flatMap((one, at) => {
|
|
320
|
+
const symbol = symbols[at];
|
|
321
|
+
return symbol !== undefined &&
|
|
322
|
+
erased.get(symbol.id) === one.parameter &&
|
|
323
|
+
(one.object === undefined || standard.has(one.object))
|
|
324
|
+
? [one.parameter]
|
|
325
|
+
: [];
|
|
326
|
+
})),
|
|
327
|
+
];
|
|
328
|
+
}
|
|
205
329
|
/** Each place a call's argument passes one of the erased parameters it can see. */
|
|
206
330
|
function handedOn(project, sites, erased, invocationOf) {
|
|
207
331
|
const spellings = new Set([...erased.values()].map((parameter) => parameter.name.getText()));
|
|
@@ -285,8 +409,8 @@ function implementedOwners(project, inAnalysis) {
|
|
|
285
409
|
* callee is a serializer an entry names; at the arguments its resolved signature types
|
|
286
410
|
* `unknown` or `any` where its callee is declared outside the analysis or only ambiently
|
|
287
411
|
* with no implementation in it; and, where its callee is a function of the project, at each
|
|
288
|
-
* parameter the function hands on to a destination, holding back the widest
|
|
289
|
-
*
|
|
412
|
+
* parameter the function enumerates or hands on to a destination, holding back the widest
|
|
413
|
+
* of what those hold back, settled by widening until no parameter moves.
|
|
290
414
|
*/
|
|
291
415
|
function destinations(input, chains, walked) {
|
|
292
416
|
const { project, held } = input;
|
|
@@ -478,6 +602,9 @@ function destinations(input, chains, walked) {
|
|
|
478
602
|
}
|
|
479
603
|
return undefined;
|
|
480
604
|
};
|
|
605
|
+
for (const parameter of enumeratedParameters(project, erased)) {
|
|
606
|
+
forwards.set(keyOf(parameter), "properties");
|
|
607
|
+
}
|
|
481
608
|
const forwarded = handedOn(project, walked.sites, erased, invocation);
|
|
482
609
|
for (let moved = true; moved;) {
|
|
483
610
|
moved = false;
|
|
@@ -551,6 +678,14 @@ function flowsOf(project, held, sites, to, stores) {
|
|
|
551
678
|
},
|
|
552
679
|
];
|
|
553
680
|
});
|
|
681
|
+
const generic = typeParameterFlows(project, reach, sites, arguments_.flatMap((one) => {
|
|
682
|
+
const type = typeOf.get(one.value);
|
|
683
|
+
const parameters = type === undefined ? [] : typeParametersOf(project, type, 0);
|
|
684
|
+
const destination = parameters.length === 0 ? undefined : to.at(one.site.call, one.argument);
|
|
685
|
+
return destination === undefined
|
|
686
|
+
? []
|
|
687
|
+
: parameters.map((parameter) => ({ parameter, to: destination }));
|
|
688
|
+
}));
|
|
554
689
|
const left = stores.outside.flatMap((store) => {
|
|
555
690
|
const reached = reach.reachedFrom(store.type);
|
|
556
691
|
return reached.classes.size === 0 && reached.members.length === 0
|
|
@@ -565,7 +700,155 @@ function flowsOf(project, held, sites, to, stores) {
|
|
|
565
700
|
},
|
|
566
701
|
];
|
|
567
702
|
});
|
|
568
|
-
return [...passed, ...left];
|
|
703
|
+
return [...passed, ...generic, ...left];
|
|
704
|
+
}
|
|
705
|
+
/** The type parameters one type names, through constituents and type arguments. */
|
|
706
|
+
function typeParametersOf(project, type, depth) {
|
|
707
|
+
if (type.isTypeParameter()) {
|
|
708
|
+
return [type];
|
|
709
|
+
}
|
|
710
|
+
if (depth >= MENTION_DEPTH) {
|
|
711
|
+
return [];
|
|
712
|
+
}
|
|
713
|
+
const { queries } = project;
|
|
714
|
+
return [
|
|
715
|
+
...(type.isUnionType() || type.isIntersectionType() ? must(queries.constituents(type)) : []),
|
|
716
|
+
...must(queries.typeArguments(type)),
|
|
717
|
+
].flatMap((one) => typeParametersOf(project, one, depth + 1));
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* The type parameters one type names, by identifier: through constituents, type arguments,
|
|
721
|
+
* an alias's arguments and the parameters and results of its call signatures, so a callback
|
|
722
|
+
* parameter's result names the parameter its callee's result does.
|
|
723
|
+
*/
|
|
724
|
+
function namedTypeParameters(project, type, depth) {
|
|
725
|
+
const { queries } = project;
|
|
726
|
+
const aliased = type.aliasTypeArguments;
|
|
727
|
+
if (type.isTypeParameter()) {
|
|
728
|
+
return [type.id];
|
|
729
|
+
}
|
|
730
|
+
if (depth >= MENTION_DEPTH) {
|
|
731
|
+
return aliased ?? [];
|
|
732
|
+
}
|
|
733
|
+
return [
|
|
734
|
+
...(aliased ?? []),
|
|
735
|
+
...[
|
|
736
|
+
...(type.isUnionType() || type.isIntersectionType() ? must(queries.constituents(type)) : []),
|
|
737
|
+
...must(queries.typeArguments(type)),
|
|
738
|
+
...must(queries.signaturesOf(type, SignatureKind.Call)).flatMap((signature) => [
|
|
739
|
+
...signature.parameters.flatMap((_parameter, at) => [
|
|
740
|
+
must(queries.typeAtPosition(signature, at)),
|
|
741
|
+
]),
|
|
742
|
+
...[must(queries.returnTypeOf(signature))].filter((one) => one !== undefined),
|
|
743
|
+
]),
|
|
744
|
+
].flatMap((one) => namedTypeParameters(project, one, depth + 1)),
|
|
745
|
+
];
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* The types an instantiated type puts where a declared type names `parameter`: matched
|
|
749
|
+
* through type arguments, and in a union every constituent the declared union does not
|
|
750
|
+
* itself name.
|
|
751
|
+
*/
|
|
752
|
+
function bound(project, declared, instantiated, parameter, depth) {
|
|
753
|
+
if (declared.id === parameter.id) {
|
|
754
|
+
return [instantiated];
|
|
755
|
+
}
|
|
756
|
+
if (depth >= MENTION_DEPTH) {
|
|
757
|
+
return [];
|
|
758
|
+
}
|
|
759
|
+
const { queries } = project;
|
|
760
|
+
if (declared.isUnionType()) {
|
|
761
|
+
const parts = must(queries.constituents(declared));
|
|
762
|
+
if (parts.some((part) => part.id === parameter.id)) {
|
|
763
|
+
const kept = new Set(parts.map((part) => part.id));
|
|
764
|
+
const given = instantiated.isUnionType()
|
|
765
|
+
? must(queries.constituents(instantiated))
|
|
766
|
+
: [instantiated];
|
|
767
|
+
return given.filter((part) => !kept.has(part.id));
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const declaredArguments = must(queries.typeArguments(declared));
|
|
771
|
+
const givenArguments = must(queries.typeArguments(instantiated));
|
|
772
|
+
return declaredArguments.length !== givenArguments.length
|
|
773
|
+
? []
|
|
774
|
+
: declaredArguments.flatMap((one, at) => {
|
|
775
|
+
const given = givenArguments[at];
|
|
776
|
+
return given === undefined ? [] : bound(project, one, given, parameter, depth + 1);
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* The flows a type parameter of a function of the project carries where a value its type
|
|
781
|
+
* names reaches a destination: each call of the function passes its type argument there,
|
|
782
|
+
* written or inferred from the arguments, and a type argument that is a type parameter
|
|
783
|
+
* of the calling function is followed on to that function's calls, to a fixpoint.
|
|
784
|
+
*/
|
|
785
|
+
function typeParameterFlows(project, reach, sites, pending) {
|
|
786
|
+
const { queries } = project;
|
|
787
|
+
const byName = new Map();
|
|
788
|
+
for (const site of sites) {
|
|
789
|
+
const name = calleeName(site.call.expression)?.getText();
|
|
790
|
+
if (name !== undefined) {
|
|
791
|
+
byName.set(name, [...(byName.get(name) ?? []), site]);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
const found = [];
|
|
795
|
+
const seen = new Set();
|
|
796
|
+
for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
|
|
797
|
+
const { parameter, to } = next;
|
|
798
|
+
if (seen.has(parameter.id)) {
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
seen.add(parameter.id);
|
|
802
|
+
const symbol = must(queries.symbolOfType(parameter));
|
|
803
|
+
const [handle] = symbol?.declarations ?? [];
|
|
804
|
+
const declaration = handle === undefined ? undefined : project.declarationAt(handle)?.node;
|
|
805
|
+
const fn = declaration?.parent;
|
|
806
|
+
const index = fn === undefined || !isFunctionLikeDeclaration(fn)
|
|
807
|
+
? -1
|
|
808
|
+
: (fn.typeParameters ?? []).findIndex((one) => one === declaration);
|
|
809
|
+
const name = fn === undefined
|
|
810
|
+
? undefined
|
|
811
|
+
: isMethodDeclaration(fn) && isIdentifier(fn.name)
|
|
812
|
+
? fn.name.text
|
|
813
|
+
: nestedName(fn);
|
|
814
|
+
if (symbol === undefined || fn === undefined || index < 0 || name === undefined) {
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
for (const site of byName.get(name) ?? []) {
|
|
818
|
+
const resolved = must(queries.resolvedSignature(site.call));
|
|
819
|
+
const declared = resolved === undefined ? undefined : must(queries.genericOf(resolved));
|
|
820
|
+
const called = declared?.declaration === undefined
|
|
821
|
+
? undefined
|
|
822
|
+
: project.declarationAt(declared.declaration)?.node;
|
|
823
|
+
if (resolved === undefined || declared === undefined || called !== fn) {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
const written = site.call.typeArguments?.[index];
|
|
827
|
+
const given = written === undefined
|
|
828
|
+
? site.call.arguments.flatMap((_argument, at) => {
|
|
829
|
+
const from = must(queries.parameterType(declared, at));
|
|
830
|
+
const into = must(queries.parameterType(resolved, at));
|
|
831
|
+
return from === undefined || into === undefined
|
|
832
|
+
? []
|
|
833
|
+
: bound(project, from, into, parameter, 0);
|
|
834
|
+
})
|
|
835
|
+
: typesAt(project, [written]).filter((type) => type !== undefined);
|
|
836
|
+
for (const type of given) {
|
|
837
|
+
const reached = reach.reachedFrom(type);
|
|
838
|
+
if (reached.classes.size > 0 || reached.members.length > 0) {
|
|
839
|
+
found.push({
|
|
840
|
+
site: site.call,
|
|
841
|
+
classes: reached.classes,
|
|
842
|
+
members: reached.members,
|
|
843
|
+
to,
|
|
844
|
+
detail: `passed to ${to.callee} through the type parameter ${symbol.name}`,
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
pending.push(...typeParametersOf(project, type, 0).map((one) => ({ parameter: one, to })));
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return found;
|
|
569
852
|
}
|
|
570
853
|
/**
|
|
571
854
|
* The serialization-contract detector: each data member and `toJSON` of each class,
|
|
@@ -588,6 +871,10 @@ export function serializationContract(input) {
|
|
|
588
871
|
const holdsBack = (flags, name, to) => (flags & SymbolFlags.Property) !== 0 ||
|
|
589
872
|
((flags & CONVERSION_MEMBERS) !== 0 &&
|
|
590
873
|
(name === SERIALIZED_CONVERSION || (to.reach === "conversions" && CONVERSIONS.has(name))));
|
|
874
|
+
// The receiver reads only what the program wrote, so a data member holds while written.
|
|
875
|
+
const evidence = (id, flags, detail, site) => (flags & SymbolFlags.Property) !== 0 && !walked.initialized.has(id)
|
|
876
|
+
? { id, detail, site, whileWritten: true }
|
|
877
|
+
: { id, detail, site };
|
|
591
878
|
const found = [];
|
|
592
879
|
for (const flow of flows) {
|
|
593
880
|
const site = renderPosition(flow.site.getSourceFile(), targetRoot, flow.site.getStart());
|
|
@@ -596,16 +883,308 @@ export function serializationContract(input) {
|
|
|
596
883
|
const symbol = byId.get(member.id);
|
|
597
884
|
const name = symbol === undefined ? "" : (memberComponent(symbol, byId) ?? "");
|
|
598
885
|
if (holdsBack(member.flags, name, flow.to)) {
|
|
599
|
-
found.push(
|
|
886
|
+
found.push(evidence(member.id, member.flags, detail, site));
|
|
600
887
|
}
|
|
601
888
|
}
|
|
602
889
|
for (const member of flow.members) {
|
|
603
890
|
if (holdsBack(member.flags, member.name, flow.to)) {
|
|
604
891
|
for (const id of chains.declarationsOf(member)) {
|
|
605
|
-
found.push(
|
|
892
|
+
found.push(evidence(id, member.flags, detail, site));
|
|
606
893
|
}
|
|
607
894
|
}
|
|
608
895
|
}
|
|
609
896
|
}
|
|
610
897
|
return found;
|
|
611
898
|
}
|
|
899
|
+
/**
|
|
900
|
+
* The data members the decodes and copies of `files` fill, as {@link Decoded} names them.
|
|
901
|
+
* A generic declared outside the analysis, or only ambiently, or one that reads a value
|
|
902
|
+
* {@link readTypes} finds untyped as its own type parameter, fills the type that a type
|
|
903
|
+
* parameter of its own no parameter names binds, where the awaited result carries that
|
|
904
|
+
* type through its data members rather than only naming it, as a store's methods do: no
|
|
905
|
+
* argument hands the value over, so it fills it where the analysis cannot see, and may
|
|
906
|
+
* read it. Where the checker leaves a question unanswered, every data member is `unanswered`.
|
|
907
|
+
*/
|
|
908
|
+
export function decodedMembers(project, held, files, consumers) {
|
|
909
|
+
try {
|
|
910
|
+
return { ...decodedOf(project, held, files, consumers), unanswered: [] };
|
|
911
|
+
}
|
|
912
|
+
catch (error) {
|
|
913
|
+
if (!(error instanceof Unanswerable)) {
|
|
914
|
+
throw error;
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
filled: [],
|
|
918
|
+
parsed: [],
|
|
919
|
+
copied: [],
|
|
920
|
+
unanswered: held.symbols
|
|
921
|
+
.filter((symbol) => symbol.kind === "class-member" || symbol.kind === "type-member")
|
|
922
|
+
.map((symbol) => symbol.id),
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
function decodedOf(project, held, files, consumers) {
|
|
927
|
+
const { queries } = project;
|
|
928
|
+
const own = project.ownPaths();
|
|
929
|
+
const inAnalysis = (path) => own.has(path) || consumers.some((root) => path === root || path.startsWith(`${root}/`));
|
|
930
|
+
const calls = [];
|
|
931
|
+
const untyped = [];
|
|
932
|
+
const spreads = [];
|
|
933
|
+
const noteRead = (node) => {
|
|
934
|
+
const at = readAs(node);
|
|
935
|
+
if (at.tops.length > 0 || at.asserted.length > 0) {
|
|
936
|
+
untyped.push(node);
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
const visit = (node) => {
|
|
940
|
+
if (isCallExpression(node)) {
|
|
941
|
+
calls.push(node);
|
|
942
|
+
noteRead(node);
|
|
943
|
+
}
|
|
944
|
+
else if (isPropertyAccessExpression(node) || isElementAccessExpression(node)) {
|
|
945
|
+
noteRead(node);
|
|
946
|
+
}
|
|
947
|
+
else if (isSpreadAssignment(node)) {
|
|
948
|
+
spreads.push(node);
|
|
949
|
+
}
|
|
950
|
+
node.forEachChild(visit);
|
|
951
|
+
};
|
|
952
|
+
for (const file of files) {
|
|
953
|
+
file.forEachChild(visit);
|
|
954
|
+
}
|
|
955
|
+
const read = readTypes(project, untyped);
|
|
956
|
+
// A generic of the analysis that reads an untyped value as its own type parameter is a
|
|
957
|
+
// client of what made the value, and fills what that parameter binds as an outside one does.
|
|
958
|
+
const clients = new Set(read.made.flatMap((type) => (type.isTypeParameter() ? [type.id] : [])));
|
|
959
|
+
const filled = [];
|
|
960
|
+
const resolved = queries.resolvedSignatures(calls).map(must);
|
|
961
|
+
for (const signature of resolved) {
|
|
962
|
+
const declared = signature?.target === undefined ? undefined : must(queries.genericOf(signature));
|
|
963
|
+
const declaration = declared?.declaration;
|
|
964
|
+
const generic = declared?.typeParameters ?? [];
|
|
965
|
+
if (signature === undefined || declared === undefined || generic.length === 0) {
|
|
966
|
+
continue;
|
|
967
|
+
}
|
|
968
|
+
const node = declaration === undefined ? undefined : project.declarationAt(declaration)?.node;
|
|
969
|
+
if (declaration === undefined ||
|
|
970
|
+
(inAnalysis(declaration.path) &&
|
|
971
|
+
(node === undefined || (node.flags & NodeFlags.Ambient) === 0) &&
|
|
972
|
+
!generic.some((id) => clients.has(id)))) {
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
const result = must(queries.returnTypeOf(declared));
|
|
976
|
+
const given = must(queries.returnTypeOf(signature));
|
|
977
|
+
if (result === undefined || given === undefined) {
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
const passed = new Set(declared.parameters.flatMap((_parameter, at) => {
|
|
981
|
+
const type = must(queries.parameterType(declared, at));
|
|
982
|
+
return type === undefined ? [] : namedTypeParameters(project, type, 0);
|
|
983
|
+
}));
|
|
984
|
+
const types = typeParametersOf(project, result, 0)
|
|
985
|
+
.filter((parameter) => generic.includes(parameter.id) && !passed.has(parameter.id))
|
|
986
|
+
.flatMap((parameter) => bound(project, result, given, parameter, 0));
|
|
987
|
+
if (types.length > 0) {
|
|
988
|
+
filled.push({ carried: awaited(project, given), types });
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
const copies = copiedNames(project, spreads, calls);
|
|
992
|
+
if (filled.length === 0 &&
|
|
993
|
+
read.parsed.length === 0 &&
|
|
994
|
+
read.made.length === 0 &&
|
|
995
|
+
copies.length === 0) {
|
|
996
|
+
return { filled: [], parsed: [], copied: [] };
|
|
997
|
+
}
|
|
998
|
+
const reach = valueReach(project, held);
|
|
999
|
+
const chains = aliasChains(project, held);
|
|
1000
|
+
const members = (type) => {
|
|
1001
|
+
const reached = reach.reachedFrom(type);
|
|
1002
|
+
const ofClasses = classMembers(project, held, reached.classes, "instance");
|
|
1003
|
+
return [
|
|
1004
|
+
...[...reached.classes].flatMap((id) => (ofClasses.get(id) ?? []).flatMap((member) => (member.flags & SymbolFlags.Property) !== 0 ? [member.id] : [])),
|
|
1005
|
+
...reached.members.flatMap((member) => (member.flags & SymbolFlags.Property) !== 0 ? chains.declarationsOf(member) : []),
|
|
1006
|
+
];
|
|
1007
|
+
};
|
|
1008
|
+
return {
|
|
1009
|
+
filled: [
|
|
1010
|
+
...new Set([
|
|
1011
|
+
...filled.flatMap(({ carried, types }) => {
|
|
1012
|
+
const carries = new Set(members(carried));
|
|
1013
|
+
return types.flatMap((type) => members(type).filter((id) => carries.has(id)));
|
|
1014
|
+
}),
|
|
1015
|
+
...read.made.flatMap(members),
|
|
1016
|
+
]),
|
|
1017
|
+
],
|
|
1018
|
+
parsed: [...new Set(read.parsed.flatMap(members))],
|
|
1019
|
+
copied: [
|
|
1020
|
+
...new Set(copies.flatMap(({ into, names }) => must(queries.constituents(into)).flatMap((part) => must(queries.propertiesOf(part)).flatMap((property) => (property.flags & SymbolFlags.Property) !== 0 && names.has(property.name)
|
|
1021
|
+
? chains.declarationsOf(property)
|
|
1022
|
+
: [])))),
|
|
1023
|
+
],
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
/**
|
|
1027
|
+
* Where one value is read as a type: each `as` and angle-bracket assertion around it,
|
|
1028
|
+
* through parentheses, `await` and `!`, and the outermost expression unless a statement
|
|
1029
|
+
* of its own or the object or callee of a further access holds it. A `satisfies` check
|
|
1030
|
+
* is stepped through and types nothing, its expression's type being its operand's. A
|
|
1031
|
+
* callback's result is read as well where the call taking the callback is, which may
|
|
1032
|
+
* hand it on inside its result, as `map` does.
|
|
1033
|
+
*/
|
|
1034
|
+
function readAs(value, depth = 0) {
|
|
1035
|
+
const asserted = [];
|
|
1036
|
+
let at = value;
|
|
1037
|
+
for (;;) {
|
|
1038
|
+
const parent = at.parent;
|
|
1039
|
+
if (isAsExpression(parent) || isTypeAssertion(parent)) {
|
|
1040
|
+
asserted.push(parent.type);
|
|
1041
|
+
}
|
|
1042
|
+
else if (!isParenthesizedExpression(parent) &&
|
|
1043
|
+
!isAwaitExpression(parent) &&
|
|
1044
|
+
!isNonNullExpression(parent) &&
|
|
1045
|
+
!isSatisfiesExpression(parent)) {
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
at = parent;
|
|
1049
|
+
}
|
|
1050
|
+
const parent = at.parent;
|
|
1051
|
+
const unused = isExpressionStatement(parent) ||
|
|
1052
|
+
((isPropertyAccessExpression(parent) ||
|
|
1053
|
+
isElementAccessExpression(parent) ||
|
|
1054
|
+
isCallExpression(parent)) &&
|
|
1055
|
+
parent.expression === at);
|
|
1056
|
+
const call = depth < MENTION_DEPTH ? callTaking(at) : undefined;
|
|
1057
|
+
const outer = call === undefined ? undefined : readAs(call, depth + 1);
|
|
1058
|
+
return {
|
|
1059
|
+
asserted: [...asserted, ...(outer?.asserted ?? [])],
|
|
1060
|
+
tops: [...(unused ? [] : [at]), ...(outer?.tops ?? [])],
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
/** The call one value is returned to as a callback's result, an argument of that call. */
|
|
1064
|
+
function callTaking(value) {
|
|
1065
|
+
const parent = value.parent;
|
|
1066
|
+
let callback;
|
|
1067
|
+
if (isArrowFunction(parent) && parent.body === value) {
|
|
1068
|
+
callback = parent;
|
|
1069
|
+
}
|
|
1070
|
+
else if (isReturnStatement(parent)) {
|
|
1071
|
+
let at = parent.parent;
|
|
1072
|
+
while (!isFunctionLikeDeclaration(at) && !isSourceFile(at)) {
|
|
1073
|
+
at = at.parent;
|
|
1074
|
+
}
|
|
1075
|
+
callback = isArrowFunction(at) || isFunctionExpression(at) ? at : undefined;
|
|
1076
|
+
}
|
|
1077
|
+
let at = callback?.parent;
|
|
1078
|
+
while (at !== undefined && isParenthesizedExpression(at)) {
|
|
1079
|
+
at = at.parent;
|
|
1080
|
+
}
|
|
1081
|
+
return at !== undefined && isCallExpression(at) && at.expression !== callback ? at : undefined;
|
|
1082
|
+
}
|
|
1083
|
+
/** The global the standard library declares one name of `JSON` or `Object` as. */
|
|
1084
|
+
function standardGlobal(callee, global, name) {
|
|
1085
|
+
return isPropertyAccessExpression(callee) &&
|
|
1086
|
+
isIdentifier(callee.expression) &&
|
|
1087
|
+
callee.expression.text === global &&
|
|
1088
|
+
callee.name.text === name
|
|
1089
|
+
? callee.expression
|
|
1090
|
+
: undefined;
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* The types each value of `values` typed `any` or `unknown` is read as ({@link readAs}):
|
|
1094
|
+
* `parsed` for a `JSON.parse` call of the standard library, `made` for any other call or
|
|
1095
|
+
* property, whose maker the analysis cannot see. `JSON` must be the standard library's
|
|
1096
|
+
* global.
|
|
1097
|
+
*/
|
|
1098
|
+
function readTypes(project, values) {
|
|
1099
|
+
if (values.length === 0) {
|
|
1100
|
+
return { parsed: [], made: [] };
|
|
1101
|
+
}
|
|
1102
|
+
const own = project.ownPaths();
|
|
1103
|
+
const parses = values.map((value) => isCallExpression(value) ? standardGlobal(value.expression, "JSON", "parse") : undefined);
|
|
1104
|
+
const globals = parses.flatMap((global) => (global === undefined ? [] : [global]));
|
|
1105
|
+
const standard = new Set(project
|
|
1106
|
+
.symbolsAt(globals.map((global) => project.handle(global)))
|
|
1107
|
+
.map(must)
|
|
1108
|
+
.flatMap((symbol, at) => symbol?.declarations.every((handle) => !own.has(handle.path)) === true ? [globals[at]] : []));
|
|
1109
|
+
// An `await` around the value reads the value it resolves to.
|
|
1110
|
+
const valueTypes = typesAt(project, values.map((value) => (isAwaitExpression(value.parent) ? value.parent : value)));
|
|
1111
|
+
const sites = values.flatMap((value, at) => {
|
|
1112
|
+
const parse = parses[at];
|
|
1113
|
+
const parsed = parse !== undefined && standard.has(parse);
|
|
1114
|
+
const type = valueTypes[at];
|
|
1115
|
+
const untyped = type !== undefined && (type.flags & (TypeFlags.Any | TypeFlags.Unknown)) !== 0;
|
|
1116
|
+
return parsed || untyped ? [{ ...readAs(value), parsed }] : [];
|
|
1117
|
+
});
|
|
1118
|
+
// Each type node and each outermost expression, with whether a `JSON.parse` call is read as it.
|
|
1119
|
+
const typed = sites.flatMap((site) => site.asserted.map((node) => ({ node, parsed: site.parsed })));
|
|
1120
|
+
const placed = sites.flatMap((site) => site.tops.map((node) => ({ node, parsed: site.parsed })));
|
|
1121
|
+
const read = [
|
|
1122
|
+
...typesAt(project, typed.map((one) => one.node)).map((type, at) => ({ type, parsed: typed[at]?.parsed })),
|
|
1123
|
+
...project.queries
|
|
1124
|
+
.contextualTypes(placed.map((one) => one.node))
|
|
1125
|
+
.map(must)
|
|
1126
|
+
.map((type, at) => ({ type, parsed: placed[at]?.parsed })),
|
|
1127
|
+
];
|
|
1128
|
+
const typesWhere = (parsed) => read.flatMap((one) => (one.type !== undefined && one.parsed === parsed ? [one.type] : []));
|
|
1129
|
+
return { parsed: typesWhere(true), made: typesWhere(false) };
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* The copies the spreads and calls of `files` make: for each spread in an object literal,
|
|
1133
|
+
* the type the literal is read as, and for each `Object.assign` call of the standard
|
|
1134
|
+
* library, its first argument's type, with the member names of the copied values' types.
|
|
1135
|
+
*/
|
|
1136
|
+
function copiedNames(project, spreads, calls) {
|
|
1137
|
+
const own = project.ownPaths();
|
|
1138
|
+
const assigns = calls.flatMap((call) => {
|
|
1139
|
+
const global = standardGlobal(call.expression, "Object", "assign");
|
|
1140
|
+
const [target, ...sources] = call.arguments;
|
|
1141
|
+
return global === undefined || target === undefined || sources.length === 0
|
|
1142
|
+
? []
|
|
1143
|
+
: [{ global, target, sources: sources.filter((source) => !isSpreadElement(source)) }];
|
|
1144
|
+
});
|
|
1145
|
+
const standard = project
|
|
1146
|
+
.symbolsAt(assigns.map((one) => project.handle(one.global)))
|
|
1147
|
+
.map(must)
|
|
1148
|
+
.map((symbol) => symbol?.declarations.every((handle) => !own.has(handle.path)) === true);
|
|
1149
|
+
const assigned = assigns.filter((_one, at) => standard[at] === true);
|
|
1150
|
+
const literals = spreads.flatMap((spread) => isObjectLiteralExpression(spread.parent)
|
|
1151
|
+
? [{ literal: spread.parent, source: spread.expression }]
|
|
1152
|
+
: []);
|
|
1153
|
+
const contexts = project.queries.contextualTypes(literals.map((one) => one.literal)).map(must);
|
|
1154
|
+
const targets = typesAt(project, assigned.map((one) => one.target));
|
|
1155
|
+
const spreadTypes = typesAt(project, literals.map((one) => one.source));
|
|
1156
|
+
const sourceTypes = assigned.map((one) => typesAt(project, one.sources));
|
|
1157
|
+
const namesOf = (types) => new Set(types.flatMap((type) => type === undefined
|
|
1158
|
+
? []
|
|
1159
|
+
: must(project.queries.propertiesOf(type)).map((property) => property.name)));
|
|
1160
|
+
const found = [];
|
|
1161
|
+
const add = (into, names) => {
|
|
1162
|
+
if (into !== undefined && names.size > 0) {
|
|
1163
|
+
found.push({ into, names });
|
|
1164
|
+
}
|
|
1165
|
+
};
|
|
1166
|
+
contexts.forEach((into, at) => {
|
|
1167
|
+
add(into, namesOf([spreadTypes[at]]));
|
|
1168
|
+
});
|
|
1169
|
+
targets.forEach((into, at) => {
|
|
1170
|
+
add(into, namesOf(sourceTypes[at] ?? []));
|
|
1171
|
+
});
|
|
1172
|
+
return found;
|
|
1173
|
+
}
|
|
1174
|
+
/** The value a promise of the standard library resolves to, through nested promises. */
|
|
1175
|
+
function awaited(project, type) {
|
|
1176
|
+
const { queries } = project;
|
|
1177
|
+
let at = type;
|
|
1178
|
+
for (let depth = 0; depth < MENTION_DEPTH; depth += 1) {
|
|
1179
|
+
const symbol = must(queries.symbolOfType(at));
|
|
1180
|
+
const [inner] = must(queries.typeArguments(at));
|
|
1181
|
+
const standard = symbol !== undefined &&
|
|
1182
|
+
PROMISES.has(symbol.name) &&
|
|
1183
|
+
symbol.declarations.every((handle) => project.program.getSourceFileMetadata(handle.path)?.isDefaultLibrary === true);
|
|
1184
|
+
if (!standard || inner === undefined) {
|
|
1185
|
+
break;
|
|
1186
|
+
}
|
|
1187
|
+
at = inner;
|
|
1188
|
+
}
|
|
1189
|
+
return at;
|
|
1190
|
+
}
|