https://github.com/itzexpoexpo updated https://github.com/llvm/llvm-project/pull/154580
From 000ea7fb5703f9a409d64c2ba61e556e21187cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Wed, 20 Aug 2025 19:28:23 +0200 Subject: [PATCH 1/9] [clang-format] Add option AllowShortRecordsOnASingleLine This commit supersedes PR #151970 by adding the option AllowShortRecordsOnASingleLine that allows the following formatting: struct foo {}; struct bar { int i; }; struct baz { int i; int j; int k; }; --- clang/docs/ClangFormatStyleOptions.rst | 32 +++++++++ clang/include/clang/Format/Format.h | 26 ++++++++ clang/lib/Format/Format.cpp | 11 ++++ clang/lib/Format/TokenAnnotator.cpp | 13 ++-- clang/lib/Format/UnwrappedLineFormatter.cpp | 22 ++++++- clang/lib/Format/UnwrappedLineParser.cpp | 23 +++++-- clang/unittests/Format/ConfigParseTest.cpp | 8 +++ clang/unittests/Format/FormatTest.cpp | 73 +++++++++++++++++++++ 8 files changed, 193 insertions(+), 15 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 3ac9e3795cae7..d7cbf877fcaa5 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2096,6 +2096,38 @@ the configuration (without a prefix: ``Auto``). **AllowShortNamespacesOnASingleLine** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ <AllowShortNamespacesOnASingleLine>` If ``true``, ``namespace a { class b; }`` can be put on a single line. +.. _AllowShortRecordsOnASingleLine: + +**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`¶ <AllowShortRecordsOnASingleLine>` + Dependent on the value, ``struct bar { int i; }`` can be put on a single + line. + + Possible values: + + * ``SRS_Never`` (in configuration: ``Never``) + Never merge records into a single line. + + * ``SRS_Empty`` (in configuration: ``Empty``) + Only merge empty records. + + .. code-block:: c++ + + struct foo {}; + struct bar + { + int i; + }; + + * ``SRS_All`` (in configuration: ``All``) + Merge all records that fit on a single line. + + .. code-block:: c++ + + struct foo {}; + struct bar { int i; }; + + + .. _AlwaysBreakAfterDefinitionReturnType: **AlwaysBreakAfterDefinitionReturnType** (``DefinitionReturnTypeBreakingStyle``) :versionbadge:`clang-format 3.7` :ref:`¶ <AlwaysBreakAfterDefinitionReturnType>` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 5dfdb23594610..e4e87b71f1268 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -992,6 +992,32 @@ struct FormatStyle { /// \version 20 bool AllowShortNamespacesOnASingleLine; + /// Different styles for merging short records + /// (``class``,``struct``,``union``). + enum ShortRecordStyle : int8_t { + /// Never merge records into a single line. + SRS_Never, + /// Only merge empty records. + /// \code + /// struct foo {}; + /// struct bar + /// { + /// int i; + /// }; + /// \endcode + SRS_Empty, + /// Merge all records that fit on a single line. + /// \code + /// struct foo {}; + /// struct bar { int i; }; + /// \endcode + SRS_All + }; + + /// Dependent on the value, ``struct bar { int i; }`` can be put on a single + /// line. + ShortRecordStyle AllowShortRecordsOnASingleLine; + /// Different ways to break after the function definition return type. /// This option is **deprecated** and is retained for backwards compatibility. enum DefinitionReturnTypeBreakingStyle : int8_t { diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index e3b22cdabaccd..6191ad81eb4f5 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -660,6 +660,14 @@ template <> struct ScalarEnumerationTraits<FormatStyle::ShortLambdaStyle> { } }; +template <> struct ScalarEnumerationTraits<FormatStyle::ShortRecordStyle> { + static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) { + IO.enumCase(Value, "Never", FormatStyle::SRS_Never); + IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty); + IO.enumCase(Value, "All", FormatStyle::SRS_All); + } +}; + template <> struct MappingTraits<FormatStyle::SortIncludesOptions> { static void enumInput(IO &IO, FormatStyle::SortIncludesOptions &Value) { IO.enumCase(Value, "Never", FormatStyle::SortIncludesOptions({})); @@ -1023,6 +1031,8 @@ template <> struct MappingTraits<FormatStyle> { Style.AllowShortIfStatementsOnASingleLine); IO.mapOptional("AllowShortLambdasOnASingleLine", Style.AllowShortLambdasOnASingleLine); + IO.mapOptional("AllowShortRecordsOnASingleLine", + Style.AllowShortRecordsOnASingleLine); IO.mapOptional("AllowShortLoopsOnASingleLine", Style.AllowShortLoopsOnASingleLine); IO.mapOptional("AllowShortNamespacesOnASingleLine", @@ -1553,6 +1563,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All; + LLVMStyle.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never; LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AllowShortNamespacesOnASingleLine = false; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index bbb7ef2c337d6..e235bc0734270 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5949,12 +5949,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.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never; } } diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 2a7bfd1a7dc5b..eb7d8301b621a 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -456,6 +456,19 @@ class LineJoiner { } } + auto ShouldMergeShortRecords = [this, &I, &NextLine, PreviousLine, + TheLine]() { + if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_All) + return true; + if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Empty && + NextLine.First->is(tok::r_brace)) { + return true; + } + return false; + }; + + bool MergeShortRecord = ShouldMergeShortRecords(); + // Don't merge an empty template class or struct if SplitEmptyRecords // is defined. if (PreviousLine && Style.BraceWrapping.SplitEmptyRecord && @@ -498,7 +511,8 @@ class LineJoiner { // elsewhere. ShouldMerge = !Style.BraceWrapping.AfterClass || (NextLine.First->is(tok::r_brace) && - !Style.BraceWrapping.SplitEmptyRecord); + !Style.BraceWrapping.SplitEmptyRecord) || + MergeShortRecord; } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, tok::kw_struct)) { @@ -873,9 +887,11 @@ class LineJoiner { return 1; } else if (Limit != 0 && !Line.startsWithNamespace() && !startsExternCBlock(Line)) { - // We don't merge short records. - if (isRecordLBrace(*Line.Last)) + // Merge short records only when requested. + if (isRecordLBrace(*Line.Last) && + Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never) { return 0; + } // Check that we still have three lines and they fit into the limit. if (I + 2 == E || I[2]->Type == LT_Invalid) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index f4bbfcf8461bc..13fb6bf8b8a74 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.AllowShortRecordsOnASingleLine != FormatStyle::SRS_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: @@ -3197,7 +3203,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 = @@ -3862,7 +3868,7 @@ bool UnwrappedLineParser::parseEnum() { } if (!Style.AllowShortEnumsOnASingleLine && - ShouldBreakBeforeBrace(Style, InitialToken)) { + ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) { addUnwrappedLine(); } // Parse enum body. @@ -4157,8 +4163,11 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) { if (ParseAsExpr) { parseChildBlock(); } else { - if (ShouldBreakBeforeBrace(Style, InitialToken)) + if (Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_All && + ShouldBreakBeforeBrace(Style, InitialToken, + *Tokens->peekNextToken())) { addUnwrappedLine(); + } unsigned AddLevels = Style.IndentAccessModifiers ? 2u : 1u; parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false); diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 7c993c0f8fd33..8fd62e422bc00 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -654,6 +654,14 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE("AllowShortLambdasOnASingleLine: true", AllowShortLambdasOnASingleLine, FormatStyle::SLS_All); + Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never; + CHECK_PARSE("AllowShortRecordsOnASingleLine: Empty", + AllowShortRecordsOnASingleLine, FormatStyle::SRS_Empty); + CHECK_PARSE("AllowShortRecordsOnASingleLine: All", + AllowShortRecordsOnASingleLine, FormatStyle::SRS_All); + CHECK_PARSE("AllowShortRecordsOnASingleLine: Never", + AllowShortRecordsOnASingleLine, FormatStyle::SRS_Never); + Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both; CHECK_PARSE("SpaceAroundPointerQualifiers: Default", SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Default); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 4e9d31895998f..d16aba7ac6140 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -8575,6 +8575,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. @@ -15277,6 +15290,66 @@ TEST_F(FormatTest, NeverMergeShortRecords) { Style); } +TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { + FormatStyle Style = getLLVMStyle(); + + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterClass = true; + Style.BraceWrapping.AfterStruct = true; + Style.BraceWrapping.AfterUnion = true; + Style.BraceWrapping.SplitEmptyRecord = false; + Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never; + + 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.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Empty; + + 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); + + Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_All; + + verifyFormat("class foo { void bar(); };", Style); + verifyFormat("class foo {};", Style); + + verifyFormat("struct foo { int bar; };", Style); + verifyFormat("struct foo {};", Style); + + verifyFormat("union foo { int bar; };", Style); + verifyFormat("union foo {};", Style); +} + TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { // Elaborate type variable declarations. verifyFormat("struct foo a = {bar};\nint n;"); From 5774f0b6b21e39807b7891bbc0cd29559dd4e601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Wed, 20 Aug 2025 23:54:07 +0200 Subject: [PATCH 2/9] Fixup: option order, inline MergeShortRecord lambda --- clang/lib/Format/Format.cpp | 4 ++-- clang/lib/Format/UnwrappedLineFormatter.cpp | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 6191ad81eb4f5..b4653ce2daa2f 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1031,12 +1031,12 @@ template <> struct MappingTraits<FormatStyle> { Style.AllowShortIfStatementsOnASingleLine); IO.mapOptional("AllowShortLambdasOnASingleLine", Style.AllowShortLambdasOnASingleLine); - IO.mapOptional("AllowShortRecordsOnASingleLine", - Style.AllowShortRecordsOnASingleLine); IO.mapOptional("AllowShortLoopsOnASingleLine", Style.AllowShortLoopsOnASingleLine); IO.mapOptional("AllowShortNamespacesOnASingleLine", Style.AllowShortNamespacesOnASingleLine); + IO.mapOptional("AllowShortRecordsOnASingleLine", + Style.AllowShortRecordsOnASingleLine); IO.mapOptional("AlwaysBreakAfterDefinitionReturnType", Style.AlwaysBreakAfterDefinitionReturnType); IO.mapOptional("AlwaysBreakBeforeMultilineStrings", diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index eb7d8301b621a..d74463766619a 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -456,18 +456,16 @@ class LineJoiner { } } - auto ShouldMergeShortRecords = [this, &I, &NextLine, PreviousLine, - TheLine]() { - if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_All) - return true; - if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Empty && - NextLine.First->is(tok::r_brace)) { + const bool MergeShortRecord = [this, &NextLine]() { + switch (Style.AllowShortRecordsOnASingleLine) { + case FormatStyle::SRS_All: return true; + case FormatStyle::SRS_Empty: + return NextLine.First->is(tok::r_brace); + case FormatStyle::SRS_Never: + return false; } - return false; - }; - - bool MergeShortRecord = ShouldMergeShortRecords(); + }(); // Don't merge an empty template class or struct if SplitEmptyRecords // is defined. From 7d2a4b4a6e013167d551955ce22b6d01726be94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Sun, 24 Aug 2025 15:24:59 +0200 Subject: [PATCH 3/9] Use consistently named enum values --- clang/include/clang/Format/Format.h | 2 +- clang/lib/Format/Format.cpp | 2 +- clang/lib/Format/UnwrappedLineFormatter.cpp | 2 +- clang/lib/Format/UnwrappedLineParser.cpp | 2 +- clang/unittests/Format/ConfigParseTest.cpp | 4 ++-- clang/unittests/Format/FormatTest.cpp | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index e4e87b71f1268..49e0211aac7ac 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1011,7 +1011,7 @@ struct FormatStyle { /// struct foo {}; /// struct bar { int i; }; /// \endcode - SRS_All + SRS_Always }; /// Dependent on the value, ``struct bar { int i; }`` can be put on a single diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index b4653ce2daa2f..b4bc49feaadf0 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -664,7 +664,7 @@ template <> struct ScalarEnumerationTraits<FormatStyle::ShortRecordStyle> { static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) { IO.enumCase(Value, "Never", FormatStyle::SRS_Never); IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty); - IO.enumCase(Value, "All", FormatStyle::SRS_All); + IO.enumCase(Value, "Always", FormatStyle::SRS_Always); } }; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index d74463766619a..31c0c7d41586c 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -458,7 +458,7 @@ class LineJoiner { const bool MergeShortRecord = [this, &NextLine]() { switch (Style.AllowShortRecordsOnASingleLine) { - case FormatStyle::SRS_All: + case FormatStyle::SRS_Always: return true; case FormatStyle::SRS_Empty: return NextLine.First->is(tok::r_brace); diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 13fb6bf8b8a74..cdb4a1cb6a738 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -4163,7 +4163,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) { if (ParseAsExpr) { parseChildBlock(); } else { - if (Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_All && + if (Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_Always && ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) { addUnwrappedLine(); diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 8fd62e422bc00..d0a62134f31af 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -657,8 +657,8 @@ TEST(ConfigParseTest, ParsesConfiguration) { Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never; CHECK_PARSE("AllowShortRecordsOnASingleLine: Empty", AllowShortRecordsOnASingleLine, FormatStyle::SRS_Empty); - CHECK_PARSE("AllowShortRecordsOnASingleLine: All", - AllowShortRecordsOnASingleLine, FormatStyle::SRS_All); + CHECK_PARSE("AllowShortRecordsOnASingleLine: Always", + AllowShortRecordsOnASingleLine, FormatStyle::SRS_Always); CHECK_PARSE("AllowShortRecordsOnASingleLine: Never", AllowShortRecordsOnASingleLine, FormatStyle::SRS_Never); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index d16aba7ac6140..a66049244b789 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15338,7 +15338,7 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style); verifyFormat("union foo {};", Style); - Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_All; + Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Always; verifyFormat("class foo { void bar(); };", Style); verifyFormat("class foo {};", Style); From e811cf22375512442f6031ecc2c1288b1651065d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Sun, 24 Aug 2025 15:54:21 +0200 Subject: [PATCH 4/9] Change option name to use singular form --- clang/include/clang/Format/Format.h | 2 +- clang/lib/Format/Format.cpp | 6 +++--- clang/lib/Format/TokenAnnotator.cpp | 2 +- clang/lib/Format/UnwrappedLineFormatter.cpp | 4 ++-- clang/lib/Format/UnwrappedLineParser.cpp | 4 ++-- clang/unittests/Format/ConfigParseTest.cpp | 14 +++++++------- clang/unittests/Format/FormatTest.cpp | 6 +++--- 7 files changed, 19 insertions(+), 19 deletions(-) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 49e0211aac7ac..4e629340947f6 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1016,7 +1016,7 @@ struct FormatStyle { /// Dependent on the value, ``struct bar { int i; }`` can be put on a single /// line. - ShortRecordStyle AllowShortRecordsOnASingleLine; + ShortRecordStyle AllowShortRecordOnASingleLine; /// Different ways to break after the function definition return type. /// This option is **deprecated** and is retained for backwards compatibility. diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index b4bc49feaadf0..f219eb1b80d37 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1035,8 +1035,8 @@ template <> struct MappingTraits<FormatStyle> { Style.AllowShortLoopsOnASingleLine); IO.mapOptional("AllowShortNamespacesOnASingleLine", Style.AllowShortNamespacesOnASingleLine); - IO.mapOptional("AllowShortRecordsOnASingleLine", - Style.AllowShortRecordsOnASingleLine); + IO.mapOptional("AllowShortRecordOnASingleLine", + Style.AllowShortRecordOnASingleLine); IO.mapOptional("AlwaysBreakAfterDefinitionReturnType", Style.AlwaysBreakAfterDefinitionReturnType); IO.mapOptional("AlwaysBreakBeforeMultilineStrings", @@ -1563,7 +1563,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All; - LLVMStyle.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never; + LLVMStyle.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AllowShortNamespacesOnASingleLine = false; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index e235bc0734270..ea0f73be14a7a 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5957,7 +5957,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, Style.BraceWrapping.AfterStruct) || (Line.startsWith(tok::kw_union) && Style.BraceWrapping.AfterUnion)) && - Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never; + Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Never; } } diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 31c0c7d41586c..268e6f2e5948a 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -457,7 +457,7 @@ class LineJoiner { } const bool MergeShortRecord = [this, &NextLine]() { - switch (Style.AllowShortRecordsOnASingleLine) { + switch (Style.AllowShortRecordOnASingleLine) { case FormatStyle::SRS_Always: return true; case FormatStyle::SRS_Empty: @@ -887,7 +887,7 @@ class LineJoiner { !startsExternCBlock(Line)) { // Merge short records only when requested. if (isRecordLBrace(*Line.Last) && - Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never) { + Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Never) { return 0; } diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index cdb4a1cb6a738..4cc81a2cb51be 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -961,7 +961,7 @@ static bool ShouldBreakBeforeBrace(const FormatStyle &Style, bool IsEmptyBlock = NextToken.is(tok::r_brace); bool WrapRecordAllowed = !(IsEmptyBlock && - Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_Never); + Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never); switch (Kind) { case tok::kw_namespace: @@ -4163,7 +4163,7 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) { if (ParseAsExpr) { parseChildBlock(); } else { - if (Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_Always && + if (Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Always && ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) { addUnwrappedLine(); diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index d0a62134f31af..97e2dcda9c90c 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -654,13 +654,13 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE("AllowShortLambdasOnASingleLine: true", AllowShortLambdasOnASingleLine, FormatStyle::SLS_All); - Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never; - CHECK_PARSE("AllowShortRecordsOnASingleLine: Empty", - AllowShortRecordsOnASingleLine, FormatStyle::SRS_Empty); - CHECK_PARSE("AllowShortRecordsOnASingleLine: Always", - AllowShortRecordsOnASingleLine, FormatStyle::SRS_Always); - CHECK_PARSE("AllowShortRecordsOnASingleLine: Never", - AllowShortRecordsOnASingleLine, FormatStyle::SRS_Never); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + CHECK_PARSE("AllowShortRecordOnASingleLine: Empty", + AllowShortRecordOnASingleLine, FormatStyle::SRS_Empty); + CHECK_PARSE("AllowShortRecordOnASingleLine: Always", + AllowShortRecordOnASingleLine, FormatStyle::SRS_Always); + CHECK_PARSE("AllowShortRecordOnASingleLine: Never", + AllowShortRecordOnASingleLine, FormatStyle::SRS_Never); Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both; CHECK_PARSE("SpaceAroundPointerQualifiers: Default", diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index a66049244b789..39aec18a82bd7 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15298,7 +15298,7 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style.BraceWrapping.AfterStruct = true; Style.BraceWrapping.AfterUnion = true; Style.BraceWrapping.SplitEmptyRecord = false; - Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never; + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; verifyFormat("class foo\n{\n" " void bar();\n" @@ -15318,7 +15318,7 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style); verifyFormat("union foo\n{};", Style); - Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Empty; + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; verifyFormat("class foo\n{\n" " void bar();\n" @@ -15338,7 +15338,7 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style); verifyFormat("union foo {};", Style); - Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Always; + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; verifyFormat("class foo { void bar(); };", Style); verifyFormat("class foo {};", Style); From d46bcb47277e2f5d5b37f5d1f4d33a1b906bd92c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Sun, 24 Aug 2025 15:56:58 +0200 Subject: [PATCH 5/9] Fix docs after rename --- clang/docs/ClangFormatStyleOptions.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index d7cbf877fcaa5..cde28a2608a34 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2096,9 +2096,9 @@ the configuration (without a prefix: ``Auto``). **AllowShortNamespacesOnASingleLine** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ <AllowShortNamespacesOnASingleLine>` If ``true``, ``namespace a { class b; }`` can be put on a single line. -.. _AllowShortRecordsOnASingleLine: +.. _AllowShortRecordOnASingleLine: -**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`¶ <AllowShortRecordsOnASingleLine>` +**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :ref:`¶ <AllowShortRecordOnASingleLine>` Dependent on the value, ``struct bar { int i; }`` can be put on a single line. @@ -2118,7 +2118,7 @@ the configuration (without a prefix: ``Auto``). int i; }; - * ``SRS_All`` (in configuration: ``All``) + * ``SRS_Always`` (in configuration: ``Always``) Merge all records that fit on a single line. .. code-block:: c++ From a482b21197228dc55ac1f498502eac7e2523fcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Mon, 25 Aug 2025 00:38:53 +0200 Subject: [PATCH 6/9] Fixup documentation --- clang/docs/ClangFormatStyleOptions.rst | 2 +- clang/include/clang/Format/Format.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index cde28a2608a34..0694d2ca33df9 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2098,7 +2098,7 @@ the configuration (without a prefix: ``Auto``). .. _AllowShortRecordOnASingleLine: -**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :ref:`¶ <AllowShortRecordOnASingleLine>` +**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :versionbadge:`clang-format 22` :ref:`¶ <AllowShortRecordOnASingleLine>` Dependent on the value, ``struct bar { int i; }`` can be put on a single line. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 4e629340947f6..af04f6d88455a 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -992,8 +992,8 @@ struct FormatStyle { /// \version 20 bool AllowShortNamespacesOnASingleLine; - /// Different styles for merging short records - /// (``class``,``struct``,``union``). + /// Different styles for merging short records (``class``,``struct``, and + /// ``union``). enum ShortRecordStyle : int8_t { /// Never merge records into a single line. SRS_Never, @@ -1016,6 +1016,7 @@ struct FormatStyle { /// Dependent on the value, ``struct bar { int i; }`` can be put on a single /// line. + /// \version 22 ShortRecordStyle AllowShortRecordOnASingleLine; /// Different ways to break after the function definition return type. From 79190abf822e94c585524eb1725841265715b0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Tue, 26 Aug 2025 23:54:53 +0200 Subject: [PATCH 7/9] Fix SplitEmptyRecord handling, docs --- clang/docs/ClangFormatStyleOptions.rst | 2 +- clang/include/clang/Format/Format.h | 2 +- clang/lib/Format/UnwrappedLineFormatter.cpp | 2 +- clang/lib/Format/UnwrappedLineParser.cpp | 3 ++- clang/unittests/Format/FormatTest.cpp | 25 ++++++++++++--------- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 0694d2ca33df9..808c447e37767 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2099,7 +2099,7 @@ the configuration (without a prefix: ``Auto``). .. _AllowShortRecordOnASingleLine: **AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :versionbadge:`clang-format 22` :ref:`¶ <AllowShortRecordOnASingleLine>` - Dependent on the value, ``struct bar { int i; }`` can be put on a single + Dependent on the value, ``struct bar { int i; };`` can be put on a single line. Possible values: diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index af04f6d88455a..12fc8a98da17c 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1014,7 +1014,7 @@ struct FormatStyle { SRS_Always }; - /// Dependent on the value, ``struct bar { int i; }`` can be put on a single + /// Dependent on the value, ``struct bar { int i; };`` can be put on a single /// line. /// \version 22 ShortRecordStyle AllowShortRecordOnASingleLine; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 268e6f2e5948a..04ab177543daf 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -465,7 +465,7 @@ class LineJoiner { case FormatStyle::SRS_Never: return false; } - }(); + }() && !Style.BraceWrapping.SplitEmptyRecord; // Don't merge an empty template class or struct if SplitEmptyRecords // is defined. diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 4cc81a2cb51be..87db07291e815 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -961,7 +961,8 @@ static bool ShouldBreakBeforeBrace(const FormatStyle &Style, bool IsEmptyBlock = NextToken.is(tok::r_brace); bool WrapRecordAllowed = !(IsEmptyBlock && - Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never); + Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never) || + Style.BraceWrapping.SplitEmptyRecord; switch (Kind) { case tok::kw_namespace: diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 39aec18a82bd7..af9d8d994b684 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15318,6 +15318,17 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style); verifyFormat("union foo\n{};", Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; + + verifyFormat("class foo { void bar(); };", Style); + verifyFormat("class foo {};", Style); + + verifyFormat("struct foo { int bar; };", Style); + verifyFormat("struct foo {};", Style); + + verifyFormat("union foo { int bar; };", Style); + verifyFormat("union foo {};", Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; verifyFormat("class foo\n{\n" @@ -15338,16 +15349,10 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style); verifyFormat("union foo {};", Style); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; - - verifyFormat("class foo { void bar(); };", Style); - verifyFormat("class foo {};", Style); - - verifyFormat("struct foo { int bar; };", Style); - verifyFormat("struct foo {};", Style); - - verifyFormat("union foo { int bar; };", Style); - verifyFormat("union foo {};", Style); + Style.BraceWrapping.SplitEmptyRecord = true; + verifyFormat("class foo\n{\n}", Style); + verifyFormat("struct foo\n{\n}", Style); + verifyFormat("union foo\n{\n}", Style); } TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { From ff80c9722d63f8767aa9af9afb1ff4fbe94f85c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Sat, 30 Aug 2025 17:41:51 +0200 Subject: [PATCH 8/9] Fix behavior of Never, add EmptyIfAttached --- clang/docs/ClangFormatStyleOptions.rst | 4 + clang/include/clang/Format/Format.h | 3 + clang/lib/Format/Format.cpp | 3 +- clang/lib/Format/UnwrappedLineFormatter.cpp | 43 ++++---- clang/lib/Format/UnwrappedLineParser.cpp | 4 +- clang/unittests/Format/ConfigParseTest.cpp | 8 +- clang/unittests/Format/FormatTest.cpp | 114 ++++++++++++++++++-- 7 files changed, 144 insertions(+), 35 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 808c447e37767..445328ebc7716 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2107,6 +2107,10 @@ the configuration (without a prefix: ``Auto``). * ``SRS_Never`` (in configuration: ``Never``) Never merge records into a single line. + * ``SRS_EmptyIfAttached`` (in configuration: ``EmptyIfAttached``) + Only merge empty records if the opening brace was not wrapped, + i.e. the corresponding ``BraceWrapping.After...`` option was not set. + * ``SRS_Empty`` (in configuration: ``Empty``) Only merge empty records. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 12fc8a98da17c..90617481ea0c0 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -997,6 +997,9 @@ struct FormatStyle { enum ShortRecordStyle : int8_t { /// Never merge records into a single line. SRS_Never, + /// Only merge empty records if the opening brace was not wrapped, + /// i.e. the corresponding ``BraceWrapping.After...`` option was not set. + SRS_EmptyIfAttached, /// Only merge empty records. /// \code /// struct foo {}; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index f219eb1b80d37..ed413e267d95f 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -663,6 +663,7 @@ template <> struct ScalarEnumerationTraits<FormatStyle::ShortLambdaStyle> { template <> struct ScalarEnumerationTraits<FormatStyle::ShortRecordStyle> { static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) { IO.enumCase(Value, "Never", FormatStyle::SRS_Never); + IO.enumCase(Value, "EmptyIfAttached", FormatStyle::SRS_EmptyIfAttached); IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty); IO.enumCase(Value, "Always", FormatStyle::SRS_Always); } @@ -1563,7 +1564,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All; - LLVMStyle.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + LLVMStyle.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AllowShortNamespacesOnASingleLine = false; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 04ab177543daf..593aafe3ca19a 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -456,17 +456,6 @@ class LineJoiner { } } - const bool MergeShortRecord = [this, &NextLine]() { - switch (Style.AllowShortRecordOnASingleLine) { - case FormatStyle::SRS_Always: - return true; - case FormatStyle::SRS_Empty: - return NextLine.First->is(tok::r_brace); - case FormatStyle::SRS_Never: - return false; - } - }() && !Style.BraceWrapping.SplitEmptyRecord; - // Don't merge an empty template class or struct if SplitEmptyRecords // is defined. if (PreviousLine && Style.BraceWrapping.SplitEmptyRecord && @@ -496,6 +485,18 @@ class LineJoiner { : 0; } + const bool MergeShortRecord = [this, &NextLine]() { + switch (Style.AllowShortRecordOnASingleLine) { + case FormatStyle::SRS_Always: + return true; + case FormatStyle::SRS_EmptyIfAttached: + case FormatStyle::SRS_Empty: + return NextLine.First->is(tok::r_brace); + case FormatStyle::SRS_Never: + return false; + } + }(); + if (TheLine->Last->is(tok::l_brace)) { bool ShouldMerge = false; // Try to merge records. @@ -503,14 +504,16 @@ class LineJoiner { ShouldMerge = Style.AllowShortEnumsOnASingleLine; } else if (TheLine->Last->is(TT_CompoundRequirementLBrace)) { ShouldMerge = Style.AllowShortCompoundRequirementOnASingleLine; - } else if (TheLine->Last->isOneOf(TT_ClassLBrace, TT_StructLBrace)) { - // 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) || - MergeShortRecord; + } else if (TheLine->Last->isOneOf(TT_ClassLBrace, TT_StructLBrace, + TT_UnionLBrace)) { + if (Style.AllowShortRecordOnASingleLine > FormatStyle::SRS_Never) { + // 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 || + (MergeShortRecord && !Style.BraceWrapping.SplitEmptyRecord); + } } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, tok::kw_struct)) { @@ -887,7 +890,7 @@ class LineJoiner { !startsExternCBlock(Line)) { // Merge short records only when requested. if (isRecordLBrace(*Line.Last) && - Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Never) { + Style.AllowShortRecordOnASingleLine < FormatStyle::SRS_Always) { return 0; } diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 87db07291e815..5378b2b32aaf4 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -960,8 +960,8 @@ static bool ShouldBreakBeforeBrace(const FormatStyle &Style, bool IsEmptyBlock = NextToken.is(tok::r_brace); bool WrapRecordAllowed = - !(IsEmptyBlock && - Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never) || + !(IsEmptyBlock && Style.AllowShortRecordOnASingleLine > + FormatStyle::SRS_EmptyIfAttached) || Style.BraceWrapping.SplitEmptyRecord; switch (Kind) { diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 97e2dcda9c90c..ec08cfab5daf9 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -654,13 +654,15 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE("AllowShortLambdasOnASingleLine: true", AllowShortLambdasOnASingleLine, FormatStyle::SLS_All); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; + CHECK_PARSE("AllowShortRecordOnASingleLine: Never", + AllowShortRecordOnASingleLine, FormatStyle::SRS_Never); + CHECK_PARSE("AllowShortRecordOnASingleLine: EmptyIfAttached", + AllowShortRecordOnASingleLine, FormatStyle::SRS_EmptyIfAttached); CHECK_PARSE("AllowShortRecordOnASingleLine: Empty", AllowShortRecordOnASingleLine, FormatStyle::SRS_Empty); CHECK_PARSE("AllowShortRecordOnASingleLine: Always", AllowShortRecordOnASingleLine, FormatStyle::SRS_Always); - CHECK_PARSE("AllowShortRecordOnASingleLine: Never", - AllowShortRecordOnASingleLine, FormatStyle::SRS_Never); Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both; CHECK_PARSE("SpaceAroundPointerQualifiers: Default", diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index af9d8d994b684..9a2f122788150 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15290,14 +15290,87 @@ TEST_F(FormatTest, NeverMergeShortRecords) { Style); } -TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { +TEST_F(FormatTest, AllowShortRecordOnASingleLine) { FormatStyle Style = getLLVMStyle(); Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.SplitEmptyRecord = false; + + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + + verifyFormat("class foo {\n" + " void bar();\n" + "};", + Style); + verifyFormat("class foo {\n};", Style); + + verifyFormat("struct foo {\n" + " int bar;\n" + "};", + Style); + verifyFormat("struct foo {\n};", Style); + + verifyFormat("union foo {\n" + " int bar;\n" + "};", + Style); + verifyFormat("union foo {\n};", Style); + + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; + + verifyFormat("class foo {\n" + " void bar();\n" + "};", + Style); + verifyFormat("class foo {};", Style); + + verifyFormat("struct foo {\n" + " void bar();\n" + "};", + Style); + verifyFormat("struct foo {};", Style); + + verifyFormat("union foo {\n" + " void bar();\n" + "};", + Style); + verifyFormat("union foo {};", Style); + + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; + + verifyFormat("class foo {\n" + " void bar();\n" + "};", + Style); + verifyFormat("class foo {};", Style); + + verifyFormat("struct foo {\n" + " int bar;\n" + "};", + Style); + verifyFormat("struct foo {};", Style); + + verifyFormat("union foo {\n" + " int bar;\n" + "};", + Style); + verifyFormat("union foo {};", Style); + + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; + + verifyFormat("class foo { void bar(); };", Style); + verifyFormat("class foo {};", Style); + + verifyFormat("struct foo { int bar; };", Style); + verifyFormat("struct foo {};", Style); + + verifyFormat("union foo { int bar; };", Style); + verifyFormat("union foo {};", Style); + Style.BraceWrapping.AfterClass = true; Style.BraceWrapping.AfterStruct = true; Style.BraceWrapping.AfterUnion = true; - Style.BraceWrapping.SplitEmptyRecord = false; + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; verifyFormat("class foo\n{\n" @@ -15318,16 +15391,25 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style); verifyFormat("union foo\n{};", Style); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; - verifyFormat("class foo { void bar(); };", Style); - verifyFormat("class foo {};", Style); + verifyFormat("class foo\n{\n" + " void bar();\n" + "};", + Style); + verifyFormat("class foo\n{};", Style); - verifyFormat("struct foo { int bar; };", Style); - verifyFormat("struct foo {};", Style); + verifyFormat("struct foo\n{\n" + " void bar();\n" + "};", + Style); + verifyFormat("struct foo\n{};", Style); - verifyFormat("union foo { int bar; };", Style); - verifyFormat("union foo {};", Style); + verifyFormat("union foo\n{\n" + " void bar();\n" + "};", + Style); + verifyFormat("union foo\n{};", Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; @@ -15349,7 +15431,21 @@ TEST_F(FormatTest, AllowShortRecordsOnASingleLine) { Style); verifyFormat("union foo {};", Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; + + verifyFormat("class foo { void bar(); };", Style); + verifyFormat("class foo {};", Style); + + verifyFormat("struct foo { int bar; };", Style); + verifyFormat("struct foo {};", Style); + + verifyFormat("union foo { int bar; };", Style); + verifyFormat("union foo {};", Style); + + // Ensure option gets overriden by SplitEmptyRecord + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; Style.BraceWrapping.SplitEmptyRecord = true; + verifyFormat("class foo\n{\n}", Style); verifyFormat("struct foo\n{\n}", Style); verifyFormat("union foo\n{\n}", Style); From 212e5e506763a5ffdab17a2f1e1a5905832e95bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoe...@gmail.com> Date: Tue, 2 Sep 2025 21:11:56 +0200 Subject: [PATCH 9/9] Fix incorrect handling of left brace wrapping --- clang/lib/Format/UnwrappedLineFormatter.cpp | 36 ++++-- clang/lib/Format/UnwrappedLineParser.cpp | 5 +- clang/unittests/Format/FormatTest.cpp | 124 +++++++------------- 3 files changed, 65 insertions(+), 100 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 593aafe3ca19a..13925e2ccf287 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -260,6 +260,14 @@ class LineJoiner { } } + // Try merging record blocks that have had their left brace wrapped. + if (TheLine->First->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_union) && + NextLine.First->is(tok::l_brace) && NextLine.First == NextLine.Last && + I + 2 != E && !I[2]->First->is(tok::r_brace)) { + if (unsigned MergedLines = tryMergeSimpleBlock(I, E, Limit)) + return MergedLines; + } + const auto *PreviousLine = I != AnnotatedLines.begin() ? I[-1] : nullptr; // Handle empty record blocks where the brace has already been wrapped. if (PreviousLine && TheLine->Last->is(tok::l_brace) && @@ -485,17 +493,17 @@ class LineJoiner { : 0; } - const bool MergeShortRecord = [this, &NextLine]() { + const bool TryMergeShortRecord = [this, &NextLine]() { switch (Style.AllowShortRecordOnASingleLine) { - case FormatStyle::SRS_Always: - return true; + case FormatStyle::SRS_Never: + return false; case FormatStyle::SRS_EmptyIfAttached: case FormatStyle::SRS_Empty: return NextLine.First->is(tok::r_brace); - case FormatStyle::SRS_Never: - return false; + case FormatStyle::SRS_Always: + return true; } - }(); + }() && !Style.BraceWrapping.SplitEmptyRecord; if (TheLine->Last->is(tok::l_brace)) { bool ShouldMerge = false; @@ -510,9 +518,7 @@ 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 || - (MergeShortRecord && !Style.BraceWrapping.SplitEmptyRecord); + ShouldMerge = !Style.BraceWrapping.AfterClass || TryMergeShortRecord; } } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, @@ -945,9 +951,15 @@ class LineJoiner { return 0; Limit -= 2; unsigned MergedLines = 0; - if (Style.AllowShortBlocksOnASingleLine != FormatStyle::SBS_Never || - (I[1]->First == I[1]->Last && I + 2 != E && - I[2]->First->is(tok::r_brace))) { + + bool TryMergeBlock = + Style.AllowShortBlocksOnASingleLine != FormatStyle::SBS_Never; + bool TryMergeRecord = + Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always; + bool NextIsEmptyBlock = I[1]->First == I[1]->Last && I + 2 != E && + I[2]->First->is(tok::r_brace); + + if (TryMergeBlock || TryMergeRecord || NextIsEmptyBlock) { MergedLines = tryMergeSimpleBlock(I + 1, E, Limit); // If we managed to merge the block, count the statement header, which // is on a separate line. diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 5378b2b32aaf4..e2b5072d1bf8c 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -4164,11 +4164,8 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) { if (ParseAsExpr) { parseChildBlock(); } else { - if (Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Always && - ShouldBreakBeforeBrace(Style, InitialToken, - *Tokens->peekNextToken())) { + if (ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) addUnwrappedLine(); - } unsigned AddLevels = Style.IndentAccessModifiers ? 2u : 1u; parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 9a2f122788150..c0affdb7148c5 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15290,162 +15290,118 @@ TEST_F(FormatTest, NeverMergeShortRecords) { Style); } -TEST_F(FormatTest, AllowShortRecordOnASingleLine) { +TEST_F(FormatTest, AllowShortRecordOnASingleLineNonSplit) { FormatStyle Style = getLLVMStyle(); Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.SplitEmptyRecord = false; Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; - verifyFormat("class foo {\n" " void bar();\n" "};", Style); verifyFormat("class foo {\n};", Style); - verifyFormat("struct foo {\n" - " int bar;\n" - "};", - Style); - verifyFormat("struct foo {\n};", Style); - - verifyFormat("union foo {\n" - " int bar;\n" - "};", - Style); - verifyFormat("union foo {\n};", Style); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; - verifyFormat("class foo {\n" " void bar();\n" "};", Style); verifyFormat("class foo {};", Style); - verifyFormat("struct foo {\n" - " void bar();\n" - "};", - Style); - verifyFormat("struct foo {};", Style); - - verifyFormat("union foo {\n" - " void bar();\n" - "};", - Style); - verifyFormat("union foo {};", Style); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; - verifyFormat("class foo {\n" " void bar();\n" "};", Style); verifyFormat("class foo {};", Style); - verifyFormat("struct foo {\n" - " int bar;\n" - "};", - Style); - verifyFormat("struct foo {};", Style); - - verifyFormat("union foo {\n" - " int bar;\n" - "};", - Style); - verifyFormat("union foo {};", Style); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; - verifyFormat("class foo { void bar(); };", Style); verifyFormat("class foo {};", Style); - verifyFormat("struct foo { int bar; };", Style); - verifyFormat("struct foo {};", Style); - - verifyFormat("union foo { int bar; };", Style); - verifyFormat("union foo {};", Style); - Style.BraceWrapping.AfterClass = true; Style.BraceWrapping.AfterStruct = true; Style.BraceWrapping.AfterUnion = true; Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; - 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.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; - verifyFormat("class foo\n{\n" " void bar();\n" "};", Style); verifyFormat("class foo\n{};", Style); - verifyFormat("struct foo\n{\n" + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; + verifyFormat("class foo\n{\n" " void bar();\n" "};", Style); - verifyFormat("struct foo\n{};", Style); + verifyFormat("class foo {};", Style); - verifyFormat("union foo\n{\n" - " void bar();\n" - "};", - Style); - verifyFormat("union foo\n{};", Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; + verifyFormat("class foo { void bar(); };", Style); + verifyFormat("class foo {};", Style); +} - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; +TEST_F(FormatTest, AllowShortRecordOnASingleLineSplit) { + FormatStyle Style = getLLVMStyle(); - verifyFormat("class foo\n{\n" + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.SplitEmptyRecord = true; + + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + verifyFormat("class foo {\n" " void bar();\n" "};", Style); - verifyFormat("class foo {};", Style); + verifyFormat("class foo {\n};", Style); - verifyFormat("struct foo\n{\n" - " int bar;\n" + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; + verifyFormat("class foo {\n" + " void bar();\n" "};", Style); - verifyFormat("struct foo {};", Style); + verifyFormat("class foo {};", Style); - verifyFormat("union foo\n{\n" - " int bar;\n" + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; + verifyFormat("class foo {\n" + " void bar();\n" "};", Style); - verifyFormat("union foo {};", Style); + verifyFormat("class foo {};", Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; - verifyFormat("class foo { void bar(); };", Style); verifyFormat("class foo {};", Style); - verifyFormat("struct foo { int bar; };", Style); - verifyFormat("struct foo {};", Style); + Style.BraceWrapping.AfterClass = true; + Style.BraceWrapping.AfterStruct = true; + Style.BraceWrapping.AfterUnion = true; + + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + verifyFormat("class foo\n{\n}", Style); + verifyFormat("struct foo\n{\n}", Style); + verifyFormat("union foo\n{\n}", Style); - verifyFormat("union foo { int bar; };", Style); - verifyFormat("union foo {};", Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; + verifyFormat("class foo\n{\n}", Style); + verifyFormat("struct foo\n{\n}", Style); + verifyFormat("union foo\n{\n}", Style); - // Ensure option gets overriden by SplitEmptyRecord Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; - Style.BraceWrapping.SplitEmptyRecord = true; + verifyFormat("class foo\n{\n}", Style); + verifyFormat("struct foo\n{\n}", Style); + verifyFormat("union foo\n{\n}", Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; verifyFormat("class foo\n{\n}", Style); verifyFormat("struct foo\n{\n}", Style); verifyFormat("union foo\n{\n}", Style); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits