Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -1541,6 +1541,19 @@ **DisableFormat** (``bool``) Disables formatting completely. +**EmptyLineBeforeAccessModifier** (``bool``) + If true, the empty line is inserted before access modifiers + + .. code-block:: c++ + + true: false: + struct foo { vs. struct foo { + int i; int i; + private: + private: int j; + int j; } + } + **ExperimentalAutoDetectBinPacking** (``bool``) If ``true``, clang-format detects whether function calls and definitions are formatted with one parameter per line. Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -1348,6 +1348,18 @@ /// Disables formatting completely. bool DisableFormat; + /// If true, the empty line is inserted before access modifiers + /// \code + /// true: false: + /// struct foo { vs. struct foo { + /// int i; int i; + /// private: + /// private: int j; + /// int j; } + /// } + /// \endcode + bool EmptyLineBeforeAccessModifier; + /// If ``true``, clang-format detects whether function calls and /// definitions are formatted with one parameter per line. /// @@ -2338,6 +2350,7 @@ DeriveLineEnding == R.DeriveLineEnding && DerivePointerAlignment == R.DerivePointerAlignment && DisableFormat == R.DisableFormat && + EmptyLineBeforeAccessModifier == R.EmptyLineBeforeAccessModifier && ExperimentalAutoDetectBinPacking == R.ExperimentalAutoDetectBinPacking && FixNamespaceComments == R.FixNamespaceComments && Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -511,6 +511,8 @@ IO.mapOptional("DeriveLineEnding", Style.DeriveLineEnding); IO.mapOptional("DerivePointerAlignment", Style.DerivePointerAlignment); IO.mapOptional("DisableFormat", Style.DisableFormat); + IO.mapOptional("EmptyLineBeforeAccessModifier", + Style.EmptyLineBeforeAccessModifier); IO.mapOptional("ExperimentalAutoDetectBinPacking", Style.ExperimentalAutoDetectBinPacking); IO.mapOptional("FixNamespaceComments", Style.FixNamespaceComments); @@ -867,6 +869,7 @@ LLVMStyle.Cpp11BracedListStyle = true; LLVMStyle.DeriveLineEnding = true; LLVMStyle.DerivePointerAlignment = false; + LLVMStyle.EmptyLineBeforeAccessModifier = true; LLVMStyle.ExperimentalAutoDetectBinPacking = false; LLVMStyle.FixNamespaceComments = true; LLVMStyle.ForEachMacros.push_back("foreach"); Index: clang/lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- clang/lib/Format/UnwrappedLineFormatter.cpp +++ clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1215,10 +1215,16 @@ !startsExternCBlock(*PreviousLine)) Newlines = 1; - // Insert extra new line before access specifiers. - if (PreviousLine && PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) && - RootToken.isAccessSpecifier() && RootToken.NewlinesBefore == 1) - ++Newlines; + // Insert or remove empty line before access specifiers. + if (PreviousLine && RootToken.isAccessSpecifier()) { + if (Style.EmptyLineBeforeAccessModifier && + PreviousLine->Last->isOneOf(tok::semi, tok::r_brace) && + RootToken.NewlinesBefore == 1) + ++Newlines; + else if (!Style.EmptyLineBeforeAccessModifier && + RootToken.NewlinesBefore > 1) + Newlines = 1; + } // Remove empty lines after access specifiers. if (PreviousLine && PreviousLine->First->isAccessSpecifier() && Index: clang/test/Format/access-modifiers.cpp =================================================================== --- /dev/null +++ clang/test/Format/access-modifiers.cpp @@ -0,0 +1,63 @@ +// RUN: grep -Ev "// *[A-Z-]+:" %s \ +// RUN: | clang-format -style="{BasedOnStyle: LLVM, EmptyLineBeforeAccessModifier: true}" -lines=1:10 \ +// RUN: | clang-format -style="{BasedOnStyle: LLVM, EmptyLineBeforeAccessModifier: false}" -lines=11:32 \ +// RUN: | FileCheck -strict-whitespace %s + +// CHECK: int i +// CHECK-NEXT: {{^$}} +// CHECK-NEXT: {{^private:$}} +// CHECK: } +struct foo1 { + int i; + +private: + int j; +} + +// CHECK: struct bar1 +// CHECK-NEXT: {{^private:$}} +// CHECK: } +struct bar1 { +private: + int i; + int j; +} + +// CHECK: int i +// CHECK-NEXT: {{^private:$}} +// CHECK: } +struct foo2 { + int i; + +private: + int j; +} + +// CHECK: struct bar2 +// CHECK-NEXT: {{^private:$}} +// CHECK: } +struct bar2 { +private: + int i; + int j; +} + +// CHECK: int j +// CHECK-NEXT: {{^private:$}} +// CHECK: } +struct foo3 { + int i; + int j; + +private: +} + +// CHECK: struct bar3 +// CHECK-NEXT: {{^private:$}} +// CHECK: } +struct bar3 { + +private: + int i; + int j; +} Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -8540,6 +8540,186 @@ getLLVMStyle()); } +TEST_F(FormatTest, FormatsAccessModifiers) { + EXPECT_EQ("struct foo {\n" + " int i;\n" + "\n" + "private:\n" + " int j;\n" + "}\n", + format("struct foo {\n" + " int i;\n" + "\n" + "private:\n" + " int j;\n" + "}\n", + getLLVMStyle())); + verifyFormat("struct foo {\n" + "private:\n" + " int i;\n" + "}\n", + "struct foo {\n" + "private:\n" + " int i;\n" + "}\n"); + EXPECT_EQ("struct foo {\n" + " int i;\n" + "\n" + "private:\n" + " int j;\n" + "}\n", + format("struct foo {\n" + " int i;\n" + "private:\n" + " int j;\n" + "}\n", + getLLVMStyle())); + verifyFormat("struct foo {\n" + " // comment\n" + "private:\n" + " int j;\n" + "}\n", + "struct foo {\n" + " // comment\n" + "private:\n" + " int j;\n" + "}\n"); + EXPECT_EQ("struct foo {\n" + " /* comment */\n" + "private:\n" + " int j;\n" + "}\n", + format("struct foo {\n" + " /* comment */\n" + "private:\n" + " int j;\n" + "}\n", + getLLVMStyle())); + verifyFormat("struct foo {\n" + "#ifdef FOO\n" + "#endif\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + "struct foo {\n" + "#ifdef FOO\n" + "#endif\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n"); + verifyFormat("struct foo {\n" + "#ifdef FOO\n" + "private:\n" + "#endif\n" + " int i;\n" + " int j;\n" + "}\n", + "struct foo {\n" + "#ifdef FOO\n" + "private:\n" + "#endif\n" + " int i;\n" + " int j;\n" + "}\n"); + FormatStyle Style = getLLVMStyle(); + Style.EmptyLineBeforeAccessModifier = false; + verifyFormat("struct foo {\n" + " int i;\n" + "private:\n" + " int j;\n" + "}\n", + "struct foo {\n" + " int i;\n" + "\n" + "private:\n" + " int j;\n" + "}\n", + Style); + verifyFormat("struct foo {\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + "struct foo {\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + Style); + verifyFormat("struct foo {\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + "struct foo {\n" + "\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + Style); + verifyFormat("struct foo {\n" + " // comment\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + "struct foo {\n" + " // comment\n" + "\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + Style); + EXPECT_EQ("struct foo {\n" + " /* comment */\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + format("struct foo {\n" + " /* comment */\n" + "\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + Style)); + verifyFormat("struct foo {\n" + "#ifdef FOO\n" + "#endif\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + "struct foo {\n" + "#ifdef FOO\n" + "#endif\n" + "private:\n" + " int i;\n" + " int j;\n" + "}\n", + Style); + verifyFormat("struct foo {\n" + "#ifdef FOO\n" + "private:\n" + "#endif\n" + " int i;\n" + " int j;\n" + "}\n", + "struct foo {\n" + "#ifdef FOO\n" + "private:\n" + "#endif\n" + " int i;\n" + " int j;\n" + "}\n", + Style); +} + TEST_F(FormatTest, FormatsArrays) { verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaa[aaaaaaaaaaaaaaaaaaaaaaaaa]\n" " [bbbbbbbbbbbbbbbbbbbbbbbbb] = c;"); @@ -13678,6 +13858,7 @@ CHECK_PARSE_BOOL(DerivePointerAlignment); CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding"); CHECK_PARSE_BOOL(DisableFormat); + CHECK_PARSE_BOOL(EmptyLineBeforeAccessModifier); CHECK_PARSE_BOOL(IndentCaseLabels); CHECK_PARSE_BOOL(IndentCaseBlocks); CHECK_PARSE_BOOL(IndentGotoLabels);