Files
renovate/tools/lint/rules/v8-ignore-reason.ts
Sebastian Poxhofer a473e73b3e fix(linters): forbid inert counts in v8 coverage-ignore comments (#45287)
Adds a custom oxlint rule renovate/v8-ignore-no-count that flags the
count form '/* v8 ignore next <N> */'. The count is not honored by
vitest's ast-v8-to-istanbul hint parser, which captures only the
keyword (if/else/next/file); 'next' always exempts exactly the next
AST node. Trailing counts therefore suggest a line range that is never
applied and mislead readers.

The rule is autofixable (drops the count, preserving actual behavior)
and has been applied to all existing occurrences.
2026-08-22 11:25:31 +00:00

65 lines
2.1 KiB
TypeScript

import { defineRule } from '@oxlint/plugins';
/**
* Note on the `next <N>` count form of these coverage-ignore hints:
* empirically verified against vitest 4 (`@vitest/coverage-v8` with
* `ast-v8-to-istanbul`) that the count `N` is NOT honored — the hint parser
* only captures the keyword (`if`/`else`/`next`/`file`) and `next` always
* exempts exactly the next AST node, however many lines it spans: `next 2`
* before two sibling statements exempts only the first, while `next 1`
* before a multi-line `if` block exempts the whole block. This rule only
* requires a justification; the count form itself is forbidden by the
* companion `v8-ignore-no-count` rule.
*
* A `stop` marker only terminates a region opened by `start`; the
* justification lives on the `start` marker, so bare stops are exempt.
*/
const bareStopRegex = /^\s*v8 ignore stop\s*$/;
/**
* `--` (or `---`) separator followed by a non-empty reason,
* e.g. `v8 ignore next -- should never happen`.
*/
const dashReasonRegex = /v8 ignore.*?--+\s+\S/;
/**
* Legacy colon form, e.g. `v8 ignore next: hard to test`. Accepted because
* it is widespread in the codebase, but the dash form is recommended.
*/
const colonReasonRegex = /v8 ignore\s+[a-z]+(?:\s+\d+)?\s*:\s*\S/;
export default defineRule({
meta: {
type: 'problem',
messages: {
missingReason:
'Coverage-ignore comments must carry a justification: /* v8 ignore <kind> -- <reason> */',
},
},
createOnce(context) {
return {
Program() {
for (const comment of context.sourceCode.getAllComments()) {
const text = comment.value;
if (!text.includes('v8 ignore')) {
continue;
}
if (bareStopRegex.test(text)) {
continue;
}
if (dashReasonRegex.test(text) || colonReasonRegex.test(text)) {
continue;
}
if (!comment.loc) {
continue;
}
context.report({
loc: comment.loc,
messageId: 'missingReason',
});
}
},
};
},
});