diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 02986a94a656c..1197ede357dd4 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2555,7 +2555,8 @@ the configuration (without a prefix: ``Auto``). {} { } - * ``bool SplitEmptyRecord`` If ``false``, empty record (e.g. class, struct or union) body + * ``bool SplitEmptyRecord`` This option is **deprecated**. See `WrapEmptyRecord`. + 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. @@ -2579,6 +2580,37 @@ the configuration (without a prefix: ``Auto``). {} { } + * ``BraceWrapEmptyRecordStyle WrapEmptyRecord`` + Wrap empty record (``class``/``struct``/``union``). + + Possible values: + + * ``BWER_Default`` (in configuration: ``Default``) + Use default wrapping rules for empty records + (``AfterClass``,``AfterStruct``,``AfterUnion``). + + .. code-block:: c++ + + class foo + { + }; + + * ``BWER_BeforeBrace`` (in configuration: ``BeforeBrace``) + Only wrap before brace. + + .. code-block:: c++ + + class foo + {}; + + * ``BWER_Never`` (in configuration: ``Never``) + Wrap neither before nor after the brace. + + .. code-block:: c++ + + class foo {}; + + .. _BracedInitializerIndentWidth: diff --git a/clang/docs/tools/dump_format_style.py b/clang/docs/tools/dump_format_style.py index f035143f6b3d1..227bf36c58024 100755 --- a/clang/docs/tools/dump_format_style.py +++ b/clang/docs/tools/dump_format_style.py @@ -384,7 +384,7 @@ class State: else: state = State.InNestedStruct field_type, field_name = re.match( - r"([<>:\w(,\s)]+)\s+(\w+);", line + r"(?:// )?([<>:\w(,\s)]+)\s+(\w+);", line ).groups() # if not version: # self.__warning(f"missing version for {field_name}", line) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 31582a40de866..4f3784e33cb45 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1355,6 +1355,29 @@ struct FormatStyle { BWACS_Always }; + /// Override wrapping of empty records. + enum BraceWrapEmptyRecordStyle : int8_t { + /// Use default wrapping rules for empty records + /// (``AfterClass``,``AfterStruct``,``AfterUnion``). + /// \code + /// class foo + /// { + /// }; + /// \endcode + BWER_Default, + /// Only wrap before brace. + /// \code + /// class foo + /// {}; + /// \endcode + BWER_BeforeBrace, + /// Wrap neither before nor after the brace. + /// \code + /// class foo {}; + /// \endcode + BWER_Never + }; + /// Precise control over the wrapping of braces. /// \code /// # Should be declared this way: @@ -1561,6 +1584,7 @@ struct FormatStyle { /// \endcode /// bool SplitEmptyFunction; + /// This option is **deprecated**. See `WrapEmptyRecord`. /// 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`` @@ -1571,8 +1595,8 @@ struct FormatStyle { /// {} { /// } /// \endcode - /// - bool SplitEmptyRecord; + // bool SplitEmptyRecord; + /// 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 @@ -1585,6 +1609,8 @@ struct FormatStyle { /// \endcode /// bool SplitEmptyNamespace; + /// Wrap empty record (``class``/``struct``/``union``). + BraceWrapEmptyRecordStyle WrapEmptyRecord; }; /// Control of individual brace wrapping cases. diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 063780721423f..8687b487b2d15 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -182,6 +182,10 @@ template <> struct ScalarEnumerationTraits { template <> struct MappingTraits { static void mapping(IO &IO, FormatStyle::BraceWrappingFlags &Wrapping) { + // For backward compatibility. + if (!IO.outputting()) + IO.mapOptional("SplitEmptyRecord", Wrapping.WrapEmptyRecord); + IO.mapOptional("AfterCaseLabel", Wrapping.AfterCaseLabel); IO.mapOptional("AfterClass", Wrapping.AfterClass); IO.mapOptional("AfterControlStatement", Wrapping.AfterControlStatement); @@ -198,8 +202,8 @@ template <> struct MappingTraits { IO.mapOptional("BeforeWhile", Wrapping.BeforeWhile); IO.mapOptional("IndentBraces", Wrapping.IndentBraces); IO.mapOptional("SplitEmptyFunction", Wrapping.SplitEmptyFunction); - IO.mapOptional("SplitEmptyRecord", Wrapping.SplitEmptyRecord); IO.mapOptional("SplitEmptyNamespace", Wrapping.SplitEmptyNamespace); + IO.mapOptional("WrapEmptyRecord", Wrapping.WrapEmptyRecord); } }; @@ -232,6 +236,20 @@ struct ScalarEnumerationTraits< } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::BraceWrapEmptyRecordStyle &Value) { + IO.enumCase(Value, "Default", FormatStyle::BWER_Default); + IO.enumCase(Value, "BeforeBrace", FormatStyle::BWER_BeforeBrace); + IO.enumCase(Value, "Never", FormatStyle::BWER_Never); + + // For backward compatibility. + IO.enumCase(Value, "false", FormatStyle::BWER_BeforeBrace); + IO.enumCase(Value, "true", FormatStyle::BWER_Default); + } +}; + template <> struct ScalarEnumerationTraits< FormatStyle::BreakBeforeConceptDeclarationsStyle> { @@ -1391,8 +1409,8 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) { /*BeforeWhile=*/false, /*IndentBraces=*/false, /*SplitEmptyFunction=*/true, - /*SplitEmptyRecord=*/true, - /*SplitEmptyNamespace=*/true}; + /*SplitEmptyNamespace=*/true, + /*WrapEmptyRecord=*/FormatStyle::BWER_Default}; switch (Expanded.BreakBeforeBraces) { case FormatStyle::BS_Linux: Expanded.BraceWrapping.AfterClass = true; @@ -1407,7 +1425,7 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) { Expanded.BraceWrapping.AfterUnion = true; Expanded.BraceWrapping.AfterExternBlock = true; Expanded.BraceWrapping.SplitEmptyFunction = true; - Expanded.BraceWrapping.SplitEmptyRecord = false; + Expanded.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_BeforeBrace; break; case FormatStyle::BS_Stroustrup: Expanded.BraceWrapping.AfterFunction = true; @@ -1461,8 +1479,8 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) { /*BeforeWhile=*/true, /*IndentBraces=*/true, /*SplitEmptyFunction=*/true, - /*SplitEmptyRecord=*/true, - /*SplitEmptyNamespace=*/true}; + /*SplitEmptyNamespace=*/true, + /*WrapEmptyRecord=*/FormatStyle::BWER_Default}; break; case FormatStyle::BS_WebKit: Expanded.BraceWrapping.AfterFunction = true; @@ -1561,8 +1579,8 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { /*BeforeWhile=*/false, /*IndentBraces=*/false, /*SplitEmptyFunction=*/true, - /*SplitEmptyRecord=*/true, - /*SplitEmptyNamespace=*/true}; + /*SplitEmptyNamespace=*/true, + /*WrapEmptyRecord=*/FormatStyle::BWER_Default}; LLVMStyle.BreakAdjacentStringLiterals = true; LLVMStyle.BreakAfterAttributes = FormatStyle::ABS_Leave; LLVMStyle.BreakAfterJavaFieldAnnotations = false; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 4801d27b1395a..804a6fb0418c6 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5933,12 +5933,15 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, return true; } - // Don't attempt to interpret struct return types as structs. + // Don't attempt to interpret record return types as records. if (Right.isNot(TT_FunctionLBrace)) { - return (Line.startsWith(tok::kw_class) && - Style.BraceWrapping.AfterClass) || - (Line.startsWith(tok::kw_struct) && - Style.BraceWrapping.AfterStruct); + return ((Line.startsWith(tok::kw_class) && + Style.BraceWrapping.AfterClass) || + (Line.startsWith(tok::kw_struct) && + Style.BraceWrapping.AfterStruct) || + (Line.startsWith(tok::kw_union) && + Style.BraceWrapping.AfterUnion)) && + Style.BraceWrapping.WrapEmptyRecord == FormatStyle::BWER_Default; } } diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 0adf7ee9ed543..01bc06a754c53 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -277,13 +277,16 @@ class LineJoiner { Tok = Tok->getNextNonComment(); if (Tok && Tok->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_union, tok::kw_extern, Keywords.kw_interface)) { - return !Style.BraceWrapping.SplitEmptyRecord && EmptyBlock + return Style.BraceWrapping.WrapEmptyRecord != + FormatStyle::BWER_Default && + EmptyBlock ? tryMergeSimpleBlock(I, E, Limit) : 0; } if (Tok && Tok->is(tok::kw_template) && - Style.BraceWrapping.SplitEmptyRecord && EmptyBlock) { + Style.BraceWrapping.WrapEmptyRecord == FormatStyle::BWER_Default && + EmptyBlock) { return 0; } } @@ -453,9 +456,10 @@ class LineJoiner { } } - // Don't merge an empty template class or struct if SplitEmptyRecords - // is defined. - if (PreviousLine && Style.BraceWrapping.SplitEmptyRecord && + // Merge an empty class or struct only if WrapEmptyRecord + // is not set to Default. + if (PreviousLine && + Style.BraceWrapping.WrapEmptyRecord == FormatStyle::BWER_Default && TheLine->Last->is(tok::l_brace) && PreviousLine->Last) { const FormatToken *Previous = PreviousLine->Last; if (Previous) { @@ -493,9 +497,10 @@ class LineJoiner { // NOTE: We use AfterClass (whereas AfterStruct exists) for both classes // and structs, but it seems that wrapping is still handled correctly // elsewhere. - ShouldMerge = !Style.BraceWrapping.AfterClass || - (NextLine.First->is(tok::r_brace) && - !Style.BraceWrapping.SplitEmptyRecord); + ShouldMerge = + !Style.BraceWrapping.AfterClass || + (NextLine.First->is(tok::r_brace) && + Style.BraceWrapping.WrapEmptyRecord != FormatStyle::BWER_Default); } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, tok::kw_struct)) { diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 91b8fdc8a3c38..5d17bef78e9d2 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -952,20 +952,26 @@ static bool isIIFE(const UnwrappedLine &Line, } static bool ShouldBreakBeforeBrace(const FormatStyle &Style, - const FormatToken &InitialToken) { + const FormatToken &InitialToken, + const FormatToken &NextToken) { tok::TokenKind Kind = InitialToken.Tok.getKind(); if (InitialToken.is(TT_NamespaceMacro)) Kind = tok::kw_namespace; + bool IsEmptyBlock = NextToken.is(tok::r_brace); + bool WrapRecordAllowed = + !IsEmptyBlock || + Style.BraceWrapping.WrapEmptyRecord != FormatStyle::BWER_Never; + switch (Kind) { case tok::kw_namespace: return Style.BraceWrapping.AfterNamespace; case tok::kw_class: - return Style.BraceWrapping.AfterClass; + return Style.BraceWrapping.AfterClass && WrapRecordAllowed; case tok::kw_union: - return Style.BraceWrapping.AfterUnion; + return Style.BraceWrapping.AfterUnion && WrapRecordAllowed; case tok::kw_struct: - return Style.BraceWrapping.AfterStruct; + return Style.BraceWrapping.AfterStruct && WrapRecordAllowed; case tok::kw_enum: return Style.BraceWrapping.AfterEnum; default: @@ -3191,7 +3197,7 @@ void UnwrappedLineParser::parseNamespace() { if (FormatTok->is(tok::l_brace)) { FormatTok->setFinalizedType(TT_NamespaceLBrace); - if (ShouldBreakBeforeBrace(Style, InitialToken)) + if (ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) addUnwrappedLine(); unsigned AddLevels = @@ -3856,7 +3862,7 @@ bool UnwrappedLineParser::parseEnum() { } if (!Style.AllowShortEnumsOnASingleLine && - ShouldBreakBeforeBrace(Style, InitialToken)) { + ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) { addUnwrappedLine(); } // Parse enum body. @@ -4151,7 +4157,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) { if (ParseAsExpr) { parseChildBlock(); } else { - if (ShouldBreakBeforeBrace(Style, InitialToken)) + if (ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) addUnwrappedLine(); unsigned AddLevels = Style.IndentAccessModifiers ? 2u : 1u; diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 9de3cca71630d..809244bf86eb2 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -237,7 +237,6 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeWhile); CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBraces); CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunction); - CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyRecord); CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyNamespace); CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtEndOfFile); CHECK_PARSE_NESTED_BOOL(KeepEmptyLines, AtStartOfBlock); @@ -759,6 +758,24 @@ TEST(ConfigParseTest, ParsesConfiguration) { " AfterControlStatement: false", BraceWrapping.AfterControlStatement, FormatStyle::BWACS_Never); + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_Default; + CHECK_PARSE("BraceWrapping:\n" + " WrapEmptyRecord: BeforeBrace", + BraceWrapping.WrapEmptyRecord, FormatStyle::BWER_BeforeBrace); + CHECK_PARSE("BraceWrapping:\n" + " WrapEmptyRecord: Default", + BraceWrapping.WrapEmptyRecord, FormatStyle::BWER_Default); + CHECK_PARSE("BraceWrapping:\n" + " WrapEmptyRecord: Never", + BraceWrapping.WrapEmptyRecord, FormatStyle::BWER_Never); + // For backward compatibility: + CHECK_PARSE("BraceWrapping:\n" + " WrapEmptyRecord: true", + BraceWrapping.WrapEmptyRecord, FormatStyle::BWER_Default); + CHECK_PARSE("BraceWrapping:\n" + " WrapEmptyRecord: false", + BraceWrapping.WrapEmptyRecord, FormatStyle::BWER_BeforeBrace); + Style.BreakAfterReturnType = FormatStyle::RTBS_All; CHECK_PARSE("BreakAfterReturnType: None", BreakAfterReturnType, FormatStyle::RTBS_None); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 96cc650f52a5d..fa03bea45a533 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -1491,7 +1491,8 @@ TEST_F(FormatTest, FormatShortBracedStatements) { AllowSimpleBracedStatements.AllowShortLoopsOnASingleLine = true; AllowSimpleBracedStatements.BreakBeforeBraces = FormatStyle::BS_Custom; AllowSimpleBracedStatements.BraceWrapping.AfterFunction = true; - AllowSimpleBracedStatements.BraceWrapping.SplitEmptyRecord = false; + AllowSimpleBracedStatements.BraceWrapping.WrapEmptyRecord = + FormatStyle::BWER_BeforeBrace; verifyFormat("if (true) {}", AllowSimpleBracedStatements); verifyFormat("if constexpr (true) {}", AllowSimpleBracedStatements); @@ -4678,7 +4679,7 @@ TEST_F(FormatTest, FormatsExternC) { Style); Style.BraceWrapping.AfterExternBlock = true; - Style.BraceWrapping.SplitEmptyRecord = false; + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_BeforeBrace; verifyFormat("extern \"C\"\n" "{}", Style); @@ -8624,6 +8625,19 @@ TEST_F(FormatTest, BreaksFunctionDeclarations) { Style); } +TEST_F(FormatTest, BreakFunctionsReturningRecords) { + FormatStyle Style = getLLVMStyle(); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterFunction = true; + Style.BraceWrapping.AfterClass = false; + Style.BraceWrapping.AfterStruct = false; + Style.BraceWrapping.AfterUnion = false; + + verifyFormat("class Bar foo() {}", Style); + verifyFormat("struct Bar foo() {}", Style); + verifyFormat("union Bar foo() {}", Style); +} + TEST_F(FormatTest, DontBreakBeforeQualifiedOperator) { // Regression test for https://bugs.llvm.org/show_bug.cgi?id=40516: // Prefer keeping `::` followed by `operator` together. @@ -15322,7 +15336,7 @@ TEST_F(FormatTest, SplitEmptyFunctionButNotRecord) { Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterFunction = true; Style.BraceWrapping.SplitEmptyFunction = true; - Style.BraceWrapping.SplitEmptyRecord = false; + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_BeforeBrace; verifyFormat("class C {};", Style); verifyFormat("struct C {};", Style); @@ -15361,7 +15375,7 @@ TEST_F(FormatTest, SplitEmptyClass) { FormatStyle Style = getLLVMStyle(); Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterClass = true; - Style.BraceWrapping.SplitEmptyRecord = false; + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_BeforeBrace; verifyFormat("class Foo\n" "{};", @@ -15382,7 +15396,7 @@ TEST_F(FormatTest, SplitEmptyClass) { "} Foo_t;", Style); - Style.BraceWrapping.SplitEmptyRecord = true; + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_Default; Style.BraceWrapping.AfterStruct = true; verifyFormat("class rep\n" "{\n" @@ -15467,7 +15481,7 @@ TEST_F(FormatTest, SplitEmptyStruct) { FormatStyle Style = getLLVMStyle(); Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterStruct = true; - Style.BraceWrapping.SplitEmptyRecord = false; + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_BeforeBrace; verifyFormat("struct Foo\n" "{};", @@ -15494,7 +15508,7 @@ TEST_F(FormatTest, SplitEmptyUnion) { FormatStyle Style = getLLVMStyle(); Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterUnion = true; - Style.BraceWrapping.SplitEmptyRecord = false; + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_BeforeBrace; verifyFormat("union Foo\n" "{};", @@ -15615,6 +15629,54 @@ TEST_F(FormatTest, NeverMergeShortRecords) { Style); } +TEST_F(FormatTest, WrapEmptyRecords) { + FormatStyle Style = getLLVMStyle(); + + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterStruct = true; + Style.BraceWrapping.AfterClass = true; + Style.BraceWrapping.AfterUnion = true; + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_BeforeBrace; + + verifyFormat("class foo\n{\n" + " void bar();\n" + "};", + Style); + verifyFormat("class foo\n{};", Style); + + verifyFormat("struct foo\n{\n" + " int bar;\n" + "};", + Style); + verifyFormat("struct foo\n{};", Style); + + verifyFormat("union foo\n{\n" + " int bar;\n" + "};", + Style); + verifyFormat("union foo\n{};", Style); + + Style.BraceWrapping.WrapEmptyRecord = FormatStyle::BWER_Never; + + verifyFormat("class foo\n{\n" + " void bar();\n" + "};", + Style); + verifyFormat("class foo {};", Style); + + verifyFormat("struct foo\n{\n" + " int bar;\n" + "};", + Style); + verifyFormat("struct foo {};", Style); + + verifyFormat("union foo\n{\n" + " int bar;\n" + "};", + Style); + verifyFormat("union foo {};", Style); +} + TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { // Elaborate type variable declarations. verifyFormat("struct foo a = {bar};\nint n;"); diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp index ea85ed6140dd0..303e6f42898e3 100644 --- a/clang/unittests/Format/FormatTestCSharp.cpp +++ b/clang/unittests/Format/FormatTestCSharp.cpp @@ -1315,7 +1315,7 @@ if (someThings[i][j][k].Contains(myThing)) { TEST_F(FormatTestCSharp, CSharpGenericTypeConstraints) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); - EXPECT_TRUE(Style.BraceWrapping.SplitEmptyRecord); + EXPECT_EQ(Style.BraceWrapping.WrapEmptyRecord, FormatStyle::BWER_Default); verifyFormat("class ItemFactory\n" " where T : new() {\n"