Author: Owen Pan Date: 2025-04-30T19:58:59-07:00 New Revision: 8effc8da292bfacb823a7e3c4134296da481fedc
URL: https://github.com/llvm/llvm-project/commit/8effc8da292bfacb823a7e3c4134296da481fedc DIFF: https://github.com/llvm/llvm-project/commit/8effc8da292bfacb823a7e3c4134296da481fedc.diff LOG: Reland [clang-format] Add OneLineFormatOffRegex option (#137577) Added: Modified: clang/docs/ClangFormatStyleOptions.rst clang/docs/ReleaseNotes.rst clang/include/clang/Format/Format.h clang/lib/Format/Format.cpp clang/lib/Format/FormatTokenLexer.cpp clang/lib/Format/FormatTokenLexer.h clang/unittests/Format/ConfigParseTest.cpp clang/unittests/Format/FormatTest.cpp Removed: ################################################################################ diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 3f8a5f49313b2..b47291599649d 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 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++ + + // 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 728ceb900bcd4..ba6e40f145655 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -746,6 +746,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..7fe41d800ccb3 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 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 + /// 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..944d30ba7874f 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -32,7 +32,8 @@ FormatTokenLexer::FormatTokenLexer( LangOpts(getFormattingLangOpts(Style)), SourceMgr(SourceMgr), ID(ID), Style(Style), IdentTable(IdentTable), Keywords(IdentTable), Encoding(Encoding), Allocator(Allocator), FirstInLineIndex(0), - FormattingDisabled(false), MacroBlockBeginRegex(Style.MacroBlockBegin), + FormattingDisabled(false), FormatOffRegex(Style.OneLineFormatOffRegex), + MacroBlockBeginRegex(Style.MacroBlockBegin), MacroBlockEndRegex(Style.MacroBlockEnd) { Lex.reset(new Lexer(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts)); Lex->SetKeepWhitespaceMode(true); @@ -83,8 +84,42 @@ FormatTokenLexer::FormatTokenLexer( ArrayRef<FormatToken *> FormatTokenLexer::lex() { assert(Tokens.empty()); assert(FirstInLineIndex == 0); + 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_None; + } else { + Tok.Finalized = true; + FormatOff = FO_CurrentLine; + } + break; + default: + if (!FormattingDisabled && FormatOffRegex.match(Tok.TokenText)) { + if (Tok.is(tok::comment) && + (NewlinesBefore > 0 || Tokens.size() == 1)) { + Tok.Finalized = true; + FormatOff = FO_NextLine; + } else { + for (auto *Token : reverse(Tokens)) { + Token->Finalized = true; + if (Token->NewlinesBefore > 0) + break; + } + FormatOff = FO_CurrentLine; + } + } + } if (Style.isJavaScript()) { tryParseJSRegexLiteral(); handleTemplateStrings(); diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h index 3f001bc69415d..105847b126e20 100644 --- a/clang/lib/Format/FormatTokenLexer.h +++ b/clang/lib/Format/FormatTokenLexer.h @@ -134,6 +134,7 @@ class FormatTokenLexer { VariableTemplates; bool FormattingDisabled; + llvm::Regex FormatOffRegex; // For one line. llvm::Regex MacroBlockBeginRegex; llvm::Regex MacroBlockEndRegex; 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..c4fcc5506d152 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -24954,6 +24954,105 @@ 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(" // format off\n" + "\n" + "int i;", + " // format off\n" + " \n" + " int i ;", + 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.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); + + 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 /**/}"); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits