[clang] 5a0b4c9 - [clang-format] Preserve trailing NOLINTEND placement with SeparateDefinitionBlocks (#190741)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 18 03:05:35 PDT 2026
Author: Jiaqi He
Date: 2026-04-18T10:05:29Z
New Revision: 5a0b4c95de4b08d9c18667fc06f17253fc18f087
URL: https://github.com/llvm/llvm-project/commit/5a0b4c95de4b08d9c18667fc06f17253fc18f087
DIFF: https://github.com/llvm/llvm-project/commit/5a0b4c95de4b08d9c18667fc06f17253fc18f087.diff
LOG: [clang-format] Preserve trailing NOLINTEND placement with SeparateDefinitionBlocks (#190741)
Fixes https://github.com/llvm/llvm-project/issues/189384.
When `SeparateDefinitionBlocks: Always` is enabled, clang-format may
separate a clearly trailing comment block by inserting an empty line
before it.
For example, it may format:
```c++
// NOLINTBEGIN
int x = 1;
int y = 2;
// NOLINTEND
void some_function() {}
```
as
```c++
// NOLINTBEGIN
int x = 1;
int y = 2;
// NOLINTEND
void some_function() {}
```
This is undesirable as the trailing comment is visually attached to the
preceding block rather than acting as a separator for the following
definition.
This change preserves comment blocks that are clearly trailing the
preceding block.
Also add a regression test.
---------
Co-authored-by: owenca <owenpiano at gmail.com>
Added:
Modified:
clang/lib/Format/DefinitionBlockSeparator.cpp
clang/unittests/Format/DefinitionBlockSeparatorTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/DefinitionBlockSeparator.cpp b/clang/lib/Format/DefinitionBlockSeparator.cpp
index 855f2efad1e53..fd1ae8c4f600e 100644
--- a/clang/lib/Format/DefinitionBlockSeparator.cpp
+++ b/clang/lib/Format/DefinitionBlockSeparator.cpp
@@ -146,21 +146,25 @@ void DefinitionBlockSeparator::separateBlocks(
if (LikelyDefinition(OperateLine))
return false;
+ const auto *NextLine =
+ OperateIndex + 1 < Lines.size() ? Lines[OperateIndex + 1] : nullptr;
+
if (const auto *Tok = OperateLine->First;
Tok->is(tok::comment) && !isClangFormatOn(Tok->TokenText)) {
- return true;
+ const bool IsEndComment = Tok->NewlinesBefore == 1 && NextLine &&
+ NextLine->First->NewlinesBefore > 1;
+ if (!IsEndComment)
+ return true;
}
// A single line identifier that is not in the last line.
if (OperateLine->First->is(tok::identifier) &&
- OperateLine->First == OperateLine->Last &&
- OperateIndex + 1 < Lines.size()) {
+ OperateLine->First == OperateLine->Last && NextLine) {
// UnwrappedLineParser's recognition of free-standing macro like
// Q_OBJECT may also recognize some uppercased type names that may be
// used as return type as that kind of macros, which is a bit hard to
// distinguish one from another purely from token patterns. Here, we
// try not to add new lines below those identifiers.
- AnnotatedLine *NextLine = Lines[OperateIndex + 1];
if (NextLine->MightBeFunctionDecl &&
NextLine->mightBeFunctionDefinition() &&
NextLine->First->NewlinesBefore == 1 &&
diff --git a/clang/unittests/Format/DefinitionBlockSeparatorTest.cpp b/clang/unittests/Format/DefinitionBlockSeparatorTest.cpp
index b1b4b1d047523..01336a8d37314 100644
--- a/clang/unittests/Format/DefinitionBlockSeparatorTest.cpp
+++ b/clang/unittests/Format/DefinitionBlockSeparatorTest.cpp
@@ -291,6 +291,22 @@ TEST_F(DefinitionBlockSeparatorTest, Always) {
"struct E {};",
Style);
+ constexpr StringRef Code("// NOLINTBEGIN\n"
+ "int x = 1;\n"
+ "int y = 2;\n"
+ "// NOLINTEND\n"
+ "\n"
+ "void some_function() {}");
+ verifyFormat(Code, Style, Code);
+
+ constexpr StringRef Code2("int x = 0;\n"
+ "int y = 0;\n"
+ "// trailing comment 1\n"
+ "// trailing comment 2\n"
+ "\n"
+ "void some_function() {}");
+ verifyFormat(Code2, Style, Code2);
+
std::string Prefix = "namespace {\n";
std::string Infix = "\n"
"// Enum test1\n"
More information about the cfe-commits
mailing list