@cplieger/deadset-ts 7.2.0-dev.4 → 8.0.0-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +24 -5
- package/dist/src/analysis.js +245 -309
- package/dist/src/augmentations.js +124 -18
- package/dist/src/automatic-types.js +170 -0
- package/dist/src/build-scripts.js +425 -0
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +9 -9
- package/dist/src/config.js +6 -81
- package/dist/src/configuration-files.js +78 -188
- package/dist/src/configured-declarations.js +15 -20
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/consumers.js +3 -30
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/contract/config.generated.js +558 -0
- package/dist/src/contract/contract.generated.js +1244 -0
- package/dist/src/contract/report.generated.js +862 -0
- package/dist/src/convention-rows.js +146 -1
- package/dist/src/conventions.js +136 -70
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +280 -62
- package/dist/src/edges.js +38 -122
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +9 -18
- package/dist/src/entry-points.js +106 -21
- 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 -40
- package/dist/src/failure.js +36 -0
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +225 -63
- package/dist/src/findings/dependencies-and-module-machinery.js +72 -27
- package/dist/src/findings/emitters.js +6 -6
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +53 -19
- package/dist/src/findings/non-code-artifacts.js +71 -39
- package/dist/src/findings/reads-and-writes.js +16 -13
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +57 -28
- package/dist/src/findings/visibility-narrowing.js +40 -18
- package/dist/src/findings-pass.js +8 -15
- package/dist/src/framework-lifecycle.js +7 -7
- package/dist/src/fs/document.js +52 -0
- package/dist/src/fs/manifest.js +72 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/generated-file.js +18 -4
- package/dist/src/git-ignore.js +99 -0
- package/dist/src/graph.js +9 -4
- package/dist/src/html-entries.js +6 -33
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/injection-container.js +1 -1
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +341 -69
- package/dist/src/inventory.js +54 -29
- package/dist/src/javascript-file.js +33 -0
- package/dist/src/json-document.js +198 -67
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +3 -366
- package/dist/src/load/classify.js +270 -0
- package/dist/src/load/diagnostics.js +80 -0
- package/dist/src/load/discover.js +277 -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/plan.js +313 -0
- package/dist/src/load/specifiers.js +115 -0
- package/dist/src/load/synthesize.js +140 -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} +88 -61
- package/dist/src/{workspace-resolution.js → load/workspace-resolution.js} +60 -59
- 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/manifest.js +49 -54
- package/dist/src/matrix.js +94 -13
- package/dist/src/mdx-entries.js +174 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/outside-tests.js +1 -1
- package/dist/src/overrides.js +41 -10
- package/dist/src/partial.js +2 -1
- package/dist/src/paths.js +26 -15
- package/dist/src/position.js +2 -1
- package/dist/src/print.js +11 -78
- package/dist/src/product.js +13 -0
- package/dist/src/query.js +1 -0
- package/dist/src/ref.js +11 -0
- package/dist/src/references.js +52 -31
- package/dist/src/report-schema.js +51 -0
- package/dist/src/report.js +59 -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 +245 -727
- package/dist/src/roots.js +374 -116
- package/dist/src/run.js +66 -77
- package/dist/src/schema.js +320 -342
- package/dist/src/scope.js +38 -115
- package/dist/src/serialization-contract.js +649 -29
- package/dist/src/session.js +41 -29
- package/dist/src/source-files.js +40 -15
- package/dist/src/suppress-file.js +34 -29
- package/dist/src/suppress-inline.js +21 -4
- package/dist/src/suppress.js +40 -56
- package/dist/src/sweep.js +32 -17
- package/dist/src/symbol-keys.js +105 -6
- package/dist/src/template-field.js +9 -19
- package/dist/src/test-support.js +119 -18
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/named-files.js +54 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/text/yaml.js +393 -0
- package/dist/src/type-errors.js +27 -221
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/unseen-loads.js +100 -0
- package/dist/src/unwrap.js +23 -0
- package/dist/src/usage.js +10 -0
- package/dist/src/verbs/analyze.js +181 -125
- package/dist/src/verbs/describe.js +9 -16
- package/dist/src/verbs/explain.js +14 -9
- 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 +4 -2
- 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/dist/src/workflow-steps.js +128 -33
- package/jsr.json +1 -1
- package/package.json +5 -4
- package/src/analysis.ts +333 -457
- package/src/augmentations.ts +152 -23
- package/src/automatic-types.ts +228 -0
- package/src/build-scripts.ts +543 -0
- package/src/component-layer.ts +4 -2
- package/src/components.ts +9 -10
- package/src/config.ts +31 -299
- package/src/configuration-files.ts +100 -191
- package/src/configured-declarations.ts +18 -23
- package/src/conformance-record.ts +2 -2
- package/src/consumers.ts +6 -57
- package/src/contextual-uses.ts +64 -12
- package/src/contract/config.generated.ts +818 -0
- package/src/contract/contract.generated.ts +1308 -0
- package/src/contract/report.generated.ts +1237 -0
- package/src/convention-rows.ts +186 -1
- package/src/conventions.ts +166 -90
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +356 -78
- package/src/edges.ts +41 -140
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +11 -20
- package/src/entry-points.ts +124 -28
- 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 +36 -50
- package/src/failure.ts +63 -0
- package/src/file-facts.ts +9 -24
- package/src/finding.ts +24 -9
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +297 -77
- package/src/findings/dependencies-and-module-machinery.ts +90 -31
- package/src/findings/emitter.ts +8 -9
- package/src/findings/emitters.ts +8 -8
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +64 -20
- package/src/findings/non-code-artifacts.ts +88 -50
- package/src/findings/reads-and-writes.ts +20 -16
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +65 -28
- package/src/findings/visibility-narrowing.ts +51 -19
- package/src/findings-pass.ts +10 -19
- package/src/framework-lifecycle.ts +7 -7
- package/src/fs/document.ts +70 -0
- package/src/fs/manifest.ts +92 -0
- package/src/fs/tree.ts +110 -0
- package/src/generated-file.ts +23 -4
- package/src/git-ignore.ts +122 -0
- package/src/graph.ts +11 -3
- package/src/host.ts +17 -14
- package/src/html-entries.ts +6 -33
- package/src/import-bindings.ts +128 -0
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/indexed-access.ts +152 -30
- package/src/injection-container.ts +1 -1
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +449 -85
- package/src/inventory.ts +73 -27
- package/src/javascript-file.ts +37 -0
- package/src/json-document.ts +241 -76
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +5 -413
- package/src/load/classify.ts +397 -0
- package/src/load/diagnostics.ts +144 -0
- package/src/load/discover.ts +455 -0
- package/src/{emit-map.ts → load/emit-map.ts} +1 -1
- package/src/load/expected-module.ts +188 -0
- package/src/load/load.ts +73 -0
- package/src/load/module-lookup.ts +50 -0
- package/src/load/plan.ts +504 -0
- package/src/load/specifiers.ts +168 -0
- package/src/load/synthesize.ts +211 -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} +127 -80
- package/src/{workspace-resolution.ts → load/workspace-resolution.ts} +73 -64
- package/src/{workspace-run.ts → load/workspace-run.ts} +18 -16
- package/src/{workspace.ts → load/workspace.ts} +90 -134
- package/src/manifest.ts +81 -75
- package/src/matrix.ts +154 -18
- package/src/mdx-entries.ts +218 -0
- package/src/module-declarations.ts +11 -13
- package/src/outside-tests.ts +1 -1
- package/src/overrides.ts +51 -12
- package/src/partial.ts +3 -2
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +12 -91
- package/src/product.ts +18 -0
- package/src/query.ts +11 -0
- package/src/ref.ts +13 -0
- package/src/references.ts +62 -32
- package/src/report-schema.ts +68 -0
- package/src/report.ts +105 -215
- 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 +283 -1111
- package/src/roots.ts +490 -137
- package/src/run.ts +75 -91
- package/src/schema.ts +391 -387
- package/src/scope.ts +46 -138
- package/src/serialization-contract.ts +818 -26
- package/src/session.ts +77 -54
- package/src/source-files.ts +67 -17
- package/src/suppress-file.ts +36 -34
- package/src/suppress-inline.ts +20 -3
- package/src/suppress.ts +52 -63
- package/src/sweep.ts +53 -18
- package/src/symbol-keys.ts +131 -7
- package/src/template-field.ts +15 -25
- package/src/test-support.ts +155 -18
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/named-files.inputs.json +1522 -0
- package/src/text/named-files.ts +68 -0
- package/src/text/shell.ts +161 -0
- package/src/text/yaml.ts +423 -0
- package/src/type-errors.ts +35 -273
- package/src/type-parameter-reads.ts +227 -0
- package/src/unseen-loads.ts +134 -0
- package/src/unwrap.ts +34 -0
- package/src/usage.ts +10 -0
- package/src/verbs/analyze.ts +245 -161
- package/src/verbs/describe.ts +14 -15
- package/src/verbs/explain.ts +15 -13
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +6 -4
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +2 -8
- package/src/verbs/version.ts +15 -3
- package/src/workflow-steps.ts +149 -34
- package/dist/src/discover.js +0 -347
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/setup-failure.js +0 -23
- package/dist/src/test-joins.js +0 -101
- package/dist/src/version.js +0 -7
- package/src/discover.ts +0 -510
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/setup-failure.ts +0 -39
- package/src/test-joins.ts +0 -120
- package/src/version.ts +0 -7
package/dist/src/references.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { isArrayLiteralExpression, isAsExpression, isBinaryExpression, isCallExpression, isDecorator, isDeleteExpression, isElementAccessExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isForInStatement, isForOfStatement, isForStatement, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isMetaProperty, isNewExpression, isNamespaceExport, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isVoidExpression, SyntaxKind, } from "@typescript/native/ast";
|
|
1
|
+
import { isArrayLiteralExpression, isAsExpression, isBinaryExpression, isCallExpression, isDecorator, isDeleteExpression, isElementAccessExpression, isExportDeclaration, isExpressionStatement, isExternalModuleReference, isForInStatement, isForOfStatement, isForStatement, isIdentifier, isImportDeclaration, isImportEqualsDeclaration, isInterfaceDeclaration, isMetaProperty, isModuleBlock, isModuleDeclaration, isNewExpression, isNamespaceExport, isNoSubstitutionTemplateLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadAssignment, isSpreadElement, isStringLiteral, isVoidExpression, SyntaxKind, } from "@typescript/native/ast";
|
|
2
|
+
import { parenthesesRemoved } from "./unwrap.js";
|
|
2
3
|
import { aliasChains } from "./alias-chain.js";
|
|
3
4
|
import { contextualUses } from "./contextual-uses.js";
|
|
4
5
|
import { destructuring, propertyReadsOf } from "./destructuring.js";
|
|
5
6
|
import { specifierCandidates } from "./configuration-files.js";
|
|
6
7
|
import { namespaceUses } from "./namespace-objects.js";
|
|
7
|
-
import { globExpression } from "./glob.js";
|
|
8
8
|
import { declarationsByName, isParameterProperty, nodeKey, } from "./inventory.js";
|
|
9
|
-
import { augmentationReferences } from "./augmentations.js";
|
|
9
|
+
import { ambientGlobalInterfaces, augmentationReferences } from "./augmentations.js";
|
|
10
10
|
import { elementAccessReferences } from "./element-access.js";
|
|
11
11
|
import { indexedAccessReferences } from "./indexed-access.js";
|
|
12
12
|
import { keyofComparisonReferences } from "./keyof-comparisons.js";
|
|
@@ -16,9 +16,12 @@ import { overrideReferences } from "./overrides.js";
|
|
|
16
16
|
import { dirnamePath, queriedModulePath, relativePath, resolvePath } from "./paths.js";
|
|
17
17
|
import { byPosition, isComponentFile, renderPosition } from "./position.js";
|
|
18
18
|
import { DEFAULT_BATCH_CAP, propertyTables, UNANSWERED } from "./query.js";
|
|
19
|
+
import { typeParameterReads } from "./type-parameter-reads.js";
|
|
19
20
|
import { typeQueryAliases } from "./type-query-alias.js";
|
|
20
|
-
import { computedKeyNames, keyNameOf, keyedUses } from "./symbol-keys.js";
|
|
21
|
+
import { computedKeyNames, iterationReferences, keyNameOf, keyedUses, } from "./symbol-keys.js";
|
|
21
22
|
import { handedOn, valuesOf } from "./value-flow.js";
|
|
23
|
+
import { bytewise } from "./text/compare.js";
|
|
24
|
+
import { globExpression } from "./text/glob.js";
|
|
22
25
|
/**
|
|
23
26
|
* The operators whose left side one assignment stores into. A compound assignment is
|
|
24
27
|
* among them: where its value is discarded it reads its target only to write the result
|
|
@@ -76,14 +79,6 @@ function valueDiscarded(node) {
|
|
|
76
79
|
function isName(node) {
|
|
77
80
|
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier;
|
|
78
81
|
}
|
|
79
|
-
/** The expression one node is, through however many parentheses are written around it. */
|
|
80
|
-
function unparenthesized(node) {
|
|
81
|
-
let held = node;
|
|
82
|
-
while (isParenthesizedExpression(held)) {
|
|
83
|
-
held = held.expression;
|
|
84
|
-
}
|
|
85
|
-
return held;
|
|
86
|
-
}
|
|
87
82
|
/**
|
|
88
83
|
* Records the name one store writes into.
|
|
89
84
|
*
|
|
@@ -93,7 +88,7 @@ function unparenthesized(node) {
|
|
|
93
88
|
* target writes each name it binds, through its spreads and nested patterns.
|
|
94
89
|
*/
|
|
95
90
|
function markWrite(target, writes, accesses) {
|
|
96
|
-
const held =
|
|
91
|
+
const held = parenthesesRemoved(target);
|
|
97
92
|
if (isName(held)) {
|
|
98
93
|
writes.add(held.pos);
|
|
99
94
|
return;
|
|
@@ -108,7 +103,7 @@ function markWrite(target, writes, accesses) {
|
|
|
108
103
|
accesses.add(held);
|
|
109
104
|
// A store through a member's value lands in storage every holder of the value
|
|
110
105
|
// reads, so it reads the member; a variable only ever stored into stays written.
|
|
111
|
-
if (!isPropertyAccessExpression(
|
|
106
|
+
if (!isPropertyAccessExpression(parenthesesRemoved(held.expression))) {
|
|
112
107
|
markWrite(held.expression, writes, accesses);
|
|
113
108
|
}
|
|
114
109
|
return;
|
|
@@ -180,7 +175,7 @@ function markStores(node, writes, reads, accesses) {
|
|
|
180
175
|
}
|
|
181
176
|
/** Whether one node is `import.meta.url`. */
|
|
182
177
|
function isImportMetaURL(node) {
|
|
183
|
-
const at =
|
|
178
|
+
const at = parenthesesRemoved(node);
|
|
184
179
|
return (isPropertyAccessExpression(at) &&
|
|
185
180
|
isMetaProperty(at.expression) &&
|
|
186
181
|
at.expression.keywordToken === SyntaxKind.ImportKeyword &&
|
|
@@ -189,7 +184,7 @@ function isImportMetaURL(node) {
|
|
|
189
184
|
/** The module URL one node builds, where it builds one. */
|
|
190
185
|
function moduleURLOf(node) {
|
|
191
186
|
const relative = (argument) => {
|
|
192
|
-
const at = argument === undefined ? undefined :
|
|
187
|
+
const at = argument === undefined ? undefined : parenthesesRemoved(argument);
|
|
193
188
|
return at !== undefined &&
|
|
194
189
|
(isStringLiteral(at) || isNoSubstitutionTemplateLiteral(at)) &&
|
|
195
190
|
/^\.\.?\//u.test(at.text)
|
|
@@ -204,7 +199,7 @@ function moduleURLOf(node) {
|
|
|
204
199
|
: undefined;
|
|
205
200
|
}
|
|
206
201
|
if (isCallExpression(node)) {
|
|
207
|
-
const callee =
|
|
202
|
+
const callee = parenthesesRemoved(node.expression);
|
|
208
203
|
const specifier = relative(node.arguments[0]);
|
|
209
204
|
if (specifier !== undefined &&
|
|
210
205
|
isPropertyAccessExpression(callee) &&
|
|
@@ -489,7 +484,7 @@ export function testFileRulesOf(testFiles, paths, supportFiles = []) {
|
|
|
489
484
|
};
|
|
490
485
|
}),
|
|
491
486
|
...(support === 0 ? [] : [{ rule: TEST_SUPPORT_RULE, matched: support }]),
|
|
492
|
-
].sort((a, b) =>
|
|
487
|
+
].sort((a, b) => bytewise(a.rule, b.rule));
|
|
493
488
|
}
|
|
494
489
|
const importedCache = new WeakMap();
|
|
495
490
|
/**
|
|
@@ -608,20 +603,13 @@ function aloneIn(file, uses, writes, found) {
|
|
|
608
603
|
.filter(([, nodes]) => nodes.some((node) => inside(node, write.value)) &&
|
|
609
604
|
nodes.every((node) => sameTarget.some((one) => inside(node, one.value))))
|
|
610
605
|
.map(([name]) => name)
|
|
611
|
-
.sort(
|
|
606
|
+
.sort(bytewise);
|
|
612
607
|
if (names.length > 0) {
|
|
613
608
|
result.push({ index: write.index, names });
|
|
614
609
|
}
|
|
615
610
|
}
|
|
616
611
|
return result;
|
|
617
612
|
}
|
|
618
|
-
/** Two strings ordered bytewise, which is the order every set of a run is read in. */
|
|
619
|
-
function compare(a, b) {
|
|
620
|
-
if (a === b) {
|
|
621
|
-
return 0;
|
|
622
|
-
}
|
|
623
|
-
return a < b ? -1 : 1;
|
|
624
|
-
}
|
|
625
613
|
/**
|
|
626
614
|
* Every reference one project's own files make to the project's own declarations, in
|
|
627
615
|
* position order, and the files the test-file rules classified.
|
|
@@ -1134,6 +1122,37 @@ export function references(project, held, targetRoot, options) {
|
|
|
1134
1122
|
}
|
|
1135
1123
|
}
|
|
1136
1124
|
}
|
|
1125
|
+
/** The ambient global interfaces of the walked files that merge into a declaration outside the project's own files. */
|
|
1126
|
+
const libraryMerged = (scripts) => {
|
|
1127
|
+
const names = new Map();
|
|
1128
|
+
const visit = (node) => {
|
|
1129
|
+
if (isInterfaceDeclaration(node)) {
|
|
1130
|
+
const id = declaredAt(node);
|
|
1131
|
+
if (id !== undefined) {
|
|
1132
|
+
names.set(id, node.name);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
else if (isModuleDeclaration(node) || isModuleBlock(node)) {
|
|
1136
|
+
node.forEachChild(visit);
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
for (const { file } of walked) {
|
|
1140
|
+
file.forEachChild(visit);
|
|
1141
|
+
}
|
|
1142
|
+
const global = new Set(ambientGlobalInterfaces(held, [...names.keys()], scripts));
|
|
1143
|
+
const asked = [...names].filter(([id]) => global.has(id));
|
|
1144
|
+
if (asked.length === 0) {
|
|
1145
|
+
return new Set();
|
|
1146
|
+
}
|
|
1147
|
+
const answers = project.symbolsAt(asked.map(([, name]) => project.handle(name)));
|
|
1148
|
+
return new Set(asked.flatMap(([id], at) => {
|
|
1149
|
+
const symbol = answers[at];
|
|
1150
|
+
return typeof symbol === "object" &&
|
|
1151
|
+
symbol.declarations.some((handle) => !ownPaths.has(handle.path))
|
|
1152
|
+
? [id]
|
|
1153
|
+
: [];
|
|
1154
|
+
}));
|
|
1155
|
+
};
|
|
1137
1156
|
const usesOf = new Map(walked.map((one) => [one.file, one.uses]));
|
|
1138
1157
|
for (const [file, writes] of valued) {
|
|
1139
1158
|
const uses = usesOf.get(file) ?? [];
|
|
@@ -1151,21 +1170,23 @@ export function references(project, held, targetRoot, options) {
|
|
|
1151
1170
|
found.push(...indexedAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1152
1171
|
found.push(...elementAccessReferences(project, held, chains, root, new Set(tests)));
|
|
1153
1172
|
found.push(...keyofComparisonReferences(project, held, chains, root, new Set(tests)));
|
|
1173
|
+
found.push(...iterationReferences(project, held, chains, root, new Set(tests)));
|
|
1174
|
+
found.push(...typeParameterReads(project, held, chains, root, new Set(tests)));
|
|
1154
1175
|
found.push(...unimplementedCalleeReads(project, held, chains, root, new Set(tests)));
|
|
1155
1176
|
found.push(...moduleDeclarationReferences(project, held, root, new Set(tests)));
|
|
1156
1177
|
typedLookups = (options.requestCount?.() ?? 0) - before;
|
|
1157
1178
|
}
|
|
1158
1179
|
const scripts = new Set(walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId));
|
|
1159
|
-
found.push(...augmentationReferences(held, found, scripts));
|
|
1180
|
+
found.push(...augmentationReferences(held, found, scripts, libraryMerged(scripts)));
|
|
1160
1181
|
found.sort((a, b) => byPosition(a.position, b.position) ||
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1182
|
+
bytewise(a.to, b.to) ||
|
|
1183
|
+
bytewise(a.from, b.from) ||
|
|
1184
|
+
bytewise(a.use, b.use));
|
|
1164
1185
|
return {
|
|
1165
1186
|
references: consumer === undefined
|
|
1166
1187
|
? found
|
|
1167
1188
|
: found.map((reference) => ({ ...reference, consumer: consumer.id })),
|
|
1168
|
-
testFilePaths: tests.sort(
|
|
1189
|
+
testFilePaths: tests.sort(bytewise),
|
|
1169
1190
|
cost: {
|
|
1170
1191
|
batched,
|
|
1171
1192
|
fileBatches,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Holds a report to `contract/report.schema.json` and `contract/finding.schema.json`
|
|
3
|
+
* before it is written, reading the schemas the generator copies from the Contract, so
|
|
4
|
+
* this analyzer never writes a document a reader refuses.
|
|
5
|
+
*/
|
|
6
|
+
import { REPORT_SCHEMAS } from "./contract/report.generated.js";
|
|
7
|
+
import { schemaRefusals } from "./schema.js";
|
|
8
|
+
const reportSchema = schemaRefusals(REPORT_SCHEMAS, "report.schema.json");
|
|
9
|
+
/**
|
|
10
|
+
* What the report schema refuses in one report document, none for an instance, and, as
|
|
11
|
+
* no schema keyword can state it, a components listing that is not one entry per
|
|
12
|
+
* component a finding or an edge evaluation's finding names.
|
|
13
|
+
*/
|
|
14
|
+
export function reportRefusals(document) {
|
|
15
|
+
const found = reportSchema(document);
|
|
16
|
+
return found.length > 0 ? found : listingRefusals(document);
|
|
17
|
+
}
|
|
18
|
+
/** The components listing held to the components the findings name, once each. */
|
|
19
|
+
function listingRefusals(report) {
|
|
20
|
+
if (report.components === undefined) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
const refuse = (pointer, reason) => [
|
|
24
|
+
{ pointer, keyword: "components", reason, member: "" },
|
|
25
|
+
];
|
|
26
|
+
const listed = new Set();
|
|
27
|
+
for (const [i, entry] of report.components.entries()) {
|
|
28
|
+
if (listed.has(entry.id)) {
|
|
29
|
+
return refuse(`/components/${String(i)}`, `the entry lists component ${entry.id} a second time`);
|
|
30
|
+
}
|
|
31
|
+
listed.add(entry.id);
|
|
32
|
+
}
|
|
33
|
+
const named = [
|
|
34
|
+
...report.findings.map((finding, i) => [`/findings/${String(i)}`, finding.component.id]),
|
|
35
|
+
...report.edge_evaluations.flatMap((one, i) => one.finding === undefined
|
|
36
|
+
? []
|
|
37
|
+
: [[`/edge_evaluations/${String(i)}/finding`, one.finding.component.id]]),
|
|
38
|
+
];
|
|
39
|
+
for (const [pointer, id] of named) {
|
|
40
|
+
if (!listed.has(id)) {
|
|
41
|
+
return refuse(pointer, `the finding names component ${id}, which the components listing lacks`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const ids = new Set(named.map(([, id]) => id));
|
|
45
|
+
for (const [i, entry] of report.components.entries()) {
|
|
46
|
+
if (!ids.has(entry.id)) {
|
|
47
|
+
return refuse(`/components/${String(i)}`, `the entry lists component ${entry.id}, which no finding names`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return [];
|
|
51
|
+
}
|
package/dist/src/report.js
CHANGED
|
@@ -3,20 +3,26 @@
|
|
|
3
3
|
* in the order its schema lists them, built from what one run answered. Every reporter
|
|
4
4
|
* renders this document, so no two renderings disagree about which findings exist.
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
import { SCHEMA_VERSION, SCHEMA_VERSIONS } from "./contract/contract.generated.js";
|
|
7
|
+
import { bytewise } from "./text/compare.js";
|
|
8
|
+
import { LANGUAGE, PRODUCT } from "./product.js";
|
|
9
|
+
/**
|
|
10
|
+
* Each component the findings name, once, with the members the first finding naming it
|
|
11
|
+
* carries, in the order the findings are: `findings` paired with their wire forms and
|
|
12
|
+
* sorted, then the edge evaluations' findings.
|
|
13
|
+
*/
|
|
14
|
+
function componentsOf(findings) {
|
|
15
|
+
const listed = new Map();
|
|
16
|
+
for (const [written, finding] of findings) {
|
|
17
|
+
const { members } = finding.component;
|
|
18
|
+
if (members !== undefined && !listed.has(written.component.id)) {
|
|
19
|
+
listed.set(written.component.id, {
|
|
20
|
+
id: written.component.id,
|
|
21
|
+
members: members.map(wireSymbol),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
18
24
|
}
|
|
19
|
-
return
|
|
25
|
+
return [...listed.values()];
|
|
20
26
|
}
|
|
21
27
|
function wirePosition(position) {
|
|
22
28
|
return {
|
|
@@ -85,9 +91,6 @@ export function wireFinding(finding) {
|
|
|
85
91
|
root: finding.component.root,
|
|
86
92
|
symbol_count: finding.component.symbolCount,
|
|
87
93
|
deletable_lines: finding.component.deletableLines,
|
|
88
|
-
...(finding.component.members === undefined
|
|
89
|
-
? {}
|
|
90
|
-
: { members: finding.component.members.map(wireSymbol) }),
|
|
91
94
|
},
|
|
92
95
|
retained_by: finding.retainedBy,
|
|
93
96
|
configurations: finding.configurations,
|
|
@@ -128,19 +131,19 @@ function wireStale(stale) {
|
|
|
128
131
|
}
|
|
129
132
|
/** The canonical key over two findings: path, line, column, code, then the symbol reference. */
|
|
130
133
|
function canonical(a, b) {
|
|
131
|
-
return (
|
|
134
|
+
return (bytewise(a.position.path, b.position.path) ||
|
|
132
135
|
a.position.line - b.position.line ||
|
|
133
136
|
a.position.column - b.position.column ||
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
bytewise(a.code, b.code) ||
|
|
138
|
+
bytewise(a.symbol.ref, b.symbol.ref));
|
|
136
139
|
}
|
|
137
140
|
/** The canonical key over two stale suppressions, each component from the record's own member. */
|
|
138
141
|
function canonicalStale(a, b) {
|
|
139
|
-
return (
|
|
142
|
+
return (bytewise(a.position.path, b.position.path) ||
|
|
140
143
|
a.position.line - b.position.line ||
|
|
141
144
|
a.position.column - b.position.column ||
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
bytewise(a.code, b.code) ||
|
|
146
|
+
bytewise(a.symbol, b.symbol));
|
|
144
147
|
}
|
|
145
148
|
/** One declared gap as a report carries it, its members in the schema's order. */
|
|
146
149
|
function wireGap(gap) {
|
|
@@ -153,9 +156,9 @@ function wireGap(gap) {
|
|
|
153
156
|
}
|
|
154
157
|
/** Edge evaluations by edge, then side, then their compact encodings. */
|
|
155
158
|
function byEdge(a, b) {
|
|
156
|
-
return (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
return (bytewise(a.edge, b.edge) ||
|
|
160
|
+
bytewise(a.side, b.side) ||
|
|
161
|
+
bytewise(JSON.stringify(a), JSON.stringify(b)));
|
|
159
162
|
}
|
|
160
163
|
/**
|
|
161
164
|
* The lines a reader deletes by acting on every finding: each distinct component a root
|
|
@@ -198,21 +201,25 @@ function totalsOf(findings, result) {
|
|
|
198
201
|
}
|
|
199
202
|
/** Builds the report of one run, every array in the order the Contract fixes for it. */
|
|
200
203
|
export function buildReport(input) {
|
|
201
|
-
const
|
|
204
|
+
const paired = input.result.findings
|
|
205
|
+
.map((finding) => [wireFinding(finding), finding])
|
|
206
|
+
.sort(([a], [b]) => canonical(a, b));
|
|
207
|
+
const findings = paired.map(([wire]) => wire);
|
|
208
|
+
const evaluated = input.result.edgeEvaluations.flatMap((one) => one.finding === undefined ? [] : [[wireFinding(one.finding), one.finding]]);
|
|
202
209
|
const unavailable = [...new Map(input.unavailable.map((one) => [one.id, one])).values()]
|
|
203
|
-
.sort((a, b) =>
|
|
210
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
204
211
|
.map((one) => ({ id: one.id, role: "consumer", reason: one.reason }));
|
|
205
212
|
const loaded = [...input.loaded]
|
|
206
|
-
.sort((a, b) =>
|
|
213
|
+
.sort((a, b) => bytewise(a.id, b.id) || bytewise(a.path, b.path))
|
|
207
214
|
.map((one) => ({ id: one.id, role: "consumer", path: one.path }));
|
|
208
215
|
return {
|
|
209
216
|
schema_version: SCHEMA_VERSION,
|
|
210
217
|
contract_version: input.contractVersion,
|
|
211
218
|
analyzer: {
|
|
212
|
-
name:
|
|
219
|
+
name: PRODUCT,
|
|
213
220
|
version: input.version,
|
|
214
221
|
languages: [LANGUAGE],
|
|
215
|
-
schema_versions_accepted:
|
|
222
|
+
schema_versions_accepted: SCHEMA_VERSIONS,
|
|
216
223
|
conformance: {
|
|
217
224
|
corpus_version: input.conformance.corpusVersion,
|
|
218
225
|
result: input.conformance.result,
|
|
@@ -221,23 +228,26 @@ export function buildReport(input) {
|
|
|
221
228
|
},
|
|
222
229
|
target: { kind: input.target.kind, root: input.target.root, identity: input.target.identity },
|
|
223
230
|
configurations: [...input.configurations]
|
|
224
|
-
.sort((a, b) =>
|
|
231
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
225
232
|
.map((one) => ({ id: one.id, project: one.project })),
|
|
226
233
|
configurations_not_built: [...input.notBuilt]
|
|
227
|
-
.sort((a, b) =>
|
|
234
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
228
235
|
.map((one) => ({ id: one.id, project: one.project, error: one.error })),
|
|
229
236
|
consumers: { declared: loaded.length + unavailable.length, loaded, unavailable },
|
|
230
237
|
findings,
|
|
238
|
+
...(input.listsComponents === true
|
|
239
|
+
? { components: componentsOf([...paired, ...evaluated]) }
|
|
240
|
+
: {}),
|
|
231
241
|
edge_evaluations: input.result.edgeEvaluations.map(wireEvaluation).sort(byEdge),
|
|
232
242
|
stale_suppressions: input.result.staleSuppressions.map(wireStale).sort(canonicalStale),
|
|
233
243
|
// Every gap shares the one component of the canonical key a gap supplies, the analyzer's
|
|
234
244
|
// name, so the compact encodings alone order them.
|
|
235
245
|
declared_gaps: input.declaredGaps
|
|
236
246
|
.map(wireGap)
|
|
237
|
-
.sort((a, b) =>
|
|
247
|
+
.sort((a, b) => bytewise(JSON.stringify(a), JSON.stringify(b))),
|
|
238
248
|
excluded_by_cgo: [],
|
|
239
249
|
test_file_rules: [...input.testFileRules]
|
|
240
|
-
.sort((a, b) =>
|
|
250
|
+
.sort((a, b) => bytewise(a.rule, b.rule) || a.matched - b.matched)
|
|
241
251
|
.map((one) => ({ rule: one.rule, matched: one.matched })),
|
|
242
252
|
type_error_skips: [
|
|
243
253
|
...new Map(input.typeErrorSkips.map((one) => {
|
|
@@ -245,12 +255,14 @@ export function buildReport(input) {
|
|
|
245
255
|
return [JSON.stringify(skip), skip];
|
|
246
256
|
})),
|
|
247
257
|
]
|
|
248
|
-
.sort(([a, x], [b, y]) =>
|
|
258
|
+
.sort(([a, x], [b, y]) => bytewise(x.path, y.path) || x.line - y.line || bytewise(a, b))
|
|
249
259
|
.map(([, skip]) => skip),
|
|
250
|
-
notes: [],
|
|
260
|
+
notes: [...new Map((input.notes ?? []).map((one) => [JSON.stringify(one), one])).entries()]
|
|
261
|
+
.sort(([a, x], [b, y]) => bytewise(x.kind, y.kind) || bytewise(x.path, y.path) || bytewise(a, b))
|
|
262
|
+
.map(([, note]) => note),
|
|
251
263
|
unanswered_questions: [...input.unanswered]
|
|
252
|
-
.sort((a, b) =>
|
|
253
|
-
|
|
264
|
+
.sort((a, b) => bytewise(a.configuration, b.configuration) ||
|
|
265
|
+
bytewise(JSON.stringify(a), JSON.stringify(b)))
|
|
254
266
|
.map((one) => ({
|
|
255
267
|
configuration: one.configuration,
|
|
256
268
|
questions: one.questions,
|
|
@@ -277,16 +289,22 @@ export function sortFindings(report, by) {
|
|
|
277
289
|
return { ...report, findings: [...report.findings].sort(by === "size" ? bySize : canonical) };
|
|
278
290
|
}
|
|
279
291
|
/**
|
|
280
|
-
* The report holding the first `most` findings of its order, the rest counted as omitted
|
|
281
|
-
*
|
|
292
|
+
* The report holding the first `most` findings of its order, the rest counted as omitted,
|
|
293
|
+
* and listing only the components those findings name. Every other total keeps describing
|
|
294
|
+
* the whole set. Zero keeps every finding.
|
|
282
295
|
*/
|
|
283
296
|
export function capFindings(report, most) {
|
|
284
297
|
if (most <= 0 || report.findings.length <= most) {
|
|
285
298
|
return report;
|
|
286
299
|
}
|
|
300
|
+
const findings = report.findings.slice(0, most);
|
|
301
|
+
const named = new Set([...findings, ...report.edge_evaluations.flatMap((one) => one.finding ?? [])].map((finding) => finding.component.id));
|
|
287
302
|
return {
|
|
288
303
|
...report,
|
|
289
|
-
findings
|
|
304
|
+
findings,
|
|
305
|
+
...(report.components === undefined
|
|
306
|
+
? {}
|
|
307
|
+
: { components: report.components.filter((listed) => named.has(listed.id)) }),
|
|
290
308
|
totals: { ...report.totals, omitted: report.findings.length - most },
|
|
291
309
|
};
|
|
292
310
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { fails } from "../config.js";
|
|
2
1
|
import { coalesced } from "../json-chunks.js";
|
|
3
2
|
import { KINDS } from "../kinds.js";
|
|
4
3
|
/** The escapes a workflow command's message needs, so a line break does not end the command. */
|
|
@@ -19,6 +18,12 @@ function escapeProperty(value) {
|
|
|
19
18
|
function kindName(code) {
|
|
20
19
|
return KINDS.get(code)?.name ?? code;
|
|
21
20
|
}
|
|
21
|
+
/** The command each finding severity is annotated with, whatever the failing severity. */
|
|
22
|
+
const LEVELS = {
|
|
23
|
+
deny: "error",
|
|
24
|
+
warn: "warning",
|
|
25
|
+
allow: "notice",
|
|
26
|
+
};
|
|
22
27
|
function command(level, at, endLine, title, message) {
|
|
23
28
|
return `::${level} file=${escapeProperty(at.path)},line=${String(at.line)},col=${String(at.column)},endLine=${String(endLine)},title=${escapeProperty(title)}::${escapeData(message)}`;
|
|
24
29
|
}
|
|
@@ -26,16 +31,15 @@ function command(level, at, endLine, title, message) {
|
|
|
26
31
|
const SKIP_TITLE = "type error skipped";
|
|
27
32
|
const SKIP_MESSAGE = "the analysis did not evaluate the function or statement holding this type error: ";
|
|
28
33
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* is fixed on at the failing severity.
|
|
34
|
+
* The annotations `contract/grammar/annotations.md` states: one per finding, at the
|
|
35
|
+
* command its severity names, and one error per stale suppression, each naming its file,
|
|
36
|
+
* line, column and last line and titled by its code and kind; then one warning per
|
|
37
|
+
* type-error skip naming its file and line, and one per note naming its directory.
|
|
34
38
|
*/
|
|
35
|
-
export function annotations(report
|
|
39
|
+
export function annotations(report) {
|
|
36
40
|
return coalesced((function* lines() {
|
|
37
41
|
for (const found of report.findings) {
|
|
38
|
-
yield `${command(
|
|
42
|
+
yield `${command(LEVELS[found.severity], found.position, found.position.end_line, `${found.code} ${found.kind}`, found.message)}\n`;
|
|
39
43
|
}
|
|
40
44
|
for (const stale of report.stale_suppressions) {
|
|
41
45
|
yield `${command("error", stale.position, stale.position.line, `${stale.code} ${kindName(stale.code)}`, stale.message)}\n`;
|
|
@@ -43,5 +47,8 @@ export function annotations(report, options) {
|
|
|
43
47
|
for (const skip of report.type_error_skips) {
|
|
44
48
|
yield `::warning file=${escapeProperty(skip.path)},line=${String(skip.line)},title=${escapeProperty(SKIP_TITLE)}::${escapeData(SKIP_MESSAGE + skip.message)}\n`;
|
|
45
49
|
}
|
|
50
|
+
for (const note of report.notes) {
|
|
51
|
+
yield `::warning file=${escapeProperty(note.path)},title=${escapeProperty(note.kind)}::${escapeData(note.message)}\n`;
|
|
52
|
+
}
|
|
46
53
|
})());
|
|
47
54
|
}
|
|
@@ -5,6 +5,8 @@ import { lineHashes, symbolFingerprint } from "./fingerprint.js";
|
|
|
5
5
|
import { RenderError } from "./reporter.js";
|
|
6
6
|
import { utf8Bytes } from "./sha256.js";
|
|
7
7
|
import { withheldLine } from "./text.js";
|
|
8
|
+
import { bytewise } from "../text/compare.js";
|
|
9
|
+
import { message } from "../product.js";
|
|
8
10
|
const SCHEMA = "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
|
|
9
11
|
const VERSION = "2.1.0";
|
|
10
12
|
/** The unit a finding's column counts. */
|
|
@@ -101,7 +103,7 @@ function lineHashCache(read) {
|
|
|
101
103
|
text = read(at.path);
|
|
102
104
|
}
|
|
103
105
|
catch (error) {
|
|
104
|
-
throw new RenderError(`read ${at.path} for its line fingerprint: ${
|
|
106
|
+
throw new RenderError(`read ${at.path} for its line fingerprint: ${message(error)}`);
|
|
105
107
|
}
|
|
106
108
|
hashes = lineHashes(text);
|
|
107
109
|
held.set(at.path, hashes);
|
|
@@ -114,7 +116,7 @@ function lineHashCache(read) {
|
|
|
114
116
|
};
|
|
115
117
|
}
|
|
116
118
|
/** Every position a finding names beyond its own, in the mapping's order, the first hundred. */
|
|
117
|
-
function relatedOf(found) {
|
|
119
|
+
function relatedOf(found, members) {
|
|
118
120
|
const related = [];
|
|
119
121
|
for (const one of found.details.implementations ?? []) {
|
|
120
122
|
related.push({ label: "implementation", at: one.position, endLine: one.position.end_line });
|
|
@@ -126,7 +128,7 @@ function relatedOf(found) {
|
|
|
126
128
|
const at = found.details.name_literal;
|
|
127
129
|
related.push({ label: "name", at, endLine: at.end_line });
|
|
128
130
|
}
|
|
129
|
-
for (const one of found.component.
|
|
131
|
+
for (const one of members(found.component.id)) {
|
|
130
132
|
if (declarationKey(one.ref, one.position.path) !==
|
|
131
133
|
declarationKey(found.symbol.ref, found.position.path)) {
|
|
132
134
|
related.push({ label: "member", at: one.position, endLine: one.position.end_line });
|
|
@@ -143,7 +145,9 @@ function messageWithLinks(message, related) {
|
|
|
143
145
|
return `${message} (see ${links.join(", ")})`;
|
|
144
146
|
}
|
|
145
147
|
/** Every member of the finding the mapping places nowhere else, under its schema name. */
|
|
146
|
-
|
|
148
|
+
/** A result's member bag, its component holding the members the report lists for it once. */
|
|
149
|
+
function propertiesOf(found, members) {
|
|
150
|
+
const listed = members(found.component.id);
|
|
147
151
|
return {
|
|
148
152
|
language: found.language,
|
|
149
153
|
symbol: found.symbol,
|
|
@@ -154,7 +158,7 @@ function propertiesOf(found) {
|
|
|
154
158
|
: { liveness_relation: found.liveness_relation }),
|
|
155
159
|
test_only: found.test_only,
|
|
156
160
|
generated: found.generated,
|
|
157
|
-
component: found.component,
|
|
161
|
+
component: listed.length === 0 ? found.component : { ...found.component, members: listed },
|
|
158
162
|
retained_by: found.retained_by,
|
|
159
163
|
configurations: found.configurations,
|
|
160
164
|
consumers_loaded: found.consumers_loaded,
|
|
@@ -169,8 +173,8 @@ function ruleIndex(index, code) {
|
|
|
169
173
|
}
|
|
170
174
|
return at;
|
|
171
175
|
}
|
|
172
|
-
function findingResult(found, index, hashOf) {
|
|
173
|
-
const related = relatedOf(found);
|
|
176
|
+
function findingResult(found, index, hashOf, members) {
|
|
177
|
+
const related = relatedOf(found, members);
|
|
174
178
|
return {
|
|
175
179
|
ruleId: found.code,
|
|
176
180
|
ruleIndex: ruleIndex(index, found.code),
|
|
@@ -190,7 +194,7 @@ function findingResult(found, index, hashOf) {
|
|
|
190
194
|
primaryLocationLineHash: hashOf(found.position),
|
|
191
195
|
"deadsetSymbolRef/v1": symbolFingerprint(found.code, found.symbol.ref),
|
|
192
196
|
},
|
|
193
|
-
properties: propertiesOf(found),
|
|
197
|
+
properties: propertiesOf(found, members),
|
|
194
198
|
};
|
|
195
199
|
}
|
|
196
200
|
/** A stale suppression at the failing level, at its own site; it is no finding, so it carries no member bag. */
|
|
@@ -218,11 +222,13 @@ export function sarif(report, options) {
|
|
|
218
222
|
const ruleList = rules(kinds);
|
|
219
223
|
const index = new Map(kinds.map((kind, at) => [kind.code, at]));
|
|
220
224
|
const hashOf = lineHashCache(options.readSource);
|
|
225
|
+
const listed = new Map((report.components ?? []).map((one) => [one.id, one.members]));
|
|
226
|
+
const members = (id) => listed.get(id) ?? [];
|
|
221
227
|
const results = [
|
|
222
|
-
...report.findings.map((found) => findingResult(found, index, hashOf)),
|
|
228
|
+
...report.findings.map((found) => findingResult(found, index, hashOf, members)),
|
|
223
229
|
...report.stale_suppressions.map((stale) => staleResult(stale, index, hashOf)),
|
|
224
230
|
];
|
|
225
|
-
const languages = [...report.analyzer.languages].sort().join("+");
|
|
231
|
+
const languages = [...report.analyzer.languages].sort(bytewise).join("+");
|
|
226
232
|
const withheld = withheldLine(report.totals);
|
|
227
233
|
const log = {
|
|
228
234
|
$schema: SCHEMA,
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { CHUNK_LENGTH } from "../json-chunks.js";
|
|
10
10
|
import { RenderError } from "./reporter.js";
|
|
11
11
|
import { utf8Bytes } from "./sha256.js";
|
|
12
|
+
import { bytewise } from "../text/compare.js";
|
|
12
13
|
/** A template that does not parse, which refuses the invocation before any analysis. */
|
|
13
14
|
export class TemplateError extends Error {
|
|
14
15
|
constructor(message) {
|
|
@@ -513,7 +514,7 @@ function formatted(value, nested) {
|
|
|
513
514
|
}
|
|
514
515
|
const object = value;
|
|
515
516
|
const entries = Object.keys(object)
|
|
516
|
-
.sort()
|
|
517
|
+
.sort(bytewise)
|
|
517
518
|
.map((key) => `${key}:${formatted(object[key] ?? null, true)}`);
|
|
518
519
|
return `map[${entries.join(" ")}]`;
|
|
519
520
|
}
|
|
@@ -538,26 +539,10 @@ function compareBasic(name, a, b) {
|
|
|
538
539
|
return Number.NaN;
|
|
539
540
|
}
|
|
540
541
|
if (kind === "string") {
|
|
541
|
-
return
|
|
542
|
+
return bytewise(a, b);
|
|
542
543
|
}
|
|
543
544
|
return a < b ? -1 : 1;
|
|
544
545
|
}
|
|
545
|
-
/**
|
|
546
|
-
* Two strings ordered by the bytes of their UTF-8 encodings, which is the order of their
|
|
547
|
-
* code points: the order of their UTF-16 code units differs above U+FFFF.
|
|
548
|
-
*/
|
|
549
|
-
function byCodePoints(a, b) {
|
|
550
|
-
let at = 0;
|
|
551
|
-
while (at < a.length && at < b.length) {
|
|
552
|
-
const x = a.codePointAt(at) ?? 0;
|
|
553
|
-
const y = b.codePointAt(at) ?? 0;
|
|
554
|
-
if (x !== y) {
|
|
555
|
-
return x < y ? -1 : 1;
|
|
556
|
-
}
|
|
557
|
-
at += x > 0xffff ? 2 : 1;
|
|
558
|
-
}
|
|
559
|
-
return a.length - b.length;
|
|
560
|
-
}
|
|
561
546
|
/** The escapes `%q` writes for the control characters that have one of their own. */
|
|
562
547
|
const QUOTED_ESCAPES = {
|
|
563
548
|
'"': '\\"',
|
|
@@ -903,7 +888,7 @@ class Execution {
|
|
|
903
888
|
}
|
|
904
889
|
else if (isObject(value)) {
|
|
905
890
|
entries = Object.keys(value)
|
|
906
|
-
.sort()
|
|
891
|
+
.sort(bytewise)
|
|
907
892
|
.map((key) => [key, value[key] ?? null]);
|
|
908
893
|
}
|
|
909
894
|
else {
|