diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 570cab262c115..0381573793621 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1701,6 +1701,16 @@ the configuration (without a prefix: ``Auto``). int abcdef; // but this isn't + * ``bool AlignPPAndNotPP`` If comments following preprocessor directive should be aligned with + comments that don't. + + .. code-block:: c++ + + true: false: + #define A // Comment vs. #define A // Comment + #define AB // Aligned #define AB // Aligned + int i; // Aligned int i; // Not aligned + .. _AllowAllArgumentsOnNextLine: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index fe77f917bb801..5680ea74d8efe 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -609,6 +609,7 @@ clang-format literals. - Add ``Leave`` suboption to ``IndentPPDirectives``. - Add ``AllowBreakBeforeQtProperty`` option. +- Add ``AlignPPAndNotPP`` suboption to ``AlignTrailingComments``. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 2852c4a2916a4..82c3a215fc94d 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -622,9 +622,19 @@ struct FormatStyle { /// int abcdef; // but this isn't /// \endcode unsigned OverEmptyLines; + /// If comments following preprocessor directive should be aligned with + /// comments that don't. + /// \code + /// true: false: + /// #define A // Comment vs. #define A // Comment + /// #define AB // Aligned #define AB // Aligned + /// int i; // Aligned int i; // Not aligned + /// \endcode + bool AlignPPAndNotPP; bool operator==(const TrailingCommentsAlignmentStyle &R) const { - return Kind == R.Kind && OverEmptyLines == R.OverEmptyLines; + return Kind == R.Kind && OverEmptyLines == R.OverEmptyLines && + AlignPPAndNotPP == R.AlignPPAndNotPP; } bool operator!=(const TrailingCommentsAlignmentStyle &R) const { return !(*this == R); diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index edd126c7724b8..62b97fbe0ffc4 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -869,27 +869,28 @@ template <> struct MappingTraits { FormatStyle::TrailingCommentsAlignmentStyle &Value) { IO.enumCase(Value, "Leave", FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Leave, 0})); + {FormatStyle::TCAS_Leave, 0, true})); IO.enumCase(Value, "Always", FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Always, 0})); + {FormatStyle::TCAS_Always, 0, true})); IO.enumCase(Value, "Never", FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Never, 0})); + {FormatStyle::TCAS_Never, 0, true})); // For backwards compatibility IO.enumCase(Value, "true", FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Always, 0})); + {FormatStyle::TCAS_Always, 0, true})); IO.enumCase(Value, "false", FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Never, 0})); + {FormatStyle::TCAS_Never, 0, true})); } static void mapping(IO &IO, FormatStyle::TrailingCommentsAlignmentStyle &Value) { + IO.mapOptional("AlignPPAndNotPP", Value.AlignPPAndNotPP); IO.mapOptional("Kind", Value.Kind); IO.mapOptional("OverEmptyLines", Value.OverEmptyLines); } @@ -1578,6 +1579,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AlignTrailingComments = {}; LLVMStyle.AlignTrailingComments.Kind = FormatStyle::TCAS_Always; LLVMStyle.AlignTrailingComments.OverEmptyLines = 0; + LLVMStyle.AlignTrailingComments.AlignPPAndNotPP = true; LLVMStyle.AllowAllArgumentsOnNextLine = true; LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; LLVMStyle.AllowBreakBeforeNoexceptSpecifier = FormatStyle::BBNSS_Never; diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index 65fc65e79fdc3..c5f738ebe42ad 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -997,9 +997,13 @@ void WhitespaceManager::alignTrailingComments() { return; const int Size = Changes.size(); + if (Size == 0) + return; + int MinColumn = 0; int StartOfSequence = 0; bool BreakBeforeNext = false; + bool IsInPP = Changes.front().Tok->Tok.is(tok::hash); int NewLineThreshold = 1; if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Always) NewLineThreshold = Style.AlignTrailingComments.OverEmptyLines + 1; @@ -1008,7 +1012,19 @@ void WhitespaceManager::alignTrailingComments() { auto &C = Changes[I]; if (C.StartOfBlockComment) continue; - Newlines += C.NewlinesBefore; + if (C.NewlinesBefore != 0) { + Newlines += C.NewlinesBefore; + const bool WasInPP = std::exchange( + IsInPP, C.Tok->Tok.is(tok::hash) || (IsInPP && C.IsTrailingComment) || + C.ContinuesPPDirective); + if (IsInPP != WasInPP && !Style.AlignTrailingComments.AlignPPAndNotPP) { + alignTrailingComments(StartOfSequence, I, MinColumn); + MinColumn = 0; + MaxColumn = INT_MAX; + StartOfSequence = I; + Newlines = 0; + } + } if (!C.IsTrailingComment) continue; diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 6488e38badee7..de22c9f7e2a15 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -576,20 +576,20 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE("AlignTrailingComments: Leave", AlignTrailingComments, FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Leave, 0})); + {FormatStyle::TCAS_Leave, 0, true})); CHECK_PARSE("AlignTrailingComments: Always", AlignTrailingComments, FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Always, 0})); + {FormatStyle::TCAS_Always, 0, true})); CHECK_PARSE("AlignTrailingComments: Never", AlignTrailingComments, FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Never, 0})); + {FormatStyle::TCAS_Never, 0, true})); // For backwards compatibility CHECK_PARSE("AlignTrailingComments: true", AlignTrailingComments, FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Always, 0})); + {FormatStyle::TCAS_Always, 0, true})); CHECK_PARSE("AlignTrailingComments: false", AlignTrailingComments, FormatStyle::TrailingCommentsAlignmentStyle( - {FormatStyle::TCAS_Never, 0})); + {FormatStyle::TCAS_Never, 0, true})); CHECK_PARSE_NESTED_VALUE("Kind: Always", AlignTrailingComments, Kind, FormatStyle::TCAS_Always); CHECK_PARSE_NESTED_VALUE("Kind: Never", AlignTrailingComments, Kind, @@ -598,6 +598,7 @@ TEST(ConfigParseTest, ParsesConfiguration) { FormatStyle::TCAS_Leave); CHECK_PARSE_NESTED_VALUE("OverEmptyLines: 1234", AlignTrailingComments, OverEmptyLines, 1234u); + CHECK_PARSE_NESTED_BOOL(AlignTrailingComments, AlignPPAndNotPP); Style.UseTab = FormatStyle::UT_ForIndentation; CHECK_PARSE("UseTab: Never", UseTab, FormatStyle::UT_Never); diff --git a/clang/unittests/Format/FormatTestComments.cpp b/clang/unittests/Format/FormatTestComments.cpp index 6b433bb384864..cfdca45360598 100644 --- a/clang/unittests/Format/FormatTestComments.cpp +++ b/clang/unittests/Format/FormatTestComments.cpp @@ -3501,6 +3501,66 @@ TEST_F(FormatTestComments, DontAlignOverScope) { "int foobar; // group"); } +TEST_F(FormatTestComments, DontAlignOverPPDirective) { + auto Style = getLLVMStyle(); + Style.AlignTrailingComments.AlignPPAndNotPP = false; + + verifyFormat("int i; // Aligned\n" + "int long; // with this\n" + "#define FOO // only aligned\n" + "#define LOOONG // with other pp directives\n" + "int loooong; // new alignment", + "int i;//Aligned\n" + "int long;//with this\n" + "#define FOO //only aligned\n" + "#define LOOONG //with other pp directives\n" + "int loooong; //new alignment", + Style); + + verifyFormat("#define A // Comment\n" + "#define AB // Comment", + Style); + + Style.ColumnLimit = 30; + verifyNoChange("#define A // Comment\n" + " // Continued\n" + "int i = 0; // New Stuff\n" + " // Continued\n" + "#define Func(X) \\\n" + " X(); \\\n" + " X(); // Comment\n" + " // Continued\n" + "long loong = 1; // Dont align", + Style); + + verifyFormat("#define A // Comment that\n" + " // would wrap\n" + "#define FOO // For the\n" + " // alignment\n" + "#define B // Also\n" + " // aligned", + "#define A // Comment that would wrap\n" + "#define FOO // For the alignment\n" + "#define B // Also\n" + " // aligned", + Style); + + Style.AlignTrailingComments.OverEmptyLines = 1; + verifyNoChange("#define A // Comment\n" + "\n" + " // Continued\n" + "int i = 0; // New Stuff\n" + "\n" + " // Continued\n" + "#define Func(X) \\\n" + " X(); \\\n" + " X(); // Comment\n" + "\n" + " // Continued\n" + "long loong = 1; // Dont align", + Style); +} + TEST_F(FormatTestComments, AlignsBlockCommentDecorations) { EXPECT_EQ("/*\n" " */",