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:
parent
7f71030b5b
commit
693e3c7af9
2 changed files with 35 additions and 7 deletions
23
lint-rules.h
23
lint-rules.h
|
|
@ -184,15 +184,24 @@ inline void AddProjectLintRules(Crafter::Configuration& cfg) {
|
|||
}
|
||||
});
|
||||
|
||||
// Scoped enums only. Kept on tokens rather than moved to the AST, even
|
||||
// though LintDecl carries an exact isScopedEnum: an AST is one
|
||||
// configuration's slice, so a plain enum inside a preprocessor branch that
|
||||
// is inactive for the host would stop being reported. Tokens are lexed
|
||||
// without evaluating #if, so every platform's code stays covered.
|
||||
cfg.AddLintRule("enum-class", [](LintContext& ctx) {
|
||||
if (!IsCppFile(ctx)) return;
|
||||
std::vector<std::string_view> lines = Lines(ctx.CommentStripped());
|
||||
static const std::regex plainEnum(R"(\benum\s+(?!class\b|struct\b)[A-Za-z_])");
|
||||
for (std::size_t i = 0; i < lines.size(); ++i) {
|
||||
std::string lineStr(lines[i]);
|
||||
if (std::regex_search(lineStr, plainEnum)) {
|
||||
ctx.Report(i + 1, "use enum class instead of plain enum");
|
||||
}
|
||||
std::span<const Crafter::LintToken> tokens = ctx.Tokens();
|
||||
for (std::size_t i = 0; i < tokens.size(); ++i) {
|
||||
if (tokens[i].kind != Crafter::LintTokenKind::Keyword) continue;
|
||||
if (ctx.TokenText(tokens[i]) != "enum") continue;
|
||||
// Asking for the next TOKEN rather than the rest of the line means a
|
||||
// declaration split across lines reads identically to one that is
|
||||
// not — the regex this replaced required the name to follow `enum`
|
||||
// on the same line, so `enum\n Color {` went unreported.
|
||||
std::string_view next = i + 1 < tokens.size() ? ctx.TokenText(tokens[i + 1]) : std::string_view{};
|
||||
if (next == "class" || next == "struct") continue;
|
||||
ctx.Report(tokens[i].line, "use enum class instead of plain enum");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue