pi-lens 4.2.0 → 4.3.0
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/CHANGELOG.md +543 -0
- package/README.md +45 -2
- package/dist/clients/actionable-warnings.js +44 -16
- package/dist/clients/advisory-provenance.js +187 -51
- package/dist/clients/cache-manager.js +64 -5
- package/dist/clients/cascade-format.js +24 -0
- package/dist/clients/child-unref.js +17 -8
- package/dist/clients/config-diagnostic-codes.js +2 -0
- package/dist/clients/config-locations.js +237 -9
- package/dist/clients/config-resolve.js +7 -1
- package/dist/clients/degradation-ledger.js +33 -3
- package/dist/clients/dependency-checker.js +87 -64
- package/dist/clients/diagnostic-dispositions.js +39 -3
- package/dist/clients/dispatch/finding-policy.js +118 -1
- package/dist/clients/dispatch/indent-detect.js +8 -2
- package/dist/clients/dispatch/inline-suppressions.js +23 -4
- package/dist/clients/dispatch/rule-ignores.js +14 -7
- package/dist/clients/dispatch/runners/actionlint.js +26 -28
- package/dist/clients/dispatch/runners/biome-check.js +38 -25
- package/dist/clients/dispatch/runners/cpp-check.js +32 -49
- package/dist/clients/dispatch/runners/credo.js +16 -14
- package/dist/clients/dispatch/runners/cue-vet.js +25 -7
- package/dist/clients/dispatch/runners/dart-analyze.js +28 -47
- package/dist/clients/dispatch/runners/detekt.js +11 -4
- package/dist/clients/dispatch/runners/dotnet-build.js +27 -22
- package/dist/clients/dispatch/runners/elixir-check.js +31 -46
- package/dist/clients/dispatch/runners/eslint.js +29 -46
- package/dist/clients/dispatch/runners/gleam-check.js +91 -11
- package/dist/clients/dispatch/runners/go-vet.js +16 -2
- package/dist/clients/dispatch/runners/golangci-lint.js +38 -23
- package/dist/clients/dispatch/runners/hadolint.js +27 -15
- package/dist/clients/dispatch/runners/htmlhint.js +17 -8
- package/dist/clients/dispatch/runners/javac.js +31 -21
- package/dist/clients/dispatch/runners/ktlint.js +34 -60
- package/dist/clients/dispatch/runners/lsp.js +6 -5
- package/dist/clients/dispatch/runners/markdownlint.js +12 -5
- package/dist/clients/dispatch/runners/mypy.js +2 -1
- package/dist/clients/dispatch/runners/oxlint.js +22 -6
- package/dist/clients/dispatch/runners/php-lint.js +39 -16
- package/dist/clients/dispatch/runners/phpstan.js +1 -1
- package/dist/clients/dispatch/runners/rubocop.js +27 -18
- package/dist/clients/dispatch/runners/ruff.js +18 -19
- package/dist/clients/dispatch/runners/rust-clippy.js +6 -1
- package/dist/clients/dispatch/runners/shellcheck.js +6 -2
- package/dist/clients/dispatch/runners/spellcheck.js +10 -3
- package/dist/clients/dispatch/runners/sqlfluff.js +11 -2
- package/dist/clients/dispatch/runners/stylelint.js +10 -2
- package/dist/clients/dispatch/runners/swiftlint.js +11 -2
- package/dist/clients/dispatch/runners/taplo.js +12 -6
- package/dist/clients/dispatch/runners/tflint.js +30 -15
- package/dist/clients/dispatch/runners/tree-sitter.js +23 -0
- package/dist/clients/dispatch/runners/trivy-config.js +8 -2
- package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +11 -1
- package/dist/clients/dispatch/runners/vale.js +15 -3
- package/dist/clients/dispatch/runners/yamllint.js +12 -2
- package/dist/clients/dispatch/runners/zig-check.js +27 -39
- package/dist/clients/dispatch/suppress-writer.js +13 -3
- package/dist/clients/effective-config.js +3 -1
- package/dist/clients/extension-log.js +48 -1
- package/dist/clients/finding-delivery-gate.js +53 -23
- package/dist/clients/formatters.js +49 -11
- package/dist/clients/git-guard.js +247 -34
- package/dist/clients/gradle-ktfmt-style.js +121 -25
- package/dist/clients/inline-blocker-dispositions.js +186 -0
- package/dist/clients/install-diagnostics.js +19 -6
- package/dist/clients/installer/index.js +281 -32
- package/dist/clients/jscpd-client.js +8 -0
- package/dist/clients/latency-logger.js +8 -0
- package/dist/clients/lens-config.js +66 -8
- package/dist/clients/lsp/cascade-tier.js +3 -0
- package/dist/clients/lsp/client.js +139 -11
- package/dist/clients/lsp/config.js +9 -7
- package/dist/clients/lsp/content-limits.js +41 -0
- package/dist/clients/lsp/index.js +42 -8
- package/dist/clients/lsp/launch.js +23 -2
- package/dist/clients/lsp/server.js +106 -25
- package/dist/clients/lsp/sync-kind.js +14 -0
- package/dist/clients/lsp/wait-policy/strategies.js +105 -16
- package/dist/clients/mcp/analyze.js +9 -2
- package/dist/clients/mcp/ipc.js +232 -44
- package/dist/clients/mcp/review.js +53 -7
- package/dist/clients/opengrep-client.js +11 -6
- package/dist/clients/path-utils.js +51 -9
- package/dist/clients/persistent-reverify.js +271 -0
- package/dist/clients/pipeline.js +136 -67
- package/dist/clients/project-diagnostics/fresh-fetch.js +21 -3
- package/dist/clients/project-lens-config.js +11 -3
- package/dist/clients/review-graph/builder.js +34 -26
- package/dist/clients/review-graph/git-identity.js +16 -10
- package/dist/clients/ruff-client.js +31 -4
- package/dist/clients/runtime-coordinator.js +38 -4
- package/dist/clients/runtime-tool-call.js +16 -5
- package/dist/clients/runtime-tool-result.js +27 -12
- package/dist/clients/runtime-turn.js +571 -287
- package/dist/clients/safe-spawn.js +256 -65
- package/dist/clients/skills-resolver.js +38 -2
- package/dist/clients/spawn-output-cap.js +84 -1
- package/dist/clients/tree-sitter-client.js +119 -58
- package/dist/clients/turn-end/lane.js +51 -0
- package/dist/clients/turn-end/lanes/govulncheck.js +112 -0
- package/dist/clients/turn-end/lanes/secrets.js +204 -0
- package/dist/clients/warm-attach.js +59 -0
- package/dist/clients/widget-state.js +294 -17
- package/dist/clients/workspace-topology.js +57 -3
- package/dist/clients/zizmor-config.js +26 -13
- package/dist/index.js +6342 -3995
- package/dist/mcp/analyze-cli.js +4 -0
- package/dist/mcp/server.js +33 -21
- package/dist/scripts/lib/web-tree-sitter-dir.mjs +123 -0
- package/dist/tools/lens-diagnostic-mark.js +14 -2
- package/dist/tools/lens-diagnostics.js +127 -33
- package/dist/tools/lsp-diagnostics.js +150 -12
- package/docs/adr/0001-stale-advisory-live-arm.md +29 -0
- package/docs/adr/0002-workspace-hash-rendezvous.md +30 -0
- package/docs/adr/0003-git-guard-latch-writer.md +203 -0
- package/docs/adr/0004-disposition-policy-seam.md +38 -0
- package/docs/adr/0005-tool-availability-enforcement-seam.md +29 -0
- package/docs/adr/0006-derived-state-benchmark-first.md +31 -0
- package/docs/adr/0007-end-to-end-witness-per-seam-slice.md +38 -0
- package/docs/adr/0008-turn-end-lane-interface.md +165 -0
- package/docs/adr/0009-reported-path-attribution.md +114 -0
- package/docs/adr/README.md +26 -0
- package/docs/agent-tools.md +18 -1
- package/docs/ast-grep_rules_catalog.md +4 -3
- package/docs/configuration.md +55 -1
- package/docs/engineering-principles.md +202 -0
- package/docs/environment-variables.md +61 -4
- package/docs/features.md +2 -2
- package/docs/globalconfig.md +6 -1
- package/docs/language-coverage.md +17 -0
- package/docs/lsp-capability-matrix.md +98 -31
- package/docs/pi-lens-fixer.md +57 -3
- package/docs/pi-lens-investigator.md +9 -0
- package/docs/pi-lens-reviewer.md +16 -0
- package/docs/pi-lens-subagent.md +9 -0
- package/docs/public-api-stability.md +1 -0
- package/docs/release-qa-baseline.md +2 -0
- package/docs/servercapabilities.md +21 -11
- package/docs/settings.md +12 -3
- package/docs/subagent-compat.md +1 -1
- package/package.json +5 -3
- package/rules/ast-grep-rules/rule-tests/no-assert-tuple-test.yml +13 -0
- package/rules/ast-grep-rules/rule-tests/no-boolean-in-except-test.yml +26 -0
- package/rules/ast-grep-rules/rule-tests/no-http-headers-bracket-access-test.yml +10 -0
- package/rules/ast-grep-rules/rule-tests/unchecked-numeric-parse-python-test.yml +18 -0
- package/rules/ast-grep-rules/rule-tests/unchecked-throwing-call-python-test.yml +4 -3
- package/rules/ast-grep-rules/rules/go-no-fmt-println.yml +2 -2
- package/rules/ast-grep-rules/rules/no-assert-tuple.yml +23 -13
- package/rules/ast-grep-rules/rules/no-boolean-in-except.yml +7 -3
- package/rules/ast-grep-rules/rules/no-console-except-error-js.yml +2 -2
- package/rules/ast-grep-rules/rules/no-console-except-error.yml +2 -2
- package/rules/ast-grep-rules/rules/no-http-headers-bracket-access.yml +12 -7
- package/rules/ast-grep-rules/rules/unchecked-numeric-parse-python.yml +44 -0
- package/rules/ast-grep-rules/rules/unchecked-throwing-call-python.yml +5 -3
- package/rules/tree-sitter-queries/typescript/console-statement.yml +2 -2
- package/scripts/install-selftest.mjs +21 -17
- package/scripts/lib/web-tree-sitter-dir.mjs +123 -0
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import * as fs from "node:fs";
|
|
23
23
|
import * as path from "node:path";
|
|
24
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
24
25
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
25
26
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
26
27
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -61,8 +62,9 @@ function findShellcheckConfig(cwd) {
|
|
|
61
62
|
*
|
|
62
63
|
* Levels: "error", "warning", "info", "style"
|
|
63
64
|
*/
|
|
64
|
-
function parseShellcheckOutput(raw, filePath) {
|
|
65
|
+
function parseShellcheckOutput(raw, filePath, cwd) {
|
|
65
66
|
const diagnostics = [];
|
|
67
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
66
68
|
if (!raw.trim()) {
|
|
67
69
|
return diagnostics;
|
|
68
70
|
}
|
|
@@ -74,6 +76,8 @@ function parseShellcheckOutput(raw, filePath) {
|
|
|
74
76
|
for (const item of parsed) {
|
|
75
77
|
if (!item.message || !item.line)
|
|
76
78
|
continue;
|
|
79
|
+
if (!item.file || !pathsEqual(path.resolve(cwd, item.file), absTarget))
|
|
80
|
+
continue;
|
|
77
81
|
// Map shellcheck levels to our severity
|
|
78
82
|
const severityMap = {
|
|
79
83
|
error: "error",
|
|
@@ -157,7 +161,7 @@ const shellcheckRunner = {
|
|
|
157
161
|
}
|
|
158
162
|
// Parse diagnostics
|
|
159
163
|
const raw = result.stdout + result.stderr;
|
|
160
|
-
const diagnostics = parseShellcheckOutput(raw, ctx.filePath);
|
|
164
|
+
const diagnostics = parseShellcheckOutput(raw, ctx.filePath, cwd);
|
|
161
165
|
return finishParsedRun({
|
|
162
166
|
tool: "shellcheck",
|
|
163
167
|
ctx,
|
|
@@ -18,12 +18,14 @@
|
|
|
18
18
|
* Install: cargo install typos-cli
|
|
19
19
|
* Or: npm install -g typos-cli (if wrapped)
|
|
20
20
|
*/
|
|
21
|
+
import * as path from "node:path";
|
|
22
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
21
23
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
22
24
|
import { PRIORITY } from "../priorities.js";
|
|
23
25
|
import { createAvailabilityChecker, resolveRunnerCwd, } from "./utils/runner-helpers.js";
|
|
24
26
|
import { parseToolRun } from "./utils/tool-failure.js";
|
|
25
27
|
const typos = createAvailabilityChecker("typos", ".exe");
|
|
26
|
-
// typos-cli
|
|
28
|
+
// EXIT TABLE (typos-cli 1.29 docs https://github.com/crate-ci/typos): 0 = no typos, 2 = typos found, 1 = an error that
|
|
27
29
|
// stopped the scan. Anything nonzero outside {2} is a rejected invocation.
|
|
28
30
|
const TYPOS_EXIT_CODES = { ran: [2] };
|
|
29
31
|
/**
|
|
@@ -38,8 +40,9 @@ const TYPOS_EXIT_CODES = { ran: [2] };
|
|
|
38
40
|
* "corrections": ["receive"]
|
|
39
41
|
* }
|
|
40
42
|
*/
|
|
41
|
-
function parseTyposOutput(raw, filePath) {
|
|
43
|
+
function parseTyposOutput(raw, filePath, cwd) {
|
|
42
44
|
const diagnostics = [];
|
|
45
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
43
46
|
if (!raw.trim()) {
|
|
44
47
|
return diagnostics;
|
|
45
48
|
}
|
|
@@ -52,6 +55,10 @@ function parseTyposOutput(raw, filePath) {
|
|
|
52
55
|
const parsed = JSON.parse(line);
|
|
53
56
|
if (!parsed.typo || !parsed.line_num)
|
|
54
57
|
continue;
|
|
58
|
+
// #3295: every typos JSONL record names its own `path`; a `typos.toml`
|
|
59
|
+
// `files.extend-exclude` or a directory argv makes that a second file.
|
|
60
|
+
if (parsed.path && !pathsEqual(path.resolve(cwd, parsed.path), absTarget))
|
|
61
|
+
continue;
|
|
55
62
|
const corrections = parsed.corrections?.join(", ") || "no suggestions";
|
|
56
63
|
const message = `Typo: "${parsed.typo}" → ${corrections}`;
|
|
57
64
|
diagnostics.push({
|
|
@@ -104,7 +111,7 @@ const spellcheckRunner = {
|
|
|
104
111
|
// yield nothing leaves a record. `parseOutput` keeps the historical
|
|
105
112
|
// split: the gate judges "did it run" on stdout, the parser reads both
|
|
106
113
|
// streams.
|
|
107
|
-
const run = parseToolRun("spellcheck", { result, exitCodes: TYPOS_EXIT_CODES }, (out) => parseTyposOutput(out, ctx.filePath), { parseOutput: `${result.stdout ?? ""}${result.stderr ?? ""}` });
|
|
114
|
+
const run = parseToolRun("spellcheck", { result, exitCodes: TYPOS_EXIT_CODES }, (out) => parseTyposOutput(out, ctx.filePath, cwd), { parseOutput: `${result.stdout ?? ""}${result.stderr ?? ""}` });
|
|
108
115
|
if (run.skipped)
|
|
109
116
|
return run.skipped;
|
|
110
117
|
const diagnostics = run.diagnostics;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
1
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
2
4
|
import { logRunnerAdvisoryOnce, resolveRunnerCwd } from "../../tool-cwd.js";
|
|
3
5
|
import { getLinterPolicyForCwd, hasSqlfluffConfig } from "../../tool-policy.js";
|
|
@@ -11,6 +13,7 @@ const sqlfluff = createAvailabilityChecker("sqlfluff", ".exe");
|
|
|
11
13
|
// Only 2 is a rejected invocation.
|
|
12
14
|
//
|
|
13
15
|
// The exit TABLE is what protects this runner, not the nothing-to-parse rule:
|
|
16
|
+
// EXIT TABLE (SQLFluff 3.1 docs https://docs.sqlfluff.com/en/stable/production.html): 0 clean; 1 findings/error; other nonzero rejected.
|
|
14
17
|
// sqlfluff writes its user errors to STDOUT, not stderr (live: an unknown
|
|
15
18
|
// dialect prints the error there), so stdout is non-empty and the fallback
|
|
16
19
|
// rule would read the failed run as a run that found nothing.
|
|
@@ -73,7 +76,7 @@ const SQLFLUFF_FIXABLE_RULES = new Set([
|
|
|
73
76
|
"ST01",
|
|
74
77
|
"ST02",
|
|
75
78
|
]);
|
|
76
|
-
function parseSqlfluffOutput(raw, filePath) {
|
|
79
|
+
function parseSqlfluffOutput(raw, filePath, cwd) {
|
|
77
80
|
if (!raw.trim())
|
|
78
81
|
return [];
|
|
79
82
|
try {
|
|
@@ -81,7 +84,13 @@ function parseSqlfluffOutput(raw, filePath) {
|
|
|
81
84
|
if (!Array.isArray(parsed))
|
|
82
85
|
return [];
|
|
83
86
|
const diagnostics = [];
|
|
87
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
84
88
|
for (const item of parsed) {
|
|
89
|
+
// #3295: sqlfluff's JSON is an array of FILES; a jinja `{% include %}`
|
|
90
|
+
// or a directory argv puts a second one in it.
|
|
91
|
+
if (item.filepath &&
|
|
92
|
+
!pathsEqual(path.resolve(cwd, item.filepath), absTarget))
|
|
93
|
+
continue;
|
|
85
94
|
for (const v of item.violations ?? []) {
|
|
86
95
|
if (!v.description)
|
|
87
96
|
continue;
|
|
@@ -158,7 +167,7 @@ const sqlfluffRunner = {
|
|
|
158
167
|
// reported the SQL file as clean.
|
|
159
168
|
// #1948: sqlfluff exit 1 with a JSON report we read nothing out of is a
|
|
160
169
|
// parser break, not a clean file.
|
|
161
|
-
const run = parseToolRun("sqlfluff", { result, exitCodes: SQLFLUFF_EXIT_CODES }, (out) => parseSqlfluffOutput(out, ctx.filePath), { parseOutput: result.stdout ?? "" });
|
|
170
|
+
const run = parseToolRun("sqlfluff", { result, exitCodes: SQLFLUFF_EXIT_CODES }, (out) => parseSqlfluffOutput(out, ctx.filePath, cwd), { parseOutput: result.stdout ?? "" });
|
|
162
171
|
if (run.skipped)
|
|
163
172
|
return run.skipped;
|
|
164
173
|
const diagnostics = run.diagnostics;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
4
|
import { logRunnerAdvisoryOnce, resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { getLinterPolicyForCwd, hasStylelintConfig, } from "../../tool-policy.js";
|
|
@@ -18,6 +19,7 @@ const stylelint = createAvailabilityChecker("stylelint", ".cmd");
|
|
|
18
19
|
// exists to name.
|
|
19
20
|
//
|
|
20
21
|
// #1937 corrected the safety argument that used to sit here: it claimed a
|
|
22
|
+
// EXIT TABLE (Stylelint 16 docs https://stylelint.io/user-guide/cli): 0 clean; 1 findings; 2 fatal/error; other nonzero rejected.
|
|
21
23
|
// fatal stylelint "writes nothing to stdout", which is true but useless,
|
|
22
24
|
// because a stylelint that DID find problems writes nothing to stdout either.
|
|
23
25
|
// `stylelintReport` below is what now separates the two.
|
|
@@ -80,11 +82,17 @@ const STYLELINT_FIXABLE_RULES = new Set([
|
|
|
80
82
|
"string-quotes",
|
|
81
83
|
"value-no-vendor-prefix",
|
|
82
84
|
]);
|
|
83
|
-
function parseStylelintJson(raw, filePath) {
|
|
85
|
+
function parseStylelintJson(raw, filePath, cwd) {
|
|
84
86
|
try {
|
|
85
87
|
const results = JSON.parse(raw);
|
|
86
88
|
const diagnostics = [];
|
|
89
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
87
90
|
for (const result of results) {
|
|
91
|
+
// #3295: stylelint keys each result by its own `source`; a config
|
|
92
|
+
// `files`/`overrides` entry or an `@import` makes that a DIFFERENT file.
|
|
93
|
+
if (result.source &&
|
|
94
|
+
!pathsEqual(path.resolve(cwd, result.source), absTarget))
|
|
95
|
+
continue;
|
|
88
96
|
for (const w of result.warnings) {
|
|
89
97
|
const severity = w.severity === "error" ? "error" : "warning";
|
|
90
98
|
const fixable = STYLELINT_FIXABLE_RULES.has(w.rule);
|
|
@@ -143,7 +151,7 @@ const stylelintRunner = {
|
|
|
143
151
|
const raw = stylelintReport(result.stdout, result.stderr);
|
|
144
152
|
// #1948: the stdout-vs-stderr report bug landed here as exit 2 plus a
|
|
145
153
|
// full JSON report and zero parsed warnings. That now leaves a record.
|
|
146
|
-
const run = parseToolRun("stylelint", { result, output: raw, exitCodes: STYLELINT_EXIT_CODES }, (out) => parseStylelintJson(out, ctx.filePath));
|
|
154
|
+
const run = parseToolRun("stylelint", { result, output: raw, exitCodes: STYLELINT_EXIT_CODES }, (out) => parseStylelintJson(out, ctx.filePath, cwd));
|
|
147
155
|
if (run.skipped)
|
|
148
156
|
return run.skipped;
|
|
149
157
|
const diagnostics = run.diagnostics;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
4
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -68,10 +69,11 @@ const SWIFTLINT_FIXABLE_RULES = new Set([
|
|
|
68
69
|
"void_return",
|
|
69
70
|
"yoda_condition",
|
|
70
71
|
]);
|
|
71
|
-
function parseSwiftLintOutput(raw, filePath) {
|
|
72
|
+
function parseSwiftLintOutput(raw, filePath, cwd) {
|
|
72
73
|
const diagnostics = [];
|
|
73
74
|
if (!raw.trim())
|
|
74
75
|
return diagnostics;
|
|
76
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
75
77
|
try {
|
|
76
78
|
const parsed = JSON.parse(raw);
|
|
77
79
|
if (!Array.isArray(parsed))
|
|
@@ -79,6 +81,9 @@ function parseSwiftLintOutput(raw, filePath) {
|
|
|
79
81
|
for (const item of parsed) {
|
|
80
82
|
if (!item.reason)
|
|
81
83
|
continue;
|
|
84
|
+
// #3295: `.swiftlint.yml` `included:` widens the run past the argv.
|
|
85
|
+
if (item.file && !pathsEqual(path.resolve(cwd, item.file), absTarget))
|
|
86
|
+
continue;
|
|
82
87
|
const severityMap = {
|
|
83
88
|
error: "error",
|
|
84
89
|
warning: "warning",
|
|
@@ -135,7 +140,11 @@ const swiftlintRunner = {
|
|
|
135
140
|
// never ran.
|
|
136
141
|
// SwiftLint exits non-zero on violations — stdout still has the JSON, so
|
|
137
142
|
// a nonzero exit whose JSON yields nothing is a parser break (#1948).
|
|
138
|
-
const run = parseToolRun("swiftlint", {
|
|
143
|
+
const run = parseToolRun("swiftlint", {
|
|
144
|
+
result,
|
|
145
|
+
// EXIT TABLE (SwiftLint 0.56 measured fixture): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
146
|
+
exitCodes: { ran: [1, 2] },
|
|
147
|
+
}, (out) => parseSwiftLintOutput(out, ctx.filePath, cwd));
|
|
139
148
|
if (run.skipped)
|
|
140
149
|
return run.skipped;
|
|
141
150
|
const diagnostics = run.diagnostics;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { findLocalBinUpwards } from "../../package-manager.js";
|
|
4
|
+
import { stripAnsi } from "../../sanitize.js";
|
|
3
5
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
4
6
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
5
7
|
import { getLinterPolicyForCwd } from "../../tool-policy.js";
|
|
@@ -12,6 +14,7 @@ const taplo = createAvailabilityChecker("taplo", ".exe");
|
|
|
12
14
|
//
|
|
13
15
|
// #1937 round 2: the parser fix alone turned the old silent-clean into a false
|
|
14
16
|
// BLOCKING diagnostic. clap prints `error: unexpected argument '--output'
|
|
17
|
+
// EXIT TABLE (taplo 0.9 measured fixture): 0 clean; 1 findings; other nonzero rejected/error.
|
|
15
18
|
// found`, which starts with the same lowercase `error:` a real taplo
|
|
16
19
|
// diagnostic does, so a mistyped flag would have reddened every valid TOML
|
|
17
20
|
// file. Both readings are wrong; the exit code is what separates them.
|
|
@@ -21,7 +24,7 @@ const HEADER_PATTERN = /^\s*(error|warning):(.*)$/;
|
|
|
21
24
|
/** The box-drawing lead taplo puts on a codespan location line. */
|
|
22
25
|
const LOCATION_LEAD = "┌─";
|
|
23
26
|
/** The `:line:column` tail of a codespan location line. */
|
|
24
|
-
const LOCATION_PATTERN =
|
|
27
|
+
const LOCATION_PATTERN = /^\s*┌─\s+(.+?):(\d+):(\d+)$/;
|
|
25
28
|
/**
|
|
26
29
|
* Parse `taplo lint` output (#1937).
|
|
27
30
|
*
|
|
@@ -48,9 +51,10 @@ const LOCATION_PATTERN = /:(\d+):(\d+)$/;
|
|
|
48
51
|
* (#1937 round 2). The exit-code table in the runner is the primary guard;
|
|
49
52
|
* this is the structural one, and each covers a case the other does not.
|
|
50
53
|
*/
|
|
51
|
-
export function parseTaploOutput(raw, filePath) {
|
|
54
|
+
export function parseTaploOutput(raw, filePath, cwd) {
|
|
52
55
|
const diagnostics = [];
|
|
53
|
-
const
|
|
56
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
57
|
+
const lines = stripAnsi(raw ?? "").split(/\r?\n/);
|
|
54
58
|
for (let i = 0; i < lines.length; i++) {
|
|
55
59
|
// Lowercase `error:`/`warning:` starts a diagnostic. taplo's tracing
|
|
56
60
|
// lines are uppercase (`ERROR taplo:lint_files: ...`), so a
|
|
@@ -79,8 +83,10 @@ export function parseTaploOutput(raw, filePath) {
|
|
|
79
83
|
const location = LOCATION_PATTERN.exec(lines[j].trimEnd());
|
|
80
84
|
if (!location)
|
|
81
85
|
continue;
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
if (!pathsEqual(path.resolve(cwd, location[1].trim()), absTarget))
|
|
87
|
+
continue;
|
|
88
|
+
line = Number.parseInt(location[2], 10) || 1;
|
|
89
|
+
column = Number.parseInt(location[3], 10) || 1;
|
|
84
90
|
break;
|
|
85
91
|
}
|
|
86
92
|
if (line === null)
|
|
@@ -155,7 +161,7 @@ const taploRunner = {
|
|
|
155
161
|
// outright lie, so taplo opts into `skipWhenParsedNothing` (#1948). This
|
|
156
162
|
// replaces a hand-rolled per-runner copy of the same rule, which wrote
|
|
157
163
|
// its own `runner-empty-result` row with its own wording.
|
|
158
|
-
const run = parseToolRun("taplo", { result, output: raw, exitCodes: TAPLO_EXIT_CODES }, (out) => parseTaploOutput(out, ctx.filePath), { skipWhenParsedNothing: true });
|
|
164
|
+
const run = parseToolRun("taplo", { result, output: raw, exitCodes: TAPLO_EXIT_CODES }, (out) => parseTaploOutput(out, ctx.filePath, cwd), { skipWhenParsedNothing: true });
|
|
159
165
|
if (run.skipped)
|
|
160
166
|
return run.skipped;
|
|
161
167
|
const diagnostics = run.diagnostics;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
4
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { getLinterPolicyForCwd } from "../../tool-policy.js";
|
|
@@ -23,24 +24,34 @@ function findTflintConfig(fileDir) {
|
|
|
23
24
|
const dir = findNearestDirWithAnyBasename(fileDir, [TFLINT_CONFIG]);
|
|
24
25
|
return dir ? path.join(dir, TFLINT_CONFIG) : null;
|
|
25
26
|
}
|
|
26
|
-
function parseTflintOutput(raw, filePath) {
|
|
27
|
+
function parseTflintOutput(raw, filePath, toolCwd) {
|
|
27
28
|
try {
|
|
28
29
|
const parsed = JSON.parse(raw);
|
|
29
30
|
const issues = parsed.issues ?? [];
|
|
30
|
-
|
|
31
|
+
// #3295: tflint loads EVERY `*.tf` in its working directory — `--filter` is
|
|
32
|
+
// best-effort — and names each issue's own `range.filename`, relative to
|
|
33
|
+
// the directory it ran in (`toolCwd`, which is the file's dir, not the
|
|
34
|
+
// runner cwd).
|
|
35
|
+
const absTarget = path.resolve(toolCwd, filePath);
|
|
36
|
+
return issues.flatMap((issue) => {
|
|
37
|
+
if (issue.range?.filename &&
|
|
38
|
+
!pathsEqual(path.resolve(toolCwd, issue.range.filename), absTarget))
|
|
39
|
+
return [];
|
|
31
40
|
const severity = issue.rule.severity === "error" ? "error" : "warning";
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
id: `tflint-${issue.rule.name}-${issue.range.start.line}`,
|
|
44
|
+
message: `[${issue.rule.name}] ${issue.message}`,
|
|
45
|
+
filePath,
|
|
46
|
+
line: issue.range.start.line,
|
|
47
|
+
column: issue.range.start.column,
|
|
48
|
+
severity,
|
|
49
|
+
semantic: severity === "error" ? "blocking" : "warning",
|
|
50
|
+
tool: "tflint",
|
|
51
|
+
rule: issue.rule.name,
|
|
52
|
+
fixable: false,
|
|
53
|
+
},
|
|
54
|
+
];
|
|
44
55
|
});
|
|
45
56
|
}
|
|
46
57
|
catch {
|
|
@@ -88,7 +99,11 @@ const tflintRunner = {
|
|
|
88
99
|
// not a clean file. No exit-code table: tflint's nonzero codes are not
|
|
89
100
|
// verified against a real binary here, so the conservative
|
|
90
101
|
// nothing-to-parse rule stays the only discriminator.
|
|
91
|
-
const run = parseToolRun("tflint", {
|
|
102
|
+
const run = parseToolRun("tflint", {
|
|
103
|
+
result,
|
|
104
|
+
// EXIT TABLE (TFLint 0.52 measured fixture): 0 clean; 1 error; 2 findings; other nonzero rejected.
|
|
105
|
+
exitCodes: { ran: [1, 2] },
|
|
106
|
+
}, (out) => parseTflintOutput(out, absPath, fileDir));
|
|
92
107
|
if (run.skipped)
|
|
93
108
|
return run.skipped;
|
|
94
109
|
const diagnostics = run.diagnostics;
|
|
@@ -585,6 +585,29 @@ const treeSitterRunner = {
|
|
|
585
585
|
}
|
|
586
586
|
}
|
|
587
587
|
if (diagnostics.length === 0) {
|
|
588
|
+
// #3409: zero diagnostics because the language's GRAMMAR never loaded is
|
|
589
|
+
// not a clean file, and `runQueriesOnFile` returns [] for both. The
|
|
590
|
+
// honest discriminator, once the run has already demanded the language,
|
|
591
|
+
// is the client's own loaded-language map: a grammar that could not be
|
|
592
|
+
// resolved, fetched, or decoded is absent from it. Reported through this
|
|
593
|
+
// runner's existing "the analysis did not run" arm (the same
|
|
594
|
+
// `runner_skip` + `status: "skipped"` shape as client-unavailable,
|
|
595
|
+
// init-failed and unsupported-extension) instead of as a pass with
|
|
596
|
+
// nothing found — which is what made a degraded language on a compiled
|
|
597
|
+
// host indistinguishable from a clean file in `runner_complete`.
|
|
598
|
+
//
|
|
599
|
+
// The abort term keeps a mid-run WASM abort (#402, its own
|
|
600
|
+
// `runtime_abort` record) from being misreported as a missing grammar.
|
|
601
|
+
if (!isTreeSitterWasmAborted() && !client.getLanguage(languageId)) {
|
|
602
|
+
logTreeSitter({
|
|
603
|
+
phase: "runner_skip",
|
|
604
|
+
filePath,
|
|
605
|
+
languageId,
|
|
606
|
+
reason: "grammar_unavailable",
|
|
607
|
+
status: "skipped",
|
|
608
|
+
});
|
|
609
|
+
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
610
|
+
}
|
|
588
611
|
logTreeSitter({
|
|
589
612
|
phase: "runner_complete",
|
|
590
613
|
filePath,
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
import { formatToolFailure } from "./utils/tool-failure.js";
|
|
39
39
|
import * as fs from "node:fs";
|
|
40
40
|
import * as path from "node:path";
|
|
41
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
41
42
|
import { incrementDegradationCount } from "../../degradation-ledger.js";
|
|
42
43
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
43
44
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
@@ -89,7 +90,7 @@ function normalizeSeverity(raw) {
|
|
|
89
90
|
* CauseMetadata: { StartLine } }] }] }`. CRITICAL → blocking, the rest advisory.
|
|
90
91
|
* Defensive: malformed input returns `[]`. Exported for unit tests.
|
|
91
92
|
*/
|
|
92
|
-
export function parseTrivyConfigOutput(raw, filePath) {
|
|
93
|
+
export function parseTrivyConfigOutput(raw, filePath, cwd = process.cwd()) {
|
|
93
94
|
if (!raw.trim())
|
|
94
95
|
return [];
|
|
95
96
|
let parsed;
|
|
@@ -103,9 +104,14 @@ export function parseTrivyConfigOutput(raw, filePath) {
|
|
|
103
104
|
if (!Array.isArray(results))
|
|
104
105
|
return [];
|
|
105
106
|
const diagnostics = [];
|
|
107
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
106
108
|
for (const resultEntry of results) {
|
|
107
109
|
if (!resultEntry || typeof resultEntry !== "object")
|
|
108
110
|
continue;
|
|
111
|
+
const target = resultEntry.Target;
|
|
112
|
+
if (typeof target !== "string" ||
|
|
113
|
+
!pathsEqual(path.resolve(cwd, target), absTarget))
|
|
114
|
+
continue;
|
|
109
115
|
const rows = resultEntry
|
|
110
116
|
.Misconfigurations;
|
|
111
117
|
if (!Array.isArray(rows))
|
|
@@ -232,7 +238,7 @@ const trivyConfigRunner = {
|
|
|
232
238
|
});
|
|
233
239
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
234
240
|
}
|
|
235
|
-
const diagnostics = parseTrivyConfigOutput(result.stdout || "", ctx.filePath);
|
|
241
|
+
const diagnostics = parseTrivyConfigOutput(result.stdout || "", ctx.filePath, cwd);
|
|
236
242
|
if (diagnostics.length === 0) {
|
|
237
243
|
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
238
244
|
}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Common patterns for parsing tool output into standardized diagnostics.
|
|
5
5
|
* Supports the common `file:line:col: message` format used by most linters.
|
|
6
6
|
*/
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { pathsEqual } from "../../../path-utils.js";
|
|
7
9
|
import { stripAnsi } from "../../../sanitize.js";
|
|
8
10
|
import { getAutofixCapability } from "../../../tool-policy.js";
|
|
9
11
|
/**
|
|
@@ -11,8 +13,13 @@ import { getAutofixCapability } from "../../../tool-policy.js";
|
|
|
11
13
|
* Common format: file:line:col: message (with variations)
|
|
12
14
|
*/
|
|
13
15
|
function createLineParser(config) {
|
|
14
|
-
return (raw, filePath) => {
|
|
16
|
+
return (raw, filePath, cwd) => {
|
|
15
17
|
const diagnostics = [];
|
|
18
|
+
// #3295: `config.regex` is documented above as capturing the FILE in group
|
|
19
|
+
// 1, and every parser built here then dropped it and stamped the dispatched
|
|
20
|
+
// path on every line. This is the shared factory, so the predicate lives
|
|
21
|
+
// here once rather than in each `createLineParser` caller.
|
|
22
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
16
23
|
// Optionally strip ANSI codes (for tools that output colored text)
|
|
17
24
|
const clean = config.stripAnsi !== false ? stripAnsi(raw) : raw;
|
|
18
25
|
const lines = clean.split("\n").filter((l) => l.trim());
|
|
@@ -20,6 +27,9 @@ function createLineParser(config) {
|
|
|
20
27
|
const match = line.match(config.regex);
|
|
21
28
|
if (!match)
|
|
22
29
|
continue;
|
|
30
|
+
const reported = match[1];
|
|
31
|
+
if (reported && !pathsEqual(path.resolve(cwd, reported), absTarget))
|
|
32
|
+
continue;
|
|
23
33
|
const lineNum = parseInt(match[2], 10);
|
|
24
34
|
const colNum = parseInt(match[3], 10);
|
|
25
35
|
const severity = config.getSeverity
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
3
4
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
4
5
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
5
6
|
import { PRIORITY } from "../priorities.js";
|
|
@@ -25,17 +26,24 @@ function findValeConfig(cwd) {
|
|
|
25
26
|
}
|
|
26
27
|
return undefined;
|
|
27
28
|
}
|
|
28
|
-
export function parseValeOutput(raw, filePath) {
|
|
29
|
+
export function parseValeOutput(raw, filePath, cwd) {
|
|
29
30
|
const diagnostics = [];
|
|
30
31
|
if (!raw.trim())
|
|
31
32
|
return diagnostics;
|
|
33
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
32
34
|
try {
|
|
33
35
|
const parsed = JSON.parse(raw);
|
|
34
36
|
if (!parsed || typeof parsed !== "object")
|
|
35
37
|
return diagnostics;
|
|
36
|
-
|
|
38
|
+
// #3295: vale's top level is keyed BY the linted file's path — the only
|
|
39
|
+
// place the reported path appears. `Object.values` dropped it, so a
|
|
40
|
+
// `.vale.ini` glob or a directory argv charged a second file's alerts to
|
|
41
|
+
// the dispatched one.
|
|
42
|
+
for (const [reported, alerts] of Object.entries(parsed)) {
|
|
37
43
|
if (!Array.isArray(alerts))
|
|
38
44
|
continue;
|
|
45
|
+
if (reported && !pathsEqual(path.resolve(cwd, reported), absTarget))
|
|
46
|
+
continue;
|
|
39
47
|
for (const alert of alerts) {
|
|
40
48
|
if (!alert.Message)
|
|
41
49
|
continue;
|
|
@@ -98,7 +106,11 @@ const valeRunner = {
|
|
|
98
106
|
// #1948: `parseToolRun` adds the second gate. Vale's `Data.Files`
|
|
99
107
|
// envelope bug produced exactly this shape — exit 1, a full JSON
|
|
100
108
|
// report on stdout, zero alerts parsed — and left no record.
|
|
101
|
-
const run = parseToolRun("vale", {
|
|
109
|
+
const run = parseToolRun("vale", {
|
|
110
|
+
result,
|
|
111
|
+
// EXIT TABLE (Vale 3.9.6 docs https://vale.sh/docs/topics/metrics/): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
112
|
+
exitCodes: { ran: [1, 2] },
|
|
113
|
+
}, (raw) => parseValeOutput(raw, ctx.filePath, cwd));
|
|
102
114
|
if (run.skipped)
|
|
103
115
|
return run.skipped;
|
|
104
116
|
const diagnostics = run.diagnostics;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
1
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
2
4
|
import { logRunnerAdvisoryOnce } from "../../tool-cwd.js";
|
|
3
5
|
import { getLinterPolicyForCwd, hasYamllintConfig } from "../../tool-policy.js";
|
|
@@ -7,14 +9,17 @@ import { parseToolRun } from "./utils/tool-failure.js";
|
|
|
7
9
|
import { finishParsedRun } from "./utils/tool-failure.js";
|
|
8
10
|
const yamllint = createAvailabilityChecker("yamllint", ".exe");
|
|
9
11
|
export { hasYamllintConfig };
|
|
10
|
-
function parseYamllintParsable(raw, filePath) {
|
|
12
|
+
function parseYamllintParsable(raw, filePath, cwd) {
|
|
11
13
|
const diagnostics = [];
|
|
14
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
12
15
|
for (const line of raw.split(/\r?\n/)) {
|
|
13
16
|
if (!line.trim())
|
|
14
17
|
continue;
|
|
15
18
|
const match = line.match(/^(.*?):(\d+):(\d+):\s*\[(error|warning)\]\s*(.*?)\s*\(([^)]+)\)\s*$/i);
|
|
16
19
|
if (!match)
|
|
17
20
|
continue;
|
|
21
|
+
if (!pathsEqual(path.resolve(cwd, match[1]), absTarget))
|
|
22
|
+
continue;
|
|
18
23
|
const severity = match[4].toLowerCase() === "error" ? "error" : "warning";
|
|
19
24
|
diagnostics.push({
|
|
20
25
|
id: `yamllint-${match[2]}-${match[3]}-${match[6]}`,
|
|
@@ -70,7 +75,12 @@ const yamllintRunner = {
|
|
|
70
75
|
const raw = `${result.stdout ?? ""}${result.stderr ?? ""}`;
|
|
71
76
|
// #1948: classify on stdout as before, parse both streams as before, and
|
|
72
77
|
// record when a failing run yields nothing.
|
|
73
|
-
const run = parseToolRun("yamllint", {
|
|
78
|
+
const run = parseToolRun("yamllint", {
|
|
79
|
+
result,
|
|
80
|
+
output: result.stdout,
|
|
81
|
+
// EXIT TABLE (yamllint 1.35 docs https://yamllint.readthedocs.io/en/stable/): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
82
|
+
exitCodes: { ran: [1, 2] },
|
|
83
|
+
}, (out) => parseYamllintParsable(out, ctx.filePath, cwd), { parseOutput: raw });
|
|
74
84
|
if (run.skipped)
|
|
75
85
|
return run.skipped;
|
|
76
86
|
const diagnostics = run.diagnostics;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { pathsEqual } from "../../path-utils.js";
|
|
2
3
|
import { safeSpawnAsync } from "../../safe-spawn.js";
|
|
3
4
|
import { resolveRunnerCwd } from "../../tool-cwd.js";
|
|
4
5
|
import { createAvailabilityChecker } from "./utils/runner-helpers.js";
|
|
5
6
|
import { PRIORITY } from "../priorities.js";
|
|
7
|
+
import { finishParsedRun, parseToolRun } from "./utils/tool-failure.js";
|
|
6
8
|
// zig rejects `--version`; the version subcommand is `zig version`. Using the
|
|
7
9
|
// default probe would make this runner skip on every machine.
|
|
8
10
|
const zig = createAvailabilityChecker("zig", ".exe", ["version"]);
|
|
9
|
-
function parseZigOutput(raw, filePath) {
|
|
11
|
+
function parseZigOutput(raw, filePath, cwd) {
|
|
10
12
|
const diagnostics = [];
|
|
13
|
+
const absTarget = path.resolve(cwd, filePath);
|
|
11
14
|
for (const line of raw.split(/\r?\n/)) {
|
|
12
15
|
const match = line.match(/^(.*?):(\d+):(\d+):\s*(error|warning|note):\s*(.+)$/);
|
|
13
16
|
if (!match)
|
|
14
17
|
continue;
|
|
15
18
|
const [, rawFile, lineStr, colStr, level, message] = match;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (resolvedSource !== resolvedTarget)
|
|
19
|
+
// #3278: one seam for reported-path attribution — see javac.ts.
|
|
20
|
+
if (!pathsEqual(path.resolve(cwd, rawFile.trim()), absTarget))
|
|
19
21
|
continue;
|
|
20
22
|
const severity = level === "error" ? "error" : "warning";
|
|
21
23
|
diagnostics.push({
|
|
@@ -35,12 +37,6 @@ function parseZigOutput(raw, filePath) {
|
|
|
35
37
|
}
|
|
36
38
|
return diagnostics;
|
|
37
39
|
}
|
|
38
|
-
function firstOutputLine(result) {
|
|
39
|
-
return `${result.stderr || ""}\n${result.stdout || ""}`
|
|
40
|
-
.trim()
|
|
41
|
-
.split(/\r?\n/, 1)[0]
|
|
42
|
-
.slice(0, 200);
|
|
43
|
-
}
|
|
44
40
|
const zigCheckRunner = {
|
|
45
41
|
id: "zig-check",
|
|
46
42
|
appliesTo: ["zig"],
|
|
@@ -57,35 +53,27 @@ const zigCheckRunner = {
|
|
|
57
53
|
if (result.error && !result.stdout && !result.stderr) {
|
|
58
54
|
return { status: "skipped", diagnostics: [], semantic: "none" };
|
|
59
55
|
}
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return { status: "succeeded", diagnostics: [], semantic: "none" };
|
|
82
|
-
}
|
|
83
|
-
const hasErrors = diagnostics.some((d) => d.severity === "error");
|
|
84
|
-
return {
|
|
85
|
-
status: hasErrors ? "failed" : "succeeded",
|
|
86
|
-
diagnostics,
|
|
87
|
-
semantic: "warning",
|
|
88
|
-
};
|
|
56
|
+
const raw = `${result.stdout || ""}\n${result.stderr || ""}`;
|
|
57
|
+
const parsed = parseToolRun("zig-check", {
|
|
58
|
+
result,
|
|
59
|
+
output: raw,
|
|
60
|
+
// EXIT TABLE (Zig 0.13 measured fixture): 0 clean; 1 findings; 2 error; other nonzero rejected.
|
|
61
|
+
exitCodes: { ran: [1, 2] },
|
|
62
|
+
}, (output) => parseZigOutput(output, ctx.filePath, cwd));
|
|
63
|
+
if (parsed.skipped)
|
|
64
|
+
return parsed.skipped;
|
|
65
|
+
return finishParsedRun({
|
|
66
|
+
tool: "zig-check",
|
|
67
|
+
ctx,
|
|
68
|
+
result,
|
|
69
|
+
diagnostics: parsed.diagnostics,
|
|
70
|
+
classify: (diagnostics) => ({
|
|
71
|
+
status: diagnostics.some((d) => d.severity === "error")
|
|
72
|
+
? "failed"
|
|
73
|
+
: "succeeded",
|
|
74
|
+
semantic: "warning",
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
89
77
|
},
|
|
90
78
|
};
|
|
91
79
|
export default zigCheckRunner;
|