[clang] [clang-format] Fix first-line width calculation for CRLF multiline tokens (PR #228752)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Oct 3 10:59:14 PDT 2026
https://github.com/w1m024 created https://github.com/llvm/llvm-project/pull/228752
The first-line width of a multiline token is calculated from the text before the first `LF`. For `CRLF` input, this includes the preceding `CR`, which is counted as an extra column and can change wrapping at the column limit.
Exclude the trailing `CR` when measuring the first-line width while preserving the token text. Add a regression test covering `LF` and `CRLF` multiline raw strings whose first line, including the preceding code, exactly fits the column limit.
Fixes #224571
Assisted-by: Codex
>From 269d2dc627036b522a383bd332f9481e48b10fba Mon Sep 17 00:00:00 2001
From: w1m024 <iwangyiming at gmail.com>
Date: Sun, 4 Oct 2026 00:43:03 +0800
Subject: [PATCH] [clang-format] Fix first-line width calculation for CRLF
multiline tokens
The first-line width of a multiline token is calculated from the text
before the first LF. For CRLF input, this includes the preceding CR,
which is counted as an extra column and can change wrapping at the
column limit.
Exclude the trailing CR when measuring the first-line width while
preserving the token text. Add a regression test covering LF and CRLF
multiline raw strings whose first line, including the preceding code,
exactly fits the column limit.
Fixes #224571
Assisted-by: Codex
---
clang/lib/Format/FormatTokenLexer.cpp | 6 +++++-
clang/unittests/Format/FormatTest.cpp | 17 +++++++++++++++++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp
index 2a8bef21ad4143..12a6fb8ea90540 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -1473,10 +1473,14 @@ FormatToken *FormatTokenLexer::getNextToken() {
Column += FormatTok->ColumnWidth;
} else {
FormatTok->IsMultiline = true;
+ // Exclude the '\r' of a CRLF line ending from the first line's width.
+ StringRef FirstLine = Text.substr(0, FirstNewlinePos);
+ if (FirstLine.ends_with("\r"))
+ FirstLine = FirstLine.drop_back();
// FIXME: ColumnWidth actually depends on the start column, we need to
// take this into account when the token is moved.
FormatTok->ColumnWidth = encoding::columnWidthWithTabs(
- Text.substr(0, FirstNewlinePos), Column, Style.TabWidth, Encoding);
+ FirstLine, Column, Style.TabWidth, Encoding);
// The last line of the token always starts in column 0.
// Thus, the length can be precomputed even in the presence of tabs.
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 0a5950c25c60ef..19e9f9695812e2 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -16681,6 +16681,23 @@ TEST_F(FormatTest, CountsCharactersInMultilineRawStringLiterals) {
" R\"(single line raw string)\" + bbbbbb);");
}
+TEST_F(FormatTest, MultilineRawStringWidthWithCRLF) {
+ auto Style = getWebKitStyle();
+ Style.ColumnLimit = 23;
+ Style.AllowAllArgumentsOnNextLine = false;
+ Style.PackArguments.BinPack = FormatStyle::BPAS_OnePerLine;
+
+ // The raw string's first line reaches ColumnLimit; CR must not add a column.
+ verifyFormat("x = a ? f(R\"(aaaaaaaaaa\n)\",\n"
+ " b)\n"
+ " : g();",
+ "x = a ? f(R\"(aaaaaaaaaa\n)\", b) : g();", Style);
+ verifyFormat("x = a ? f(R\"(aaaaaaaaaa\r\n)\",\r\n"
+ " b)\r\n"
+ " : g();",
+ "x = a ? f(R\"(aaaaaaaaaa\r\n)\", b) : g();", Style);
+}
+
TEST_F(FormatTest, SkipsUnknownStringLiterals) {
verifyFormat("string a = \"unterminated;");
verifyFormat("function(\"unterminated,\n"
More information about the cfe-commits
mailing list