fix(lint): make the reflow guards token-accurate instead of textual
The transforms guard themselves against comments and raw strings before
joining or rewriting a line, because pulling text up past a `//` buries it
and reflowing a multi-line literal changes the string. Those guards were
substring probes over the raw line, so they answered the wrong question:
Line(n).contains("//") fires on // inside a string literal
Line(n).contains("R\"") fires on the characters R" inside a literal, and
MISSES a raw string opened on an earlier line
Both misfire on this repo's own sources. "MARKER" ends in R", and any string
mentioning a lint-disable directive contains //. Two wrapped call sites in
tests/Lint were being left unjoined for exactly these reasons; they join now,
and the results are in this commit.
Replaced by LineHasComment() (added with the token layer) and a new
LineHasMultiLineToken(), which reports whether any token actually covering
that line spans a line boundary — a raw string or a block comment. Backed by
a per-line bitmap derived from the token cache and invalidated with it.
The guards themselves stay: joining across a real comment or a real
multi-line literal is still unsafe, and there are tests for both. What
changes is that they now fire on comments and literals rather than on the
characters that spell them.
format-concat gets narrower as a result. It used to refuse any line
containing R" and ask for a manual fix; now only a literal that genuinely
spans lines does that, because a single-line raw string reduces to R"…" in
the stripped view, fails the plain-literal test, and travels through as an
argument with its spelling intact.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
04bc58b2fa
commit
78fbf8f80c
5 changed files with 87 additions and 16 deletions
|
|
@ -252,6 +252,49 @@ int main() {
|
|||
Check(second.summary.changedFiles.empty(), "wrap-join is idempotent after the fixpoint");
|
||||
}
|
||||
|
||||
// The guards used to be substring probes over the raw line, so a literal
|
||||
// whose TEXT contained `//` or `R"` looked like a comment or a raw string
|
||||
// and silently disabled the rule. Both shapes occur in this repo's own
|
||||
// sources — "MARKER" ends in the characters R", and any string mentioning
|
||||
// a lint-disable directive contains //.
|
||||
{
|
||||
RuleRun r = RunRule("void F() {\n" " auto hit = text.find(\"MARKER\",\n" " start);\n" "}\n", "wrap-join", LintMode::Apply);
|
||||
Check(r.text.contains("text.find(\"MARKER\", start);"), "R\" inside a literal no longer blocks wrap-join");
|
||||
}
|
||||
{
|
||||
RuleRun r = RunRule("void F() {\n" " Check(read() == \"// lint-disable-next-line trim\\n\",\n" " \"message\");\n" "}\n", "wrap-join", LintMode::Apply);
|
||||
Check(r.text.contains("\\n\", \"message\");"), "// inside a literal no longer blocks wrap-join");
|
||||
}
|
||||
|
||||
// A real trailing comment still blocks the join: text pulled up past a
|
||||
// `//` would be swallowed by it.
|
||||
{
|
||||
RuleRun r = RunRule("void F() {\n" " auto v = g(alpha, // why\n" " beta);\n" "}\n", "wrap-join", LintMode::Apply);
|
||||
Check(r.text.contains("g(alpha, // why\n"), "a real comment still blocks wrap-join");
|
||||
}
|
||||
|
||||
// A genuinely multi-line literal is never reflowed — joining its lines
|
||||
// would change the string's contents.
|
||||
{
|
||||
std::string_view source = "void F() {\n"
|
||||
" auto text = R\"sql(SELECT a,\n"
|
||||
" b FROM t)sql\";\n"
|
||||
"}\n";
|
||||
RuleRun r = RunRule(source, "wrap-join", LintMode::Apply);
|
||||
Check(r.text == source, "wrap-join leaves a multi-line raw string alone");
|
||||
RuleRun paren = RunRule(source, "paren-spacing", LintMode::Apply);
|
||||
Check(paren.text == source, "paren-spacing leaves a multi-line raw string alone");
|
||||
}
|
||||
|
||||
// Type keywords inside a literal are text, not declarations.
|
||||
{
|
||||
std::string_view source = "void F() {\n"
|
||||
" auto sql = R\"q(int x; unsigned long y;)q\";\n"
|
||||
"}\n";
|
||||
RuleRun r = RunRule(source, "fixed-width-types", LintMode::Apply);
|
||||
Check(r.text == source, "fixed-width-types leaves type names inside a raw string alone");
|
||||
}
|
||||
|
||||
if (Failures > 0) {
|
||||
std::println(std::cerr, "{} assertions failed", Failures);
|
||||
return 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue