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 cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits