mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 23:56:49 +00:00
[clang-format] Correctly annotate user-defined conversion functions (#131434)
Also fix/delete existing invalid/redundant test cases. Fix #130894
This commit is contained in:
parent
950bc6cd77
commit
91328dbae9
@ -746,6 +746,10 @@ public:
|
||||
return isOneOf(tok::star, tok::amp, tok::ampamp);
|
||||
}
|
||||
|
||||
bool isPlacementOperator() const {
|
||||
return isOneOf(tok::kw_new, tok::kw_delete);
|
||||
}
|
||||
|
||||
bool isUnaryOperator() const {
|
||||
switch (Tok.getKind()) {
|
||||
case tok::plus:
|
||||
|
@ -610,9 +610,9 @@ bool FormatTokenLexer::precedesOperand(FormatToken *Tok) {
|
||||
tok::r_brace, tok::l_square, tok::semi, tok::exclaim,
|
||||
tok::colon, tok::question, tok::tilde) ||
|
||||
Tok->isOneOf(tok::kw_return, tok::kw_do, tok::kw_case, tok::kw_throw,
|
||||
tok::kw_else, tok::kw_new, tok::kw_delete, tok::kw_void,
|
||||
tok::kw_typeof, Keywords.kw_instanceof, Keywords.kw_in) ||
|
||||
Tok->isBinaryOperator();
|
||||
tok::kw_else, tok::kw_void, tok::kw_typeof,
|
||||
Keywords.kw_instanceof, Keywords.kw_in) ||
|
||||
Tok->isPlacementOperator() || Tok->isBinaryOperator();
|
||||
}
|
||||
|
||||
bool FormatTokenLexer::canPrecedeRegexLiteral(FormatToken *Prev) {
|
||||
|
@ -1639,6 +1639,31 @@ private:
|
||||
case tok::kw_operator:
|
||||
if (Style.isProto())
|
||||
break;
|
||||
// Handle C++ user-defined conversion function.
|
||||
if (IsCpp && CurrentToken) {
|
||||
const auto *Info = CurrentToken->Tok.getIdentifierInfo();
|
||||
// What follows Tok is an identifier or a non-operator keyword.
|
||||
if (Info && !(CurrentToken->isPlacementOperator() ||
|
||||
CurrentToken->is(tok::kw_co_await) ||
|
||||
Info->isCPlusPlusOperatorKeyword())) {
|
||||
FormatToken *LParen;
|
||||
if (CurrentToken->startsSequence(tok::kw_decltype, tok::l_paren,
|
||||
tok::kw_auto, tok::r_paren)) {
|
||||
// Skip `decltype(auto)`.
|
||||
LParen = CurrentToken->Next->Next->Next->Next;
|
||||
} else {
|
||||
// Skip to l_paren.
|
||||
for (LParen = CurrentToken->Next;
|
||||
LParen && LParen->isNot(tok::l_paren); LParen = LParen->Next) {
|
||||
}
|
||||
}
|
||||
if (LParen && LParen->is(tok::l_paren)) {
|
||||
Tok->setFinalizedType(TT_FunctionDeclarationName);
|
||||
LParen->setFinalizedType(TT_FunctionDeclarationLParen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (CurrentToken &&
|
||||
!CurrentToken->isOneOf(tok::l_paren, tok::semi, tok::r_paren)) {
|
||||
if (CurrentToken->isOneOf(tok::star, tok::amp))
|
||||
@ -2999,7 +3024,7 @@ private:
|
||||
return TT_UnaryOperator;
|
||||
if (PrevToken->is(TT_TypeName))
|
||||
return TT_PointerOrReference;
|
||||
if (PrevToken->isOneOf(tok::kw_new, tok::kw_delete) && Tok.is(tok::ampamp))
|
||||
if (PrevToken->isPlacementOperator() && Tok.is(tok::ampamp))
|
||||
return TT_BinaryOperator;
|
||||
|
||||
const FormatToken *NextToken = Tok.getNextNonComment();
|
||||
@ -3071,12 +3096,10 @@ private:
|
||||
if (InTemplateArgument && NextToken->Tok.isAnyIdentifier())
|
||||
return TT_BinaryOperator;
|
||||
|
||||
// "&&" followed by "(", "*", or "&" is quite unlikely to be two successive
|
||||
// unary "&".
|
||||
if (Tok.is(tok::ampamp) &&
|
||||
NextToken->isOneOf(tok::l_paren, tok::star, tok::amp)) {
|
||||
// "&&" followed by "*" or "&" is quite unlikely to be two successive unary
|
||||
// "&".
|
||||
if (Tok.is(tok::ampamp) && NextToken->isOneOf(tok::star, tok::amp))
|
||||
return TT_BinaryOperator;
|
||||
}
|
||||
|
||||
// This catches some cases where evaluation order is used as control flow:
|
||||
// aaa && aaa->f();
|
||||
@ -3791,7 +3814,7 @@ static bool isFunctionDeclarationName(const LangOptions &LangOpts,
|
||||
return Next;
|
||||
if (Next->is(TT_OverloadedOperator))
|
||||
continue;
|
||||
if (Next->isOneOf(tok::kw_new, tok::kw_delete, tok::kw_co_await)) {
|
||||
if (Next->isPlacementOperator() || Next->is(tok::kw_co_await)) {
|
||||
// For 'new[]' and 'delete[]'.
|
||||
if (Next->Next &&
|
||||
Next->Next->startsSequence(tok::l_square, tok::r_square)) {
|
||||
@ -4802,7 +4825,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
|
||||
spaceRequiredBeforeParens(Right);
|
||||
}
|
||||
if (Style.SpaceBeforeParens == FormatStyle::SBPO_Custom &&
|
||||
Left.isOneOf(tok::kw_new, tok::kw_delete) &&
|
||||
Left.isPlacementOperator() &&
|
||||
Right.isNot(TT_OverloadedOperatorLParen) &&
|
||||
!(Line.MightBeFunctionDecl && Left.is(TT_FunctionDeclarationName))) {
|
||||
const auto *RParen = Right.MatchingParen;
|
||||
@ -4845,7 +4868,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
|
||||
return Style.SpaceBeforeParensOptions.AfterControlStatements ||
|
||||
spaceRequiredBeforeParens(Right);
|
||||
}
|
||||
if (Left.isOneOf(tok::kw_new, tok::kw_delete) ||
|
||||
if (Left.isPlacementOperator() ||
|
||||
(Left.is(tok::r_square) && Left.MatchingParen &&
|
||||
Left.MatchingParen->Previous &&
|
||||
Left.MatchingParen->Previous->is(tok::kw_delete))) {
|
||||
|
@ -10443,27 +10443,17 @@ TEST_F(FormatTest, ReturnTypeBreakingStyle) {
|
||||
"void\n"
|
||||
"A::operator->() {}\n"
|
||||
"void\n"
|
||||
"A::operator void *() {}\n"
|
||||
"A::operator&() {}\n"
|
||||
"void\n"
|
||||
"A::operator void &() {}\n"
|
||||
"void\n"
|
||||
"A::operator void &&() {}\n"
|
||||
"void\n"
|
||||
"A::operator char *() {}\n"
|
||||
"A::operator&&() {}\n"
|
||||
"void\n"
|
||||
"A::operator[]() {}\n"
|
||||
"void\n"
|
||||
"A::operator!() {}\n"
|
||||
"void\n"
|
||||
"A::operator**() {}\n"
|
||||
"void\n"
|
||||
"A::operator<Foo> *() {}\n"
|
||||
"void\n"
|
||||
"A::operator<Foo> **() {}\n"
|
||||
"void\n"
|
||||
"A::operator<Foo> &() {}\n"
|
||||
"void\n"
|
||||
"A::operator void **() {}",
|
||||
"A::operator<Foo> &() {}\n",
|
||||
Style);
|
||||
verifyFormat("constexpr auto\n"
|
||||
"operator()() const -> reference {}\n"
|
||||
@ -10486,7 +10476,7 @@ TEST_F(FormatTest, ReturnTypeBreakingStyle) {
|
||||
"constexpr auto\n"
|
||||
"operator void &() const -> reference {}\n"
|
||||
"constexpr auto\n"
|
||||
"operator void &&() const -> reference {}\n"
|
||||
"operator&&() const -> reference {}\n"
|
||||
"constexpr auto\n"
|
||||
"operator char *() const -> reference {}\n"
|
||||
"constexpr auto\n"
|
||||
@ -28032,6 +28022,16 @@ TEST_F(FormatTest, BreakAfterAttributes) {
|
||||
" --d;",
|
||||
CtrlStmtCode, Style);
|
||||
|
||||
verifyFormat("[[nodiscard]]\n"
|
||||
"operator bool();\n"
|
||||
"[[nodiscard]]\n"
|
||||
"operator bool() {\n"
|
||||
" return true;\n"
|
||||
"}",
|
||||
"[[nodiscard]] operator bool();\n"
|
||||
"[[nodiscard]] operator bool() { return true; }",
|
||||
Style);
|
||||
|
||||
constexpr StringRef CtorDtorCode("struct Foo {\n"
|
||||
" [[deprecated]] Foo();\n"
|
||||
" [[deprecated]] Foo() {}\n"
|
||||
|
@ -3856,6 +3856,56 @@ TEST_F(TokenAnnotatorTest, AfterPPDirective) {
|
||||
EXPECT_TOKEN(Tokens[2], tok::minusminus, TT_AfterPPDirective);
|
||||
}
|
||||
|
||||
TEST_F(TokenAnnotatorTest, UserDefinedConversionFunction) {
|
||||
auto Tokens = annotate("operator int(void);");
|
||||
ASSERT_EQ(Tokens.size(), 7u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[0], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
|
||||
Tokens = annotate("explicit operator int *();");
|
||||
ASSERT_EQ(Tokens.size(), 8u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[1], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[3], tok::star, TT_PointerOrReference);
|
||||
EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
|
||||
Tokens = annotate("operator int &();");
|
||||
ASSERT_EQ(Tokens.size(), 7u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[0], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[2], tok::amp, TT_PointerOrReference);
|
||||
EXPECT_TOKEN(Tokens[3], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
|
||||
Tokens = annotate("operator auto() const { return 2; }");
|
||||
ASSERT_EQ(Tokens.size(), 11u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[0], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
EXPECT_TOKEN(Tokens[4], tok::kw_const, TT_TrailingAnnotation);
|
||||
EXPECT_TOKEN(Tokens[5], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("operator decltype(auto)() const;");
|
||||
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[0], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_TypeDeclarationParen);
|
||||
EXPECT_TOKEN(Tokens[4], tok::r_paren, TT_TypeDeclarationParen);
|
||||
EXPECT_TOKEN(Tokens[5], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
EXPECT_TOKEN(Tokens[7], tok::kw_const, TT_TrailingAnnotation);
|
||||
|
||||
Tokens = annotate("virtual operator Foo() = 0;");
|
||||
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[1], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[3], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
|
||||
Tokens = annotate("operator Foo() override { return Foo(); }");
|
||||
ASSERT_EQ(Tokens.size(), 13u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[0], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[2], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
EXPECT_TOKEN(Tokens[5], tok::l_brace, TT_FunctionLBrace);
|
||||
|
||||
Tokens = annotate("friend Bar::operator Foo();");
|
||||
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
|
||||
EXPECT_TOKEN(Tokens[3], tok::kw_operator, TT_FunctionDeclarationName);
|
||||
EXPECT_TOKEN(Tokens[5], tok::l_paren, TT_FunctionDeclarationLParen);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace format
|
||||
} // namespace clang
|
||||
|
Loading…
x
Reference in New Issue
Block a user