refactor(lint): enum-class asks the token stream, not a regex

The regex required the enum's name to follow `enum` on the same line, so a
declaration split over lines went unreported. Asking for the next token instead
makes the split and unsplit spellings read identically, and distinguishes the
`enum` KEYWORD from the same letters appearing elsewhere without needing word
boundaries to stand in for lexing.

Deliberately not moved to the AST, even though LintDecl already carries an exact
isScopedEnum. An AST is one configuration's slice, so a plain enum inside a
preprocessor branch inactive for the host would silently stop being reported;
tokens are lexed without evaluating #if, so every platform stays covered. Being
right about less is not an improvement here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-07-31 00:21:12 +02:00
commit 693e3c7af9
2 changed files with 35 additions and 7 deletions

View file

@ -395,6 +395,25 @@ int main() {
Check(r.text.contains("for (std::int32_t i = 0;"), "fixed-width: main's body is not exempt, only its signature");
}
// enum-class asks for the next TOKEN after `enum`, so a declaration split
// over lines reads the same as one that is not. The regex it replaced
// required the name to follow `enum` on the same line.
{
RuleRun r = RunRule("enum Plain { A };\n"
"enum\n"
" Split { B };\n"
"enum class Scoped { C };\n"
"enum\n"
" class SplitScoped { D };\n"
"enum struct AlsoFine { E };\n",
"enum-class", LintMode::Report);
Check(r.summary.findings.size() == 2, std::format("enum-class: exactly the two plain enums ({} found)", r.summary.findings.size()));
bool onFirst = std::any_of(r.summary.findings.begin(), r.summary.findings.end(), [](const LintFinding& f) { return f.line == 1; });
bool onSplit = std::any_of(r.summary.findings.begin(), r.summary.findings.end(), [](const LintFinding& f) { return f.line == 2; });
Check(onFirst, "enum-class: single-line plain enum reported");
Check(onSplit, "enum-class: line-split plain enum reported at the keyword");
}
if (Failures > 0) {
std::println(std::cerr, "{} assertions failed", Failures);
return 1;