@cplieger/deadset-ts 8.0.0-dev.4 → 8.0.0-dev.6
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/src/analysis.js +3 -3
- package/dist/src/config.js +1 -1
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contract/contract.generated.js +1071 -0
- package/dist/src/contract/report.generated.js +796 -0
- package/dist/src/convention-rows.js +1 -1
- package/dist/src/exempt-classes.js +1 -59
- package/dist/src/exempt.js +8 -19
- 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 +9 -8
- package/dist/src/findings/self-check.js +7 -10
- package/dist/src/findings/unused-declarations.js +28 -27
- package/dist/src/findings/visibility-narrowing.js +9 -11
- package/dist/src/findings-pass.js +1 -3
- package/dist/src/index.js +1 -1
- package/dist/src/intra-function-parts.js +8 -13
- package/dist/src/kinds.js +4 -360
- package/dist/src/report-schema.js +273 -0
- package/dist/src/report.js +17 -11
- package/dist/src/resolve.js +1 -6
- package/dist/src/run.js +2 -1
- package/dist/src/setup-failure.js +2 -6
- package/dist/src/suppress-file.js +4 -3
- package/dist/src/suppress-inline.js +3 -2
- package/dist/src/suppress.js +29 -48
- package/dist/src/verbs/analyze.js +7 -2
- package/dist/src/verbs/describe.js +4 -4
- package/dist/src/verbs/explain.js +2 -1
- package/dist/src/verbs/print-config.js +2 -1
- package/dist/src/verbs/print-projects.js +2 -1
- package/dist/src/verbs/print-retained.js +2 -1
- package/dist/src/verbs/print-roots.js +2 -1
- package/dist/src/verbs/verb.js +1 -5
- package/dist/src/verbs/version.js +2 -2
- package/jsr.json +1 -1
- package/package.json +3 -2
- package/src/analysis.ts +3 -3
- package/src/config.ts +7 -13
- package/src/conformance-record.ts +1 -1
- package/src/contract/contract.generated.ts +1123 -0
- package/src/contract/report.generated.ts +1157 -0
- package/src/convention-rows.ts +1 -1
- package/src/exempt-classes.ts +1 -70
- package/src/exempt.ts +12 -20
- 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 +13 -11
- package/src/findings/self-check.ts +8 -15
- package/src/findings/unused-declarations.ts +28 -27
- package/src/findings/visibility-narrowing.ts +9 -12
- package/src/findings-pass.ts +1 -4
- package/src/index.ts +1 -1
- package/src/intra-function-parts.ts +8 -14
- package/src/kinds.ts +6 -406
- package/src/report-schema.ts +366 -0
- package/src/report.ts +42 -195
- 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 +13 -8
- package/src/reporters/text.ts +3 -3
- package/src/resolve.ts +11 -11
- package/src/run.ts +2 -1
- package/src/setup-failure.ts +5 -7
- package/src/suppress-file.ts +3 -4
- package/src/suppress-inline.ts +2 -2
- package/src/suppress.ts +39 -55
- package/src/verbs/analyze.ts +16 -4
- package/src/verbs/describe.ts +10 -4
- package/src/verbs/explain.ts +2 -1
- package/src/verbs/print-config.ts +2 -1
- package/src/verbs/print-projects.ts +2 -1
- package/src/verbs/print-retained.ts +2 -1
- package/src/verbs/print-roots.ts +2 -1
- package/src/verbs/verb.ts +0 -6
- package/src/verbs/version.ts +2 -2
- package/dist/src/version.js +0 -7
- 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 {
|
|
@@ -18,134 +20,12 @@ 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
22
|
|
|
21
|
-
/** The version of the report schema this analyzer writes a report to. */
|
|
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
23
|
/** The name this analyzer writes every document under and mints every identifier with. */
|
|
28
24
|
export const ANALYZER_NAME = "deadset-ts";
|
|
29
25
|
|
|
30
26
|
/** The language this analyzer claims. */
|
|
31
27
|
export const LANGUAGE = "ts";
|
|
32
28
|
|
|
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
|
-
}
|
|
148
|
-
|
|
149
29
|
/** One project configuration of the matrix a run analyzed. */
|
|
150
30
|
export interface ReportConfiguration {
|
|
151
31
|
readonly id: string;
|
|
@@ -191,52 +71,6 @@ interface UnavailableConsumer {
|
|
|
191
71
|
readonly reason: string;
|
|
192
72
|
}
|
|
193
73
|
|
|
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
74
|
/** One hint about the run's setup, as the report schema's `notes` spells it. */
|
|
241
75
|
export type ReportNote =
|
|
242
76
|
| {
|
|
@@ -282,13 +116,16 @@ export interface ReportInput {
|
|
|
282
116
|
* sorted, then the edge evaluations' findings.
|
|
283
117
|
*/
|
|
284
118
|
function componentsOf(
|
|
285
|
-
findings: readonly (readonly [
|
|
286
|
-
): readonly
|
|
287
|
-
const listed = new Map<string,
|
|
288
|
-
for (const [
|
|
119
|
+
findings: readonly (readonly [wire.Finding, CompletedFinding])[],
|
|
120
|
+
): readonly wire.ComponentsEntry[] {
|
|
121
|
+
const listed = new Map<string, wire.ComponentsEntry>();
|
|
122
|
+
for (const [written, finding] of findings) {
|
|
289
123
|
const { members } = finding.component;
|
|
290
|
-
if (members !== undefined && !listed.has(
|
|
291
|
-
listed.set(
|
|
124
|
+
if (members !== undefined && !listed.has(written.component.id)) {
|
|
125
|
+
listed.set(written.component.id, {
|
|
126
|
+
id: written.component.id,
|
|
127
|
+
members: members.map(wireSymbol),
|
|
128
|
+
});
|
|
292
129
|
}
|
|
293
130
|
}
|
|
294
131
|
return [...listed.values()];
|
|
@@ -302,7 +139,7 @@ function compare(a: string, b: string): number {
|
|
|
302
139
|
return a < b ? -1 : 1;
|
|
303
140
|
}
|
|
304
141
|
|
|
305
|
-
function wirePosition(position: FindingPosition):
|
|
142
|
+
function wirePosition(position: FindingPosition): wire.Position {
|
|
306
143
|
return {
|
|
307
144
|
path: position.path,
|
|
308
145
|
line: position.line,
|
|
@@ -314,13 +151,13 @@ function wirePosition(position: FindingPosition): WirePosition {
|
|
|
314
151
|
function wireSymbol(symbol: PositionedSymbol): {
|
|
315
152
|
ref: string;
|
|
316
153
|
name: string;
|
|
317
|
-
position:
|
|
154
|
+
position: wire.Position;
|
|
318
155
|
} {
|
|
319
156
|
return { ref: symbol.ref, name: symbol.name, position: wirePosition(symbol.position) };
|
|
320
157
|
}
|
|
321
158
|
|
|
322
159
|
/** The details one code carries, in the order the finding schema lists them. */
|
|
323
|
-
function wireDetails(details: FindingDetails):
|
|
160
|
+
function wireDetails(details: FindingDetails): wire.Details {
|
|
324
161
|
const { entry } = details;
|
|
325
162
|
return {
|
|
326
163
|
...(details.narrowerVisibility === undefined
|
|
@@ -352,7 +189,7 @@ function wireDetails(details: FindingDetails): WireDetails {
|
|
|
352
189
|
}
|
|
353
190
|
|
|
354
191
|
/** One finding as the finding schema spells it, member for member in its order. */
|
|
355
|
-
export function wireFinding(finding: CompletedFinding):
|
|
192
|
+
export function wireFinding(finding: CompletedFinding): wire.Finding {
|
|
356
193
|
return {
|
|
357
194
|
code: finding.code,
|
|
358
195
|
kind: finding.kind,
|
|
@@ -387,7 +224,7 @@ export function wireFinding(finding: CompletedFinding): WireFinding {
|
|
|
387
224
|
};
|
|
388
225
|
}
|
|
389
226
|
|
|
390
|
-
function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>):
|
|
227
|
+
function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>): wire.EdgeEvaluationsEntry {
|
|
391
228
|
return {
|
|
392
229
|
edge: evaluation.edge,
|
|
393
230
|
side: evaluation.side,
|
|
@@ -397,7 +234,7 @@ function wireEvaluation(evaluation: EdgeEvaluation<CompletedFinding>): WireEdgeE
|
|
|
397
234
|
};
|
|
398
235
|
}
|
|
399
236
|
|
|
400
|
-
function wireStale(stale: StaleSuppression):
|
|
237
|
+
function wireStale(stale: StaleSuppression): wire.StaleSuppressionsEntry {
|
|
401
238
|
return {
|
|
402
239
|
code: stale.code,
|
|
403
240
|
mechanism: stale.mechanism,
|
|
@@ -418,7 +255,7 @@ function wireStale(stale: StaleSuppression): WireStaleSuppression {
|
|
|
418
255
|
}
|
|
419
256
|
|
|
420
257
|
/** The canonical key over two findings: path, line, column, code, then the symbol reference. */
|
|
421
|
-
function canonical(a:
|
|
258
|
+
function canonical(a: wire.Finding, b: wire.Finding): number {
|
|
422
259
|
return (
|
|
423
260
|
compare(a.position.path, b.position.path) ||
|
|
424
261
|
a.position.line - b.position.line ||
|
|
@@ -429,7 +266,7 @@ function canonical(a: WireFinding, b: WireFinding): number {
|
|
|
429
266
|
}
|
|
430
267
|
|
|
431
268
|
/** The canonical key over two stale suppressions, each component from the record's own member. */
|
|
432
|
-
function canonicalStale(a:
|
|
269
|
+
function canonicalStale(a: wire.StaleSuppressionsEntry, b: wire.StaleSuppressionsEntry): number {
|
|
433
270
|
return (
|
|
434
271
|
compare(a.position.path, b.position.path) ||
|
|
435
272
|
a.position.line - b.position.line ||
|
|
@@ -440,7 +277,7 @@ function canonicalStale(a: WireStaleSuppression, b: WireStaleSuppression): numbe
|
|
|
440
277
|
}
|
|
441
278
|
|
|
442
279
|
/** One declared gap as a report carries it, its members in the schema's order. */
|
|
443
|
-
function wireGap(gap: DeclaredGap):
|
|
280
|
+
function wireGap(gap: DeclaredGap): wire.DeclaredGapsEntry {
|
|
444
281
|
return {
|
|
445
282
|
fixture: gap.fixture,
|
|
446
283
|
...(gap.symbol === undefined ? {} : { symbol: gap.symbol }),
|
|
@@ -450,7 +287,7 @@ function wireGap(gap: DeclaredGap): WireDeclaredGap {
|
|
|
450
287
|
}
|
|
451
288
|
|
|
452
289
|
/** Edge evaluations by edge, then side, then their compact encodings. */
|
|
453
|
-
function byEdge(a:
|
|
290
|
+
function byEdge(a: wire.EdgeEvaluationsEntry, b: wire.EdgeEvaluationsEntry): number {
|
|
454
291
|
return (
|
|
455
292
|
compare(a.edge, b.edge) ||
|
|
456
293
|
compare(a.side, b.side) ||
|
|
@@ -462,7 +299,7 @@ function byEdge(a: WireEdgeEvaluation, b: WireEdgeEvaluation): number {
|
|
|
462
299
|
* The lines a reader deletes by acting on every finding: each distinct component a root
|
|
463
300
|
* finding names, counted once however many of its roots the findings hold.
|
|
464
301
|
*/
|
|
465
|
-
function deletableLines(findings: readonly
|
|
302
|
+
function deletableLines(findings: readonly wire.Finding[]): number {
|
|
466
303
|
const counted = new Map<string, number>();
|
|
467
304
|
for (const finding of findings) {
|
|
468
305
|
if (finding.component.root) {
|
|
@@ -477,7 +314,7 @@ function deletableLines(findings: readonly WireFinding[]): number {
|
|
|
477
314
|
}
|
|
478
315
|
|
|
479
316
|
/** The totals of one finding set, counted over the whole set. */
|
|
480
|
-
function totalsOf(findings: readonly
|
|
317
|
+
function totalsOf(findings: readonly wire.Finding[], result: PassResult): wire.Totals {
|
|
481
318
|
const bySeverity = { allow: 0, warn: 0, deny: 0 };
|
|
482
319
|
for (const finding of findings) {
|
|
483
320
|
bySeverity[finding.severity] += 1;
|
|
@@ -500,7 +337,7 @@ function totalsOf(findings: readonly WireFinding[], result: PassResult): WireTot
|
|
|
500
337
|
}
|
|
501
338
|
|
|
502
339
|
/** Builds the report of one run, every array in the order the Contract fixes for it. */
|
|
503
|
-
export function buildReport(input: ReportInput): Report {
|
|
340
|
+
export function buildReport(input: ReportInput): wire.Report {
|
|
504
341
|
const paired = input.result.findings
|
|
505
342
|
.map((finding) => [wireFinding(finding), finding] as const)
|
|
506
343
|
.sort(([a], [b]) => canonical(a, b));
|
|
@@ -521,7 +358,7 @@ export function buildReport(input: ReportInput): Report {
|
|
|
521
358
|
name: ANALYZER_NAME,
|
|
522
359
|
version: input.version,
|
|
523
360
|
languages: [LANGUAGE],
|
|
524
|
-
schema_versions_accepted:
|
|
361
|
+
schema_versions_accepted: SCHEMA_VERSIONS,
|
|
525
362
|
conformance: {
|
|
526
363
|
corpus_version: input.conformance.corpusVersion,
|
|
527
364
|
result: input.conformance.result,
|
|
@@ -590,8 +427,8 @@ export function buildReport(input: ReportInput): Report {
|
|
|
590
427
|
* whose component a deletion shrinks more comes first, then one whose subject is larger,
|
|
591
428
|
* then the canonical key, so the order stays total; by position it is the canonical key.
|
|
592
429
|
*/
|
|
593
|
-
export function sortFindings(report: Report, by: Sort): Report {
|
|
594
|
-
const bySize = (a:
|
|
430
|
+
export function sortFindings(report: wire.Report, by: Sort): wire.Report {
|
|
431
|
+
const bySize = (a: wire.Finding, b: wire.Finding): number =>
|
|
595
432
|
b.component.deletable_lines - a.component.deletable_lines ||
|
|
596
433
|
b.symbol.size_lines - a.symbol.size_lines ||
|
|
597
434
|
canonical(a, b);
|
|
@@ -599,16 +436,26 @@ export function sortFindings(report: Report, by: Sort): Report {
|
|
|
599
436
|
}
|
|
600
437
|
|
|
601
438
|
/**
|
|
602
|
-
* The report holding the first `most` findings of its order, the rest counted as omitted
|
|
603
|
-
*
|
|
439
|
+
* The report holding the first `most` findings of its order, the rest counted as omitted,
|
|
440
|
+
* and listing only the components those findings name. Every other total keeps describing
|
|
441
|
+
* the whole set. Zero keeps every finding.
|
|
604
442
|
*/
|
|
605
|
-
export function capFindings(report: Report, most: number): Report {
|
|
443
|
+
export function capFindings(report: wire.Report, most: number): wire.Report {
|
|
606
444
|
if (most <= 0 || report.findings.length <= most) {
|
|
607
445
|
return report;
|
|
608
446
|
}
|
|
447
|
+
const findings = report.findings.slice(0, most);
|
|
448
|
+
const named = new Set(
|
|
449
|
+
[...findings, ...report.edge_evaluations.flatMap((one) => one.finding ?? [])].map(
|
|
450
|
+
(finding) => finding.component.id,
|
|
451
|
+
),
|
|
452
|
+
);
|
|
609
453
|
return {
|
|
610
454
|
...report,
|
|
611
|
-
findings
|
|
455
|
+
findings,
|
|
456
|
+
...(report.components === undefined
|
|
457
|
+
? {}
|
|
458
|
+
: { components: report.components.filter((listed) => named.has(listed.id)) }),
|
|
612
459
|
totals: { ...report.totals, omitted: report.findings.length - most },
|
|
613
460
|
};
|
|
614
461
|
}
|
|
@@ -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,8 +1,13 @@
|
|
|
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";
|
|
@@ -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> {
|
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) {
|
package/src/resolve.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SEVERITIES,
|
|
3
|
+
LANGUAGES,
|
|
4
|
+
CONFIDENCES,
|
|
5
|
+
FORMATS,
|
|
6
|
+
type Confidence,
|
|
7
|
+
type Format,
|
|
8
|
+
type Language,
|
|
9
|
+
type Severity,
|
|
10
|
+
FAMILY_KEY_LENGTH,
|
|
11
|
+
} from "./contract/contract.generated.ts";
|
|
1
12
|
import {
|
|
2
13
|
ConfigError,
|
|
3
14
|
defaultConfig,
|
|
4
15
|
type BuildConfiguration,
|
|
5
16
|
type Cascade,
|
|
6
17
|
type Config,
|
|
7
|
-
type Confidence,
|
|
8
18
|
type ConsumerTests,
|
|
9
19
|
type DeclarationEntry,
|
|
10
|
-
type Format,
|
|
11
20
|
type GeneratedFiles,
|
|
12
21
|
type Inputs,
|
|
13
|
-
type Language,
|
|
14
22
|
type LifecycleContract,
|
|
15
23
|
type Origin,
|
|
16
24
|
type Provenance,
|
|
17
25
|
type Provider,
|
|
18
|
-
type Severity,
|
|
19
26
|
type Sort,
|
|
20
27
|
type Source,
|
|
21
28
|
type TargetKind,
|
|
@@ -36,9 +43,6 @@ import {
|
|
|
36
43
|
/** Names the command-line flags in a refusal, where a document is named by its path. */
|
|
37
44
|
const FLAGS_LABEL = "the command-line flags";
|
|
38
45
|
|
|
39
|
-
/** The length of a severity key naming a whole family: the prefix and two digits. */
|
|
40
|
-
export const FAMILY_KEY_LENGTH = 4;
|
|
41
|
-
|
|
42
46
|
const SEVERITY_KEY = /^DS[0-9]{2}([0-9]{2})?$/u;
|
|
43
47
|
const EXEMPTION_CLASS = /^[a-z][a-z0-9-]*$/u;
|
|
44
48
|
const CONTRACT_VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+$/u;
|
|
@@ -69,12 +73,8 @@ const PROJECT_SEGMENT = String.raw`(?:[^/.\x00-\x1f\x7f][^/\x00-\x1f\x7f]*|\.[^/
|
|
|
69
73
|
const PROJECT_PATH = new RegExp(`^${PROJECT_SEGMENT}(?:/${PROJECT_SEGMENT})*$`, "u");
|
|
70
74
|
|
|
71
75
|
const TARGET_KINDS: readonly TargetKind[] = ["application", "library"];
|
|
72
|
-
const CONFIDENCES: readonly Confidence[] = ["certain", "probable", "possible"];
|
|
73
76
|
const GENERATED_FILES: readonly GeneratedFiles[] = ["exclude", "include"];
|
|
74
77
|
const CONSUMER_TESTS: readonly ConsumerTests[] = ["test", "production"];
|
|
75
|
-
const LANGUAGES: readonly Language[] = ["go", "ts"];
|
|
76
|
-
const SEVERITIES: readonly Severity[] = ["allow", "warn", "deny"];
|
|
77
|
-
const FORMATS: readonly Format[] = ["text", "json", "github", "sarif", "template"];
|
|
78
78
|
const SORTS: readonly Sort[] = ["position", "size"];
|
|
79
79
|
const CASCADES: readonly Cascade[] = ["roots", "full"];
|
|
80
80
|
|
package/src/run.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EXIT_FAILURE, EXIT_USAGE } from "./contract/contract.generated.ts";
|
|
1
2
|
import { ConfigError, REPOSITORY_DOCUMENT, type Inputs } from "./config.ts";
|
|
2
3
|
import { DiscoveryError, renderDiagnostic } from "./discover.ts";
|
|
3
4
|
import type { Host } from "./host.ts";
|
|
@@ -12,7 +13,7 @@ import { printRootsVerb } from "./verbs/print-roots.ts";
|
|
|
12
13
|
import { ANALYZE_OPTIONS, analyzeVerb } from "./verbs/analyze.ts";
|
|
13
14
|
import { describeVerb } from "./verbs/describe.ts";
|
|
14
15
|
import { EXPLAIN_OPTIONS, explainVerb } from "./verbs/explain.ts";
|
|
15
|
-
import {
|
|
16
|
+
import { type Verb, type VerbOptions } from "./verbs/verb.ts";
|
|
16
17
|
import { versionVerb } from "./verbs/version.ts";
|
|
17
18
|
|
|
18
19
|
/** One output stream of the command line. `process.stdout` and `process.stderr` satisfy it. */
|
package/src/setup-failure.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
+
import { SETUP_FAILURE_PREFIX, type SETUP_CLASSES } from "./contract/contract.generated.ts";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* A setup failure: a file or a component the analysis needs and the project does not
|
|
3
5
|
* provide. Each one names what is missing and the fix, on one line of the error stream
|
|
4
6
|
* that starts with its class.
|
|
5
7
|
*/
|
|
6
8
|
|
|
7
|
-
/**
|
|
8
|
-
type SetupClass =
|
|
9
|
-
| "missing-module"
|
|
10
|
-
| "missing-consumer"
|
|
11
|
-
| "workspace-member-without-source"
|
|
12
|
-
| "convention-not-literal";
|
|
9
|
+
/** A class of setup failure the Contract names. */
|
|
10
|
+
type SetupClass = (typeof SETUP_CLASSES)[number]["class"];
|
|
13
11
|
|
|
14
12
|
export interface SetupFailure {
|
|
15
13
|
readonly setupClass: SetupClass;
|
|
@@ -19,7 +17,7 @@ export interface SetupFailure {
|
|
|
19
17
|
|
|
20
18
|
/** The failure as the one line of the error stream that names it. */
|
|
21
19
|
export function setupLine(failure: SetupFailure): string {
|
|
22
|
-
return
|
|
20
|
+
return `${SETUP_FAILURE_PREFIX}${failure.setupClass}: ${failure.detail}`;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
/**
|
package/src/suppress-file.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* ignore file by gaining a maintainer's reason.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { CODE } from "./contract/contract.generated.ts";
|
|
8
9
|
import type { Host } from "./host.ts";
|
|
9
10
|
import type { InventorySymbol } from "./inventory.ts";
|
|
10
11
|
import { jsonChunks } from "./json-chunks.ts";
|
|
@@ -14,9 +15,7 @@ import type { Position } from "./position.ts";
|
|
|
14
15
|
import { declarationKey } from "./ref.ts";
|
|
15
16
|
import {
|
|
16
17
|
CODE_FORM,
|
|
17
|
-
SUPPRESSION_WITHOUT_REASON,
|
|
18
18
|
SuppressionError,
|
|
19
|
-
UNSCOPED_ENTRY,
|
|
20
19
|
blankReason,
|
|
21
20
|
isEntryPath,
|
|
22
21
|
isPartKind,
|
|
@@ -148,10 +147,10 @@ function readRecord(value: unknown, site: Position, byReference: ByReference, sh
|
|
|
148
147
|
};
|
|
149
148
|
const refusals: Refusal[] = [];
|
|
150
149
|
if (blankReason(reason)) {
|
|
151
|
-
refusals.push({ ...written, reported:
|
|
150
|
+
refusals.push({ ...written, reported: CODE.suppressionWithoutReason });
|
|
152
151
|
}
|
|
153
152
|
if (written.path === "") {
|
|
154
|
-
refusals.push({ ...written, reported:
|
|
153
|
+
refusals.push({ ...written, reported: CODE.unscopedIgnoreEntry });
|
|
155
154
|
}
|
|
156
155
|
if (refusals.length > 0) {
|
|
157
156
|
return { records: [], refusals };
|
package/src/suppress-inline.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* below it, or on its own line where it trails a token there.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { CODE } from "./contract/contract.generated.ts";
|
|
7
8
|
import {
|
|
8
9
|
isConstructorDeclaration,
|
|
9
10
|
SyntaxKind,
|
|
@@ -18,7 +19,6 @@ import type { ProjectView } from "./session.ts";
|
|
|
18
19
|
import { signatureOf } from "./signatures.ts";
|
|
19
20
|
import {
|
|
20
21
|
isPartKind,
|
|
21
|
-
SUPPRESSION_WITHOUT_REASON,
|
|
22
22
|
SuppressionError,
|
|
23
23
|
siteText,
|
|
24
24
|
type Refusal,
|
|
@@ -354,7 +354,7 @@ export function inlineSuppressions(
|
|
|
354
354
|
case "no-reason":
|
|
355
355
|
for (const code of directive.codes) {
|
|
356
356
|
refusals.push({
|
|
357
|
-
reported:
|
|
357
|
+
reported: CODE.suppressionWithoutReason,
|
|
358
358
|
code,
|
|
359
359
|
symbol: "",
|
|
360
360
|
path: site.path,
|