diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2128,6 +2128,56 @@ **DisableFormat** (``bool``) Disables formatting completely. +**EmptyLineAfterAccessModifier** (``EmptyLineAfterAccessModifierStyle``) + Defines when to put an empty line after access modifiers. + ``EmptyLineBeforeAccessModifier`` configuration handles the number of + empty lines between two access modifiers. + + Possible values: + + * ``ELAAMS_Never`` (in configuration: ``Never``) + Remove all empty lines after access modifiers. + + .. code-block:: c++ + + struct foo { + private: + int i; + protected: + int j; + /* comment */ + public: + foo() {} + private: + protected: + }; + + * ``ELAAMS_Leave`` (in configuration: ``Leave``) + Keep existing empty lines after access modifiers. + MaxEmptyLinesToKeep is applied instead. + + * ``ELAAMS_Always`` (in configuration: ``Always``) + Always add empty line after access modifiers if there are none. + MaxEmptyLinesToKeep is applied also. + + .. code-block:: c++ + + struct foo { + private: + + int i; + protected: + + int j; + /* comment */ + public: + + foo() {} + private: + + protected: + }; + **EmptyLineBeforeAccessModifier** (``EmptyLineBeforeAccessModifierStyle``) Defines in which cases to put empty line before access modifiers. @@ -2195,8 +2245,6 @@ protected: }; - - **ExperimentalAutoDetectBinPacking** (``bool``) If ``true``, clang-format detects whether function calls and definitions are formatted with one parameter per line. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -216,6 +216,9 @@ - Support for Whitesmiths has been improved, with fixes for ``namespace`` blocks and ``case`` blocks and labels. +- Option ``EmptyLineAfterAccessModifier`` has been added to remove, force or keep + new lines after access modifiers. + libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1891,6 +1891,54 @@ /// Disables formatting completely. bool DisableFormat; + /// Different styles for empty line after access modifiers. + /// ``EmptyLineBeforeAccessModifier`` configuration handles the number of + /// empty lines between two access modifiers. + enum EmptyLineAfterAccessModifierStyle : unsigned char { + /// Remove all empty lines after access modifiers. + /// \code + /// struct foo { + /// private: + /// int i; + /// protected: + /// int j; + /// /* comment */ + /// public: + /// foo() {} + /// private: + /// protected: + /// }; + /// \endcode + ELAAMS_Never, + /// Keep existing empty lines after access modifiers. + /// MaxEmptyLinesToKeep is applied instead. + ELAAMS_Leave, + /// Always add empty line after access modifiers if there are none. + /// MaxEmptyLinesToKeep is applied also. + /// \code + /// struct foo { + /// private: + // + /// int i; + /// protected: + // + /// int j; + /// /* comment */ + /// public: + // + /// foo() {} + /// private: + /// + /// protected: + // + /// }; + /// \endcode + ELAAMS_Always, + }; + + /// Defines in which cases to put empty line after access modifiers. + EmptyLineAfterAccessModifierStyle EmptyLineAfterAccessModifier; + /// Different styles for empty line before access modifiers. enum EmptyLineBeforeAccessModifierStyle : unsigned char { /// Remove all empty lines before access modifiers. @@ -3200,6 +3248,7 @@ DeriveLineEnding == R.DeriveLineEnding && DerivePointerAlignment == R.DerivePointerAlignment && DisableFormat == R.DisableFormat && + EmptyLineAfterAccessModifier == R.EmptyLineAfterAccessModifier && EmptyLineBeforeAccessModifier == R.EmptyLineBeforeAccessModifier && ExperimentalAutoDetectBinPacking == R.ExperimentalAutoDetectBinPacking && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -241,6 +241,16 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void + enumeration(IO &IO, FormatStyle::EmptyLineAfterAccessModifierStyle &Value) { + IO.enumCase(Value, "Never", FormatStyle::ELAAMS_Never); + IO.enumCase(Value, "Leave", FormatStyle::ELAAMS_Leave); + IO.enumCase(Value, "Always", FormatStyle::ELAAMS_Always); + } +}; + template <> struct ScalarEnumerationTraits< FormatStyle::EmptyLineBeforeAccessModifierStyle> { @@ -584,6 +594,8 @@ IO.mapOptional("DeriveLineEnding", Style.DeriveLineEnding); IO.mapOptional("DerivePointerAlignment", Style.DerivePointerAlignment); IO.mapOptional("DisableFormat", Style.DisableFormat); + IO.mapOptional("EmptyLineAfterAccessModifier", + Style.EmptyLineAfterAccessModifier); IO.mapOptional("EmptyLineBeforeAccessModifier", Style.EmptyLineBeforeAccessModifier); IO.mapOptional("ExperimentalAutoDetectBinPacking", @@ -974,6 +986,7 @@ LLVMStyle.Cpp11BracedListStyle = true; LLVMStyle.DeriveLineEnding = true; LLVMStyle.DerivePointerAlignment = false; + LLVMStyle.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never; LLVMStyle.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_LogicalBlock; LLVMStyle.ExperimentalAutoDetectBinPacking = false; LLVMStyle.FixNamespaceComments = true; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1264,6 +1264,8 @@ if (PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) && RootToken.NewlinesBefore <= 1) Newlines = 2; + if (PreviousLine->First->isAccessSpecifier()) + Newlines = 1; // Previous is an access modifier remove all new lines. break; case FormatStyle::ELBAMS_Always: { const FormatToken *previousToken; @@ -1278,10 +1280,28 @@ } } - // Remove empty lines after access specifiers. + // Insert or remove empty line after access specifiers. if (PreviousLine && PreviousLine->First->isAccessSpecifier() && - (!PreviousLine->InPPDirective || !RootToken.HasUnescapedNewline)) - Newlines = std::min(1u, Newlines); + (!PreviousLine->InPPDirective || !RootToken.HasUnescapedNewline)) { + // EmptyLineBeforeAccessModifier is handling the case when two access + // modifiers follow each other. + if (!RootToken.isAccessSpecifier()) { + switch (Style.EmptyLineAfterAccessModifier) { + case FormatStyle::ELAAMS_Never: + Newlines = 1; + break; + case FormatStyle::ELAAMS_Leave: + Newlines = std::max(Newlines, 1u); + break; + case FormatStyle::ELAAMS_Always: + if (RootToken.is(tok::r_brace)) // Do not add at end of class. + Newlines = 1u; + else + Newlines = std::max(Newlines, 2u); + break; + } + } + } if (Newlines) Indent = NewlineIndent; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -9179,6 +9179,554 @@ Style); } +TEST_F(FormatTest, FormatsAfterAccessModifiers) { + + FormatStyle Style = getLLVMStyle(); + EXPECT_EQ(Style.EmptyLineAfterAccessModifier, FormatStyle::ELAAMS_Never); + verifyFormat("struct foo {\n" + "private:\n" + " void f() {}\n" + "\n" + "private:\n" + " int i;\n" + "\n" + "protected:\n" + " int j;\n" + "};\n", + Style); + + // Check if lines are removed. + verifyFormat("struct foo {\n" + "private:\n" + " void f() {}\n" + "\n" + "private:\n" + " int i;\n" + "\n" + "protected:\n" + " int j;\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n" + " void f() {}\n" + "\n" + "private:\n" + "\n" + " int i;\n" + "\n" + "protected:\n" + "\n" + " int j;\n" + "};\n", + Style); + + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Always; + verifyFormat("struct foo {\n" + "private:\n" + "\n" + " void f() {}\n" + "\n" + "private:\n" + "\n" + " int i;\n" + "\n" + "protected:\n" + "\n" + " int j;\n" + "};\n", + Style); + + // Check if lines are added. + verifyFormat("struct foo {\n" + "private:\n" + "\n" + " void f() {}\n" + "\n" + "private:\n" + "\n" + " int i;\n" + "\n" + "protected:\n" + "\n" + " int j;\n" + "};\n", + "struct foo {\n" + "private:\n" + " void f() {}\n" + "\n" + "private:\n" + " int i;\n" + "\n" + "protected:\n" + " int j;\n" + "};\n", + Style); + + // Leave tests rely on the code layout, test::messUp can not be used. + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Leave; + Style.MaxEmptyLinesToKeep = 0u; + EXPECT_EQ("struct foo {\n" + "private:\n" + " void f() {}\n" + "private:\n" + " int i;\n" + "protected:\n" + " int j;\n" + "};\n", + format("struct foo {\n" + "private:\n" + " void f() {}\n" + "\n" + "private:\n" + " int i;\n" + "\n" + "protected:\n" + " int j;\n" + "};\n", + Style)); + + // Check if MaxEmptyLinesToKeep is respected. + EXPECT_EQ("struct foo {\n" + "private:\n" + " void f() {}\n" + "private:\n" + " int i;\n" + "protected:\n" + " int j;\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + " void f() {}\n" + "\n" + "private:\n" + "\n\n\n" + " int i;\n" + "\n" + "protected:\n" + "\n\n\n" + " int j;\n" + "};\n", + Style)); + + Style.MaxEmptyLinesToKeep = 1u; + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n" + " void f() {}\n" + "\n" + "private:\n" + "\n" + " int i;\n" + "\n" + "protected:\n" + "\n" + " int j;\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n" + " void f() {}\n" + "\n" + "private:\n" + "\n" + " int i;\n" + "\n" + "protected:\n" + "\n" + " int j;\n" + "};\n", + Style)); + // Check if no lines are kept. + EXPECT_EQ("struct foo {\n" + "private:\n" + " void f() {}\n" + "\n" + "private:\n" + " int i;\n" + "\n" + "protected:\n" + " int j;\n" + "};\n", + format("struct foo {\n" + "private:\n" + " void f() {}\n" + "\n" + "private:\n" + " int i;\n" + "\n" + "protected:\n" + " int j;\n" + "};\n", + Style)); + // Check if MaxEmptyLinesToKeep is respected. + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n" + " void f() {}\n" + "\n" + "private:\n" + "\n" + " int i;\n" + "\n" + "protected:\n" + "\n" + " int j;\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + " void f() {}\n" + "\n" + "private:\n" + "\n\n\n" + " int i;\n" + "\n" + "protected:\n" + "\n\n\n" + " int j;\n" + "};\n", + Style)); + + Style.MaxEmptyLinesToKeep = 10u; + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + " void f() {}\n" + "\n" + "private:\n" + "\n\n\n" + " int i;\n" + "\n" + "protected:\n" + "\n\n\n" + " int j;\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + " void f() {}\n" + "\n" + "private:\n" + "\n\n\n" + " int i;\n" + "\n" + "protected:\n" + "\n\n\n" + " int j;\n" + "};\n", + Style)); + + // Test with comments. + Style = getLLVMStyle(); + verifyFormat("struct foo {\n" + "private:\n" + " // comment\n" + " void f() {}\n" + "\n" + "private: /* comment */\n" + " int i;\n" + "};\n", + Style); + verifyFormat("struct foo {\n" + "private:\n" + " // comment\n" + " void f() {}\n" + "\n" + "private: /* comment */\n" + " int i;\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n" + " // comment\n" + " void f() {}\n" + "\n" + "private: /* comment */\n" + "\n" + " int i;\n" + "};\n", + Style); + + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Always; + verifyFormat("struct foo {\n" + "private:\n" + "\n" + " // comment\n" + " void f() {}\n" + "\n" + "private: /* comment */\n" + "\n" + " int i;\n" + "};\n", + "struct foo {\n" + "private:\n" + " // comment\n" + " void f() {}\n" + "\n" + "private: /* comment */\n" + " int i;\n" + "};\n", + Style); + verifyFormat("struct foo {\n" + "private:\n" + "\n" + " // comment\n" + " void f() {}\n" + "\n" + "private: /* comment */\n" + "\n" + " int i;\n" + "};\n", + Style); + + // Test with preprocessor defines. + Style = getLLVMStyle(); + verifyFormat("struct foo {\n" + "private:\n" + "#ifdef FOO\n" + "#endif\n" + " void f() {}\n" + "};\n", + Style); + verifyFormat("struct foo {\n" + "private:\n" + "#ifdef FOO\n" + "#endif\n" + " void f() {}\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n" + "#ifdef FOO\n" + "#endif\n" + " void f() {}\n" + "};\n", + Style); + + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Always; + verifyFormat("struct foo {\n" + "private:\n" + "\n" + "#ifdef FOO\n" + "#endif\n" + " void f() {}\n" + "};\n", + "struct foo {\n" + "private:\n" + "#ifdef FOO\n" + "#endif\n" + " void f() {}\n" + "};\n", + Style); + verifyFormat("struct foo {\n" + "private:\n" + "\n" + "#ifdef FOO\n" + "#endif\n" + " void f() {}\n" + "};\n", + Style); +} + +TEST_F(FormatTest, FormatsAfterAndBeforeAccessModifiersInteraction) { + // Combined tests of EmptyLineAfterAccessModifier and + // EmptyLineBeforeAccessModifier. + FormatStyle Style = getLLVMStyle(); + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Always; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Always; + verifyFormat("struct foo {\n" + "private:\n" + "\n" + "protected:\n" + "};\n", + Style); + + Style.MaxEmptyLinesToKeep = 10u; + // Both remove all new lines. + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Never; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never; + verifyFormat("struct foo {\n" + "private:\n" + "protected:\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style); + + // Leave tests rely on the code layout, test::messUp can not be used. + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Leave; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Leave; + Style.MaxEmptyLinesToKeep = 10u; + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style)); + Style.MaxEmptyLinesToKeep = 3u; + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style)); + Style.MaxEmptyLinesToKeep = 1u; + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style)); // Based on new lines in original document and not + // on the setting. + + Style.MaxEmptyLinesToKeep = 10u; + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Always; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Leave; + // Newlines are kept if they are greater than zero, + // test::messUp removes all new lines which changes the logic + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style)); + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Leave; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Always; + // test::messUp removes all new lines which changes the logic + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style)); + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Leave; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never; + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style)); // test::messUp removes all new lines which changes + // the logic. + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Never; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Leave; + verifyFormat("struct foo {\n" + "private:\n" + "protected:\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style); + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Always; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never; + EXPECT_EQ("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + format("struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style)); // test::messUp removes all new lines which changes + // the logic. + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_Never; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Always; + verifyFormat("struct foo {\n" + "private:\n" + "protected:\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style); + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_LogicalBlock; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Always; + verifyFormat("struct foo {\n" + "private:\n" + "protected:\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style); + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_LogicalBlock; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Leave; + verifyFormat("struct foo {\n" + "private:\n" + "protected:\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style); + + Style.EmptyLineBeforeAccessModifier = FormatStyle::ELBAMS_LogicalBlock; + Style.EmptyLineAfterAccessModifier = FormatStyle::ELAAMS_Never; + verifyFormat("struct foo {\n" + "private:\n" + "protected:\n" + "};\n", + "struct foo {\n" + "private:\n" + "\n\n\n" + "protected:\n" + "};\n", + Style); +} + TEST_F(FormatTest, FormatsArrays) { verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaa[aaaaaaaaaaaaaaaaaaaaaaaaa]\n" " [bbbbbbbbbbbbbbbbbbbbbbbbb] = c;");