Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[clang-format] Add OneLineFormatOffRegex option #137577

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 30, 2025
Merged

Conversation

owenca
Copy link
Contributor

@owenca owenca commented Apr 28, 2025

Close #54334

@llvmbot llvmbot added clang Clang issues not falling into any other category clang-format labels Apr 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 28, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-format

Author: Owen Pan (owenca)

Changes

Close #54334


Full diff: https://github.com/llvm/llvm-project/pull/137577.diff

7 Files Affected:

  • (modified) clang/docs/ClangFormatStyleOptions.rst (+22)
  • (modified) clang/docs/ReleaseNotes.rst (+1)
  • (modified) clang/include/clang/Format/Format.h (+21)
  • (modified) clang/lib/Format/Format.cpp (+1)
  • (modified) clang/lib/Format/FormatTokenLexer.cpp (+34)
  • (modified) clang/unittests/Format/ConfigParseTest.cpp (+1)
  • (modified) clang/unittests/Format/FormatTest.cpp (+76)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 3f8a5f49313b2..e3fe4a7529559 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -5195,6 +5195,28 @@ 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;
+     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..ce9a05f40be61 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3654,6 +3654,26 @@ 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;
+  ///    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 +5419,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 /**/}");

@owenca owenca merged commit b8bb1cc into llvm:main Apr 30, 2025
12 checks passed
@owenca owenca deleted the one-line-off branch April 30, 2025 02:22
@dyung
Copy link
Collaborator

dyung commented Apr 30, 2025

@owenca your change seems to be causing a unit test failure in CodeGenTest.TestNonAlterTest which is impacting quite a few bots. Can you take a look and revert if you need time to investigate?

@owenca
Copy link
Contributor Author

owenca commented Apr 30, 2025

@dyung I don't see anything in this patch could cause a failure in CodGen tests. Have you verified it e.g. by running the tests on f175030?

owenca added a commit that referenced this pull request Apr 30, 2025
This reverts commit b8bb1cc which triggered
an assertion failure in CodeGenTest.TestNonAlterTest.
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@llvm llvm deleted a comment from llvm-ci May 1, 2025
@owenca owenca removed the clang Clang issues not falling into any other category label May 3, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This reverts commit b8bb1cc which triggered
an assertion failure in CodeGenTest.TestNonAlterTest.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This reverts commit b8bb1cc which triggered
an assertion failure in CodeGenTest.TestNonAlterTest.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This reverts commit b8bb1cc which triggered
an assertion failure in CodeGenTest.TestNonAlterTest.
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
This reverts commit b8bb1cc which triggered
an assertion failure in CodeGenTest.TestNonAlterTest.
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

clang-format: Disabling Formatting on a one line
4 participants