[clang] [clang-format] Add option AllowShortRecordOnASingleLine (PR #154580)
Tomáš Slanina via cfe-commits
cfe-commits at lists.llvm.org
Sat Sep 27 16:02:41 PDT 2025
https://github.com/itzexpoexpo updated https://github.com/llvm/llvm-project/pull/154580
>From 58f7f5ea100520ff0436f55ac432813d6fbabf35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoexpo at gmail.com>
Date: Wed, 20 Aug 2025 19:28:23 +0200
Subject: [PATCH 01/24] [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:`¶ <AllowShortNamespacesOnASingleLine>`
If ``true``, ``namespace a { class b; }`` can be put on a single line.
+.. _AllowShortRecordsOnASingleLine:
+
+**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`¶ <AllowShortRecordsOnASingleLine>`
+ Dependent on the value, ``struct bar { int i; }`` can be put on a single
+ line.
+
+ Possible values:
+
+ * ``SRS_Never`` (in configuration: ``Never``)
+ Never merge records into a single line.
+
+ * ``SRS_Empty`` (in configuration: ``Empty``)
+ Only merge empty records.
+
+ .. code-block:: c++
+
+ struct foo {};
+ struct bar
+ {
+ int i;
+ };
+
+ * ``SRS_All`` (in configuration: ``All``)
+ Merge all records that fit on a single line.
+
+ .. code-block:: c++
+
+ struct foo {};
+ struct bar { int i; };
+
+
+
.. _AlwaysBreakAfterDefinitionReturnType:
**AlwaysBreakAfterDefinitionReturnType** (``DefinitionReturnTypeBreakingStyle``) :versionbadge:`clang-format 3.7` :ref:`¶ <AlwaysBreakAfterDefinitionReturnType>`
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 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<FormatStyle::ShortLambdaStyle> {
}
};
+template <> struct ScalarEnumerationTraits<FormatStyle::ShortRecordStyle> {
+ static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) {
+ IO.enumCase(Value, "Never", FormatStyle::SRS_Never);
+ IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty);
+ IO.enumCase(Value, "All", FormatStyle::SRS_All);
+ }
+};
+
template <> struct MappingTraits<FormatStyle::SortIncludesOptions> {
static void enumInput(IO &IO, FormatStyle::SortIncludesOptions &Value) {
IO.enumCase(Value, "Never", FormatStyle::SortIncludesOptions({}));
@@ -1046,6 +1054,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.AllowShortIfStatementsOnASingleLine);
IO.mapOptional("AllowShortLambdasOnASingleLine",
Style.AllowShortLambdasOnASingleLine);
+ IO.mapOptional("AllowShortRecordsOnASingleLine",
+ Style.AllowShortRecordsOnASingleLine);
IO.mapOptional("AllowShortLoopsOnASingleLine",
Style.AllowShortLoopsOnASingleLine);
IO.mapOptional("AllowShortNamespacesOnASingleLine",
@@ -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?= <itzexpoexpo at gmail.com>
Date: Wed, 20 Aug 2025 23:54:07 +0200
Subject: [PATCH 02/24] 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<FormatStyle> {
Style.AllowShortIfStatementsOnASingleLine);
IO.mapOptional("AllowShortLambdasOnASingleLine",
Style.AllowShortLambdasOnASingleLine);
- IO.mapOptional("AllowShortRecordsOnASingleLine",
- Style.AllowShortRecordsOnASingleLine);
IO.mapOptional("AllowShortLoopsOnASingleLine",
Style.AllowShortLoopsOnASingleLine);
IO.mapOptional("AllowShortNamespacesOnASingleLine",
Style.AllowShortNamespacesOnASingleLine);
+ IO.mapOptional("AllowShortRecordsOnASingleLine",
+ Style.AllowShortRecordsOnASingleLine);
IO.mapOptional("AlwaysBreakAfterDefinitionReturnType",
Style.AlwaysBreakAfterDefinitionReturnType);
IO.mapOptional("AlwaysBreakBeforeMultilineStrings",
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 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?= <itzexpoexpo at gmail.com>
Date: Sun, 24 Aug 2025 15:24:59 +0200
Subject: [PATCH 03/24] 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<FormatStyle::ShortRecordStyle> {
static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::SRS_Never);
IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty);
- IO.enumCase(Value, "All", FormatStyle::SRS_All);
+ IO.enumCase(Value, "Always", FormatStyle::SRS_Always);
}
};
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 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?= <itzexpoexpo at gmail.com>
Date: Sun, 24 Aug 2025 15:54:21 +0200
Subject: [PATCH 04/24] 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<FormatStyle> {
Style.AllowShortLoopsOnASingleLine);
IO.mapOptional("AllowShortNamespacesOnASingleLine",
Style.AllowShortNamespacesOnASingleLine);
- IO.mapOptional("AllowShortRecordsOnASingleLine",
- Style.AllowShortRecordsOnASingleLine);
+ IO.mapOptional("AllowShortRecordOnASingleLine",
+ Style.AllowShortRecordOnASingleLine);
IO.mapOptional("AlwaysBreakAfterDefinitionReturnType",
Style.AlwaysBreakAfterDefinitionReturnType);
IO.mapOptional("AlwaysBreakBeforeMultilineStrings",
@@ -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?= <itzexpoexpo at gmail.com>
Date: Sun, 24 Aug 2025 15:56:58 +0200
Subject: [PATCH 05/24] 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:`¶ <AllowShortNamespacesOnASingleLine>`
If ``true``, ``namespace a { class b; }`` can be put on a single line.
-.. _AllowShortRecordsOnASingleLine:
+.. _AllowShortRecordOnASingleLine:
-**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`¶ <AllowShortRecordsOnASingleLine>`
+**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :ref:`¶ <AllowShortRecordOnASingleLine>`
Dependent on the value, ``struct bar { int i; }`` can be put on a single
line.
@@ -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?= <itzexpoexpo at gmail.com>
Date: Mon, 25 Aug 2025 00:38:53 +0200
Subject: [PATCH 06/24] 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>`
+**AllowShortRecordOnASingleLine** (``ShortRecordStyle``) :versionbadge:`clang-format 22` :ref:`¶ <AllowShortRecordOnASingleLine>`
Dependent on the value, ``struct bar { int i; }`` can be put on a single
line.
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 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?= <itzexpoexpo at gmail.com>
Date: Tue, 26 Aug 2025 23:54:53 +0200
Subject: [PATCH 07/24] 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:`¶ <AllowShortRecordOnASingleLine>`
- Dependent on the value, ``struct bar { int i; }`` can be put on a single
+ Dependent on the value, ``struct bar { int i; };`` can be put on a single
line.
Possible values:
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 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?= <itzexpoexpo at gmail.com>
Date: Sat, 30 Aug 2025 17:41:51 +0200
Subject: [PATCH 08/24] 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<FormatStyle::ShortLambdaStyle> {
template <> struct ScalarEnumerationTraits<FormatStyle::ShortRecordStyle> {
static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::SRS_Never);
+ IO.enumCase(Value, "EmptyIfAttached", FormatStyle::SRS_EmptyIfAttached);
IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty);
IO.enumCase(Value, "Always", FormatStyle::SRS_Always);
}
@@ -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?= <itzexpoexpo at gmail.com>
Date: Tue, 2 Sep 2025 21:11:56 +0200
Subject: [PATCH 09/24] 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?= <itzexpoexpo at gmail.com>
Date: Wed, 3 Sep 2025 17:49:31 +0200
Subject: [PATCH 10/24] 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?= <itzexpoexpo at gmail.com>
Date: Wed, 3 Sep 2025 17:59:08 +0200
Subject: [PATCH 11/24] 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?= <itzexpoexpo at gmail.com>
Date: Thu, 4 Sep 2025 16:08:04 +0200
Subject: [PATCH 12/24] 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?= <itzexpoexpo at gmail.com>
Date: Fri, 5 Sep 2025 17:02:45 +0200
Subject: [PATCH 13/24] 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?= <itzexpoexpo at gmail.com>
Date: Sun, 7 Sep 2025 13:21:26 +0200
Subject: [PATCH 14/24] 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?= <itzexpoexpo at gmail.com>
Date: Mon, 8 Sep 2025 01:28:12 +0200
Subject: [PATCH 15/24] 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?= <itzexpoexpo at gmail.com>
Date: Mon, 8 Sep 2025 10:27:25 +0200
Subject: [PATCH 16/24] 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?= <itzexpoexpo at gmail.com>
Date: Mon, 8 Sep 2025 23:05:13 +0200
Subject: [PATCH 17/24] 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?= <itzexpoexpo at gmail.com>
Date: Mon, 15 Sep 2025 17:44:12 +0200
Subject: [PATCH 18/24] 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<AnnotatedLine *>::const_iterator I,
+ ArrayRef<AnnotatedLine *>::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<AnnotatedLine *>::const_iterator I,
ArrayRef<AnnotatedLine *>::const_iterator E,
>From b2b5f913cb5a59c8994b58dd1e4b36f69d056ff3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoexpo at gmail.com>
Date: Mon, 15 Sep 2025 18:48:00 +0200
Subject: [PATCH 19/24] 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 <queue>
@@ -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?= <itzexpoexpo at gmail.com>
Date: Thu, 18 Sep 2025 21:32:35 +0200
Subject: [PATCH 20/24] 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?= <itzexpoexpo at gmail.com>
Date: Fri, 19 Sep 2025 16:52:11 +0200
Subject: [PATCH 21/24] 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 <queue>
>From f464367559667a870c3471d5c97b8a56f5366743 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Slanina?= <itzexpoexpo at gmail.com>
Date: Fri, 19 Sep 2025 16:59:52 +0200
Subject: [PATCH 22/24] 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?= <itzexpoexpo at gmail.com>
Date: Sat, 20 Sep 2025 03:36:53 +0200
Subject: [PATCH 23/24] 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?= <itzexpoexpo at gmail.com>
Date: Sun, 21 Sep 2025 13:49:33 +0200
Subject: [PATCH 24/24] 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 <queue>
@@ -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);
More information about the cfe-commits
mailing list