From 58f7f5ea100520ff0436f55ac432813d6fbabf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Wed, 20 Aug 2025 19:28:23 +0200 Subject: [PATCH 01/25] [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 b746df5dab264..8ca42a25f8125 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2103,6 +2103,38 @@ the configuration (without a prefix: ``Auto``). **AllowShortNamespacesOnASingleLine** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ ` If ``true``, ``namespace a { class b; }`` can be put on a single line. +.. _AllowShortRecordsOnASingleLine: + +**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`¶ ` + 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:`¶ ` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 3df5b92654094..0c8f841e4266a 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -998,6 +998,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 835071dbe715d..40c95d40b2fb4 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -681,6 +681,14 @@ template <> struct ScalarEnumerationTraits { } }; +template <> struct ScalarEnumerationTraits { + 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 { static void enumInput(IO &IO, FormatStyle::SortIncludesOptions &Value) { IO.enumCase(Value, "Never", FormatStyle::SortIncludesOptions({})); @@ -1046,6 +1054,8 @@ template <> struct MappingTraits { Style.AllowShortIfStatementsOnASingleLine); IO.mapOptional("AllowShortLambdasOnASingleLine", Style.AllowShortLambdasOnASingleLine); + IO.mapOptional("AllowShortRecordsOnASingleLine", + Style.AllowShortRecordsOnASingleLine); IO.mapOptional("AllowShortLoopsOnASingleLine", Style.AllowShortLoopsOnASingleLine); IO.mapOptional("AllowShortNamespacesOnASingleLine", @@ -1578,6 +1588,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 6a8286da73442..1060a42abb1af 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5959,12 +5959,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 ac9d147defc13..6201215e65ad1 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -462,6 +462,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 && @@ -504,7 +517,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)) { @@ -879,9 +893,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 2c9766c9b7bc0..77e848d374600 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -948,20 +948,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: @@ -3193,7 +3199,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 = @@ -3858,7 +3864,7 @@ bool UnwrappedLineParser::parseEnum() { } if (!Style.AllowShortEnumsOnASingleLine && - ShouldBreakBeforeBrace(Style, InitialToken)) { + ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) { addUnwrappedLine(); } // Parse enum body. @@ -4153,8 +4159,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 6111e86ff7076..64592fdcd87dc 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -655,6 +655,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 6a3385a56f53e..f1cc78ac38b7d 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -8632,6 +8632,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. @@ -15334,6 +15347,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 999b2fafbb0abcf0c06d7b629b847d4ac5733e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Wed, 20 Aug 2025 23:54:07 +0200 Subject: [PATCH 02/25] 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 40c95d40b2fb4..b7fb8e4c34948 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1054,12 +1054,12 @@ template <> struct MappingTraits { 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 6201215e65ad1..22ad2a3c0812d 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -462,18 +462,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 eec3dfa9f8f50b38257b5748fe1c0c12cf844eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sun, 24 Aug 2025 15:24:59 +0200 Subject: [PATCH 03/25] 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 0c8f841e4266a..6bb36ab5ac1e1 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1017,7 +1017,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 b7fb8e4c34948..5ac5ff09954a8 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -685,7 +685,7 @@ template <> struct ScalarEnumerationTraits { 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 22ad2a3c0812d..bc3975944d8f1 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -464,7 +464,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 77e848d374600..0f6ca0c291102 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -4159,7 +4159,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 64592fdcd87dc..a92ba6d5d6225 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -658,8 +658,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 f1cc78ac38b7d..40da75d6778c3 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15395,7 +15395,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 670708811de4ca4d38770d43996ec905052c49c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sun, 24 Aug 2025 15:54:21 +0200 Subject: [PATCH 04/25] 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 6bb36ab5ac1e1..fc033ecdb3228 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1022,7 +1022,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 5ac5ff09954a8..23f67d34a559a 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1058,8 +1058,8 @@ template <> struct MappingTraits { 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", @@ -1588,7 +1588,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 1060a42abb1af..e3a82e3b3651a 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5967,7 +5967,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 bc3975944d8f1..0a55a1c9bb3ea 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -463,7 +463,7 @@ class LineJoiner { } const bool MergeShortRecord = [this, &NextLine]() { - switch (Style.AllowShortRecordsOnASingleLine) { + switch (Style.AllowShortRecordOnASingleLine) { case FormatStyle::SRS_Always: return true; case FormatStyle::SRS_Empty: @@ -893,7 +893,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 0f6ca0c291102..968629cd77f23 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -957,7 +957,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: @@ -4159,7 +4159,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 a92ba6d5d6225..3b72d6933591e 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -655,13 +655,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 40da75d6778c3..71778449fceaa 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15355,7 +15355,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" @@ -15375,7 +15375,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" @@ -15395,7 +15395,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 07927165f31db7174931be002093fe5d021040e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sun, 24 Aug 2025 15:56:58 +0200 Subject: [PATCH 05/25] 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 8ca42a25f8125..bf583532bca00 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2103,9 +2103,9 @@ the configuration (without a prefix: ``Auto``). **AllowShortNamespacesOnASingleLine** (``Boolean``) :versionbadge:`clang-format 20` :ref:`¶ ` If ``true``, ``namespace a { class b; }`` can be put on a single line. -.. _AllowShortRecordsOnASingleLine: +.. _AllowShortRecordOnASingleLine: -**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`¶ ` +**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :ref:`¶ ` Dependent on the value, ``struct bar { int i; }`` can be put on a single line. @@ -2125,7 +2125,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 b3d6b3b508d83fe16bd75029f434cba2518ad30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Mon, 25 Aug 2025 00:38:53 +0200 Subject: [PATCH 06/25] 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 bf583532bca00..4dabfd70055d8 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2105,7 +2105,7 @@ the configuration (without a prefix: ``Auto``). .. _AllowShortRecordOnASingleLine: -**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :ref:`¶ ` +**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :versionbadge:`clang-format 22` :ref:`¶ ` 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 fc033ecdb3228..29b297bfbaf44 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -998,8 +998,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, @@ -1022,6 +1022,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 928418067bc220476750bd4322f10787914b54d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Tue, 26 Aug 2025 23:54:53 +0200 Subject: [PATCH 07/25] 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 4dabfd70055d8..014e2983f2f07 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2106,7 +2106,7 @@ the configuration (without a prefix: ``Auto``). .. _AllowShortRecordOnASingleLine: **AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :versionbadge:`clang-format 22` :ref:`¶ ` - 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 29b297bfbaf44..94611431a7075 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1020,7 +1020,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 0a55a1c9bb3ea..cebd468efb371 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -471,7 +471,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 968629cd77f23..ef99e1f89d80e 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -957,7 +957,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 71778449fceaa..55adc3799f97a 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15375,6 +15375,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" @@ -15395,16 +15406,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 42b19182dd1e5fb571008cba932d598856074617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sat, 30 Aug 2025 17:41:51 +0200 Subject: [PATCH 08/25] 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 014e2983f2f07..0f60cdb530b8b 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2114,6 +2114,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 94611431a7075..dae125f7f72e7 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1003,6 +1003,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 23f67d34a559a..6bbc7e234a7fb 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -684,6 +684,7 @@ template <> struct ScalarEnumerationTraits { template <> struct ScalarEnumerationTraits { 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); } @@ -1588,7 +1589,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 cebd468efb371..f79b9c5a73602 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -462,17 +462,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 && @@ -502,6 +491,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. @@ -509,14 +510,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)) { @@ -893,7 +896,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 ef99e1f89d80e..1eb82fb766b40 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -956,8 +956,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 3b72d6933591e..76d0e6762fc2e 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -655,13 +655,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 55adc3799f97a..4267a0bde5d1d 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15347,14 +15347,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" @@ -15375,16 +15448,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; @@ -15406,7 +15488,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 4a11e4df30f9e2396fa0aa1552860586bd43f8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Tue, 2 Sep 2025 21:11:56 +0200 Subject: [PATCH 09/25] 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 f79b9c5a73602..44564d220f8ca 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -266,6 +266,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) && @@ -491,17 +499,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; @@ -516,9 +524,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, @@ -951,9 +957,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 1eb82fb766b40..4786109701cfb 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -4160,11 +4160,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 4267a0bde5d1d..2b312fb694bf2 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15347,162 +15347,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); From 6f11ac208f2719a420849c304541b24fa0f94311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Wed, 3 Sep 2025 17:49:31 +0200 Subject: [PATCH 10/25] Fixup test cases --- clang/unittests/Format/FormatTest.cpp | 64 +++++++++++++++------------ 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 2b312fb694bf2..0a6a88d91d0a2 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15348,7 +15348,7 @@ TEST_F(FormatTest, NeverMergeShortRecords) { } TEST_F(FormatTest, AllowShortRecordOnASingleLineNonSplit) { - FormatStyle Style = getLLVMStyle(); + auto Style = getLLVMStyle(); Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.SplitEmptyRecord = false; @@ -15358,7 +15358,9 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLineNonSplit) { " void bar();\n" "};", Style); - verifyFormat("class foo {\n};", Style); + verifyFormat("class foo {\n" + "};", + Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; verifyFormat("class foo {\n" @@ -15379,25 +15381,26 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLineNonSplit) { verifyFormat("class foo {};", Style); Style.BraceWrapping.AfterClass = true; - Style.BraceWrapping.AfterStruct = true; - Style.BraceWrapping.AfterUnion = true; Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; - verifyFormat("class foo\n{\n" + verifyFormat("class foo\n" + "{\n" " void bar();\n" "};", Style); verifyFormat("class foo\n{};", Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; - verifyFormat("class foo\n{\n" + verifyFormat("class foo\n" + "{\n" " void bar();\n" "};", Style); verifyFormat("class foo\n{};", Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; - verifyFormat("class foo\n{\n" + verifyFormat("class foo\n" + "{\n" " void bar();\n" "};", Style); @@ -15409,24 +15412,26 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLineNonSplit) { } TEST_F(FormatTest, AllowShortRecordOnASingleLineSplit) { - FormatStyle Style = getLLVMStyle(); + auto Style = getLLVMStyle(); - Style.BreakBeforeBraces = FormatStyle::BS_Custom; - Style.BraceWrapping.SplitEmptyRecord = true; + EXPECT_EQ(Style.BraceWrapping.SplitEmptyRecord, true); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + EXPECT_EQ(Style.AllowShortRecordOnASingleLine, + FormatStyle::SRS_EmptyIfAttached); verifyFormat("class foo {\n" " void bar();\n" "};", Style); - verifyFormat("class foo {\n};", Style); + verifyFormat("class foo {};", Style); - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; verifyFormat("class foo {\n" " void bar();\n" "};", Style); - verifyFormat("class foo {};", Style); + verifyFormat("class foo {\n" + "};", + Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; verifyFormat("class foo {\n" @@ -15439,29 +15444,32 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLineSplit) { verifyFormat("class foo { void bar(); };", Style); verifyFormat("class foo {};", Style); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; 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("class foo\n" + "{\n" + "}", + Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; - verifyFormat("class foo\n{\n}", Style); - verifyFormat("struct foo\n{\n}", Style); - verifyFormat("union foo\n{\n}", Style); + verifyFormat("class foo\n" + "{\n" + "}", + Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; - verifyFormat("class foo\n{\n}", Style); - verifyFormat("struct foo\n{\n}", Style); - verifyFormat("union foo\n{\n}", Style); + verifyFormat("class 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); + verifyFormat("class foo\n" + "{\n" + "}", + Style); } TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { From e1062d76a672858c164ade40379bafde88de0b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Wed, 3 Sep 2025 17:59:08 +0200 Subject: [PATCH 11/25] Fixup ShouldBreakBeforeBrace --- clang/lib/Format/UnwrappedLineParser.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 4786109701cfb..540c1903b6800 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -949,15 +949,14 @@ static bool isIIFE(const UnwrappedLine &Line, static bool ShouldBreakBeforeBrace(const FormatStyle &Style, const FormatToken &InitialToken, - const FormatToken &NextToken) { + bool IsEmptyBlock) { 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.AllowShortRecordOnASingleLine > - FormatStyle::SRS_EmptyIfAttached) || + !IsEmptyBlock || + Style.AllowShortRecordOnASingleLine < FormatStyle::SRS_Empty || Style.BraceWrapping.SplitEmptyRecord; switch (Kind) { @@ -3200,8 +3199,10 @@ void UnwrappedLineParser::parseNamespace() { if (FormatTok->is(tok::l_brace)) { FormatTok->setFinalizedType(TT_NamespaceLBrace); - if (ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) + if (ShouldBreakBeforeBrace(Style, InitialToken, + Tokens->peekNextToken()->is(tok::r_brace))) { addUnwrappedLine(); + } unsigned AddLevels = Style.NamespaceIndentation == FormatStyle::NI_All || @@ -3865,7 +3866,8 @@ bool UnwrappedLineParser::parseEnum() { } if (!Style.AllowShortEnumsOnASingleLine && - ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) { + ShouldBreakBeforeBrace(Style, InitialToken, + Tokens->peekNextToken()->is(tok::r_brace))) { addUnwrappedLine(); } // Parse enum body. @@ -4160,8 +4162,10 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) { if (ParseAsExpr) { parseChildBlock(); } else { - if (ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) + if (ShouldBreakBeforeBrace(Style, InitialToken, + Tokens->peekNextToken()->is(tok::r_brace))) { addUnwrappedLine(); + } unsigned AddLevels = Style.IndentAccessModifiers ? 2u : 1u; parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false); From 7604f558bfadb80445567095496b6ff508183529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Thu, 4 Sep 2025 16:08:04 +0200 Subject: [PATCH 12/25] Fixups --- clang/lib/Format/TokenAnnotator.cpp | 1 + clang/lib/Format/UnwrappedLineFormatter.cpp | 13 +- clang/unittests/Format/FormatTest.cpp | 139 +++++--------------- 3 files changed, 40 insertions(+), 113 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index e3a82e3b3651a..14a0aa77dda37 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5960,6 +5960,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, } // Don't attempt to interpret record return types as records. + // FIXME: Not covered by tests. if (Right.isNot(TT_FunctionLBrace)) { return ((Line.startsWith(tok::kw_class) && Style.BraceWrapping.AfterClass) || diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 44564d220f8ca..198adb13829b8 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -267,9 +267,10 @@ 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 (NextLine.First->isOneOf(TT_ClassLBrace, TT_StructLBrace, + TT_UnionLBrace) && + NextLine.First == NextLine.Last && I + 2 != E && + !I[2]->First->is(tok::r_brace)) { if (unsigned MergedLines = tryMergeSimpleBlock(I, E, Limit)) return MergedLines; } @@ -499,7 +500,7 @@ class LineJoiner { : 0; } - const bool TryMergeShortRecord = [this, &NextLine]() { + const bool TryMergeShortRecord = [&]() { switch (Style.AllowShortRecordOnASingleLine) { case FormatStyle::SRS_Never: return false; @@ -520,7 +521,7 @@ class LineJoiner { ShouldMerge = Style.AllowShortCompoundRequirementOnASingleLine; } else if (TheLine->Last->isOneOf(TT_ClassLBrace, TT_StructLBrace, TT_UnionLBrace)) { - if (Style.AllowShortRecordOnASingleLine > FormatStyle::SRS_Never) { + 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. @@ -902,7 +903,7 @@ class LineJoiner { !startsExternCBlock(Line)) { // Merge short records only when requested. if (isRecordLBrace(*Line.Last) && - Style.AllowShortRecordOnASingleLine < FormatStyle::SRS_Always) { + Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Always) { return 0; } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 0a6a88d91d0a2..1708b82f0f596 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -8632,19 +8632,6 @@ 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. @@ -15347,129 +15334,67 @@ TEST_F(FormatTest, NeverMergeShortRecords) { Style); } -TEST_F(FormatTest, AllowShortRecordOnASingleLineNonSplit) { +TEST_F(FormatTest, AllowShortRecordOnASingleLine) { auto Style = getLLVMStyle(); - - Style.BreakBeforeBraces = FormatStyle::BS_Custom; - Style.BraceWrapping.SplitEmptyRecord = false; + EXPECT_EQ(Style.AllowShortRecordOnASingleLine, + FormatStyle::SRS_EmptyIfAttached); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; verifyFormat("class foo {\n" - " void bar();\n" - "};", - Style); - verifyFormat("class foo {\n" - "};", - Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; - verifyFormat("class foo {\n" - " void bar();\n" - "};", - Style); - verifyFormat("class foo {};", Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; - verifyFormat("class foo {\n" - " void bar();\n" + "};\n" + "class bar {\n" + " int i;\n" "};", Style); - verifyFormat("class foo {};", Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; - verifyFormat("class foo { void bar(); };", Style); - verifyFormat("class foo {};", Style); - + Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterClass = true; - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; verifyFormat("class foo\n" "{\n" - " void bar();\n" - "};", - Style); - verifyFormat("class foo\n{};", Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; - verifyFormat("class foo\n" + "};\n" + "class bar\n" "{\n" - " void bar();\n" + " int i;\n" "};", Style); - verifyFormat("class foo\n{};", Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; + Style.BraceWrapping.SplitEmptyRecord = false; verifyFormat("class foo\n" - "{\n" - " void bar();\n" - "};", - Style); - verifyFormat("class foo {};", Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; - verifyFormat("class foo { void bar(); };", Style); - verifyFormat("class foo {};", Style); -} - -TEST_F(FormatTest, AllowShortRecordOnASingleLineSplit) { - auto Style = getLLVMStyle(); - - EXPECT_EQ(Style.BraceWrapping.SplitEmptyRecord, true); - - EXPECT_EQ(Style.AllowShortRecordOnASingleLine, - FormatStyle::SRS_EmptyIfAttached); - verifyFormat("class foo {\n" - " void bar();\n" - "};", - Style); - verifyFormat("class foo {};", Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; - verifyFormat("class foo {\n" - " void bar();\n" - "};", - Style); - verifyFormat("class foo {\n" - "};", + "{};", Style); + Style = getLLVMStyle(); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; - verifyFormat("class foo {\n" - " void bar();\n" + verifyFormat("class foo {};\n" + "class bar {\n" + " int i;\n" "};", Style); - verifyFormat("class foo {};", Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; - verifyFormat("class foo { void bar(); };", Style); - verifyFormat("class foo {};", Style); - Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterClass = true; - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; - verifyFormat("class foo\n" - "{\n" - "}", - Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; verifyFormat("class foo\n" "{\n" - "}", - Style); - - Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; - verifyFormat("class foo\n" + "};\n" + "class bar\n" "{\n" - "}", + " int i;\n" + "};", Style); + Style.BraceWrapping.SplitEmptyRecord = false; + verifyFormat("class foo {};", Style); + Style = getLLVMStyle(); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; + verifyFormat("class foo {};\n" + "class bar { int i; };", + Style); + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterClass = true; verifyFormat("class foo\n" "{\n" - "}", + "};\n" + "class bar { int i; };", Style); + Style.BraceWrapping.SplitEmptyRecord = false; + verifyFormat("class foo {};", Style); } TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { From 03ca00635f09bc06d4a97877c7130f62acba5660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Fri, 5 Sep 2025 17:02:45 +0200 Subject: [PATCH 13/25] Update release notes, fixup UnwrappedLineFormatter --- clang/docs/ReleaseNotes.rst | 4 ++++ clang/lib/Format/UnwrappedLineFormatter.cpp | 16 +++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 977396e249622..28866cbb2b694 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -543,7 +543,11 @@ clang-format - Add ``NumericLiteralCase`` option for enforcing character case in numeric literals. - Add ``Leave`` suboption to ``IndentPPDirectives``. +<<<<<<< HEAD - Add ``AllowBreakBeforeQtProperty`` option. +======= +- Add ``AllowShortRecordOnASingleLine`` option and set it to ``EmptyIfAttached`` for LLVM style. +>>>>>>> 47fe17fb9e64 (Update release notes, fixup UnwrappedLineFormatter) libclang -------- diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 198adb13829b8..4f2e0282beb51 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -500,7 +500,7 @@ class LineJoiner { : 0; } - const bool TryMergeShortRecord = [&]() { + auto TryMergeShortRecord = [&]() { switch (Style.AllowShortRecordOnASingleLine) { case FormatStyle::SRS_Never: return false; @@ -510,7 +510,7 @@ class LineJoiner { case FormatStyle::SRS_Always: return true; } - }() && !Style.BraceWrapping.SplitEmptyRecord; + }; if (TheLine->Last->is(tok::l_brace)) { bool ShouldMerge = false; @@ -525,7 +525,9 @@ 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 || TryMergeShortRecord; + ShouldMerge = + !Style.BraceWrapping.AfterClass || + (TryMergeShortRecord() && !Style.BraceWrapping.SplitEmptyRecord); } } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, @@ -959,12 +961,12 @@ class LineJoiner { Limit -= 2; unsigned MergedLines = 0; - bool TryMergeBlock = + const bool TryMergeBlock = Style.AllowShortBlocksOnASingleLine != FormatStyle::SBS_Never; - bool TryMergeRecord = + const 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); + const 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); From 0fa29c1d4b8cf75582a8d07e2c1fe65145817edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sun, 7 Sep 2025 13:21:26 +0200 Subject: [PATCH 14/25] Fixup FormatStyle::operator==, misc UnwrappedLineFormatter --- clang/include/clang/Format/Format.h | 1 + clang/lib/Format/UnwrappedLineFormatter.cpp | 26 +++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index dae125f7f72e7..4d3f416975942 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -5511,6 +5511,7 @@ struct FormatStyle { AllowShortLoopsOnASingleLine == R.AllowShortLoopsOnASingleLine && AllowShortNamespacesOnASingleLine == R.AllowShortNamespacesOnASingleLine && + AllowShortRecordOnASingleLine == R.AllowShortRecordOnASingleLine && AlwaysBreakBeforeMultilineStrings == R.AlwaysBreakBeforeMultilineStrings && AttributeMacros == R.AttributeMacros && diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 4f2e0282beb51..65dfd56609c39 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -500,7 +500,7 @@ class LineJoiner { : 0; } - auto TryMergeShortRecord = [&]() { + auto TryMergeShortRecord = [&] { switch (Style.AllowShortRecordOnASingleLine) { case FormatStyle::SRS_Never: return false; @@ -527,7 +527,7 @@ class LineJoiner { // correctly elsewhere. ShouldMerge = !Style.BraceWrapping.AfterClass || - (TryMergeShortRecord() && !Style.BraceWrapping.SplitEmptyRecord); + (!Style.BraceWrapping.SplitEmptyRecord && TryMergeShortRecord()); } } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, @@ -904,7 +904,11 @@ class LineJoiner { } else if (Limit != 0 && !Line.startsWithNamespace() && !startsExternCBlock(Line)) { // Merge short records only when requested. - if (isRecordLBrace(*Line.Last) && + if (Line.Last->isOneOf(TT_EnumLBrace, TT_RecordLBrace)) + return 0; + + if (Line.Last->isOneOf(TT_ClassLBrace, TT_StructLBrace, + TT_UnionLBrace) && Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Always) { return 0; } @@ -961,14 +965,16 @@ class LineJoiner { Limit -= 2; unsigned MergedLines = 0; - const bool TryMergeBlock = - Style.AllowShortBlocksOnASingleLine != FormatStyle::SBS_Never; - const bool TryMergeRecord = - Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always; - const bool NextIsEmptyBlock = I[1]->First == I[1]->Last && I + 2 != E && - I[2]->First->is(tok::r_brace); + auto TryMergeBlock = [&] { + if (Style.AllowShortBlocksOnASingleLine != FormatStyle::SBS_Never || + Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always) { + return true; + } + return I[1]->First == I[1]->Last && I + 2 != E && + I[2]->First->is(tok::r_brace); + }; - if (TryMergeBlock || TryMergeRecord || NextIsEmptyBlock) { + if (TryMergeBlock()) { MergedLines = tryMergeSimpleBlock(I + 1, E, Limit); // If we managed to merge the block, count the statement header, which // is on a separate line. From 86073c92cf4a96c51cd72c55c47da41325b42899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Mon, 8 Sep 2025 01:28:12 +0200 Subject: [PATCH 15/25] Fix interaction between AllowShortRecord and AllowShortBlocks options --- clang/lib/Format/UnwrappedLineFormatter.cpp | 10 +++++++--- clang/unittests/Format/FormatTest.cpp | 3 +++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 65dfd56609c39..89c94895e48d9 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -270,7 +270,8 @@ class LineJoiner { if (NextLine.First->isOneOf(TT_ClassLBrace, TT_StructLBrace, TT_UnionLBrace) && NextLine.First == NextLine.Last && I + 2 != E && - !I[2]->First->is(tok::r_brace)) { + !I[2]->First->is(tok::r_brace) && + Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never) { if (unsigned MergedLines = tryMergeSimpleBlock(I, E, Limit)) return MergedLines; } @@ -279,7 +280,7 @@ class LineJoiner { // Handle empty record blocks where the brace has already been wrapped. if (PreviousLine && TheLine->Last->is(tok::l_brace) && TheLine->First == TheLine->Last) { - bool EmptyBlock = NextLine.First->is(tok::r_brace); + const bool EmptyBlock = NextLine.First->is(tok::r_brace); const FormatToken *Tok = PreviousLine->First; if (Tok && Tok->is(tok::comment)) @@ -295,7 +296,9 @@ 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 (EmptyBlock && !Style.BraceWrapping.SplitEmptyRecord) || + (!EmptyBlock && Style.AllowShortBlocksOnASingleLine == + FormatStyle::SBS_Always) ? tryMergeSimpleBlock(I, E, Limit) : 0; } @@ -909,6 +912,7 @@ class LineJoiner { if (Line.Last->isOneOf(TT_ClassLBrace, TT_StructLBrace, TT_UnionLBrace) && + Line.Last != Line.First && Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Always) { return 0; } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 1708b82f0f596..e340ec7539c38 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15360,6 +15360,9 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLine) { verifyFormat("class foo\n" "{};", Style); + Style.BraceWrapping.SplitEmptyRecord = true; + Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always; + verifyFormat("class foo\n{ int i; };", Style); Style = getLLVMStyle(); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; From d35768aa93e2c14c3b1bbded4e10cf5b4cf3e686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Mon, 8 Sep 2025 10:27:25 +0200 Subject: [PATCH 16/25] Fix incorrect merge check --- clang/lib/Format/UnwrappedLineFormatter.cpp | 7 +++---- clang/unittests/Format/FormatTest.cpp | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 89c94895e48d9..9ea375566f14a 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -271,7 +271,7 @@ class LineJoiner { TT_UnionLBrace) && NextLine.First == NextLine.Last && I + 2 != E && !I[2]->First->is(tok::r_brace) && - Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never) { + Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always) { if (unsigned MergedLines = tryMergeSimpleBlock(I, E, Limit)) return MergedLines; } @@ -282,9 +282,7 @@ class LineJoiner { TheLine->First == TheLine->Last) { const bool EmptyBlock = NextLine.First->is(tok::r_brace); - const FormatToken *Tok = PreviousLine->First; - if (Tok && Tok->is(tok::comment)) - Tok = Tok->getNextNonComment(); + const FormatToken *Tok = PreviousLine->getFirstNonComment(); if (Tok && Tok->getNamespaceToken()) { return !Style.BraceWrapping.SplitEmptyNamespace && EmptyBlock @@ -294,6 +292,7 @@ class LineJoiner { if (Tok && Tok->is(tok::kw_typedef)) Tok = Tok->getNextNonComment(); + if (Tok && Tok->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_union, tok::kw_extern, Keywords.kw_interface)) { return (EmptyBlock && !Style.BraceWrapping.SplitEmptyRecord) || diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index e340ec7539c38..c660107821de6 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15360,9 +15360,6 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLine) { verifyFormat("class foo\n" "{};", Style); - Style.BraceWrapping.SplitEmptyRecord = true; - Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always; - verifyFormat("class foo\n{ int i; };", Style); Style = getLLVMStyle(); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; @@ -15398,6 +15395,22 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLine) { Style); Style.BraceWrapping.SplitEmptyRecord = false; verifyFormat("class foo {};", Style); + + Style = getLLVMStyle(); + Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always; + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterClass = true; + + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Never; + verifyFormat("class foo\n" + "{ int i; };", + Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Empty; + verifyFormat("class foo\n" + "{ int i; };", + Style); + Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; + verifyFormat("class foo { int i; };", Style); } TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { From 5892208acc210a6fa772a988d2b1436b90720128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Mon, 8 Sep 2025 23:05:13 +0200 Subject: [PATCH 17/25] Add const, change is to isNot --- clang/lib/Format/UnwrappedLineFormatter.cpp | 2 +- clang/lib/Format/UnwrappedLineParser.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 9ea375566f14a..2ed2c874a649b 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -270,7 +270,7 @@ class LineJoiner { if (NextLine.First->isOneOf(TT_ClassLBrace, TT_StructLBrace, TT_UnionLBrace) && NextLine.First == NextLine.Last && I + 2 != E && - !I[2]->First->is(tok::r_brace) && + I[2]->First->isNot(tok::r_brace) && Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always) { if (unsigned MergedLines = tryMergeSimpleBlock(I, E, Limit)) return MergedLines; diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 540c1903b6800..0f0023c243bd2 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -954,7 +954,7 @@ static bool ShouldBreakBeforeBrace(const FormatStyle &Style, if (InitialToken.is(TT_NamespaceMacro)) Kind = tok::kw_namespace; - bool WrapRecordAllowed = + const bool WrapRecordAllowed = !IsEmptyBlock || Style.AllowShortRecordOnASingleLine < FormatStyle::SRS_Empty || Style.BraceWrapping.SplitEmptyRecord; From 73cdd988d49f9e3ed19eb8cc598d47f320c2a1a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Mon, 15 Sep 2025 17:44:12 +0200 Subject: [PATCH 18/25] Extract record merging into a separate function --- clang/lib/Format/UnwrappedLineFormatter.cpp | 111 ++++++++++++++------ 1 file changed, 79 insertions(+), 32 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 2ed2c874a649b..25480cb387517 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -266,18 +266,17 @@ class LineJoiner { } } - // Try merging record blocks that have had their left brace wrapped. + // Try merging record blocks that have had their left brace wrapped into + // a single line. if (NextLine.First->isOneOf(TT_ClassLBrace, TT_StructLBrace, - TT_UnionLBrace) && - NextLine.First == NextLine.Last && I + 2 != E && - I[2]->First->isNot(tok::r_brace) && - Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always) { - if (unsigned MergedLines = tryMergeSimpleBlock(I, E, Limit)) + TT_UnionLBrace)) { + if (unsigned MergedLines = tryMergeRecord(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. + + // Handle blocks where the brace has already been wrapped. if (PreviousLine && TheLine->Last->is(tok::l_brace) && TheLine->First == TheLine->Last) { const bool EmptyBlock = NextLine.First->is(tok::r_brace); @@ -293,11 +292,11 @@ class LineJoiner { if (Tok && Tok->is(tok::kw_typedef)) Tok = Tok->getNextNonComment(); - if (Tok && Tok->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_union, - tok::kw_extern, Keywords.kw_interface)) { - return (EmptyBlock && !Style.BraceWrapping.SplitEmptyRecord) || - (!EmptyBlock && Style.AllowShortBlocksOnASingleLine == - FormatStyle::SBS_Always) + if (Tok && Tok->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_union)) + return tryMergeRecord(I, E, Limit); + + if (Tok && Tok->isOneOf(tok::kw_extern, Keywords.kw_interface)) { + return !Style.BraceWrapping.SplitEmptyRecord && EmptyBlock ? tryMergeSimpleBlock(I, E, Limit) : 0; } @@ -502,18 +501,6 @@ class LineJoiner { : 0; } - auto TryMergeShortRecord = [&] { - switch (Style.AllowShortRecordOnASingleLine) { - case FormatStyle::SRS_Never: - return false; - case FormatStyle::SRS_EmptyIfAttached: - case FormatStyle::SRS_Empty: - return NextLine.First->is(tok::r_brace); - case FormatStyle::SRS_Always: - return true; - } - }; - if (TheLine->Last->is(tok::l_brace)) { bool ShouldMerge = false; // Try to merge records. @@ -523,14 +510,7 @@ class LineJoiner { ShouldMerge = Style.AllowShortCompoundRequirementOnASingleLine; } 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 || - (!Style.BraceWrapping.SplitEmptyRecord && TryMergeShortRecord()); - } + return tryMergeRecord(I, E, Limit); } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, tok::kw_struct)) { @@ -600,6 +580,73 @@ class LineJoiner { return 0; } + unsigned tryMergeRecord(ArrayRef::const_iterator I, + ArrayRef::const_iterator E, + unsigned Limit) { + const auto *Line = I[0]; + const auto *NextLine = I[1]; + + auto GetRelevantAfterOption = [&](const FormatToken *tok) { + switch (tok->getType()) { + case TT_StructLBrace: + return Style.BraceWrapping.AfterStruct; + case TT_ClassLBrace: + return Style.BraceWrapping.AfterClass; + case TT_UnionLBrace: + return Style.BraceWrapping.AfterUnion; + }; + }; + + // Current line begins both record and block, brace was not wrapped. + if (Line->Last->isOneOf(TT_StructLBrace, TT_ClassLBrace, TT_UnionLBrace)) { + auto TryMergeShortRecord = [&] { + switch (Style.AllowShortRecordOnASingleLine) { + case FormatStyle::SRS_EmptyIfAttached: + case FormatStyle::SRS_Empty: + return NextLine->First->is(tok::r_brace); + case FormatStyle::SRS_Always: + return true; + } + }; + + if (Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never && + (!GetRelevantAfterOption(Line->Last) || + (!Style.BraceWrapping.SplitEmptyRecord && TryMergeShortRecord()))) { + return tryMergeSimpleBlock(I, E, Limit); + } + } + + // Cases where the l_brace was wrapped. + // Current line begins record, next line block. + if (NextLine->First->isOneOf(TT_StructLBrace, TT_ClassLBrace, + TT_UnionLBrace)) { + if (NextLine->First == NextLine->Last && I + 2 != E && + I[2]->First->isNot(tok::r_brace) && + Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always) { + return tryMergeSimpleBlock(I, E, Limit); + } + } + + if (I == AnnotatedLines.begin()) + return 0; + + const auto *PreviousLine = I[-1]; + + // Previous line begins record, current line block. + if (PreviousLine->First->isOneOf(tok::kw_struct, tok::kw_class, + tok::kw_union)) { + const bool IsEmptyBlock = + Line->Last->is(tok::l_brace) && NextLine->First->is(tok::r_brace); + + if (IsEmptyBlock && !Style.BraceWrapping.SplitEmptyRecord || + Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Always) { + return tryMergeSimpleBlock(I, E, Limit); + } + } + + return 0; + } + unsigned tryMergeSimplePPDirective(ArrayRef::const_iterator I, ArrayRef::const_iterator E, From b2b5f913cb5a59c8994b58dd1e4b36f69d056ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Mon, 15 Sep 2025 18:48:00 +0200 Subject: [PATCH 19/25] Minor fixes for tryMergeRecord --- clang/lib/Format/UnwrappedLineFormatter.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 25480cb387517..5fc0fe29e4742 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -10,6 +10,7 @@ #include "FormatToken.h" #include "NamespaceEndCommentsFixer.h" #include "WhitespaceManager.h" +#include "clang/Basic/TokenKinds.h" #include "llvm/Support/Debug.h" #include @@ -586,8 +587,8 @@ class LineJoiner { const auto *Line = I[0]; const auto *NextLine = I[1]; - auto GetRelevantAfterOption = [&](const FormatToken *tok) { - switch (tok->getType()) { + auto GetRelevantAfterOption = [&](const FormatToken *Tok) { + switch (Tok->getType()) { case TT_StructLBrace: return Style.BraceWrapping.AfterStruct; case TT_ClassLBrace: @@ -620,8 +621,12 @@ class LineJoiner { // Current line begins record, next line block. if (NextLine->First->isOneOf(TT_StructLBrace, TT_ClassLBrace, TT_UnionLBrace)) { - if (NextLine->First == NextLine->Last && I + 2 != E && - I[2]->First->isNot(tok::r_brace) && + if (I + 2 == E) + return 0; + + const bool IsEmptyBlock = I[2]->First->is(tok::r_brace); + + if (!IsEmptyBlock && Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always) { return tryMergeSimpleBlock(I, E, Limit); } From 1b4f84fbc724cfbb415219559db75c13e869ff4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Thu, 18 Sep 2025 21:32:35 +0200 Subject: [PATCH 20/25] Fix -Wswitch and -Wlogical-op-parentheses errors --- clang/lib/Format/UnwrappedLineFormatter.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 5fc0fe29e4742..6b1b8711becc8 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -595,6 +595,8 @@ class LineJoiner { return Style.BraceWrapping.AfterClass; case TT_UnionLBrace: return Style.BraceWrapping.AfterUnion; + default: + return false; }; }; @@ -602,6 +604,8 @@ class LineJoiner { if (Line->Last->isOneOf(TT_StructLBrace, TT_ClassLBrace, TT_UnionLBrace)) { auto TryMergeShortRecord = [&] { switch (Style.AllowShortRecordOnASingleLine) { + case FormatStyle::SRS_Never: + return false; case FormatStyle::SRS_EmptyIfAttached: case FormatStyle::SRS_Empty: return NextLine->First->is(tok::r_brace); @@ -643,7 +647,7 @@ class LineJoiner { const bool IsEmptyBlock = Line->Last->is(tok::l_brace) && NextLine->First->is(tok::r_brace); - if (IsEmptyBlock && !Style.BraceWrapping.SplitEmptyRecord || + if ((IsEmptyBlock && !Style.BraceWrapping.SplitEmptyRecord) || Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Always) { return tryMergeSimpleBlock(I, E, Limit); } From 1346788ea6af42b33e067cfe848235ef0df68661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Fri, 19 Sep 2025 16:52:11 +0200 Subject: [PATCH 21/25] Fixup comments --- clang/lib/Format/Format.cpp | 2 +- clang/lib/Format/TokenAnnotator.cpp | 13 ++++++------- clang/lib/Format/UnwrappedLineFormatter.cpp | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 6bbc7e234a7fb..8eeb0ee51e413 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1589,9 +1589,9 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All; - LLVMStyle.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; LLVMStyle.AllowShortLoopsOnASingleLine = false; LLVMStyle.AllowShortNamespacesOnASingleLine = false; + LLVMStyle.AllowShortRecordOnASingleLine = FormatStyle::SRS_EmptyIfAttached; LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; LLVMStyle.AlwaysBreakBeforeMultilineStrings = false; LLVMStyle.AttributeMacros.push_back("__capability"); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 14a0aa77dda37..cb79f72e76d7e 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5962,13 +5962,12 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // Don't attempt to interpret record return types as records. // FIXME: Not covered by tests. if (Right.isNot(TT_FunctionLBrace)) { - 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.AllowShortRecordOnASingleLine == FormatStyle::SRS_Never; + return Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Never && + (Line.startsWith(tok::kw_class) && + Style.BraceWrapping.AfterClass) || + (Line.startsWith(tok::kw_struct) && + Style.BraceWrapping.AfterStruct) || + (Line.startsWith(tok::kw_union) && Style.BraceWrapping.AfterUnion); } } diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 6b1b8711becc8..f56ffabaa3d6c 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -10,7 +10,6 @@ #include "FormatToken.h" #include "NamespaceEndCommentsFixer.h" #include "WhitespaceManager.h" -#include "clang/Basic/TokenKinds.h" #include "llvm/Support/Debug.h" #include From f464367559667a870c3471d5c97b8a56f5366743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Fri, 19 Sep 2025 16:59:52 +0200 Subject: [PATCH 22/25] Fix missing empty block check --- clang/lib/Format/UnwrappedLineFormatter.cpp | 3 ++- clang/unittests/Format/FormatTest.cpp | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index f56ffabaa3d6c..e77c3c6c17a4e 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -647,7 +647,8 @@ class LineJoiner { Line->Last->is(tok::l_brace) && NextLine->First->is(tok::r_brace); if ((IsEmptyBlock && !Style.BraceWrapping.SplitEmptyRecord) || - Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Always) { + (!IsEmptyBlock && + Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Always)) { return tryMergeSimpleBlock(I, E, Limit); } } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index c660107821de6..69fcb8db7f906 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -15410,7 +15410,11 @@ TEST_F(FormatTest, AllowShortRecordOnASingleLine) { "{ int i; };", Style); Style.AllowShortRecordOnASingleLine = FormatStyle::SRS_Always; - verifyFormat("class foo { int i; };", Style); + verifyFormat("class foo\n" + "{\n" + "};\n" + "class foo { int i; };", + Style); } TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) { From a75325ba07a1624074c01d24ced53fe5fe11ef34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sat, 20 Sep 2025 03:36:53 +0200 Subject: [PATCH 23/25] Fix linux build failing --- clang/lib/Format/TokenAnnotator.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index cb79f72e76d7e..adc3115f66e35 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5963,11 +5963,12 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // FIXME: Not covered by tests. if (Right.isNot(TT_FunctionLBrace)) { return Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Never && - (Line.startsWith(tok::kw_class) && - Style.BraceWrapping.AfterClass) || - (Line.startsWith(tok::kw_struct) && - Style.BraceWrapping.AfterStruct) || - (Line.startsWith(tok::kw_union) && Style.BraceWrapping.AfterUnion); + ((Line.startsWith(tok::kw_class) && + Style.BraceWrapping.AfterClass) || + (Line.startsWith(tok::kw_struct) && + Style.BraceWrapping.AfterStruct) || + (Line.startsWith(tok::kw_union) && + Style.BraceWrapping.AfterUnion)); } } From 0e5f5898e5bdb7ef87f34b216dfd689fba1d4b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sun, 21 Sep 2025 13:49:33 +0200 Subject: [PATCH 24/25] Comment fixups --- clang/lib/Format/UnwrappedLineFormatter.cpp | 58 +++++++++------------ 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index e77c3c6c17a4e..9fb52455c4306 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -10,6 +10,7 @@ #include "FormatToken.h" #include "NamespaceEndCommentsFixer.h" #include "WhitespaceManager.h" +#include "clang/Basic/TokenKinds.h" #include "llvm/Support/Debug.h" #include @@ -513,7 +514,7 @@ class LineJoiner { return tryMergeRecord(I, E, Limit); } else if (TheLine->InPPDirective || !TheLine->First->isOneOf(tok::kw_class, tok::kw_enum, - tok::kw_struct)) { + tok::kw_struct, tok::kw_union)) { // Try to merge a block with left brace unwrapped that wasn't yet // covered. ShouldMerge = !Style.BraceWrapping.AfterFunction || @@ -586,35 +587,34 @@ class LineJoiner { const auto *Line = I[0]; const auto *NextLine = I[1]; - auto GetRelevantAfterOption = [&](const FormatToken *Tok) { - switch (Tok->getType()) { - case TT_StructLBrace: - return Style.BraceWrapping.AfterStruct; - case TT_ClassLBrace: - return Style.BraceWrapping.AfterClass; - case TT_UnionLBrace: - return Style.BraceWrapping.AfterUnion; - default: - return false; - }; - }; - // Current line begins both record and block, brace was not wrapped. if (Line->Last->isOneOf(TT_StructLBrace, TT_ClassLBrace, TT_UnionLBrace)) { + auto ShouldWrapLBrace = [&](TokenType LBraceType) { + switch (LBraceType) { + case TT_StructLBrace: + return Style.BraceWrapping.AfterStruct; + case TT_ClassLBrace: + return Style.BraceWrapping.AfterClass; + case TT_UnionLBrace: + return Style.BraceWrapping.AfterUnion; + default: + return false; + }; + }; + auto TryMergeShortRecord = [&] { switch (Style.AllowShortRecordOnASingleLine) { case FormatStyle::SRS_Never: return false; - case FormatStyle::SRS_EmptyIfAttached: - case FormatStyle::SRS_Empty: - return NextLine->First->is(tok::r_brace); case FormatStyle::SRS_Always: return true; + default: + return NextLine->First->is(tok::r_brace); } }; if (Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Never && - (!GetRelevantAfterOption(Line->Last) || + (!ShouldWrapLBrace(Line->Last->getType()) || (!Style.BraceWrapping.SplitEmptyRecord && TryMergeShortRecord()))) { return tryMergeSimpleBlock(I, E, Limit); } @@ -622,27 +622,19 @@ class LineJoiner { // Cases where the l_brace was wrapped. // Current line begins record, next line block. - if (NextLine->First->isOneOf(TT_StructLBrace, TT_ClassLBrace, + if (NextLine->First->isOneOf(TT_ClassLBrace, TT_StructLBrace, TT_UnionLBrace)) { - if (I + 2 == E) + if (I + 2 == E || I[2]->First->is(tok::r_brace) || + Style.AllowShortRecordOnASingleLine != FormatStyle::SRS_Always) { return 0; - - const bool IsEmptyBlock = I[2]->First->is(tok::r_brace); - - if (!IsEmptyBlock && - Style.AllowShortRecordOnASingleLine == FormatStyle::SRS_Always) { - return tryMergeSimpleBlock(I, E, Limit); } - } - - if (I == AnnotatedLines.begin()) - return 0; - const auto *PreviousLine = I[-1]; + return tryMergeSimpleBlock(I, E, Limit); + } // Previous line begins record, current line block. - if (PreviousLine->First->isOneOf(tok::kw_struct, tok::kw_class, - tok::kw_union)) { + if (I != AnnotatedLines.begin() && + I[-1]->First->isOneOf(tok::kw_struct, tok::kw_class, tok::kw_union)) { const bool IsEmptyBlock = Line->Last->is(tok::l_brace) && NextLine->First->is(tok::r_brace); From 471561508096bce29e548db6363769a33de3f3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= Date: Sun, 28 Sep 2025 01:05:31 +0200 Subject: [PATCH 25/25] Fix ReleaseNotes conflict --- clang/docs/ReleaseNotes.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 28866cbb2b694..5b44f9aa3d370 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -543,11 +543,8 @@ clang-format - Add ``NumericLiteralCase`` option for enforcing character case in numeric literals. - Add ``Leave`` suboption to ``IndentPPDirectives``. -<<<<<<< HEAD - Add ``AllowBreakBeforeQtProperty`` option. -======= - Add ``AllowShortRecordOnASingleLine`` option and set it to ``EmptyIfAttached`` for LLVM style. ->>>>>>> 47fe17fb9e64 (Update release notes, fixup UnwrappedLineFormatter) libclang --------