Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -699,7 +699,51 @@ /// } /// \endcode /// - bool SplitEmptyFunctionBody; + bool SplitEmptyFunction; + /// \brief If ``false``, empty class body can be put on a single line. + /// This option is used only if the opening brace of the class has + /// already been wrapped, i.e. the `AfterClass` brace wrapping mode is + /// set. + /// \code + /// class Foo vs. class Foo + /// {} { + /// } + /// \endcode + /// + bool SplitEmptyClass; + /// \brief If ``false``, empty struct body can be put on a single line. + /// This option is used only if the opening brace of the struct has + /// already been wrapped, i.e. the `AfterStruct` brace wrapping mode is + /// set. + /// \code + /// struct Foo vs. struct Foo + /// {} { + /// } + /// \endcode + /// + bool SplitEmptyStruct; + /// \brief If ``false``, empty union can be put on a single line. + /// This option is used only if the opening brace of the union has + /// already been wrapped, i.e. the `AfterUnion` brace wrapping mode is + /// set. + /// \code + /// union Foo vs. union Foo + /// {} { + /// } + /// \endcode + /// + bool SplitEmptyUnion; + /// \brief If ``false``, empty namespace body can be put on a single line. + /// This option is used only if the opening brace of the namespace has + /// already been wrapped, i.e. the `AfterNamespace` brace wrapping mode is + /// set. + /// \code + /// namespace Foo vs. namespace Foo + /// {} { + /// } + /// \endcode + /// + bool SplitEmptyNamespace; }; /// \brief Control of individual brace wrapping cases. Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -411,7 +411,11 @@ IO.mapOptional("BeforeCatch", Wrapping.BeforeCatch); IO.mapOptional("BeforeElse", Wrapping.BeforeElse); IO.mapOptional("IndentBraces", Wrapping.IndentBraces); - IO.mapOptional("SplitEmptyFunctionBody", Wrapping.SplitEmptyFunctionBody); + IO.mapOptional("SplitEmptyFunction", Wrapping.SplitEmptyFunction); + IO.mapOptional("SplitEmptyClass", Wrapping.SplitEmptyClass); + IO.mapOptional("SplitEmptyStruct", Wrapping.SplitEmptyStruct); + IO.mapOptional("SplitEmptyUnion", Wrapping.SplitEmptyUnion); + IO.mapOptional("SplitEmptyNamespace", Wrapping.SplitEmptyNamespace); } }; @@ -487,7 +491,8 @@ return Style; FormatStyle Expanded = Style; Expanded.BraceWrapping = {false, false, false, false, false, false, - false, false, false, false, false, true}; + false, false, false, false, false, true, + true, true, true, true}; switch (Style.BreakBeforeBraces) { case FormatStyle::BS_Linux: Expanded.BraceWrapping.AfterClass = true; @@ -500,7 +505,7 @@ Expanded.BraceWrapping.AfterFunction = true; Expanded.BraceWrapping.AfterStruct = true; Expanded.BraceWrapping.AfterUnion = true; - Expanded.BraceWrapping.SplitEmptyFunctionBody = false; + Expanded.BraceWrapping.SplitEmptyFunction = false; break; case FormatStyle::BS_Stroustrup: Expanded.BraceWrapping.AfterFunction = true; @@ -520,7 +525,8 @@ break; case FormatStyle::BS_GNU: Expanded.BraceWrapping = {true, true, true, true, true, true, - true, true, true, true, true, true}; + true, true, true, true, true, true, + true, true, true, true}; break; case FormatStyle::BS_WebKit: Expanded.BraceWrapping.AfterFunction = true; @@ -557,7 +563,8 @@ LLVMStyle.BreakBeforeTernaryOperators = true; LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; LLVMStyle.BraceWrapping = {false, false, false, false, false, false, - false, false, false, false, false, true}; + false, false, false, false, false, true, + true, true, true, true}; LLVMStyle.BreakAfterJavaFieldAnnotations = false; LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon; LLVMStyle.BreakBeforeInheritanceComma = false; Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -475,6 +475,19 @@ return MatchingParen && MatchingParen->opensBlockOrBlockTypeList(Style); } + /// \brief Return the actual namespace token, if this token starts a namespace + /// block. + const FormatToken *getNamespaceToken() const { + const FormatToken *NamespaceTok = this; + if (is(tok::comment)) + NamespaceTok = NamespaceTok->getNextNonComment(); + // Detect "(inline)? namespace" in the beginning of a line. + if (NamespaceTok && NamespaceTok->is(tok::kw_inline)) + NamespaceTok = NamespaceTok->getNextNonComment(); + return NamespaceTok && NamespaceTok->is(tok::kw_namespace) ? NamespaceTok + : nullptr; + } + private: // Disallow copying. FormatToken(const FormatToken &) = delete; Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -136,10 +136,7 @@ bool isNamespaceDeclaration(const AnnotatedLine *Line) { const FormatToken *NamespaceTok = Line->First; - // Detect "(inline)? namespace" in the beginning of a line. - if (NamespaceTok->is(tok::kw_inline)) - NamespaceTok = NamespaceTok->getNextNonComment(); - return NamespaceTok && NamespaceTok->is(tok::kw_namespace); + return NamespaceTok && NamespaceTok->getNamespaceToken(); } bool isEndOfNamespace(const AnnotatedLine *Line, @@ -216,10 +213,38 @@ if (TheLine->Last->is(TT_FunctionLBrace) && TheLine->First == TheLine->Last && - !Style.BraceWrapping.SplitEmptyFunctionBody && + !Style.BraceWrapping.SplitEmptyFunction && I[1]->First->is(tok::r_brace)) return tryMergeSimpleBlock(I, E, Limit); + // Handle empty record blocks where the brace has already been wrapped + if (TheLine->Last->is(tok::l_brace) && TheLine->First == TheLine->Last && + I != AnnotatedLines.begin()) { + bool EmptyBlock = I[1]->First->is(tok::r_brace); + + const FormatToken *Tok = I[-1]->First; + if (Tok && Tok->is(tok::comment)) + Tok = Tok->getNextNonComment(); + + if (Tok && Tok->getNamespaceToken()) + return !Style.BraceWrapping.SplitEmptyNamespace && EmptyBlock + ? tryMergeSimpleBlock(I, E, Limit) : 0; + + if (Tok && Tok->is(tok::kw_typedef)) + Tok = Tok->getNextNonComment(); + if (Tok) { + if (Tok->isOneOf(tok::kw_class, Keywords.kw_interface)) + return !Style.BraceWrapping.SplitEmptyClass && EmptyBlock + ? tryMergeSimpleBlock(I, E, Limit) : 0; + if (Tok->is(tok::kw_struct)) + return !Style.BraceWrapping.SplitEmptyStruct && EmptyBlock + ? tryMergeSimpleBlock(I, E, Limit) : 0; + if (Tok->is(tok::kw_union)) + return !Style.BraceWrapping.SplitEmptyUnion && EmptyBlock + ? tryMergeSimpleBlock(I, E, Limit) : 0; + } + } + // FIXME: TheLine->Level != 0 might or might not be the right check to do. // If necessary, change to something smarter. bool MergeShortFunctions = Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -6509,12 +6509,12 @@ MergeInlineOnly); } -TEST_F(FormatTest, SplitEmptyFunctionBody) { +TEST_F(FormatTest, SplitEmptyFunction) { FormatStyle Style = getLLVMStyle(); Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterFunction = true; - Style.BraceWrapping.SplitEmptyFunctionBody = false; + Style.BraceWrapping.SplitEmptyFunction = false; Style.ColumnLimit = 40; verifyFormat("int f()\n" @@ -6577,6 +6577,178 @@ Style); } +TEST_F(FormatTest, SplitEmptyClass) { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterClass = true; + Style.BraceWrapping.SplitEmptyClass = false; + + verifyFormat("class Foo\n" + "{};", + Style); + verifyFormat("/* something */ class Foo\n" + "{};", + Style); + verifyFormat("template class Foo\n" + "{};", + Style); + verifyFormat("class Foo\n" + "{\n" + " Foo();\n" + "};", + Style); + verifyFormat("typedef class Foo\n" + "{\n" + "} Foo_t;", + Style); +} + +TEST_F(FormatTest, SplitEmptyStruct) { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterStruct = true; + Style.BraceWrapping.SplitEmptyStruct = false; + + verifyFormat("struct Foo\n" + "{};", + Style); + verifyFormat("/* something */ struct Foo\n" + "{};", + Style); + verifyFormat("template struct Foo\n" + "{};", + Style); + verifyFormat("struct Foo\n" + "{\n" + " Foo();\n" + "};", + Style); + verifyFormat("typedef struct Foo\n" + "{\n" + "} Foo_t;", + Style); + //typedef struct Bar {} Bar_t; +} + +TEST_F(FormatTest, SplitEmptyUnion) { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterUnion = true; + Style.BraceWrapping.SplitEmptyUnion = false; + + verifyFormat("union Foo\n" + "{};", + Style); + verifyFormat("/* something */ union Foo\n" + "{};", + Style); + verifyFormat("union Foo\n" + "{\n" + " A,\n" + "};", + Style); + verifyFormat("typedef union Foo\n" + "{\n" + "} Foo_t;", + Style); +} + +TEST_F(FormatTest, SplitEmptyNamespace) { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterNamespace = true; + Style.BraceWrapping.SplitEmptyNamespace = false; + + verifyFormat("namespace Foo\n" + "{};", + Style); + verifyFormat("/* something */ namespace Foo\n" + "{};", + Style); + verifyFormat("inline namespace Foo\n" + "{};", + Style); + verifyFormat("namespace Foo\n" + "{\n" + "void Bar();\n" + "};", + Style); +} + +TEST_F(FormatTest, NeverMergeShortRecords) { + FormatStyle Style = getLLVMStyle(); + + verifyFormat("class Foo {\n" + " Foo();\n" + "};", + Style); + verifyFormat("typedef class Foo {\n" + " Foo();\n" + "} Foo_t;", + Style); + verifyFormat("struct Foo {\n" + " Foo();\n" + "};", + Style); + verifyFormat("typedef struct Foo {\n" + " Foo();\n" + "} Foo_t;", + Style); + verifyFormat("union Foo {\n" + " A,\n" + "};", + Style); + verifyFormat("typedef union Foo {\n" + " A,\n" + "} Foo_t;", + Style); + verifyFormat("namespace Foo {\n" + "void Bar();\n" + "};", + Style); + + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterClass = true; + Style.BraceWrapping.AfterStruct = true; + Style.BraceWrapping.AfterUnion = true; + Style.BraceWrapping.AfterNamespace = true; + verifyFormat("class Foo\n" + "{\n" + " Foo();\n" + "};", + Style); + verifyFormat("typedef class Foo\n" + "{\n" + " Foo();\n" + "} Foo_t;", + Style); + verifyFormat("struct Foo\n" + "{\n" + " Foo();\n" + "};", + Style); + verifyFormat("typedef struct Foo\n" + "{\n" + " Foo();\n" + "} Foo_t;", + Style); + verifyFormat("union Foo\n" + "{\n" + " A,\n" + "};", + Style); + verifyFormat("typedef union Foo\n" + "{\n" + " A,\n" + "} Foo_t;", + Style); + verifyFormat("namespace Foo\n" + "{\n" + "void Bar();\n" + "};", + Style); +} + TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { // Elaborate type variable declarations. verifyFormat("struct foo a = {bar};\nint n;"); @@ -9308,7 +9480,11 @@ CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeCatch); CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeElse); CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBraces); - CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunctionBody); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunction); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyClass); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyStruct); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyUnion); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyNamespace); } #undef CHECK_PARSE_BOOL