diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -1944,6 +1944,7 @@ for (const CastExpr *E = this; E; E = dyn_cast(SubExpr)) { SubExpr = skipImplicitTemporary(E->getSubExpr()); + SubExpr = SubExpr->IgnoreImplicit(); if (E->getCastKind() == CK_ConstructorConversion) return cast(SubExpr)->getConstructor(); diff --git a/clang/unittests/Tooling/CastExprTest.cpp b/clang/unittests/Tooling/CastExprTest.cpp --- a/clang/unittests/Tooling/CastExprTest.cpp +++ b/clang/unittests/Tooling/CastExprTest.cpp @@ -14,12 +14,19 @@ struct CastExprVisitor : TestVisitor { std::function OnExplicitCast; + std::function OnCast; bool VisitExplicitCastExpr(ExplicitCastExpr *Expr) { if (OnExplicitCast) OnExplicitCast(Expr); return true; } + + bool VisitCastExpr(CastExpr *Expr) { + if (OnCast) + OnCast(Expr); + return true; + } }; TEST(CastExprTest, GetSubExprAsWrittenThroughMaterializedTemporary) { @@ -54,4 +61,57 @@ CastExprVisitor::Lang_CXX2a); } +// Verify that getConversionFunction looks through a ConstantExpr for implicit +// constructor conversions (https://github.com/llvm/llvm-project/issues/53044): +// +// `-ImplicitCastExpr 'S' +// `-ConstantExpr 'S' +// |-value: Struct +// `-CXXConstructExpr 'S' 'void (const char *)' +// `-ImplicitCastExpr 'const char *' +// `-StringLiteral 'const char [7]' lvalue "foobar" +TEST(CastExprTest, GetCtorConversionFunctionThroughConstantExpr) { + CastExprVisitor Visitor; + Visitor.OnCast = [](CastExpr *Expr) { + if (Expr->getCastKind() == CK_ConstructorConversion) { + auto *Conv = Expr->getConversionFunction(); + EXPECT_TRUE(isa(Conv)) + << "Expected CXXConstructorDecl, but saw " << Conv->getDeclKindName(); + } + }; + Visitor.runOver("struct X { consteval X(const char *) {} };\n" + "void f() { X x = \"foobar\"; }\n", + CastExprVisitor::Lang_CXX2a); +} + +// Verify that getConversionFunction looks through a ConstantExpr for implicit +// user-defined conversions. +// +// `-ImplicitCastExpr 'const char *' +// `-ConstantExpr 'const char *' +// |-value: LValue +// `-CXXMemberCallExpr 'const char *' +// `-MemberExpr '' .operator const char * +// `-DeclRefExpr 'const X' lvalue Var 'x' 'const X' +TEST(CastExprTest, GetUserDefinedConversionFunctionThroughConstantExpr) { + CastExprVisitor Visitor; + Visitor.OnCast = [](CastExpr *Expr) { + if (Expr->getCastKind() == CK_UserDefinedConversion) { + auto *Conv = Expr->getConversionFunction(); + EXPECT_TRUE(isa(Conv)) + << "Expected CXXMethodDecl, but saw " << Conv->getDeclKindName(); + } + }; + Visitor.runOver("struct X {\n" + " consteval operator const char *() const {\n" + " return nullptr;\n" + " }\n" + "};\n" + "const char *f() {\n" + " constexpr X x;\n" + " return x;\n" + "}\n", + CastExprVisitor::Lang_CXX2a); +} + } // namespace