Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,9 @@ clang-format
``enum`` enumerator lists.
- Add ``OneLineFormatOffRegex`` option for turning formatting off for one line.
- Add ``SpaceAfterOperatorKeyword`` option.
- Support trailing whitespace in line splicing.
(P2223R2 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2223r2.pdf>_, #GH145226)


clang-refactor
--------------
Expand Down
31 changes: 23 additions & 8 deletions clang/lib/Format/FormatTokenLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "FormatTokenLexer.h"
#include "FormatToken.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
Expand Down Expand Up @@ -1205,14 +1206,23 @@ static size_t countLeadingWhitespace(StringRef Text) {
while (Cur < End) {
if (isspace(Cur[0])) {
++Cur;
} else if (Cur[0] == '\\' && (Cur[1] == '\n' || Cur[1] == '\r')) {
// A '\' followed by a newline always escapes the newline, regardless
// of whether there is another '\' before it.
} else if (Cur[0] == '\\') {
// A '\' followed by a optional horizontal whitespace (P22232R2) and then
// newline always escapes the newline, regardless of whether there is
// another '\' before it.
// The source has a null byte at the end. So the end of the entire input
// isn't reached yet. Also the lexer doesn't break apart an escaped
// newline.
assert(End - Cur >= 2);
Cur += 2;
const unsigned char *Lookahead = Cur + 1;
while (isHorizontalWhitespace(*Lookahead))
++Lookahead;
if (*Lookahead == '\n' || *Lookahead == '\r') {
// Splice found, consume it.
Cur = Lookahead + 1;
continue;
}
// No line splice found; the '\' is a token.
break;
} else if (Cur[0] == '?' && Cur[1] == '?' && Cur[2] == '/' &&
(Cur[3] == '\n' || Cur[3] == '\r')) {
// Newlines can also be escaped by a '?' '?' '/' trigraph. By the way, the
Expand Down Expand Up @@ -1295,13 +1305,18 @@ FormatToken *FormatTokenLexer::getNextToken() {
case '/':
// The text was entirely whitespace when this loop was entered. Thus
// this has to be an escape sequence.
assert(Text.substr(i, 2) == "\\\r" || Text.substr(i, 2) == "\\\n" ||
Text.substr(i, 4) == "\?\?/\r" ||
assert(Text.substr(i, 4) == "\?\?/\r" ||
Text.substr(i, 4) == "\?\?/\n" ||
(i >= 1 && (Text.substr(i - 1, 4) == "\?\?/\r" ||
Text.substr(i - 1, 4) == "\?\?/\n")) ||
(i >= 2 && (Text.substr(i - 2, 4) == "\?\?/\r" ||
Text.substr(i - 2, 4) == "\?\?/\n")));
Text.substr(i - 2, 4) == "\?\?/\n")) ||
(Text[i] == '\\' && [&]() -> bool {
size_t j = i + 1;
while (j < Text.size() && isHorizontalWhitespace(Text[j]))
++j;
return j < Text.size() && (Text[j] == '\n' || Text[j] == '\r');
}()));
InEscape = true;
break;
default:
Expand Down
14 changes: 14 additions & 0 deletions clang/test/Format/splice-trailing-whitespace-p2223r2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s \
// RUN: | clang-format -style='{BasedOnStyle: LLVM, AlignEscapedNewlines: DontAlign}' \
// RUN: | FileCheck -strict-whitespace %s

// CHECK: {{^#define TAG\(\.\.\.\) \\}}
// CHECK: {{^ struct a \{\};}}
// There is whitespace following v this backslash!
#define TAG(...) struct a { \
};

// CHECK: {{^int i;}}
// The comment below eats its following line because of the line splice.
// I also have trailing whitespace. Nom nom nom \
int i;
Loading