@cplieger/deadset-ts 8.0.0-dev.5 → 8.0.0-dev.7
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/dist/bin/mapper-connection.js +2 -1
- package/dist/bin/node-host.js +3 -2
- package/dist/src/analysis.js +28 -46
- package/dist/src/automatic-types.js +10 -29
- package/dist/src/build-scripts.js +17 -29
- package/dist/src/component-layer.js +4 -2
- package/dist/src/components.js +2 -3
- package/dist/src/config.js +3 -2
- package/dist/src/configuration-files.js +27 -168
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +1084 -0
- package/dist/src/contract/report.generated.js +796 -0
- package/dist/src/conventions.js +20 -60
- package/dist/src/dependencies.js +25 -43
- package/dist/src/discover.js +13 -39
- package/dist/src/edges.js +7 -15
- package/dist/src/element-access.js +4 -6
- package/dist/src/entry-point-gaps.js +2 -8
- package/dist/src/entry-points.js +3 -10
- package/dist/src/enum-group.js +3 -10
- package/dist/src/erased-stores.js +4 -6
- package/dist/src/exempt-classes.js +3 -63
- package/dist/src/exempt.js +13 -40
- package/dist/src/file-facts.js +2 -25
- package/dist/src/findings/completion.js +11 -20
- package/dist/src/findings/dependencies-and-module-machinery.js +4 -5
- package/dist/src/findings/emitters.js +4 -4
- package/dist/src/findings/interfaces.js +4 -5
- package/dist/src/findings/intra-function.js +8 -7
- package/dist/src/findings/non-code-artifacts.js +5 -6
- package/dist/src/findings/reads-and-writes.js +11 -9
- package/dist/src/findings/self-check.js +10 -12
- package/dist/src/findings/unused-declarations.js +28 -27
- package/dist/src/findings/visibility-narrowing.js +9 -11
- package/dist/src/findings-pass.js +7 -14
- package/dist/src/fs/manifest.js +43 -0
- package/dist/src/fs/tree.js +88 -0
- package/dist/src/git-ignore.js +34 -25
- package/dist/src/html-entries.js +6 -33
- package/dist/src/in-operands.js +2 -1
- package/dist/src/index.js +1 -1
- package/dist/src/intra-function-parts.js +18 -26
- package/dist/src/inventory.js +2 -21
- package/dist/src/json-document.js +4 -0
- package/dist/src/keyof-comparisons.js +4 -6
- package/dist/src/kinds.js +4 -360
- package/dist/src/manifest.js +5 -43
- package/dist/src/mdx-entries.js +3 -26
- 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 +3 -2
- package/dist/src/product.js +13 -0
- package/dist/src/references.js +14 -27
- package/dist/src/report-schema.js +272 -0
- package/dist/src/report.js +39 -42
- package/dist/src/reporters/sarif.js +4 -2
- package/dist/src/reporters/template.js +4 -19
- package/dist/src/resolve.js +12 -26
- package/dist/src/roots.js +7 -17
- package/dist/src/run.js +10 -8
- package/dist/src/schema.js +4 -3
- package/dist/src/scope.js +4 -5
- package/dist/src/serialization-contract.js +4 -8
- package/dist/src/session.js +2 -1
- package/dist/src/setup-failure.js +2 -6
- package/dist/src/suppress-file.js +12 -13
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +29 -48
- package/dist/src/symbol-keys.js +3 -5
- package/dist/src/template-field.js +5 -15
- package/dist/src/test-globals.js +6 -28
- package/dist/src/test-joins.js +6 -6
- package/dist/src/text/compare.js +26 -0
- package/dist/src/text/glob.js +194 -0
- package/dist/src/text/shell.js +159 -0
- package/dist/src/{yaml-document.js → text/yaml.js} +38 -0
- package/dist/src/type-errors.js +14 -38
- package/dist/src/unseen-loads.js +2 -9
- package/dist/src/unwrap.js +23 -0
- package/dist/src/verbs/analyze.js +22 -15
- package/dist/src/verbs/describe.js +7 -7
- package/dist/src/verbs/explain.js +7 -4
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +4 -2
- 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 +4 -3
- package/dist/src/workflow-steps.js +9 -46
- package/dist/src/workspace-manifests.js +2 -2
- package/dist/src/workspace-programs.js +5 -3
- package/dist/src/workspace-resolution.js +13 -17
- package/dist/src/workspace.js +28 -116
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/analysis.ts +29 -54
- package/src/automatic-types.ts +11 -29
- package/src/build-scripts.ts +17 -38
- package/src/component-layer.ts +4 -2
- package/src/components.ts +2 -4
- package/src/config.ts +11 -15
- package/src/configuration-files.ts +27 -170
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +1148 -0
- package/src/contract/report.generated.ts +1157 -0
- package/src/conventions.ts +26 -74
- package/src/dependencies.ts +32 -50
- package/src/discover.ts +14 -39
- package/src/edges.ts +7 -21
- package/src/element-access.ts +4 -7
- package/src/entry-point-gaps.ts +3 -9
- package/src/entry-points.ts +2 -13
- package/src/enum-group.ts +2 -11
- package/src/erased-stores.ts +3 -7
- package/src/exempt-classes.ts +5 -74
- package/src/exempt.ts +17 -47
- package/src/file-facts.ts +6 -24
- package/src/finding.ts +9 -9
- package/src/findings/completion.ts +22 -23
- package/src/findings/dependencies-and-module-machinery.ts +4 -7
- package/src/findings/emitter.ts +6 -7
- package/src/findings/emitters.ts +6 -6
- package/src/findings/interfaces.ts +4 -6
- package/src/findings/intra-function.ts +8 -8
- package/src/findings/non-code-artifacts.ts +5 -7
- package/src/findings/reads-and-writes.ts +15 -12
- package/src/findings/self-check.ts +11 -17
- package/src/findings/unused-declarations.ts +28 -27
- package/src/findings/visibility-narrowing.ts +9 -12
- package/src/findings-pass.ts +7 -16
- package/src/fs/manifest.ts +52 -0
- package/src/fs/tree.ts +110 -0
- package/src/git-ignore.ts +38 -25
- package/src/html-entries.ts +6 -33
- package/src/in-operands.ts +2 -1
- package/src/index.ts +1 -1
- package/src/intra-function-parts.ts +18 -28
- package/src/inventory.ts +2 -20
- package/src/json-document.ts +5 -0
- package/src/keyof-comparisons.ts +3 -7
- package/src/kinds.ts +6 -406
- package/src/manifest.ts +5 -46
- package/src/mdx-entries.ts +3 -26
- package/src/partial.ts +2 -1
- package/src/paths.ts +27 -15
- package/src/position.ts +2 -1
- package/src/print.ts +3 -2
- package/src/product.ts +18 -0
- package/src/references.ts +14 -29
- package/src/report-schema.ts +364 -0
- package/src/report.ts +66 -229
- package/src/reporters/annotations.ts +2 -2
- package/src/reporters/json.ts +1 -1
- package/src/reporters/reporter.ts +2 -2
- package/src/reporters/reporters.ts +1 -1
- package/src/reporters/sarif.ts +17 -12
- package/src/reporters/template.ts +4 -20
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +31 -38
- package/src/roots.ts +7 -20
- package/src/run.ts +10 -14
- package/src/schema.ts +4 -3
- package/src/scope.ts +4 -10
- package/src/serialization-contract.ts +4 -9
- package/src/session.ts +2 -1
- package/src/setup-failure.ts +5 -7
- package/src/suppress-file.ts +11 -15
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +39 -55
- package/src/symbol-keys.ts +2 -5
- package/src/template-field.ts +5 -18
- package/src/test-globals.ts +9 -27
- package/src/test-joins.ts +6 -8
- package/src/text/compare.ts +27 -0
- package/src/text/glob.ts +210 -0
- package/src/text/shell.ts +161 -0
- package/src/{yaml-document.ts → text/yaml.ts} +46 -0
- package/src/type-errors.ts +25 -36
- package/src/unseen-loads.ts +1 -12
- package/src/unwrap.ts +34 -0
- package/src/verbs/analyze.ts +38 -25
- package/src/verbs/describe.ts +13 -7
- package/src/verbs/explain.ts +7 -4
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +4 -2
- package/src/verbs/print-retained.ts +4 -2
- package/src/verbs/print-roots.ts +4 -2
- package/src/verbs/verb.ts +0 -6
- package/src/verbs/version.ts +4 -3
- package/src/workflow-steps.ts +9 -44
- package/src/workspace-manifests.ts +2 -2
- package/src/workspace-programs.ts +5 -3
- package/src/workspace-resolution.ts +21 -17
- package/src/workspace.ts +35 -121
- package/dist/src/glob.js +0 -65
- package/dist/src/pnpm-workspace.js +0 -120
- package/dist/src/version.js +0 -7
- package/src/glob.ts +0 -67
- package/src/pnpm-workspace.ts +0 -135
- package/src/version.ts +0 -7
package/src/report.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* renders this document, so no two renderings disagree about which findings exist.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import { SCHEMA_VERSION, SCHEMA_VERSIONS } from "./contract/contract.generated.ts";
|
|
8
|
+
import type * as wire from "./contract/report.generated.ts";
|
|
9
|
+
import type { Sort, TargetKind } from "./config.ts";
|
|
8
10
|
import type { Conformance, DeclaredGap } from "./conformance.ts";
|
|
9
11
|
import type { EdgeEvaluation } from "./edges.ts";
|
|
10
12
|
import type {
|
|
@@ -17,134 +19,8 @@ import type { AppliedConvention } from "./conventions.ts";
|
|
|
17
19
|
import type { PassResult } from "./findings-pass.ts";
|
|
18
20
|
import type { StaleSuppression } from "./findings/self-check.ts";
|
|
19
21
|
import type { TestFileRule } from "./references.ts";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const SCHEMA_VERSION = "9.0.0";
|
|
23
|
-
|
|
24
|
-
/** Every report schema version this analyzer reads: the one the Contract it implements admits. */
|
|
25
|
-
export const SCHEMA_VERSIONS_ACCEPTED: readonly string[] = [SCHEMA_VERSION];
|
|
26
|
-
|
|
27
|
-
/** The name this analyzer writes every document under and mints every identifier with. */
|
|
28
|
-
export const ANALYZER_NAME = "deadset-ts";
|
|
29
|
-
|
|
30
|
-
/** The language this analyzer claims. */
|
|
31
|
-
export const LANGUAGE = "ts";
|
|
32
|
-
|
|
33
|
-
interface WirePosition {
|
|
34
|
-
readonly path: string;
|
|
35
|
-
readonly line: number;
|
|
36
|
-
readonly column: number;
|
|
37
|
-
readonly end_line: number;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface WireDetails {
|
|
41
|
-
readonly narrower_visibility?: string;
|
|
42
|
-
readonly implementations?: readonly {
|
|
43
|
-
readonly ref: string;
|
|
44
|
-
readonly name: string;
|
|
45
|
-
readonly position: WirePosition;
|
|
46
|
-
}[];
|
|
47
|
-
readonly write_positions?: readonly WirePosition[];
|
|
48
|
-
/** Set under the name-literal rule, which no finding of this analyzer meets; SARIF still maps it. */
|
|
49
|
-
readonly name_literal?: WirePosition;
|
|
50
|
-
readonly dependency_class?: string;
|
|
51
|
-
readonly mechanism?: string;
|
|
52
|
-
readonly entry?: {
|
|
53
|
-
readonly code: string;
|
|
54
|
-
readonly symbol?: string;
|
|
55
|
-
readonly path?: string;
|
|
56
|
-
readonly reason?: string;
|
|
57
|
-
};
|
|
58
|
-
readonly overlap?: readonly string[];
|
|
59
|
-
readonly removes_last_use_of?: readonly string[];
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** One finding as the finding schema spells it. */
|
|
63
|
-
export interface WireFinding {
|
|
64
|
-
readonly code: string;
|
|
65
|
-
readonly kind: string;
|
|
66
|
-
readonly language: string;
|
|
67
|
-
readonly position: WirePosition;
|
|
68
|
-
readonly symbol: {
|
|
69
|
-
readonly ref: string;
|
|
70
|
-
readonly kind: string;
|
|
71
|
-
readonly name: string;
|
|
72
|
-
readonly size_lines: number;
|
|
73
|
-
};
|
|
74
|
-
readonly reachability_class: string;
|
|
75
|
-
readonly confidence: string;
|
|
76
|
-
readonly liveness_relation?: string;
|
|
77
|
-
readonly test_only: boolean;
|
|
78
|
-
readonly generated: boolean;
|
|
79
|
-
readonly component: {
|
|
80
|
-
readonly id: string;
|
|
81
|
-
readonly root: boolean;
|
|
82
|
-
readonly symbol_count: number;
|
|
83
|
-
readonly deletable_lines: number;
|
|
84
|
-
};
|
|
85
|
-
readonly retained_by: readonly string[];
|
|
86
|
-
readonly configurations: readonly string[];
|
|
87
|
-
readonly consumers_loaded: readonly string[];
|
|
88
|
-
readonly fixability: string;
|
|
89
|
-
readonly severity: Severity;
|
|
90
|
-
readonly message: string;
|
|
91
|
-
readonly details: WireDetails;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** One dead component a finding names, with its members, as the report lists it once. */
|
|
95
|
-
export interface WireComponent {
|
|
96
|
-
readonly id: string;
|
|
97
|
-
readonly members: readonly {
|
|
98
|
-
readonly ref: string;
|
|
99
|
-
readonly name: string;
|
|
100
|
-
readonly position: WirePosition;
|
|
101
|
-
}[];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
interface WireEdgeEvaluation {
|
|
105
|
-
readonly edge: string;
|
|
106
|
-
readonly side: string;
|
|
107
|
-
readonly symbol: string;
|
|
108
|
-
readonly state: string;
|
|
109
|
-
readonly finding?: WireFinding;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export interface WireStaleSuppression {
|
|
113
|
-
readonly code: string;
|
|
114
|
-
readonly mechanism: string;
|
|
115
|
-
readonly entry: {
|
|
116
|
-
readonly code: string;
|
|
117
|
-
readonly symbol?: string;
|
|
118
|
-
readonly path: string;
|
|
119
|
-
readonly reason: string;
|
|
120
|
-
};
|
|
121
|
-
readonly position: { readonly path: string; readonly line: number; readonly column: number };
|
|
122
|
-
readonly symbol: string;
|
|
123
|
-
readonly message: string;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
interface WireDeclaredGap {
|
|
127
|
-
readonly fixture: string;
|
|
128
|
-
readonly symbol?: string;
|
|
129
|
-
readonly capability: string;
|
|
130
|
-
readonly reason: string;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export interface WireTotals {
|
|
134
|
-
readonly findings: number;
|
|
135
|
-
readonly by_severity: { readonly allow: number; readonly warn: number; readonly deny: number };
|
|
136
|
-
readonly deletable_lines: number;
|
|
137
|
-
readonly suppressions_in_effect: number;
|
|
138
|
-
readonly reasons_recorded: number;
|
|
139
|
-
readonly stale_suppressions: number;
|
|
140
|
-
readonly pending: number;
|
|
141
|
-
readonly omitted: number;
|
|
142
|
-
readonly withheld: {
|
|
143
|
-
readonly certain: number;
|
|
144
|
-
readonly probable: number;
|
|
145
|
-
readonly possible: number;
|
|
146
|
-
};
|
|
147
|
-
}
|
|
22
|
+
import { bytewise } from "./text/compare.ts";
|
|
23
|
+
import { LANGUAGE, PRODUCT } from "./product.ts";
|
|
148
24
|
|
|
149
25
|
/** One project configuration of the matrix a run analyzed. */
|
|
150
26
|
export interface ReportConfiguration {
|
|
@@ -191,52 +67,6 @@ interface UnavailableConsumer {
|
|
|
191
67
|
readonly reason: string;
|
|
192
68
|
}
|
|
193
69
|
|
|
194
|
-
/** The report document. */
|
|
195
|
-
export interface Report {
|
|
196
|
-
readonly schema_version: string;
|
|
197
|
-
readonly contract_version: string;
|
|
198
|
-
readonly analyzer: {
|
|
199
|
-
readonly name: string;
|
|
200
|
-
readonly version: string;
|
|
201
|
-
readonly languages: readonly string[];
|
|
202
|
-
readonly schema_versions_accepted: readonly string[];
|
|
203
|
-
readonly conformance: {
|
|
204
|
-
readonly corpus_version: string;
|
|
205
|
-
readonly result: string;
|
|
206
|
-
readonly digest: string;
|
|
207
|
-
};
|
|
208
|
-
};
|
|
209
|
-
readonly target: { readonly kind: TargetKind; readonly root: string; readonly identity: string };
|
|
210
|
-
readonly configurations: readonly ReportConfiguration[];
|
|
211
|
-
readonly configurations_not_built: readonly ReportNotBuilt[];
|
|
212
|
-
readonly consumers: {
|
|
213
|
-
readonly declared: number;
|
|
214
|
-
readonly loaded: readonly {
|
|
215
|
-
readonly id: string;
|
|
216
|
-
readonly role: "consumer";
|
|
217
|
-
readonly path: string;
|
|
218
|
-
}[];
|
|
219
|
-
readonly unavailable: readonly {
|
|
220
|
-
readonly id: string;
|
|
221
|
-
readonly role: "consumer";
|
|
222
|
-
readonly reason: string;
|
|
223
|
-
}[];
|
|
224
|
-
};
|
|
225
|
-
readonly findings: readonly WireFinding[];
|
|
226
|
-
/** Present where the run lists each component in full. */
|
|
227
|
-
readonly components?: readonly WireComponent[];
|
|
228
|
-
readonly edge_evaluations: readonly WireEdgeEvaluation[];
|
|
229
|
-
readonly stale_suppressions: readonly WireStaleSuppression[];
|
|
230
|
-
readonly declared_gaps: readonly WireDeclaredGap[];
|
|
231
|
-
readonly excluded_by_cgo: readonly never[];
|
|
232
|
-
readonly test_file_rules: readonly TestFileRule[];
|
|
233
|
-
readonly type_error_skips: readonly TypeErrorSkip[];
|
|
234
|
-
readonly notes: readonly ReportNote[];
|
|
235
|
-
readonly unanswered_questions: readonly UnansweredCount[];
|
|
236
|
-
readonly conventions_applied: readonly AppliedConvention[];
|
|
237
|
-
readonly totals: WireTotals;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
70
|
/** One hint about the run's setup, as the report schema's `notes` spells it. */
|
|
241
71
|
export type ReportNote =
|
|
242
72
|
| {
|
|
@@ -282,27 +112,22 @@ export interface ReportInput {
|
|
|
282
112
|
* sorted, then the edge evaluations' findings.
|
|
283
113
|
*/
|
|
284
114
|
function componentsOf(
|
|
285
|
-
findings: readonly (readonly [
|
|
286
|
-
): readonly
|
|
287
|
-
const listed = new Map<string,
|
|
288
|
-
for (const [
|
|
115
|
+
findings: readonly (readonly [wire.Finding, CompletedFinding])[],
|
|
116
|
+
): readonly wire.ComponentsEntry[] {
|
|
117
|
+
const listed = new Map<string, wire.ComponentsEntry>();
|
|
118
|
+
for (const [written, finding] of findings) {
|
|
289
119
|
const { members } = finding.component;
|
|
290
|
-
if (members !== undefined && !listed.has(
|
|
291
|
-
listed.set(
|
|
120
|
+
if (members !== undefined && !listed.has(written.component.id)) {
|
|
121
|
+
listed.set(written.component.id, {
|
|
122
|
+
id: written.component.id,
|
|
123
|
+
members: members.map(wireSymbol),
|
|
124
|
+
});
|
|
292
125
|
}
|
|
293
126
|
}
|
|
294
127
|
return [...listed.values()];
|
|
295
128
|
}
|
|
296
129
|
|
|
297
|
-
|
|
298
|
-
function compare(a: string, b: string): number {
|
|
299
|
-
if (a === b) {
|
|
300
|
-
return 0;
|
|
301
|
-
}
|
|
302
|
-
return a < b ? -1 : 1;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function wirePosition(position: FindingPosition): WirePosition {
|
|
130
|
+
function wirePosition(position: FindingPosition): wire.Position {
|
|
306
131
|
return {
|
|
307
132
|
path: position.path,
|
|
308
133
|
line: position.line,
|
|
@@ -314,13 +139,13 @@ function wirePosition(position: FindingPosition): WirePosition {
|
|
|
314
139
|
function wireSymbol(symbol: PositionedSymbol): {
|
|
315
140
|
ref: string;
|
|
316
141
|
name: string;
|
|
317
|
-
position:
|
|
142
|
+
position: wire.Position;
|
|
318
143
|
} {
|
|
319
144
|
return { ref: symbol.ref, name: symbol.name, position: wirePosition(symbol.position) };
|
|
320
145
|
}
|
|
321
146
|
|
|
322
147
|
/** The details one code carries, in the order the finding schema lists them. */
|
|
323
|
-
function wireDetails(details: FindingDetails):
|
|
148
|
+
function wireDetails(details: FindingDetails): wire.Details {
|
|
324
149
|
const { entry } = details;
|
|
325
150
|
return {
|
|
326
151
|
...(details.narrowerVisibility === undefined
|
|
@@ -352,7 +177,7 @@ function wireDetails(details: FindingDetails): WireDetails {
|
|
|
352
177
|
}
|
|
353
178
|
|
|
354
179
|
/** One finding as the finding schema spells it, member for member in its order. */
|
|
355
|
-
export function wireFinding(finding: CompletedFinding):
|
|
180
|
+
export function wireFinding(finding: CompletedFinding): wire.Finding {
|
|
356
181
|
return {
|
|
357
182
|
code: finding.code,
|
|
358
183
|
kind: finding.kind,
|
|
@@ -387,7 +212,7 @@ export function wireFinding(finding: CompletedFinding): WireFinding {
|
|
|
387
212
|
};
|
|
388
213
|
}
|
|
389
214
|
|
|
390
|
-
function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>):
|
|
215
|
+
function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>): wire.EdgeEvaluationsEntry {
|
|
391
216
|
return {
|
|
392
217
|
edge: evaluation.edge,
|
|
393
218
|
side: evaluation.side,
|
|
@@ -397,7 +222,7 @@ function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>): WireEdgeE
|
|
|
397
222
|
};
|
|
398
223
|
}
|
|
399
224
|
|
|
400
|
-
function wireStale(stale: StaleSuppression):
|
|
225
|
+
function wireStale(stale: StaleSuppression): wire.StaleSuppressionsEntry {
|
|
401
226
|
return {
|
|
402
227
|
code: stale.code,
|
|
403
228
|
mechanism: stale.mechanism,
|
|
@@ -418,29 +243,29 @@ function wireStale(stale: StaleSuppression): WireStaleSuppression {
|
|
|
418
243
|
}
|
|
419
244
|
|
|
420
245
|
/** The canonical key over two findings: path, line, column, code, then the symbol reference. */
|
|
421
|
-
function canonical(a:
|
|
246
|
+
function canonical(a: wire.Finding, b: wire.Finding): number {
|
|
422
247
|
return (
|
|
423
|
-
|
|
248
|
+
bytewise(a.position.path, b.position.path) ||
|
|
424
249
|
a.position.line - b.position.line ||
|
|
425
250
|
a.position.column - b.position.column ||
|
|
426
|
-
|
|
427
|
-
|
|
251
|
+
bytewise(a.code, b.code) ||
|
|
252
|
+
bytewise(a.symbol.ref, b.symbol.ref)
|
|
428
253
|
);
|
|
429
254
|
}
|
|
430
255
|
|
|
431
256
|
/** The canonical key over two stale suppressions, each component from the record's own member. */
|
|
432
|
-
function canonicalStale(a:
|
|
257
|
+
function canonicalStale(a: wire.StaleSuppressionsEntry, b: wire.StaleSuppressionsEntry): number {
|
|
433
258
|
return (
|
|
434
|
-
|
|
259
|
+
bytewise(a.position.path, b.position.path) ||
|
|
435
260
|
a.position.line - b.position.line ||
|
|
436
261
|
a.position.column - b.position.column ||
|
|
437
|
-
|
|
438
|
-
|
|
262
|
+
bytewise(a.code, b.code) ||
|
|
263
|
+
bytewise(a.symbol, b.symbol)
|
|
439
264
|
);
|
|
440
265
|
}
|
|
441
266
|
|
|
442
267
|
/** One declared gap as a report carries it, its members in the schema's order. */
|
|
443
|
-
function wireGap(gap: DeclaredGap):
|
|
268
|
+
function wireGap(gap: DeclaredGap): wire.DeclaredGapsEntry {
|
|
444
269
|
return {
|
|
445
270
|
fixture: gap.fixture,
|
|
446
271
|
...(gap.symbol === undefined ? {} : { symbol: gap.symbol }),
|
|
@@ -450,11 +275,11 @@ function wireGap(gap: DeclaredGap): WireDeclaredGap {
|
|
|
450
275
|
}
|
|
451
276
|
|
|
452
277
|
/** Edge evaluations by edge, then side, then their compact encodings. */
|
|
453
|
-
function byEdge(a:
|
|
278
|
+
function byEdge(a: wire.EdgeEvaluationsEntry, b: wire.EdgeEvaluationsEntry): number {
|
|
454
279
|
return (
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
280
|
+
bytewise(a.edge, b.edge) ||
|
|
281
|
+
bytewise(a.side, b.side) ||
|
|
282
|
+
bytewise(JSON.stringify(a), JSON.stringify(b))
|
|
458
283
|
);
|
|
459
284
|
}
|
|
460
285
|
|
|
@@ -462,7 +287,7 @@ function byEdge(a: WireEdgeEvaluation, b: WireEdgeEvaluation): number {
|
|
|
462
287
|
* The lines a reader deletes by acting on every finding: each distinct component a root
|
|
463
288
|
* finding names, counted once however many of its roots the findings hold.
|
|
464
289
|
*/
|
|
465
|
-
function deletableLines(findings: readonly
|
|
290
|
+
function deletableLines(findings: readonly wire.Finding[]): number {
|
|
466
291
|
const counted = new Map<string, number>();
|
|
467
292
|
for (const finding of findings) {
|
|
468
293
|
if (finding.component.root) {
|
|
@@ -477,7 +302,7 @@ function deletableLines(findings: readonly WireFinding[]): number {
|
|
|
477
302
|
}
|
|
478
303
|
|
|
479
304
|
/** The totals of one finding set, counted over the whole set. */
|
|
480
|
-
function totalsOf(findings: readonly
|
|
305
|
+
function totalsOf(findings: readonly wire.Finding[], result: PassResult): wire.Totals {
|
|
481
306
|
const bySeverity = { allow: 0, warn: 0, deny: 0 };
|
|
482
307
|
for (const finding of findings) {
|
|
483
308
|
bySeverity[finding.severity] += 1;
|
|
@@ -500,7 +325,7 @@ function totalsOf(findings: readonly WireFinding[], result: PassResult): WireTot
|
|
|
500
325
|
}
|
|
501
326
|
|
|
502
327
|
/** Builds the report of one run, every array in the order the Contract fixes for it. */
|
|
503
|
-
export function buildReport(input: ReportInput): Report {
|
|
328
|
+
export function buildReport(input: ReportInput): wire.Report {
|
|
504
329
|
const paired = input.result.findings
|
|
505
330
|
.map((finding) => [wireFinding(finding), finding] as const)
|
|
506
331
|
.sort(([a], [b]) => canonical(a, b));
|
|
@@ -509,19 +334,19 @@ export function buildReport(input: ReportInput): Report {
|
|
|
509
334
|
one.finding === undefined ? [] : [[wireFinding(one.finding), one.finding] as const],
|
|
510
335
|
);
|
|
511
336
|
const unavailable = [...new Map(input.unavailable.map((one) => [one.id, one])).values()]
|
|
512
|
-
.sort((a, b) =>
|
|
337
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
513
338
|
.map((one) => ({ id: one.id, role: "consumer" as const, reason: one.reason }));
|
|
514
339
|
const loaded = [...input.loaded]
|
|
515
|
-
.sort((a, b) =>
|
|
340
|
+
.sort((a, b) => bytewise(a.id, b.id) || bytewise(a.path, b.path))
|
|
516
341
|
.map((one) => ({ id: one.id, role: "consumer" as const, path: one.path }));
|
|
517
342
|
return {
|
|
518
343
|
schema_version: SCHEMA_VERSION,
|
|
519
344
|
contract_version: input.contractVersion,
|
|
520
345
|
analyzer: {
|
|
521
|
-
name:
|
|
346
|
+
name: PRODUCT,
|
|
522
347
|
version: input.version,
|
|
523
348
|
languages: [LANGUAGE],
|
|
524
|
-
schema_versions_accepted:
|
|
349
|
+
schema_versions_accepted: SCHEMA_VERSIONS,
|
|
525
350
|
conformance: {
|
|
526
351
|
corpus_version: input.conformance.corpusVersion,
|
|
527
352
|
result: input.conformance.result,
|
|
@@ -530,10 +355,10 @@ export function buildReport(input: ReportInput): Report {
|
|
|
530
355
|
},
|
|
531
356
|
target: { kind: input.target.kind, root: input.target.root, identity: input.target.identity },
|
|
532
357
|
configurations: [...input.configurations]
|
|
533
|
-
.sort((a, b) =>
|
|
358
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
534
359
|
.map((one) => ({ id: one.id, project: one.project })),
|
|
535
360
|
configurations_not_built: [...input.notBuilt]
|
|
536
|
-
.sort((a, b) =>
|
|
361
|
+
.sort((a, b) => bytewise(a.id, b.id))
|
|
537
362
|
.map((one) => ({ id: one.id, project: one.project, error: one.error })),
|
|
538
363
|
consumers: { declared: loaded.length + unavailable.length, loaded, unavailable },
|
|
539
364
|
findings,
|
|
@@ -546,10 +371,10 @@ export function buildReport(input: ReportInput): Report {
|
|
|
546
371
|
// name, so the compact encodings alone order them.
|
|
547
372
|
declared_gaps: input.declaredGaps
|
|
548
373
|
.map(wireGap)
|
|
549
|
-
.sort((a, b) =>
|
|
374
|
+
.sort((a, b) => bytewise(JSON.stringify(a), JSON.stringify(b))),
|
|
550
375
|
excluded_by_cgo: [],
|
|
551
376
|
test_file_rules: [...input.testFileRules]
|
|
552
|
-
.sort((a, b) =>
|
|
377
|
+
.sort((a, b) => bytewise(a.rule, b.rule) || a.matched - b.matched)
|
|
553
378
|
.map((one) => ({ rule: one.rule, matched: one.matched })),
|
|
554
379
|
type_error_skips: [
|
|
555
380
|
...new Map(
|
|
@@ -559,16 +384,18 @@ export function buildReport(input: ReportInput): Report {
|
|
|
559
384
|
}),
|
|
560
385
|
),
|
|
561
386
|
]
|
|
562
|
-
.sort(([a, x], [b, y]) =>
|
|
387
|
+
.sort(([a, x], [b, y]) => bytewise(x.path, y.path) || x.line - y.line || bytewise(a, b))
|
|
563
388
|
.map(([, skip]) => skip),
|
|
564
389
|
notes: [...new Map((input.notes ?? []).map((one) => [JSON.stringify(one), one])).entries()]
|
|
565
|
-
.sort(
|
|
390
|
+
.sort(
|
|
391
|
+
([a, x], [b, y]) => bytewise(x.kind, y.kind) || bytewise(x.path, y.path) || bytewise(a, b),
|
|
392
|
+
)
|
|
566
393
|
.map(([, note]) => note),
|
|
567
394
|
unanswered_questions: [...input.unanswered]
|
|
568
395
|
.sort(
|
|
569
396
|
(a, b) =>
|
|
570
|
-
|
|
571
|
-
|
|
397
|
+
bytewise(a.configuration, b.configuration) ||
|
|
398
|
+
bytewise(JSON.stringify(a), JSON.stringify(b)),
|
|
572
399
|
)
|
|
573
400
|
.map((one) => ({
|
|
574
401
|
configuration: one.configuration,
|
|
@@ -590,8 +417,8 @@ export function buildReport(input: ReportInput): Report {
|
|
|
590
417
|
* whose component a deletion shrinks more comes first, then one whose subject is larger,
|
|
591
418
|
* then the canonical key, so the order stays total; by position it is the canonical key.
|
|
592
419
|
*/
|
|
593
|
-
export function sortFindings(report: Report, by: Sort): Report {
|
|
594
|
-
const bySize = (a:
|
|
420
|
+
export function sortFindings(report: wire.Report, by: Sort): wire.Report {
|
|
421
|
+
const bySize = (a: wire.Finding, b: wire.Finding): number =>
|
|
595
422
|
b.component.deletable_lines - a.component.deletable_lines ||
|
|
596
423
|
b.symbol.size_lines - a.symbol.size_lines ||
|
|
597
424
|
canonical(a, b);
|
|
@@ -599,16 +426,26 @@ export function sortFindings(report: Report, by: Sort): Report {
|
|
|
599
426
|
}
|
|
600
427
|
|
|
601
428
|
/**
|
|
602
|
-
* The report holding the first `most` findings of its order, the rest counted as omitted
|
|
603
|
-
*
|
|
429
|
+
* The report holding the first `most` findings of its order, the rest counted as omitted,
|
|
430
|
+
* and listing only the components those findings name. Every other total keeps describing
|
|
431
|
+
* the whole set. Zero keeps every finding.
|
|
604
432
|
*/
|
|
605
|
-
export function capFindings(report: Report, most: number): Report {
|
|
433
|
+
export function capFindings(report: wire.Report, most: number): wire.Report {
|
|
606
434
|
if (most <= 0 || report.findings.length <= most) {
|
|
607
435
|
return report;
|
|
608
436
|
}
|
|
437
|
+
const findings = report.findings.slice(0, most);
|
|
438
|
+
const named = new Set(
|
|
439
|
+
[...findings, ...report.edge_evaluations.flatMap((one) => one.finding ?? [])].map(
|
|
440
|
+
(finding) => finding.component.id,
|
|
441
|
+
),
|
|
442
|
+
);
|
|
609
443
|
return {
|
|
610
444
|
...report,
|
|
611
|
-
findings
|
|
445
|
+
findings,
|
|
446
|
+
...(report.components === undefined
|
|
447
|
+
? {}
|
|
448
|
+
: { components: report.components.filter((listed) => named.has(listed.id)) }),
|
|
612
449
|
totals: { ...report.totals, omitted: report.findings.length - most },
|
|
613
450
|
};
|
|
614
451
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Severity } from "../
|
|
1
|
+
import type { Severity } from "../contract/contract.generated.ts";
|
|
2
2
|
import { coalesced } from "../json-chunks.ts";
|
|
3
3
|
import { KINDS } from "../kinds.ts";
|
|
4
|
-
import type { Report } from "../report.ts";
|
|
4
|
+
import type { Report } from "../contract/report.generated.ts";
|
|
5
5
|
|
|
6
6
|
/** The escapes a workflow command's message needs, so a line break does not end the command. */
|
|
7
7
|
const DATA_ESCAPES: Readonly<Record<string, string>> = { "%": "%25", "\r": "%0D", "\n": "%0A" };
|
package/src/reporters/json.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsonChunks } from "../json-chunks.ts";
|
|
2
|
-
import type { Report } from "../report.ts";
|
|
2
|
+
import type { Report } from "../contract/report.generated.ts";
|
|
3
3
|
|
|
4
4
|
/** The indentation of the report document, which with the schema's member order fixes its bytes. */
|
|
5
5
|
const INDENT = 2;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Format } from "../
|
|
2
|
-
import type { Report } from "../report.ts";
|
|
1
|
+
import type { Format } from "../contract/contract.generated.ts";
|
|
2
|
+
import type { Report } from "../contract/report.generated.ts";
|
|
3
3
|
import type { Template } from "./template.ts";
|
|
4
4
|
|
|
5
5
|
/** What a rendering reads beside the report. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Format } from "../
|
|
1
|
+
import type { Format } from "../contract/contract.generated.ts";
|
|
2
2
|
import { annotations } from "./annotations.ts";
|
|
3
3
|
import { json } from "./json.ts";
|
|
4
4
|
import { RenderError, type Rendering, type Renderings } from "./reporter.ts";
|
package/src/reporters/sarif.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import type { Confidence, Severity } from "../config.ts";
|
|
2
1
|
import { jsonChunks } from "../json-chunks.ts";
|
|
3
|
-
import {
|
|
2
|
+
import type { KindRow, Confidence, Severity } from "../contract/contract.generated.ts";
|
|
3
|
+
import { KINDS } from "../kinds.ts";
|
|
4
4
|
import { declarationKey } from "../ref.ts";
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
ComponentsEntry,
|
|
7
|
+
Finding,
|
|
8
|
+
Report,
|
|
9
|
+
StaleSuppressionsEntry,
|
|
10
|
+
} from "../contract/report.generated.ts";
|
|
6
11
|
import { lineHashes, symbolFingerprint } from "./fingerprint.ts";
|
|
7
12
|
import { RenderError, type RenderOptions } from "./reporter.ts";
|
|
8
13
|
import { utf8Bytes } from "./sha256.ts";
|
|
9
14
|
import { withheldLine } from "./text.ts";
|
|
15
|
+
import { bytewise } from "../text/compare.ts";
|
|
16
|
+
import { message } from "../product.ts";
|
|
10
17
|
|
|
11
18
|
const SCHEMA =
|
|
12
19
|
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
|
|
@@ -128,9 +135,7 @@ function lineHashCache(read: (path: string) => string): (at: Located) => string
|
|
|
128
135
|
try {
|
|
129
136
|
text = read(at.path);
|
|
130
137
|
} catch (error: unknown) {
|
|
131
|
-
throw new RenderError(
|
|
132
|
-
`read ${at.path} for its line fingerprint: ${error instanceof Error ? error.message : String(error)}`,
|
|
133
|
-
);
|
|
138
|
+
throw new RenderError(`read ${at.path} for its line fingerprint: ${message(error)}`);
|
|
134
139
|
}
|
|
135
140
|
hashes = lineHashes(text);
|
|
136
141
|
held.set(at.path, hashes);
|
|
@@ -152,10 +157,10 @@ interface Related {
|
|
|
152
157
|
}
|
|
153
158
|
|
|
154
159
|
/** The members the report lists for one component, none where it lists none. */
|
|
155
|
-
type MembersOf = (id: string) =>
|
|
160
|
+
type MembersOf = (id: string) => ComponentsEntry["members"];
|
|
156
161
|
|
|
157
162
|
/** Every position a finding names beyond its own, in the mapping's order, the first hundred. */
|
|
158
|
-
function relatedOf(found:
|
|
163
|
+
function relatedOf(found: Finding, members: MembersOf): readonly Related[] {
|
|
159
164
|
const related: Related[] = [];
|
|
160
165
|
for (const one of found.details.implementations ?? []) {
|
|
161
166
|
related.push({ label: "implementation", at: one.position, endLine: one.position.end_line });
|
|
@@ -192,7 +197,7 @@ function messageWithLinks(message: string, related: readonly Related[]): string
|
|
|
192
197
|
|
|
193
198
|
/** Every member of the finding the mapping places nowhere else, under its schema name. */
|
|
194
199
|
/** A result's member bag, its component holding the members the report lists for it once. */
|
|
195
|
-
function propertiesOf(found:
|
|
200
|
+
function propertiesOf(found: Finding, members: MembersOf): Record<string, unknown> {
|
|
196
201
|
const listed = members(found.component.id);
|
|
197
202
|
return {
|
|
198
203
|
language: found.language,
|
|
@@ -222,7 +227,7 @@ function ruleIndex(index: ReadonlyMap<string, number>, code: string): number {
|
|
|
222
227
|
}
|
|
223
228
|
|
|
224
229
|
function findingResult(
|
|
225
|
-
found:
|
|
230
|
+
found: Finding,
|
|
226
231
|
index: ReadonlyMap<string, number>,
|
|
227
232
|
hashOf: (at: Located) => string,
|
|
228
233
|
members: MembersOf,
|
|
@@ -253,7 +258,7 @@ function findingResult(
|
|
|
253
258
|
|
|
254
259
|
/** A stale suppression at the failing level, at its own site; it is no finding, so it carries no member bag. */
|
|
255
260
|
function staleResult(
|
|
256
|
-
stale:
|
|
261
|
+
stale: StaleSuppressionsEntry,
|
|
257
262
|
index: ReadonlyMap<string, number>,
|
|
258
263
|
hashOf: (at: Located) => string,
|
|
259
264
|
): Record<string, unknown> {
|
|
@@ -287,7 +292,7 @@ export function sarif(report: Report, options: RenderOptions): Iterable<string>
|
|
|
287
292
|
...report.findings.map((found) => findingResult(found, index, hashOf, members)),
|
|
288
293
|
...report.stale_suppressions.map((stale) => staleResult(stale, index, hashOf)),
|
|
289
294
|
];
|
|
290
|
-
const languages = [...report.analyzer.languages].sort().join("+");
|
|
295
|
+
const languages = [...report.analyzer.languages].sort(bytewise).join("+");
|
|
291
296
|
const withheld = withheldLine(report.totals);
|
|
292
297
|
const log = {
|
|
293
298
|
$schema: SCHEMA,
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { CHUNK_LENGTH } from "../json-chunks.ts";
|
|
11
11
|
import { RenderError } from "./reporter.ts";
|
|
12
12
|
import { utf8Bytes } from "./sha256.ts";
|
|
13
|
+
import { bytewise } from "../text/compare.ts";
|
|
13
14
|
|
|
14
15
|
/** A template that does not parse, which refuses the invocation before any analysis. */
|
|
15
16
|
export class TemplateError extends Error {
|
|
@@ -618,7 +619,7 @@ function formatted(value: Value, nested: boolean): string {
|
|
|
618
619
|
}
|
|
619
620
|
const object: ValueObject = value;
|
|
620
621
|
const entries = Object.keys(object)
|
|
621
|
-
.sort()
|
|
622
|
+
.sort(bytewise)
|
|
622
623
|
.map((key) => `${key}:${formatted(object[key] ?? null, true)}`);
|
|
623
624
|
return `map[${entries.join(" ")}]`;
|
|
624
625
|
}
|
|
@@ -647,28 +648,11 @@ function compareBasic(name: string, a: Value, b: Value): number {
|
|
|
647
648
|
return Number.NaN;
|
|
648
649
|
}
|
|
649
650
|
if (kind === "string") {
|
|
650
|
-
return
|
|
651
|
+
return bytewise(a as string, b as string);
|
|
651
652
|
}
|
|
652
653
|
return (a as number) < (b as number) ? -1 : 1;
|
|
653
654
|
}
|
|
654
655
|
|
|
655
|
-
/**
|
|
656
|
-
* Two strings ordered by the bytes of their UTF-8 encodings, which is the order of their
|
|
657
|
-
* code points: the order of their UTF-16 code units differs above U+FFFF.
|
|
658
|
-
*/
|
|
659
|
-
function byCodePoints(a: string, b: string): number {
|
|
660
|
-
let at = 0;
|
|
661
|
-
while (at < a.length && at < b.length) {
|
|
662
|
-
const x = a.codePointAt(at) ?? 0;
|
|
663
|
-
const y = b.codePointAt(at) ?? 0;
|
|
664
|
-
if (x !== y) {
|
|
665
|
-
return x < y ? -1 : 1;
|
|
666
|
-
}
|
|
667
|
-
at += x > 0xffff ? 2 : 1;
|
|
668
|
-
}
|
|
669
|
-
return a.length - b.length;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
656
|
/** The escapes `%q` writes for the control characters that have one of their own. */
|
|
673
657
|
const QUOTED_ESCAPES: Readonly<Record<string, string>> = {
|
|
674
658
|
'"': '\\"',
|
|
@@ -1022,7 +1006,7 @@ class Execution {
|
|
|
1022
1006
|
entries = value.map((one, i) => [i, one]);
|
|
1023
1007
|
} else if (isObject(value)) {
|
|
1024
1008
|
entries = Object.keys(value)
|
|
1025
|
-
.sort()
|
|
1009
|
+
.sort(bytewise)
|
|
1026
1010
|
.map((key) => [key, value[key] ?? null]);
|
|
1027
1011
|
} else {
|
|
1028
1012
|
throw new RenderError(`range cannot iterate over a ${kindOf(value)}`);
|
package/src/reporters/text.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { coalesced } from "../json-chunks.ts";
|
|
2
|
-
import type { Report,
|
|
2
|
+
import type { Report, Totals } from "../contract/report.generated.ts";
|
|
3
3
|
|
|
4
4
|
/** The kind token and the confidence a stale suppression is rendered with, both fixed. */
|
|
5
5
|
const STALE_KIND = "suppression";
|
|
@@ -11,7 +11,7 @@ function counted(n: number, one: string, many: string): string {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/** The report's totals in one line, in the order the report declares them. */
|
|
14
|
-
function summary(totals:
|
|
14
|
+
function summary(totals: Totals): string {
|
|
15
15
|
const { allow, warn, deny } = totals.by_severity;
|
|
16
16
|
return [
|
|
17
17
|
`summary: ${counted(totals.findings, "finding", "findings")} (${String(allow)} allow, ${String(warn)} warn, ${String(deny)} deny)`,
|
|
@@ -31,7 +31,7 @@ const WITHHELD_ORDER = ["probable", "possible"] as const;
|
|
|
31
31
|
* The line naming what the minimum confidence withheld and the setting that shows it all,
|
|
32
32
|
* or undefined where it withheld nothing.
|
|
33
33
|
*/
|
|
34
|
-
export function withheldLine(totals:
|
|
34
|
+
export function withheldLine(totals: Totals): string | undefined {
|
|
35
35
|
const named = WITHHELD_ORDER.filter((confidence) => totals.withheld[confidence] > 0);
|
|
36
36
|
const lowest = named.at(-1);
|
|
37
37
|
if (lowest === undefined) {
|