owenpan created this revision.
owenpan added reviewers: MyDeveloperDay, HazardyKnusperkeks, rymiel.
owenpan added a project: clang-format.
Herald added a project: All.
owenpan requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.
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 https://github.com/llvm/llvm-project/issues/58602.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D143755
Files:
clang/lib/Format/TokenAnnotator.cpp
clang/unittests/Format/FormatTest.cpp
clang/unittests/Format/TokenAnnotatorTest.cpp
Index: clang/unittests/Format/TokenAnnotatorTest.cpp
===================================================================
--- clang/unittests/Format/TokenAnnotatorTest.cpp
+++ clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -574,6 +574,72 @@
EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_OverloadedOperatorLParen);
}
+TEST_F(TokenAnnotatorTest, OverloadedOperatorInTemplate) {
+ struct {
+ const char *Text;
+ tok::TokenKind Kind;
+ } Operators[] = {{"+", tok::plus},
+ {"-", tok::minus},
+ // FIXME:
+ // {"*", tok::star},
+ {"/", tok::slash},
+ {"%", tok::percent},
+ {"^", tok::caret},
+ // FIXME:
+ // {"&", tok::amp},
+ {"|", tok::pipe},
+ {"~", tok::tilde},
+ {"!", tok::exclaim},
+ {"=", tok::equal},
+ // FIXME:
+ // {"<", tok::less},
+ {">", tok::greater},
+ {"+=", tok::plusequal},
+ {"-=", tok::minusequal},
+ {"*=", tok::starequal},
+ {"/=", tok::slashequal},
+ {"%=", tok::percentequal},
+ {"^=", tok::caretequal},
+ {"&=", tok::ampequal},
+ {"|=", tok::pipeequal},
+ // FIXME:
+ // {"<<", tok::lessless},
+ // {">>", tok::greatergreater},
+ {">>=", tok::greatergreaterequal},
+ {"<<=", tok::lesslessequal},
+ {"==", tok::equalequal},
+ {"!=", tok::exclaimequal},
+ {"<=", tok::lessequal},
+ {">=", tok::greaterequal},
+ {"<=>", tok::spaceship},
+ {"&&", tok::ampamp},
+ {"||", tok::pipepipe},
+ {"++", tok::plusplus},
+ {"--", tok::minusminus},
+ {",", tok::comma},
+ {"->*", tok::arrowstar},
+ {"->", tok::arrow}};
+
+ for (const auto &Operator : Operators) {
+ std::string Input("C<&operator ");
+ Input += Operator.Text;
+ Input += " > a;";
+ auto Tokens = annotate(std::string(Input));
+ ASSERT_EQ(Tokens.size(), 9u) << Tokens;
+ EXPECT_TOKEN(Tokens[1], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[4], Operator.Kind, TT_OverloadedOperator);
+ EXPECT_TOKEN(Tokens[5], tok::greater, TT_TemplateCloser);
+ }
+
+ auto Tokens = annotate("C<&operator< <X>> lt;");
+ ASSERT_EQ(Tokens.size(), 12u) << Tokens;
+ EXPECT_TOKEN(Tokens[1], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[4], tok::less, TT_OverloadedOperator);
+ EXPECT_TOKEN(Tokens[5], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[7], tok::greater, TT_TemplateCloser);
+ EXPECT_TOKEN(Tokens[8], tok::greater, TT_TemplateCloser);
+}
+
TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {
auto Tokens = annotate("template <typename T>\n"
"concept C = (Foo && Bar) && (Bar && Baz);");
Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -10663,6 +10663,14 @@
verifyFormat("foo() { ::operator new(n * sizeof(foo)); }");
}
+TEST_F(FormatTest, SpaceBeforeTemplateCloser) {
+ verifyFormat("C<&operator- > minus;");
+ verifyFormat("C<&operator> > gt;");
+ verifyFormat("C<&operator>= > ge;");
+ verifyFormat("C<&operator<= > le;");
+ verifyFormat("C<&operator< <X>> lt;");
+}
+
TEST_F(FormatTest, UnderstandsFunctionRefQualification) {
verifyFormat("void A::b() && {}");
verifyFormat("void A::b() && noexcept {}");
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -1219,9 +1219,14 @@
!CurrentToken->isOneOf(tok::l_paren, tok::semi, tok::r_paren)) {
if (CurrentToken->isOneOf(tok::star, tok::amp))
CurrentToken->setType(TT_PointerOrReference);
- consumeToken();
+ if (auto NextNonComment = CurrentToken->getNextNonComment();
+ NextNonComment && NextNonComment->is(tok::less)) {
+ next();
+ } else {
+ consumeToken();
+ }
if (!CurrentToken)
- continue;
+ break;
if (CurrentToken->is(tok::comma) &&
CurrentToken->Previous->isNot(tok::kw_operator)) {
break;
@@ -1232,6 +1237,15 @@
// User defined literal.
CurrentToken->Previous->TokenText.startswith("\"\"")) {
CurrentToken->Previous->setType(TT_OverloadedOperator);
+ // Checks whether CurrentToken is a token by itself, not the second
+ // character split off from >> or <<.
+ auto IsStandAloneToken = [this](auto Kind) {
+ return CurrentToken->is(Kind) &&
+ (CurrentToken->Previous->isNot(Kind) ||
+ CurrentToken->Previous->Tok.getLength() == 1);
+ };
+ if (IsStandAloneToken(tok::greater) || IsStandAloneToken(tok::less))
+ break;
}
}
if (CurrentToken && CurrentToken->is(tok::l_paren))
@@ -3893,6 +3907,8 @@
return true;
if (Style.isCpp()) {
+ if (Right.is(TT_TemplateCloser) && Left.is(TT_OverloadedOperator))
+ return true;
// Space between UDL and dot: auto b = 4s .count();
if (Right.is(tok::period) && Left.is(tok::numeric_constant))
return true;
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits