Skip to content

Commit b05dc1b

Browse files
committed
[clang-format] Add a space between an overloaded operator and '>'
The token annotator doesn't annotate the template opener and closer as such if they enclose an overloaded operator. This causes the space between the operator and the closer to be removed, resulting in invalid C++ code. Fixes llvm#58602. Differential Revision: https://reviews.llvm.org/D143755
1 parent 7c9df77 commit b05dc1b

File tree

6 files changed

+126
-17
lines changed

6 files changed

+126
-17
lines changed

clang/lib/Format/FormatToken.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,8 @@ struct FormatToken {
743743
}
744744

745745
/// Returns the next token ignoring comments.
746-
[[nodiscard]] const FormatToken *getNextNonComment() const {
747-
const FormatToken *Tok = Next;
746+
[[nodiscard]] FormatToken *getNextNonComment() const {
747+
FormatToken *Tok = Next;
748748
while (Tok && Tok->is(tok::comment))
749749
Tok = Tok->Next;
750750
return Tok;

clang/lib/Format/FormatTokenLexer.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ void FormatTokenLexer::tryMergePreviousTokens() {
103103
return;
104104
if (tryMergeLessLess())
105105
return;
106+
if (tryMergeGreaterGreater())
107+
return;
106108
if (tryMergeForEach())
107109
return;
108110
if (Style.isCpp() && tryTransformTryUsageForC())
@@ -460,12 +462,11 @@ bool FormatTokenLexer::tryMergeLessLess() {
460462
return false;
461463

462464
auto X = Tokens.size() > 3 ? First[-1] : nullptr;
463-
auto Y = First[2];
464-
if ((X && X->is(tok::less)) || Y->is(tok::less))
465+
if (X && X->is(tok::less))
465466
return false;
466467

467-
// Do not remove a whitespace between the two "<" e.g. "operator< <>".
468-
if (X && X->is(tok::kw_operator) && Y->is(tok::greater))
468+
auto Y = First[2];
469+
if ((!X || X->isNot(tok::kw_operator)) && Y->is(tok::less))
469470
return false;
470471

471472
First[0]->Tok.setKind(tok::lessless);
@@ -475,6 +476,30 @@ bool FormatTokenLexer::tryMergeLessLess() {
475476
return true;
476477
}
477478

479+
bool FormatTokenLexer::tryMergeGreaterGreater() {
480+
// Merge kw_operator,greater,greater into kw_operator,greatergreater.
481+
if (Tokens.size() < 2)
482+
return false;
483+
484+
auto First = Tokens.end() - 2;
485+
if (First[0]->isNot(tok::greater) || First[1]->isNot(tok::greater))
486+
return false;
487+
488+
// Only merge if there currently is no whitespace between the first two ">".
489+
if (First[1]->hasWhitespaceBefore())
490+
return false;
491+
492+
auto Tok = Tokens.size() > 2 ? First[-1] : nullptr;
493+
if (Tok && Tok->isNot(tok::kw_operator))
494+
return false;
495+
496+
First[0]->Tok.setKind(tok::greatergreater);
497+
First[0]->TokenText = ">>";
498+
First[0]->ColumnWidth += 1;
499+
Tokens.erase(Tokens.end() - 1);
500+
return true;
501+
}
502+
478503
bool FormatTokenLexer::tryMergeTokens(ArrayRef<tok::TokenKind> Kinds,
479504
TokenType NewType) {
480505
if (Tokens.size() < Kinds.size())

clang/lib/Format/FormatTokenLexer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class FormatTokenLexer {
5151
void tryMergePreviousTokens();
5252

5353
bool tryMergeLessLess();
54+
bool tryMergeGreaterGreater();
5455
bool tryMergeNSStringLiteral();
5556
bool tryMergeJSPrivateIdentifier();
5657
bool tryMergeCSharpStringLiteral();

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,19 +1219,25 @@ class AnnotatingParser {
12191219
!CurrentToken->isOneOf(tok::l_paren, tok::semi, tok::r_paren)) {
12201220
if (CurrentToken->isOneOf(tok::star, tok::amp))
12211221
CurrentToken->setType(TT_PointerOrReference);
1222-
consumeToken();
1223-
if (!CurrentToken)
1224-
continue;
1225-
if (CurrentToken->is(tok::comma) &&
1226-
CurrentToken->Previous->isNot(tok::kw_operator)) {
1222+
auto Next = CurrentToken->getNextNonComment();
1223+
if (!Next)
12271224
break;
1228-
}
1229-
if (CurrentToken->Previous->isOneOf(TT_BinaryOperator, TT_UnaryOperator,
1230-
tok::comma, tok::star, tok::arrow,
1231-
tok::amp, tok::ampamp) ||
1225+
if (Next->is(tok::less))
1226+
next();
1227+
else
1228+
consumeToken();
1229+
assert(CurrentToken);
1230+
auto Previous = CurrentToken->getPreviousNonComment();
1231+
assert(Previous);
1232+
if (CurrentToken->is(tok::comma) && Previous->isNot(tok::kw_operator))
1233+
break;
1234+
if (Previous->isOneOf(TT_BinaryOperator, TT_UnaryOperator, tok::comma,
1235+
tok::star, tok::arrow, tok::amp, tok::ampamp) ||
12321236
// User defined literal.
1233-
CurrentToken->Previous->TokenText.startswith("\"\"")) {
1234-
CurrentToken->Previous->setType(TT_OverloadedOperator);
1237+
Previous->TokenText.startswith("\"\"")) {
1238+
Previous->setType(TT_OverloadedOperator);
1239+
if (CurrentToken->isOneOf(tok::less, tok::greater))
1240+
break;
12351241
}
12361242
}
12371243
if (CurrentToken && CurrentToken->is(tok::l_paren))
@@ -3893,6 +3899,10 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
38933899
return true;
38943900

38953901
if (Style.isCpp()) {
3902+
if (Left.is(TT_OverloadedOperator) &&
3903+
Right.isOneOf(TT_TemplateOpener, TT_TemplateCloser)) {
3904+
return true;
3905+
}
38963906
// Space between UDL and dot: auto b = 4s .count();
38973907
if (Right.is(tok::period) && Left.is(tok::numeric_constant))
38983908
return true;

clang/unittests/Format/FormatTest.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10663,6 +10663,14 @@ TEST_F(FormatTest, UnderstandsOverloadedOperators) {
1066310663
verifyFormat("foo() { ::operator new(n * sizeof(foo)); }");
1066410664
}
1066510665

10666+
TEST_F(FormatTest, SpaceBeforeTemplateCloser) {
10667+
verifyFormat("C<&operator- > minus;");
10668+
verifyFormat("C<&operator> > gt;");
10669+
verifyFormat("C<&operator>= > ge;");
10670+
verifyFormat("C<&operator<= > le;");
10671+
verifyFormat("C<&operator< <X>> lt;");
10672+
}
10673+
1066610674
TEST_F(FormatTest, UnderstandsFunctionRefQualification) {
1066710675
verifyFormat("void A::b() && {}");
1066810676
verifyFormat("void A::b() && noexcept {}");

clang/unittests/Format/TokenAnnotatorTest.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,71 @@ TEST_F(TokenAnnotatorTest, UnderstandsOverloadedOperators) {
574574
EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_OverloadedOperatorLParen);
575575
}
576576

577+
TEST_F(TokenAnnotatorTest, OverloadedOperatorInTemplate) {
578+
struct {
579+
const char *Text;
580+
tok::TokenKind Kind;
581+
} Operators[] = {{"+", tok::plus},
582+
{"-", tok::minus},
583+
// FIXME:
584+
// {"*", tok::star},
585+
{"/", tok::slash},
586+
{"%", tok::percent},
587+
{"^", tok::caret},
588+
// FIXME:
589+
// {"&", tok::amp},
590+
{"|", tok::pipe},
591+
{"~", tok::tilde},
592+
{"!", tok::exclaim},
593+
{"=", tok::equal},
594+
// FIXME:
595+
// {"<", tok::less},
596+
{">", tok::greater},
597+
{"+=", tok::plusequal},
598+
{"-=", tok::minusequal},
599+
{"*=", tok::starequal},
600+
{"/=", tok::slashequal},
601+
{"%=", tok::percentequal},
602+
{"^=", tok::caretequal},
603+
{"&=", tok::ampequal},
604+
{"|=", tok::pipeequal},
605+
{"<<", tok::lessless},
606+
{">>", tok::greatergreater},
607+
{">>=", tok::greatergreaterequal},
608+
{"<<=", tok::lesslessequal},
609+
{"==", tok::equalequal},
610+
{"!=", tok::exclaimequal},
611+
{"<=", tok::lessequal},
612+
{">=", tok::greaterequal},
613+
{"<=>", tok::spaceship},
614+
{"&&", tok::ampamp},
615+
{"||", tok::pipepipe},
616+
{"++", tok::plusplus},
617+
{"--", tok::minusminus},
618+
{",", tok::comma},
619+
{"->*", tok::arrowstar},
620+
{"->", tok::arrow}};
621+
622+
for (const auto &Operator : Operators) {
623+
std::string Input("C<&operator");
624+
Input += Operator.Text;
625+
Input += " > a;";
626+
auto Tokens = annotate(std::string(Input));
627+
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
628+
EXPECT_TOKEN(Tokens[1], tok::less, TT_TemplateOpener);
629+
EXPECT_TOKEN(Tokens[4], Operator.Kind, TT_OverloadedOperator);
630+
EXPECT_TOKEN(Tokens[5], tok::greater, TT_TemplateCloser);
631+
}
632+
633+
auto Tokens = annotate("C<&operator< <X>> lt;");
634+
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
635+
EXPECT_TOKEN(Tokens[1], tok::less, TT_TemplateOpener);
636+
EXPECT_TOKEN(Tokens[4], tok::less, TT_OverloadedOperator);
637+
EXPECT_TOKEN(Tokens[5], tok::less, TT_TemplateOpener);
638+
EXPECT_TOKEN(Tokens[7], tok::greater, TT_TemplateCloser);
639+
EXPECT_TOKEN(Tokens[8], tok::greater, TT_TemplateCloser);
640+
}
641+
577642
TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {
578643
auto Tokens = annotate("template <typename T>\n"
579644
"concept C = (Foo && Bar) && (Bar && Baz);");

0 commit comments

Comments
 (0)