Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -699,7 +699,29 @@ /// } /// \endcode /// - bool SplitEmptyFunctionBody; + bool SplitEmptyFunction; + /// \brief If ``false``, empty record (e.g. class, struct or union) body + /// can be put on a single line. This option is used only if the opening + /// brace of the record has already been wrapped, i.e. the `AfterClass` + /// (for classes) brace wrapping mode is set. + /// \code + /// class Foo vs. class Foo + /// {} { + /// } + /// \endcode + /// + bool SplitEmptyRecord; + /// \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,9 @@ 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("SplitEmptyRecord", Wrapping.SplitEmptyRecord); + IO.mapOptional("SplitEmptyNamespace", Wrapping.SplitEmptyNamespace); } }; @@ -487,7 +489,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}; switch (Style.BreakBeforeBraces) { case FormatStyle::BS_Linux: Expanded.BraceWrapping.AfterClass = true; @@ -500,7 +503,8 @@ Expanded.BraceWrapping.AfterFunction = true; Expanded.BraceWrapping.AfterStruct = true; Expanded.BraceWrapping.AfterUnion = true; - Expanded.BraceWrapping.SplitEmptyFunctionBody = false; + Expanded.BraceWrapping.SplitEmptyFunction = false; + Expanded.BraceWrapping.SplitEmptyRecord = false; break; case FormatStyle::BS_Stroustrup: Expanded.BraceWrapping.AfterFunction = true; @@ -520,7 +524,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}; break; case FormatStyle::BS_WebKit: Expanded.BraceWrapping.AfterFunction = true; @@ -557,7 +562,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}; 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,31 @@ 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 && Tok->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_union, + Keywords.kw_interface)) + return !Style.BraceWrapping.SplitEmptyRecord && 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.SplitEmptyRecord = 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.SplitEmptyRecord = 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.SplitEmptyRecord = 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,9 @@ 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, SplitEmptyRecord); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyNamespace); } #undef CHECK_PARSE_BOOL