[clang] 91328db - [clang-format] Correctly annotate user-defined conversion functions (#131434)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 16 16:11:43 PDT 2025
Author: Owen Pan
Date: 2025-03-16T16:11:39-07:00
New Revision: 91328dbae986dfa93cf2acef0a93361fd5ced66d
URL: https://github.com/llvm/llvm-project/commit/91328dbae986dfa93cf2acef0a93361fd5ced66d
DIFF: https://github.com/llvm/llvm-project/commit/91328dbae986dfa93cf2acef0a93361fd5ced66d.diff
LOG: [clang-format] Correctly annotate user-defined conversion functions (#131434)
Also fix/delete existing invalid/redundant test cases.
Fix #130894
Added:
Modified:
clang/lib/Format/FormatToken.h
clang/lib/Format/FormatTokenLexer.cpp
clang/lib/Format/TokenAnnotator.cpp
clang/unittests/Format/FormatTest.cpp
clang/unittests/Format/TokenAnnotatorTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 77935e75d4b4c..3808872d227a9 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -746,6 +746,10 @@ struct FormatToken {
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:
diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp
index 16f0a76f3a954..eed54a11684b5 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -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) {
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 08539de405c67..35577cd6db7a1 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1639,6 +1639,31 @@ class AnnotatingParser {
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 @@ class AnnotatingParser {
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 @@ class AnnotatingParser {
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))) {
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 9864e7ec1b2ec..5df7865f5a629 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -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"
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 5e2d301c5d1f3..1ff785110fc34 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -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
More information about the cfe-commits
mailing list