https://github.com/owenca updated 
https://github.com/llvm/llvm-project/pull/137577

>From ce33e11aedf297e9cfb5e20efb4ce316886e6cb1 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpi...@gmail.com>
Date: Sun, 27 Apr 2025 21:18:03 -0700
Subject: [PATCH 1/2] [clang-format] Add OneLineFormatOffRegex option

Close #54334
---
 clang/docs/ClangFormatStyleOptions.rst     | 23 +++++++
 clang/docs/ReleaseNotes.rst                |  1 +
 clang/include/clang/Format/Format.h        | 22 +++++++
 clang/lib/Format/Format.cpp                |  1 +
 clang/lib/Format/FormatTokenLexer.cpp      | 34 ++++++++++
 clang/unittests/Format/ConfigParseTest.cpp |  1 +
 clang/unittests/Format/FormatTest.cpp      | 76 ++++++++++++++++++++++
 7 files changed, 158 insertions(+)

diff --git a/clang/docs/ClangFormatStyleOptions.rst 
b/clang/docs/ClangFormatStyleOptions.rst
index 3f8a5f49313b2..f1343ee6ee516 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -5195,6 +5195,29 @@ the configuration (without a prefix: ``Auto``).
   Add a space in front of an Objective-C protocol list, i.e. use
   ``Foo <Protocol>`` instead of ``Foo<Protocol>``.
 
+.. _OneLineFormatOffRegex:
+
+**OneLineFormatOffRegex** (``String``) :versionbadge:`clang-format 21` :ref:`¶ 
<OneLineFormatOffRegex>`
+  A regular expression that describes markers for turning formatting off for
+  one line. If it matches a line comment that is the first/only token of a
+  line, clang-format skips the next line. Otherwise, clang-format skips the
+  line that contains a matched token.
+
+  .. code-block:: c++
+
+     // OneLineFormatOffRegex: ^(// NOLINT|logger$)
+     // results in the output below:
+     int a;
+     int b ;  // NOLINT
+     int c;
+     // NOLINTNEXTLINE
+     int d ;
+     int e;
+     s = "// NOLINT";
+     logger() ;
+     logger2();
+     my_logger();
+
 .. _PPIndentWidth:
 
 **PPIndentWidth** (``Integer``) :versionbadge:`clang-format 13` :ref:`¶ 
<PPIndentWidth>`
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3724c8cbc70fe..b22b3f13659ce 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -692,6 +692,7 @@ clang-format
   top of the file.
 - Add ``EnumTrailingComma`` option for inserting/removing commas at the end of
   ``enum`` enumerator lists.
+- Add ``OneLineFormatOffRegex`` option for turning formatting off for one line.
 
 libclang
 --------
diff --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index f6ceef08b46da..e5606a8a2a419 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3654,6 +3654,27 @@ struct FormatStyle {
   /// \version 3.7
   bool ObjCSpaceBeforeProtocolList;
 
+  /// A regular expression that describes markers for turning formatting off 
for
+  /// one line. If it matches a line comment that is the first/only token of a
+  /// line, clang-format skips the next line. Otherwise, clang-format skips the
+  /// line that contains a matched token.
+  /// \code
+  ///    // OneLineFormatOffRegex: ^(// NOLINT|logger$)
+  ///    // results in the output below:
+  ///    int a;
+  ///    int b ;  // NOLINT
+  ///    int c;
+  ///    // NOLINTNEXTLINE
+  ///    int d ;
+  ///    int e;
+  ///    s = "// NOLINT";
+  ///    logger() ;
+  ///    logger2();
+  ///    my_logger();
+  /// \endcode
+  /// \version 21
+  std::string OneLineFormatOffRegex;
+
   /// Different ways to try to fit all constructor initializers on a line.
   enum PackConstructorInitializersStyle : int8_t {
     /// Always put each constructor initializer on its own line.
@@ -5399,6 +5420,7 @@ struct FormatStyle {
            ObjCPropertyAttributeOrder == R.ObjCPropertyAttributeOrder &&
            ObjCSpaceAfterProperty == R.ObjCSpaceAfterProperty &&
            ObjCSpaceBeforeProtocolList == R.ObjCSpaceBeforeProtocolList &&
+           OneLineFormatOffRegex == R.OneLineFormatOffRegex &&
            PackConstructorInitializers == R.PackConstructorInitializers &&
            PenaltyBreakAssignment == R.PenaltyBreakAssignment &&
            PenaltyBreakBeforeFirstCallParameter ==
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 5a1c3f556b331..2f4b64ef4f5fe 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1100,6 +1100,7 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("ObjCSpaceAfterProperty", Style.ObjCSpaceAfterProperty);
     IO.mapOptional("ObjCSpaceBeforeProtocolList",
                    Style.ObjCSpaceBeforeProtocolList);
+    IO.mapOptional("OneLineFormatOffRegex", Style.OneLineFormatOffRegex);
     IO.mapOptional("PackConstructorInitializers",
                    Style.PackConstructorInitializers);
     IO.mapOptional("PenaltyBreakAssignment", Style.PenaltyBreakAssignment);
diff --git a/clang/lib/Format/FormatTokenLexer.cpp 
b/clang/lib/Format/FormatTokenLexer.cpp
index a4c94ac411fe0..58991f9681c97 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -83,8 +83,42 @@ FormatTokenLexer::FormatTokenLexer(
 ArrayRef<FormatToken *> FormatTokenLexer::lex() {
   assert(Tokens.empty());
   assert(FirstInLineIndex == 0);
+  const llvm::Regex FormatOffRegex(Style.OneLineFormatOffRegex);
+  enum { FO_None, FO_CurrentLine, FO_NextLine } FormatOff = FO_None;
   do {
     Tokens.push_back(getNextToken());
+    auto &Tok = *Tokens.back();
+    const auto NewlinesBefore = Tok.NewlinesBefore;
+    switch (FormatOff) {
+    case FO_CurrentLine:
+      if (NewlinesBefore == 0)
+        Tok.Finalized = true;
+      else
+        FormatOff = FO_None;
+      break;
+    case FO_NextLine:
+      if (NewlinesBefore == 1) {
+        FormatOff = FO_CurrentLine;
+        Tok.Finalized = true;
+      } else {
+        FormatOff = FO_None;
+      }
+      break;
+    default:
+      if (!FormattingDisabled && FormatOffRegex.match(Tok.TokenText)) {
+        if (Tok.TokenText.starts_with("//") &&
+            (NewlinesBefore > 0 || &Tok == Tokens.front())) {
+          FormatOff = FO_NextLine;
+        } else {
+          FormatOff = FO_CurrentLine;
+          for (auto *Token : reverse(Tokens)) {
+            Token->Finalized = true;
+            if (Token->NewlinesBefore > 0)
+              break;
+          }
+        }
+      }
+    }
     if (Style.isJavaScript()) {
       tryParseJSRegexLiteral();
       handleTemplateStrings();
diff --git a/clang/unittests/Format/ConfigParseTest.cpp 
b/clang/unittests/Format/ConfigParseTest.cpp
index 2b08b794792e9..f7ab5546c7193 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -295,6 +295,7 @@ TEST(ConfigParseTest, ParsesConfiguration) {
   FormatStyle Style = {};
   Style.Language = FormatStyle::LK_Cpp;
   CHECK_PARSE("CommentPragmas: '// abc$'", CommentPragmas, "// abc$");
+  CHECK_PARSE("OneLineFormatOffRegex: // ab$", OneLineFormatOffRegex, "// 
ab$");
 
   Style.QualifierAlignment = FormatStyle::QAS_Right;
   CHECK_PARSE("QualifierAlignment: Leave", QualifierAlignment,
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index 333d40d481025..3b07fc8e189c6 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24954,6 +24954,82 @@ TEST_F(FormatTest, DisableRegions) {
                  "// clang-format on");
 }
 
+TEST_F(FormatTest, OneLineFormatOffRegex) {
+  auto Style = getLLVMStyle();
+  Style.OneLineFormatOffRegex = "// format off$";
+
+  verifyFormat("// format off\n"
+               "int i ;\n"
+               "int j;",
+               " // format off\n"
+               "int i ;\n"
+               "int j ;",
+               Style);
+  verifyFormat("// format off?\n"
+               "int i;",
+               "// format off?\n"
+               "int i ;",
+               Style);
+  verifyFormat("f(\"// format off\");", "f(\"// format off\") ;", Style);
+
+  verifyFormat("int i;\n"
+               "// format off\n"
+               "int j ;\n"
+               "int k;",
+               "int i ;\n"
+               " // format off\n"
+               "int j ;\n"
+               "int k ;",
+               Style);
+
+  verifyFormat("int i;\n"
+               "int j ; // format off\n"
+               "int k;",
+               "int i ;\n"
+               "int j ; // format off\n"
+               "int k ;",
+               Style);
+
+  verifyFormat("// clang-format off\n"
+               "int i ;\n"
+               "int j ; // format off\n"
+               "int k ;\n"
+               "// clang-format on\n"
+               "f();",
+               "// clang-format off\n"
+               "int i ;\n"
+               "int j ; // format off\n"
+               "int k ;\n"
+               "// clang-format on\n"
+               "f() ;",
+               Style);
+
+  Style.OneLineFormatOffRegex = "^/\\* format off \\*/";
+  verifyFormat("int i;\n"
+               " /* format off */ int j ;\n"
+               "int k;",
+               "int i ;\n"
+               " /* format off */ int j ;\n"
+               "int k ;",
+               Style);
+  verifyFormat("f(\"/* format off */\");", "f(\"/* format off */\") ;", Style);
+
+  Style.ColumnLimit = 50;
+  Style.OneLineFormatOffRegex = "^LogErrorPrint$";
+  verifyFormat("myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
+               "myproject::MyLogErrorPrinter(myLogger,\n"
+               "                             \"Split me!\");",
+               "myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
+               "myproject::MyLogErrorPrinter(myLogger, \"Split me!\");",
+               Style);
+
+  Style.OneLineFormatOffRegex = "//(< clang-format off| NO_TRANSLATION)$";
+  verifyNoChange(
+      "int i ;  //< clang-format off\n"
+      "msg = sprintf(\"Long string with placeholders.\"); // NO_TRANSLATION",
+      Style);
+}
+
 TEST_F(FormatTest, DoNotCrashOnInvalidInput) {
   format("? ) =");
   verifyNoCrash("#define a\\\n /**/}");

>From 5cd0610b680e42a029f1f5afe4e24e1dcdc9a5b9 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpi...@gmail.com>
Date: Mon, 28 Apr 2025 22:45:59 -0700
Subject: [PATCH 2/2] Also allow block comments as markers for turning off
 formatting for next line

---
 clang/docs/ClangFormatStyleOptions.rst | 10 +--
 clang/include/clang/Format/Format.h    | 10 +--
 clang/lib/Format/FormatTokenLexer.cpp  | 13 ++--
 clang/unittests/Format/FormatTest.cpp  | 89 ++++++++++++++++----------
 4 files changed, 73 insertions(+), 49 deletions(-)

diff --git a/clang/docs/ClangFormatStyleOptions.rst 
b/clang/docs/ClangFormatStyleOptions.rst
index f1343ee6ee516..b47291599649d 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -5199,9 +5199,9 @@ the configuration (without a prefix: ``Auto``).
 
 **OneLineFormatOffRegex** (``String``) :versionbadge:`clang-format 21` :ref:`¶ 
<OneLineFormatOffRegex>`
   A regular expression that describes markers for turning formatting off for
-  one line. If it matches a line comment that is the first/only token of a
-  line, clang-format skips the next line. Otherwise, clang-format skips the
-  line that contains a matched token.
+  one line. If it matches a comment that is the only token of a line,
+  clang-format skips the comment and the next line. Otherwise, clang-format
+  skips lines containing a matched token.
 
   .. code-block:: c++
 
@@ -5210,11 +5210,11 @@ the configuration (without a prefix: ``Auto``).
      int a;
      int b ;  // NOLINT
      int c;
-     // NOLINTNEXTLINE
+      // NOLINTNEXTLINE
      int d ;
      int e;
      s = "// NOLINT";
-     logger() ;
+      logger() ;
      logger2();
      my_logger();
 
diff --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index e5606a8a2a419..7fe41d800ccb3 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3655,20 +3655,20 @@ struct FormatStyle {
   bool ObjCSpaceBeforeProtocolList;
 
   /// A regular expression that describes markers for turning formatting off 
for
-  /// one line. If it matches a line comment that is the first/only token of a
-  /// line, clang-format skips the next line. Otherwise, clang-format skips the
-  /// line that contains a matched token.
+  /// one line. If it matches a comment that is the only token of a line,
+  /// clang-format skips the comment and the next line. Otherwise, clang-format
+  /// skips lines containing a matched token.
   /// \code
   ///    // OneLineFormatOffRegex: ^(// NOLINT|logger$)
   ///    // results in the output below:
   ///    int a;
   ///    int b ;  // NOLINT
   ///    int c;
-  ///    // NOLINTNEXTLINE
+  ///     // NOLINTNEXTLINE
   ///    int d ;
   ///    int e;
   ///    s = "// NOLINT";
-  ///    logger() ;
+  ///     logger() ;
   ///    logger2();
   ///    my_logger();
   /// \endcode
diff --git a/clang/lib/Format/FormatTokenLexer.cpp 
b/clang/lib/Format/FormatTokenLexer.cpp
index 58991f9681c97..bff9db22ac657 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -97,25 +97,26 @@ ArrayRef<FormatToken *> FormatTokenLexer::lex() {
         FormatOff = FO_None;
       break;
     case FO_NextLine:
-      if (NewlinesBefore == 1) {
-        FormatOff = FO_CurrentLine;
-        Tok.Finalized = true;
-      } else {
+      if (NewlinesBefore > 1) {
         FormatOff = FO_None;
+      } else {
+        Tok.Finalized = true;
+        FormatOff = FO_CurrentLine;
       }
       break;
     default:
       if (!FormattingDisabled && FormatOffRegex.match(Tok.TokenText)) {
-        if (Tok.TokenText.starts_with("//") &&
+        if (Tok.is(tok::comment) &&
             (NewlinesBefore > 0 || &Tok == Tokens.front())) {
+          Tok.Finalized = true;
           FormatOff = FO_NextLine;
         } else {
-          FormatOff = FO_CurrentLine;
           for (auto *Token : reverse(Tokens)) {
             Token->Finalized = true;
             if (Token->NewlinesBefore > 0)
               break;
           }
+          FormatOff = FO_CurrentLine;
         }
       }
     }
diff --git a/clang/unittests/Format/FormatTest.cpp 
b/clang/unittests/Format/FormatTest.cpp
index 3b07fc8e189c6..c4fcc5506d152 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24958,75 +24958,98 @@ TEST_F(FormatTest, OneLineFormatOffRegex) {
   auto Style = getLLVMStyle();
   Style.OneLineFormatOffRegex = "// format off$";
 
-  verifyFormat("// format off\n"
-               "int i ;\n"
+  verifyFormat(" // format off\n"
+               " int i ;\n"
                "int j;",
                " // format off\n"
-               "int i ;\n"
-               "int j ;",
+               " int i ;\n"
+               " int j ;",
                Style);
   verifyFormat("// format off?\n"
                "int i;",
-               "// format off?\n"
-               "int i ;",
+               " // format off?\n"
+               " int i ;",
                Style);
-  verifyFormat("f(\"// format off\");", "f(\"// format off\") ;", Style);
+  verifyFormat("f(\"// format off\");", " f(\"// format off\") ;", Style);
 
   verifyFormat("int i;\n"
-               "// format off\n"
-               "int j ;\n"
+               " // format off\n"
+               " int j ;\n"
                "int k;",
-               "int i ;\n"
+               " int i ;\n"
                " // format off\n"
-               "int j ;\n"
-               "int k ;",
+               " int j ;\n"
+               " int k ;",
+               Style);
+
+  verifyFormat(" // format off\n"
+               "\n"
+               "int i;",
+               " // format off\n"
+               " \n"
+               " int i ;",
                Style);
 
   verifyFormat("int i;\n"
-               "int j ; // format off\n"
+               " int j ; // format off\n"
                "int k;",
-               "int i ;\n"
-               "int j ; // format off\n"
-               "int k ;",
+               " int i ;\n"
+               " int j ; // format off\n"
+               " int k ;",
                Style);
 
   verifyFormat("// clang-format off\n"
-               "int i ;\n"
-               "int j ; // format off\n"
-               "int k ;\n"
+               " int i ;\n"
+               " int j ; // format off\n"
+               " int k ;\n"
                "// clang-format on\n"
                "f();",
-               "// clang-format off\n"
-               "int i ;\n"
-               "int j ; // format off\n"
-               "int k ;\n"
-               "// clang-format on\n"
-               "f() ;",
+               " // clang-format off\n"
+               " int i ;\n"
+               " int j ; // format off\n"
+               " int k ;\n"
+               " // clang-format on\n"
+               " f() ;",
                Style);
 
   Style.OneLineFormatOffRegex = "^/\\* format off \\*/";
   verifyFormat("int i;\n"
                " /* format off */ int j ;\n"
                "int k;",
-               "int i ;\n"
+               " int i ;\n"
                " /* format off */ int j ;\n"
-               "int k ;",
+               " int k ;",
+               Style);
+  verifyFormat("f(\"/* format off */\");", " f(\"/* format off */\") ;", 
Style);
+
+  Style.AlignEscapedNewlines = FormatStyle::ENAS_DontAlign;
+  verifyFormat("#define A \\\n"
+               "  do { \\\n"
+               "  /* format off */\\\n"
+               "  f() ; \\\n"
+               "    g(); \\\n"
+               "  } while (0)",
+               "# define A\\\n"
+               " do{ \\\n"
+               "  /* format off */\\\n"
+               "  f() ; \\\n"
+               "  g() ;\\\n"
+               " } while (0 )",
                Style);
-  verifyFormat("f(\"/* format off */\");", "f(\"/* format off */\") ;", Style);
 
   Style.ColumnLimit = 50;
   Style.OneLineFormatOffRegex = "^LogErrorPrint$";
-  verifyFormat("myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
+  verifyFormat(" myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
                "myproject::MyLogErrorPrinter(myLogger,\n"
                "                             \"Split me!\");",
-               "myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
-               "myproject::MyLogErrorPrinter(myLogger, \"Split me!\");",
+               " myproject::LogErrorPrint(logger, \"Don't split me!\");\n"
+               " myproject::MyLogErrorPrinter(myLogger, \"Split me!\");",
                Style);
 
   Style.OneLineFormatOffRegex = "//(< clang-format off| NO_TRANSLATION)$";
   verifyNoChange(
-      "int i ;  //< clang-format off\n"
-      "msg = sprintf(\"Long string with placeholders.\"); // NO_TRANSLATION",
+      " int i ;  //< clang-format off\n"
+      " msg = sprintf(\"Long string with placeholders.\"); // NO_TRANSLATION",
       Style);
 }
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to