Skip to content

Commit 46f08a1

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 06c8ee6 commit 46f08a1

File tree

8 files changed

+195
-14
lines changed

8 files changed

+195
-14
lines changed

clang/docs/ClangFormatStyleOptions.rst

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

2088+
.. _AllowShortRecordsOnASingleLine:
2089+
2090+
**AllowShortRecordsOnASingleLine** (``ShortRecordStyle``) :ref:`<AllowShortRecordsOnASingleLine>`
2091+
Dependent on the value, ``struct bar { int i; }`` can be put on a single
2092+
line.
2093+
2094+
Possible values:
2095+
2096+
* ``SRS_Never`` (in configuration: ``Never``)
2097+
Never merge records into a single line.
2098+
2099+
* ``SRS_Empty`` (in configuration: ``Empty``)
2100+
Only merge empty records.
2101+
2102+
.. code-block:: c++
2103+
2104+
struct foo {};
2105+
struct bar
2106+
{
2107+
int i;
2108+
};
2109+
2110+
* ``SRS_All`` (in configuration: ``All``)
2111+
Merge all records that fit on a single line.
2112+
2113+
.. code-block:: c++
2114+
2115+
struct foo {};
2116+
struct bar { int i; };
2117+
2118+
2119+
20882120
.. _AlwaysBreakAfterDefinitionReturnType:
20892121

20902122
**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
@@ -987,6 +987,32 @@ struct FormatStyle {
987987
/// \version 20
988988
bool AllowShortNamespacesOnASingleLine;
989989

990+
/// Different styles for merging short records
991+
/// (``class``,``struct``,``union``).
992+
enum ShortRecordStyle : int8_t {
993+
/// Never merge records into a single line.
994+
SRS_Never,
995+
/// Only merge empty records.
996+
/// \code
997+
/// struct foo {};
998+
/// struct bar
999+
/// {
1000+
/// int i;
1001+
/// };
1002+
/// \endcode
1003+
SRS_Empty,
1004+
/// Merge all records that fit on a single line.
1005+
/// \code
1006+
/// struct foo {};
1007+
/// struct bar { int i; };
1008+
/// \endcode
1009+
SRS_All
1010+
};
1011+
1012+
/// Dependent on the value, ``struct bar { int i; }`` can be put on a single
1013+
/// line.
1014+
ShortRecordStyle AllowShortRecordsOnASingleLine;
1015+
9901016
/// Different ways to break after the function definition return type.
9911017
/// This option is **deprecated** and is retained for backwards compatibility.
9921018
enum DefinitionReturnTypeBreakingStyle : int8_t {

clang/lib/Format/Format.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,14 @@ template <> struct ScalarEnumerationTraits<FormatStyle::ShortLambdaStyle> {
708708
}
709709
};
710710

711+
template <> struct ScalarEnumerationTraits<FormatStyle::ShortRecordStyle> {
712+
static void enumeration(IO &IO, FormatStyle::ShortRecordStyle &Value) {
713+
IO.enumCase(Value, "Never", FormatStyle::SRS_Never);
714+
IO.enumCase(Value, "Empty", FormatStyle::SRS_Empty);
715+
IO.enumCase(Value, "All", FormatStyle::SRS_All);
716+
}
717+
};
718+
711719
template <> struct MappingTraits<FormatStyle::SortIncludesOptions> {
712720
static void enumInput(IO &IO, FormatStyle::SortIncludesOptions &Value) {
713721
IO.enumCase(Value, "Never", FormatStyle::SortIncludesOptions({}));
@@ -1121,6 +1129,8 @@ template <> struct MappingTraits<FormatStyle> {
11211129
Style.AllowShortIfStatementsOnASingleLine);
11221130
IO.mapOptional("AllowShortLambdasOnASingleLine",
11231131
Style.AllowShortLambdasOnASingleLine);
1132+
IO.mapOptional("AllowShortRecordsOnASingleLine",
1133+
Style.AllowShortRecordsOnASingleLine);
11241134
IO.mapOptional("AllowShortLoopsOnASingleLine",
11251135
Style.AllowShortLoopsOnASingleLine);
11261136
IO.mapOptional("AllowShortNamespacesOnASingleLine",
@@ -1673,6 +1683,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
16731683
LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All;
16741684
LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
16751685
LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All;
1686+
LLVMStyle.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never;
16761687
LLVMStyle.AllowShortLoopsOnASingleLine = false;
16771688
LLVMStyle.AllowShortNamespacesOnASingleLine = false;
16781689
LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None;

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5977,12 +5977,15 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
59775977
return true;
59785978
}
59795979

5980-
// Don't attempt to interpret struct return types as structs.
5980+
// Don't attempt to interpret record return types as records.
59815981
if (Right.isNot(TT_FunctionLBrace)) {
5982-
return (Line.startsWith(tok::kw_class) &&
5983-
Style.BraceWrapping.AfterClass) ||
5984-
(Line.startsWith(tok::kw_struct) &&
5985-
Style.BraceWrapping.AfterStruct);
5982+
return ((Line.startsWith(tok::kw_class) &&
5983+
Style.BraceWrapping.AfterClass) ||
5984+
(Line.startsWith(tok::kw_struct) &&
5985+
Style.BraceWrapping.AfterStruct) ||
5986+
(Line.startsWith(tok::kw_union) &&
5987+
Style.BraceWrapping.AfterUnion)) &&
5988+
Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never;
59865989
}
59875990
}
59885991

clang/lib/Format/UnwrappedLineFormatter.cpp

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

466+
auto ShouldMergeShortRecords = [this, &I, &NextLine, PreviousLine,
467+
TheLine]() {
468+
if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_All)
469+
return true;
470+
if (Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Empty &&
471+
NextLine.First->is(tok::r_brace)) {
472+
return true;
473+
}
474+
return false;
475+
};
476+
477+
bool MergeShortRecord = ShouldMergeShortRecords();
478+
466479
// Don't merge an empty template class or struct if SplitEmptyRecords
467480
// is defined.
468481
if (PreviousLine && Style.BraceWrapping.SplitEmptyRecord &&
@@ -506,7 +519,8 @@ class LineJoiner {
506519
// elsewhere.
507520
ShouldMerge = !Style.BraceWrapping.AfterClass ||
508521
(NextLine.First->is(tok::r_brace) &&
509-
!Style.BraceWrapping.SplitEmptyRecord);
522+
!Style.BraceWrapping.SplitEmptyRecord) ||
523+
MergeShortRecord;
510524
} else if (TheLine->InPPDirective ||
511525
TheLine->First->isNoneOf(tok::kw_class, tok::kw_enum,
512526
tok::kw_struct, Keywords.kw_record)) {
@@ -881,9 +895,11 @@ class LineJoiner {
881895
return 1;
882896
} else if (Limit != 0 && !Line.startsWithNamespace() &&
883897
!startsExternCBlock(Line)) {
884-
// We don't merge short records.
885-
if (isRecordLBrace(*Line.Last))
898+
// Merge short records only when requested.
899+
if (isRecordLBrace(*Line.Last) &&
900+
Style.AllowShortRecordsOnASingleLine == FormatStyle::SRS_Never) {
886901
return 0;
902+
}
887903

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

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ static bool isIIFE(const UnwrappedLine &Line,
949949

950950
static bool ShouldBreakBeforeBrace(const FormatStyle &Style,
951951
const FormatToken &InitialToken,
952+
const FormatToken &NextToken,
952953
const bool IsJavaRecord) {
953954
if (IsJavaRecord)
954955
return Style.BraceWrapping.AfterClass;
@@ -957,15 +958,20 @@ static bool ShouldBreakBeforeBrace(const FormatStyle &Style,
957958
if (InitialToken.is(TT_NamespaceMacro))
958959
Kind = tok::kw_namespace;
959960

961+
bool IsEmptyBlock = NextToken.is(tok::r_brace);
962+
bool WrapRecordAllowed =
963+
!(IsEmptyBlock &&
964+
Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_Never);
965+
960966
switch (Kind) {
961967
case tok::kw_namespace:
962968
return Style.BraceWrapping.AfterNamespace;
963969
case tok::kw_class:
964-
return Style.BraceWrapping.AfterClass;
970+
return Style.BraceWrapping.AfterClass && WrapRecordAllowed;
965971
case tok::kw_union:
966-
return Style.BraceWrapping.AfterUnion;
972+
return Style.BraceWrapping.AfterUnion && WrapRecordAllowed;
967973
case tok::kw_struct:
968-
return Style.BraceWrapping.AfterStruct;
974+
return Style.BraceWrapping.AfterStruct && WrapRecordAllowed;
969975
case tok::kw_enum:
970976
return Style.BraceWrapping.AfterEnum;
971977
default:
@@ -3202,8 +3208,10 @@ void UnwrappedLineParser::parseNamespace() {
32023208
if (FormatTok->is(tok::l_brace)) {
32033209
FormatTok->setFinalizedType(TT_NamespaceLBrace);
32043210

3205-
if (ShouldBreakBeforeBrace(Style, InitialToken, /*IsJavaRecord=*/false))
3211+
if (ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken(),
3212+
/*IsJavaRecord=*/false)) {
32063213
addUnwrappedLine();
3214+
}
32073215

32083216
unsigned AddLevels =
32093217
Style.NamespaceIndentation == FormatStyle::NI_All ||
@@ -3857,7 +3865,8 @@ bool UnwrappedLineParser::parseEnum() {
38573865
}
38583866

38593867
if (!Style.AllowShortEnumsOnASingleLine &&
3860-
ShouldBreakBeforeBrace(Style, InitialToken, /*IsJavaRecord=*/false)) {
3868+
ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken(),
3869+
/*IsJavaRecord=*/false)) {
38613870
addUnwrappedLine();
38623871
}
38633872
// Parse enum body.
@@ -4152,8 +4161,11 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr, bool IsJavaRecord) {
41524161
if (ParseAsExpr) {
41534162
parseChildBlock();
41544163
} else {
4155-
if (ShouldBreakBeforeBrace(Style, InitialToken, IsJavaRecord))
4164+
if (Style.AllowShortRecordsOnASingleLine != FormatStyle::SRS_All &&
4165+
ShouldBreakBeforeBrace(Style, InitialToken, *Tokens->peekNextToken(),
4166+
IsJavaRecord)) {
41564167
addUnwrappedLine();
4168+
}
41574169

41584170
unsigned AddLevels = Style.IndentAccessModifiers ? 2u : 1u;
41594171
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
@@ -668,6 +668,14 @@ TEST(ConfigParseTest, ParsesConfiguration) {
668668
CHECK_PARSE("AllowShortLambdasOnASingleLine: true",
669669
AllowShortLambdasOnASingleLine, FormatStyle::SLS_All);
670670

671+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never;
672+
CHECK_PARSE("AllowShortRecordsOnASingleLine: Empty",
673+
AllowShortRecordsOnASingleLine, FormatStyle::SRS_Empty);
674+
CHECK_PARSE("AllowShortRecordsOnASingleLine: All",
675+
AllowShortRecordsOnASingleLine, FormatStyle::SRS_All);
676+
CHECK_PARSE("AllowShortRecordsOnASingleLine: Never",
677+
AllowShortRecordsOnASingleLine, FormatStyle::SRS_Never);
678+
671679
Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both;
672680
CHECK_PARSE("SpaceAroundPointerQualifiers: Default",
673681
SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Default);

clang/unittests/Format/FormatTest.cpp

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8655,6 +8655,19 @@ TEST_F(FormatTest, BreaksFunctionDeclarations) {
86558655
Style);
86568656
}
86578657

8658+
TEST_F(FormatTest, BreakFunctionsReturningRecords) {
8659+
FormatStyle Style = getLLVMStyle();
8660+
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
8661+
Style.BraceWrapping.AfterFunction = true;
8662+
Style.BraceWrapping.AfterClass = false;
8663+
Style.BraceWrapping.AfterStruct = false;
8664+
Style.BraceWrapping.AfterUnion = false;
8665+
8666+
verifyFormat("class Bar foo() {}", Style);
8667+
verifyFormat("struct Bar foo() {}", Style);
8668+
verifyFormat("union Bar foo() {}", Style);
8669+
}
8670+
86588671
TEST_F(FormatTest, DontBreakBeforeQualifiedOperator) {
86598672
// Regression test for https://bugs.llvm.org/show_bug.cgi?id=40516:
86608673
// Prefer keeping `::` followed by `operator` together.
@@ -15357,6 +15370,66 @@ TEST_F(FormatTest, NeverMergeShortRecords) {
1535715370
Style);
1535815371
}
1535915372

15373+
TEST_F(FormatTest, AllowShortRecordsOnASingleLine) {
15374+
FormatStyle Style = getLLVMStyle();
15375+
15376+
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
15377+
Style.BraceWrapping.AfterClass = true;
15378+
Style.BraceWrapping.AfterStruct = true;
15379+
Style.BraceWrapping.AfterUnion = true;
15380+
Style.BraceWrapping.SplitEmptyRecord = false;
15381+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Never;
15382+
15383+
verifyFormat("class foo\n{\n"
15384+
" void bar();\n"
15385+
"};",
15386+
Style);
15387+
verifyFormat("class foo\n{};", Style);
15388+
15389+
verifyFormat("struct foo\n{\n"
15390+
" int bar;\n"
15391+
"};",
15392+
Style);
15393+
verifyFormat("struct foo\n{};", Style);
15394+
15395+
verifyFormat("union foo\n{\n"
15396+
" int bar;\n"
15397+
"};",
15398+
Style);
15399+
verifyFormat("union foo\n{};", Style);
15400+
15401+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_Empty;
15402+
15403+
verifyFormat("class foo\n{\n"
15404+
" void bar();\n"
15405+
"};",
15406+
Style);
15407+
verifyFormat("class foo {};", Style);
15408+
15409+
verifyFormat("struct foo\n{\n"
15410+
" int bar;\n"
15411+
"};",
15412+
Style);
15413+
verifyFormat("struct foo {};", Style);
15414+
15415+
verifyFormat("union foo\n{\n"
15416+
" int bar;\n"
15417+
"};",
15418+
Style);
15419+
verifyFormat("union foo {};", Style);
15420+
15421+
Style.AllowShortRecordsOnASingleLine = FormatStyle::SRS_All;
15422+
15423+
verifyFormat("class foo { void bar(); };", Style);
15424+
verifyFormat("class foo {};", Style);
15425+
15426+
verifyFormat("struct foo { int bar; };", Style);
15427+
verifyFormat("struct foo {};", Style);
15428+
15429+
verifyFormat("union foo { int bar; };", Style);
15430+
verifyFormat("union foo {};", Style);
15431+
}
15432+
1536015433
TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) {
1536115434
// Elaborate type variable declarations.
1536215435
verifyFormat("struct foo a = {bar};\nint n;");

0 commit comments

Comments
 (0)