diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2146,6 +2146,42 @@ return true; } + /// Returns true if the token is used as a unary operator. + bool determineUnaryOperatorByUsage(const FormatToken &Tok) { + const FormatToken *PrevToken = Tok.getPreviousNonComment(); + if (!PrevToken) + return true; + + // These keywords are deliberately not included here because they may + // precede only one of unary star/amp and plus/minus but not both. They are + // either included in determineStarAmpUsage or determinePlusMinusCaretUsage. + // + // @ - It may be followed by a unary `-` in Objective-C literals. We don't + // know how they can be followed by a star or amp. + if (PrevToken->isOneOf( + TT_ConditionalExpr, tok::l_paren, tok::comma, tok::colon, tok::semi, + tok::equal, tok::question, tok::l_square, tok::l_brace, + tok::kw_case, tok::kw_co_await, tok::kw_co_return, tok::kw_co_yield, + tok::kw_delete, tok::kw_return, tok::kw_throw)) + return true; + + // We put sizeof here instead of only in determineStarAmpUsage. In the cases + // where the unary `+` operator is overloaded, it is reasonable to write + // things like `sizeof +x`. Like commit 446d6ec996c6c3. + if (PrevToken->is(tok::kw_sizeof)) + return true; + + // A sequence of leading unary operators. + if (PrevToken->isOneOf(TT_CastRParen, TT_UnaryOperator)) + return true; + + // There can't be two consecutive binary operators. + if (PrevToken->is(TT_BinaryOperator)) + return true; + + return false; + } + /// Return the type of the given token assuming it is * or &. TokenType determineStarAmpUsage(const FormatToken &Tok, bool IsExpression, bool InTemplateArgument) { @@ -2177,12 +2213,7 @@ if (PrevToken->is(tok::r_paren) && PrevToken->is(TT_TypeDeclarationParen)) return TT_PointerOrReference; - if (PrevToken->isOneOf(tok::l_paren, tok::l_square, tok::l_brace, - tok::comma, tok::semi, tok::kw_return, tok::colon, - tok::kw_co_return, tok::kw_co_await, - tok::kw_co_yield, tok::equal, tok::kw_delete, - tok::kw_sizeof, tok::kw_throw, TT_BinaryOperator, - TT_ConditionalExpr, TT_UnaryOperator, TT_CastRParen)) + if (determineUnaryOperatorByUsage(Tok)) return TT_UnaryOperator; if (NextToken->is(tok::l_square) && NextToken->isNot(TT_LambdaLSquare)) @@ -2232,23 +2263,14 @@ } TokenType determinePlusMinusCaretUsage(const FormatToken &Tok) { - const FormatToken *PrevToken = Tok.getPreviousNonComment(); - if (!PrevToken) + if (determineUnaryOperatorByUsage(Tok)) return TT_UnaryOperator; - if (PrevToken->isOneOf(TT_CastRParen, TT_UnaryOperator)) - // This must be a sequence of leading unary operators. - return TT_UnaryOperator; - - // Use heuristics to recognize unary operators. - if (PrevToken->isOneOf(tok::equal, tok::l_paren, tok::comma, tok::l_square, - tok::question, tok::colon, tok::kw_return, - tok::kw_case, tok::at, tok::l_brace, tok::kw_throw, - tok::kw_co_return, tok::kw_co_yield)) + const FormatToken *PrevToken = Tok.getPreviousNonComment(); + if (!PrevToken) return TT_UnaryOperator; - // There can't be two consecutive binary operators. - if (PrevToken->is(TT_BinaryOperator)) + if (PrevToken->is(tok::at)) return TT_UnaryOperator; // Fall back to marking the token as binary operator. diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -9754,6 +9754,15 @@ verifyFormat("if (!(a->f())) {\n}"); verifyFormat("if (!+i) {\n}"); verifyFormat("~&a;"); + verifyFormat("for (x = 0; -10 < x; --x) {\n}"); + verifyFormat("sizeof -x"); + verifyFormat("sizeof +x"); + verifyFormat("sizeof *x"); + verifyFormat("sizeof &x"); + verifyFormat("delete +x;"); + verifyFormat("co_await +x;"); + verifyFormat("case *x:"); + verifyFormat("case &x:"); verifyFormat("a-- > b;"); verifyFormat("b ? -a : c;"); diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -78,6 +78,102 @@ EXPECT_EQ(Tokens.size(), 21u) << Tokens; EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[11], tok::star, TT_UnaryOperator); + + Tokens = annotate("case *x:"); + EXPECT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::star, TT_UnaryOperator); + Tokens = annotate("case &x:"); + EXPECT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::amp, TT_UnaryOperator); +} + +TEST_F(TokenAnnotatorTest, UnderstandsUsesOfPlusAndMinus) { + auto Tokens = annotate("x - 0"); + ASSERT_EQ(Tokens.size(), 4u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_BinaryOperator); + Tokens = annotate("0 + 0"); + ASSERT_EQ(Tokens.size(), 4u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_BinaryOperator); + Tokens = annotate("x + +0"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::plus, TT_UnaryOperator); + Tokens = annotate("x ? -0 : +0"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); + EXPECT_TOKEN(Tokens[5], tok::plus, TT_UnaryOperator); + Tokens = annotate("(-0)"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("0, -0"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); + Tokens = annotate("for (; -1;) {\n}"); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::minus, TT_UnaryOperator); + Tokens = annotate("x = -1;"); + ASSERT_EQ(Tokens.size(), 6u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); + Tokens = annotate("x[-1]"); + ASSERT_EQ(Tokens.size(), 6u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::minus, TT_UnaryOperator); + Tokens = annotate("x = {-1};"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::minus, TT_UnaryOperator); + Tokens = annotate("case -x:"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("co_await -x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("co_return -x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("co_yield -x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("delete -x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("return -x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("throw -x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("sizeof -x"); + ASSERT_EQ(Tokens.size(), 4u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("co_await +x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); + Tokens = annotate("co_return +x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); + Tokens = annotate("co_yield +x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); + Tokens = annotate("delete +x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); + Tokens = annotate("return +x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); + Tokens = annotate("throw +x;"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); + Tokens = annotate("sizeof +x"); + ASSERT_EQ(Tokens.size(), 4u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); + Tokens = annotate("(int)-x"); + ASSERT_EQ(Tokens.size(), 6u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::minus, TT_UnaryOperator); + Tokens = annotate("(-x)"); + ASSERT_EQ(Tokens.size(), 5u) << Tokens; + EXPECT_TOKEN(Tokens[1], tok::minus, TT_UnaryOperator); + Tokens = annotate("!+x"); + ASSERT_EQ(Tokens.size(), 4u) << Tokens; + EXPECT_TOKEN(Tokens[0], tok::exclaim, TT_UnaryOperator); + EXPECT_TOKEN(Tokens[1], tok::plus, TT_UnaryOperator); } TEST_F(TokenAnnotatorTest, UnderstandsClasses) {