[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