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