Skip to content

Commit 58f7f5e

Browse files
committed
[clang-format] Add option AllowShortRecordsOnASingleLine
This commit supersedes PR llvm#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; };
1 parent 378b6d5 commit 58f7f5e

File tree

8 files changed

+193
-15
lines changed

8 files changed

+193
-15
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,6 +2103,38 @@ the configuration (without a prefix: ``Auto``).
21032103
**AllowShortNamespacesOnASingleLine** (``Boolean``) :versionbadge:`clang-format 20` :ref:`<AllowShortNamespacesOnASingleLine>`
21042104
If ``true``, ``namespace a { class b; }`` can be put on a single line.
21052105

2106+
.. _AllowShortRecordsOnASingleLine:
2107+
2108+
**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`<AllowShortRecordsOnASingleLine>`
2109+
Dependent on the value, ``struct bar { int i; }`` can be put on a single
2110+
line.
2111+
2112+
Possible values:
2113+
2114+
* ``SRS_Never`` (in configuration: ``Never``)
2115+
Never merge records into a single line.
2116+
2117+
* ``SRS_Empty`` (in configuration: ``Empty``)
2118+
Only merge empty records.
2119+
2120+
.. code-block:: c++
2121+
2122+
struct foo {};
2123+
struct bar
2124+
{
2125+
int i;
2126+
};
2127+
2128+
* ``SRS_All`` (in configuration: ``All``)
2129+
Merge all records that fit on a single line.
2130+
2131+
.. code-block:: c++
2132+
2133+
struct foo {};
2134+
struct bar { int i; };
2135+
2136+
2137+
21062138
.. _AlwaysBreakAfterDefinitionReturnType:
21072139

21082140
**AlwaysBreakAfterDefinitionReturnType** (``DefinitionReturnTypeBreakingStyle``) :versionbadge:`clang-format 3.7` :ref:`<AlwaysBreakAfterDefinitionReturnType>`

clang/include/clang/Format/Format.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,32 @@ struct FormatStyle {
998998
/// \version 20
999999
bool AllowShortNamespacesOnASingleLine;
10001000

1001+
/// Different styles for merging short records
1002+
/// (``class``,``struct``,``union``).
1003+
enum ShortRecordStyle : int8_t {
1004+
/// Never merge records into a single line.
1005+
SRS_Never,
1006+
/// Only merge empty records.
1007+
/// \code
1008+
/// struct foo {};
1009+
/// struct bar
1010+
/// {
1011+
/// int i;
1012+
/// };
1013+
/// \endcode
1014+
SRS_Empty,
1015+
/// Merge all records that fit on a single line.
1016+
/// \code
1017+
/// struct foo {};
1018+
/// struct bar { int i; };
1019+
/// \endcode
1020+
SRS_All
1021+
};
1022+
1023+
/// Dependent on the value, ``struct bar { int i; }`` can be put on a single
1024+
/// line.
1025+
ShortRecordStyle AllowShortRecordsOnASingleLine;
1026+
10011027
/// Different ways to break after the function definition return type.
10021028
/// This option is **deprecated** and is retained for backwards compatibility.
10031029
enum DefinitionReturnTypeBreakingStyle : int8_t {

clang/lib/Format/Format.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,14 @@ template <> struct ScalarEnumerationTraits<FormatStyle::ShortLambdaStyle> {
681681
}
682682
};
683683

684+
template <> struct ScalarEnumerationTraits<FormatStyle::ShortRecordStyle> {
685+
static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) {
686+
IO.enumCase(Value, "Never", FormatStyle::SRS_Never);
687+
IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty);
688+
IO.enumCase(Value, "All", FormatStyle::SRS_All);
689+
}
690+
};
691+
684692
template <> struct MappingTraits<FormatStyle::SortIncludesOptions> {
685693
static void enumInput(IO &IO, FormatStyle::SortIncludesOptions &Value) {
686694
IO.enumCase(Value, "Never", FormatStyle::SortIncludesOptions({}));
@@ -1046,6 +1054,8 @@ template <> struct MappingTraits<FormatStyle> {
10461054
Style.AllowShortIfStatementsOnASingleLine);
10471055
IO.mapOptional("AllowShortLambdasOnASingleLine",
10481056
Style.AllowShortLambdasOnASingleLine);
1057+
IO.mapOptional("AllowShortRecordsOnASingleLine",
1058+
Style.AllowShortRecordsOnASingleLine);
10491059
IO.mapOptional("AllowShortLoopsOnASingleLine",
10501060
Style.AllowShortLoopsOnASingleLine);
10511061
IO.mapOptional("AllowShortNamespacesOnASingleLine",
@@ -1578,6 +1588,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
15781588
LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All;
15791589
LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
15801590
LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All;
1591+
LLVMStyle.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never;
15811592
LLVMStyle.AllowShortLoopsOnASingleLine = false;
15821593
LLVMStyle.AllowShortNamespacesOnASingleLine = false;
15831594
LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None;

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5959,12 +5959,15 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
59595959
return true;
59605960
}
59615961

5962-
// Don't attempt to interpret struct return types as structs.
5962+
// Don't attempt to interpret record return types as records.
59635963
if (Right.isNot(TT_FunctionLBrace)) {
5964-
return (Line.startsWith(tok::kw_class) &&
5965-
Style.BraceWrapping.AfterClass) ||
5966-
(Line.startsWith(tok::kw_struct) &&
5967-
Style.BraceWrapping.AfterStruct);
5964+
return ((Line.startsWith(tok::kw_class) &&
5965+
Style.BraceWrapping.AfterClass) ||
5966+
(Line.startsWith(tok::kw_struct) &&
5967+
Style.BraceWrapping.AfterStruct) ||
5968+
(Line.startsWith(tok::kw_union) &&
5969+
Style.BraceWrapping.AfterUnion)) &&
5970+
Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never;
59685971
}
59695972
}
59705973

clang/lib/Format/UnwrappedLineFormatter.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,19 @@ class LineJoiner {
462462
}
463463
}
464464

465+
auto ShouldMergeShortRecords = [this, &I, &NextLine, PreviousLine,
466+
TheLine]() {
467+
if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_All)
468+
return true;
469+
if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Empty &&
470+
NextLine.First->is(tok::r_brace)) {
471+
return true;
472+
}
473+
return false;
474+
};
475+
476+
bool MergeShortRecord = ShouldMergeShortRecords();
477+
465478
// Don't merge an empty template class or struct if SplitEmptyRecords
466479
// is defined.
467480
if (PreviousLine && Style.BraceWrapping.SplitEmptyRecord &&
@@ -504,7 +517,8 @@ class LineJoiner {
504517
// elsewhere.
505518
ShouldMerge = !Style.BraceWrapping.AfterClass ||
506519
(NextLine.First->is(tok::r_brace) &&
507-
!Style.BraceWrapping.SplitEmptyRecord);
520+
!Style.BraceWrapping.SplitEmptyRecord) ||
521+
MergeShortRecord;
508522
} else if (TheLine->InPPDirective ||
509523
!TheLine->First->isOneOf(tok::kw_class, tok::kw_enum,
510524
tok::kw_struct)) {
@@ -879,9 +893,11 @@ class LineJoiner {
879893
return 1;
880894
} else if (Limit != 0 && !Line.startsWithNamespace() &&
881895
!startsExternCBlock(Line)) {
882-
// We don't merge short records.
883-
if (isRecordLBrace(*Line.Last))
896+
// Merge short records only when requested.
897+
if (isRecordLBrace(*Line.Last) &&
898+
Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never) {
884899
return 0;
900+
}
885901

886902
// Check that we still have three lines and they fit into the limit.
887903
if (I + 2 == E || I[2]->Type == LT_Invalid)

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -948,20 +948,26 @@ static bool isIIFE(const UnwrappedLine &Line,
948948
}
949949

950950
static bool ShouldBreakBeforeBrace(const FormatStyle &Style,
951-
const FormatToken &InitialToken) {
951+
const FormatToken &InitialToken,
952+
const FormatToken &NextToken) {
952953
tok::TokenKind Kind = InitialToken.Tok.getKind();
953954
if (InitialToken.is(TT_NamespaceMacro))
954955
Kind = tok::kw_namespace;
955956

957+
bool IsEmptyBlock = NextToken.is(tok::r_brace);
958+
bool WrapRecordAllowed =
959+
!(IsEmptyBlock &&
960+
Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_Never);
961+
956962
switch (Kind) {
957963
case tok::kw_namespace:
958964
return Style.BraceWrapping.AfterNamespace;
959965
case tok::kw_class:
960-
return Style.BraceWrapping.AfterClass;
966+
return Style.BraceWrapping.AfterClass && WrapRecordAllowed;
961967
case tok::kw_union:
962-
return Style.BraceWrapping.AfterUnion;
968+
return Style.BraceWrapping.AfterUnion && WrapRecordAllowed;
963969
case tok::kw_struct:
964-
return Style.BraceWrapping.AfterStruct;
970+
return Style.BraceWrapping.AfterStruct && WrapRecordAllowed;
965971
case tok::kw_enum:
966972
return Style.BraceWrapping.AfterEnum;
967973
default:
@@ -3193,7 +3199,7 @@ void UnwrappedLineParser::parseNamespace() {
31933199
if (FormatTok->is(tok::l_brace)) {
31943200
FormatTok->setFinalizedType(TT_NamespaceLBrace);
31953201

3196-
if (ShouldBreakBeforeBrace(Style, InitialToken))
3202+
if (ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken()))
31973203
addUnwrappedLine();
31983204

31993205
unsigned AddLevels =
@@ -3858,7 +3864,7 @@ bool UnwrappedLineParser::parseEnum() {
38583864
}
38593865

38603866
if (!Style.AllowShortEnumsOnASingleLine &&
3861-
ShouldBreakBeforeBrace(Style, InitialToken)) {
3867+
ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken())) {
38623868
addUnwrappedLine();
38633869
}
38643870
// Parse enum body.
@@ -4153,8 +4159,11 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) {
41534159
if (ParseAsExpr) {
41544160
parseChildBlock();
41554161
} else {
4156-
if (ShouldBreakBeforeBrace(Style, InitialToken))
4162+
if (Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_All &&
4163+
ShouldBreakBeforeBrace(Style, InitialToken,
4164+
*Tokens->peekNextToken())) {
41574165
addUnwrappedLine();
4166+
}
41584167

41594168
unsigned AddLevels = Style.IndentAccessModifiers ? 2u : 1u;
41604169
parseBlock(/*MustBeDeclaration=*/true, AddLevels, /*MunchSemi=*/false);

clang/unittests/Format/ConfigParseTest.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,14 @@ TEST(ConfigParseTest, ParsesConfiguration) {
655655
CHECK_PARSE("AllowShortLambdasOnASingleLine: true",
656656
AllowShortLambdasOnASingleLine, FormatStyle::SLS_All);
657657

658+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never;
659+
CHECK_PARSE("AllowShortRecordsOnASingleLine: Empty",
660+
AllowShortRecordsOnASingleLine, FormatStyle::SRS_Empty);
661+
CHECK_PARSE("AllowShortRecordsOnASingleLine: All",
662+
AllowShortRecordsOnASingleLine, FormatStyle::SRS_All);
663+
CHECK_PARSE("AllowShortRecordsOnASingleLine: Never",
664+
AllowShortRecordsOnASingleLine, FormatStyle::SRS_Never);
665+
658666
Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both;
659667
CHECK_PARSE("SpaceAroundPointerQualifiers: Default",
660668
SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Default);

clang/unittests/Format/FormatTest.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8632,6 +8632,19 @@ TEST_F(FormatTest, BreaksFunctionDeclarations) {
86328632
Style);
86338633
}
86348634

8635+
TEST_F(FormatTest, BreakFunctionsReturningRecords) {
8636+
FormatStyle Style = getLLVMStyle();
8637+
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
8638+
Style.BraceWrapping.AfterFunction = true;
8639+
Style.BraceWrapping.AfterClass = false;
8640+
Style.BraceWrapping.AfterStruct = false;
8641+
Style.BraceWrapping.AfterUnion = false;
8642+
8643+
verifyFormat("class Bar foo() {}", Style);
8644+
verifyFormat("struct Bar foo() {}", Style);
8645+
verifyFormat("union Bar foo() {}", Style);
8646+
}
8647+
86358648
TEST_F(FormatTest, DontBreakBeforeQualifiedOperator) {
86368649
// Regression test for https://bugs.llvm.org/show_bug.cgi?id=40516:
86378650
// Prefer keeping `::` followed by `operator` together.
@@ -15334,6 +15347,66 @@ TEST_F(FormatTest, NeverMergeShortRecords) {
1533415347
Style);
1533515348
}
1533615349

15350+
TEST_F(FormatTest, AllowShortRecordsOnASingleLine) {
15351+
FormatStyle Style = getLLVMStyle();
15352+
15353+
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
15354+
Style.BraceWrapping.AfterClass = true;
15355+
Style.BraceWrapping.AfterStruct = true;
15356+
Style.BraceWrapping.AfterUnion = true;
15357+
Style.BraceWrapping.SplitEmptyRecord = false;
15358+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never;
15359+
15360+
verifyFormat("class foo\n{\n"
15361+
" void bar();\n"
15362+
"};",
15363+
Style);
15364+
verifyFormat("class foo\n{};", Style);
15365+
15366+
verifyFormat("struct foo\n{\n"
15367+
" int bar;\n"
15368+
"};",
15369+
Style);
15370+
verifyFormat("struct foo\n{};", Style);
15371+
15372+
verifyFormat("union foo\n{\n"
15373+
" int bar;\n"
15374+
"};",
15375+
Style);
15376+
verifyFormat("union foo\n{};", Style);
15377+
15378+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Empty;
15379+
15380+
verifyFormat("class foo\n{\n"
15381+
" void bar();\n"
15382+
"};",
15383+
Style);
15384+
verifyFormat("class foo {};", Style);
15385+
15386+
verifyFormat("struct foo\n{\n"
15387+
" int bar;\n"
15388+
"};",
15389+
Style);
15390+
verifyFormat("struct foo {};", Style);
15391+
15392+
verifyFormat("union foo\n{\n"
15393+
" int bar;\n"
15394+
"};",
15395+
Style);
15396+
verifyFormat("union foo {};", Style);
15397+
15398+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_All;
15399+
15400+
verifyFormat("class foo { void bar(); };", Style);
15401+
verifyFormat("class foo {};", Style);
15402+
15403+
verifyFormat("struct foo { int bar; };", Style);
15404+
verifyFormat("struct foo {};", Style);
15405+
15406+
verifyFormat("union foo { int bar; };", Style);
15407+
verifyFormat("union foo {};", Style);
15408+
}
15409+
1533715410
TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) {
1533815411
// Elaborate type variable declarations.
1533915412
verifyFormat("struct foo a = {bar};\nint n;");

0 commit comments

Comments
 (0)