Skip to content

Commit 083f099

Browse files
galeneliasowenca
andauthored
[clang-format] Support BraceWrapping.AfterNamespace with AllowShortNamespacesOnASingleLine (#123010)
AllowShortNamespacesOnASingleLine assumes that there is no newline before the namespace brace, however, there is no actual reason this shouldn't be compatible with BraceWrapping.AfterNamespace = true. This is a little tricky in the implementation because UnwrappedLineFormatter works on lines, so being flexible about the offsets is awkward. Not sure if there is a better pattern for combining the 'AllowShort' options with the various configurations of BraceWrapping, but this seemed mostly reasonable. Really, it would almost be preferable to just pattern match on the direct token stream, rather than the AnnotatedLines, but I'm not seeing a straightforward way to do that. --------- Co-authored-by: Owen Pan <owenpiano@gmail.com>
1 parent 0ec1693 commit 083f099

File tree

2 files changed

+60
-17
lines changed

2 files changed

+60
-17
lines changed

clang/lib/Format/UnwrappedLineFormatter.cpp

+31-17
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,7 @@ class LineJoiner {
366366
// instead of TheLine->First.
367367

368368
if (Style.AllowShortNamespacesOnASingleLine &&
369-
TheLine->First->is(tok::kw_namespace) &&
370-
TheLine->Last->is(tok::l_brace)) {
369+
TheLine->First->is(tok::kw_namespace)) {
371370
const auto result = tryMergeNamespace(I, E, Limit);
372371
if (result > 0)
373372
return result;
@@ -633,24 +632,37 @@ class LineJoiner {
633632
if (Limit == 0)
634633
return 0;
635634

636-
assert(I[1]);
637-
const auto &L1 = *I[1];
635+
// The merging code is relative to the opening namespace brace, which could
636+
// be either on the first or second line due to the brace wrapping rules.
637+
const bool OpenBraceWrapped = Style.BraceWrapping.AfterNamespace;
638+
const auto *BraceOpenLine = I + OpenBraceWrapped;
639+
640+
assert(*BraceOpenLine);
641+
if (BraceOpenLine[0]->Last->isNot(TT_NamespaceLBrace))
642+
return 0;
643+
644+
if (std::distance(BraceOpenLine, E) <= 2)
645+
return 0;
646+
647+
if (BraceOpenLine[0]->Last->is(tok::comment))
648+
return 0;
649+
650+
assert(BraceOpenLine[1]);
651+
const auto &L1 = *BraceOpenLine[1];
638652
if (L1.InPPDirective != (*I)->InPPDirective ||
639653
(L1.InPPDirective && L1.First->HasUnescapedNewline)) {
640654
return 0;
641655
}
642656

643-
if (std::distance(I, E) <= 2)
644-
return 0;
645-
646-
assert(I[2]);
647-
const auto &L2 = *I[2];
657+
assert(BraceOpenLine[2]);
658+
const auto &L2 = *BraceOpenLine[2];
648659
if (L2.Type == LT_Invalid)
649660
return 0;
650661

651662
Limit = limitConsideringMacros(I + 1, E, Limit);
652663

653-
if (!nextTwoLinesFitInto(I, Limit))
664+
const auto LinesToBeMerged = OpenBraceWrapped + 2;
665+
if (!nextNLinesFitInto(I, I + LinesToBeMerged, Limit))
654666
return 0;
655667

656668
// Check if it's a namespace inside a namespace, and call recursively if so.
@@ -661,17 +673,19 @@ class LineJoiner {
661673

662674
assert(Limit >= L1.Last->TotalLength + 3);
663675
const auto InnerLimit = Limit - L1.Last->TotalLength - 3;
664-
const auto MergedLines = tryMergeNamespace(I + 1, E, InnerLimit);
676+
const auto MergedLines =
677+
tryMergeNamespace(BraceOpenLine + 1, E, InnerLimit);
665678
if (MergedLines == 0)
666679
return 0;
667-
const auto N = MergedLines + 2;
680+
const auto N = MergedLines + LinesToBeMerged;
668681
// Check if there is even a line after the inner result.
669682
if (std::distance(I, E) <= N)
670683
return 0;
671684
// Check that the line after the inner result starts with a closing brace
672685
// which we are permitted to merge into one line.
673-
if (I[N]->First->is(tok::r_brace) && !I[N]->First->MustBreakBefore &&
674-
I[MergedLines + 1]->Last->isNot(tok::comment) &&
686+
if (I[N]->First->is(TT_NamespaceRBrace) &&
687+
!I[N]->First->MustBreakBefore &&
688+
BraceOpenLine[MergedLines + 1]->Last->isNot(tok::comment) &&
675689
nextNLinesFitInto(I, I + N + 1, Limit)) {
676690
return N;
677691
}
@@ -686,11 +700,11 @@ class LineJoiner {
686700
return 0;
687701

688702
// Last, check that the third line starts with a closing brace.
689-
if (L2.First->isNot(tok::r_brace) || L2.First->MustBreakBefore)
703+
if (L2.First->isNot(TT_NamespaceRBrace) || L2.First->MustBreakBefore)
690704
return 0;
691705

692-
// If so, merge all three lines.
693-
return 2;
706+
// If so, merge all lines.
707+
return LinesToBeMerged;
694708
}
695709

696710
unsigned

clang/unittests/Format/FormatTest.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -28867,6 +28867,35 @@ TEST_F(FormatTest, ShortNamespacesOption) {
2886728867
"}}} // namespace foo::bar::baz",
2886828868
"namespace foo { namespace bar { namespace baz { class qux; } } }",
2886928869
Style);
28870+
Style.FixNamespaceComments = false;
28871+
28872+
Style.BreakBeforeBraces = FormatStyle::BS_Custom;
28873+
Style.BraceWrapping.AfterNamespace = true;
28874+
verifyFormat("namespace foo { class bar; }", Style);
28875+
verifyFormat("namespace foo { namespace bar { class baz; } }", Style);
28876+
verifyFormat("namespace foo\n"
28877+
"{ // comment\n"
28878+
"class bar;\n"
28879+
"}",
28880+
Style);
28881+
verifyFormat("namespace foo { class bar; }",
28882+
"namespace foo {\n"
28883+
"class bar;\n"
28884+
"}",
28885+
Style);
28886+
verifyFormat("namespace foo\n"
28887+
"{\n"
28888+
"namespace bar\n"
28889+
"{ // comment\n"
28890+
"class baz;\n"
28891+
"}\n"
28892+
"}",
28893+
Style);
28894+
verifyFormat("namespace foo // comment\n"
28895+
"{\n"
28896+
"class baz;\n"
28897+
"}",
28898+
Style);
2887028899
}
2887128900

2887228901
TEST_F(FormatTest, WrapNamespaceBodyWithEmptyLinesNever) {

0 commit comments

Comments
 (0)