diff --git a/clang/lib/Tooling/Transformer/Stencil.cpp b/clang/lib/Tooling/Transformer/Stencil.cpp index 9223b4a290a08..4dc3544bb06db 100644 --- a/clang/lib/Tooling/Transformer/Stencil.cpp +++ b/clang/lib/Tooling/Transformer/Stencil.cpp @@ -73,6 +73,21 @@ static bool isSmartPointerType(QualType Ty, ASTContext &Context) { return match(SmartPointer, Ty, Context).size() > 0; } +// Identifies use of `operator*` on smart pointers, and returns the underlying +// smart-pointer expression; otherwise, returns null. +static const Expr *isSmartDereference(const Expr &E, ASTContext &Context) { + using namespace ::clang::ast_matchers; + + const auto HasOverloadedArrow = cxxRecordDecl(hasMethod(cxxMethodDecl( + hasOverloadedOperatorName("->"), returns(qualType(pointsTo(type())))))); + // Verify it is a smart pointer by finding `operator->` in the class + // declaration. + auto Deref = cxxOperatorCallExpr( + hasOverloadedOperatorName("*"), hasUnaryOperand(expr().bind("arg")), + callee(cxxMethodDecl(ofClass(HasOverloadedArrow)))); + return selectFirst("arg", match(Deref, E, Context)); +} + namespace { // An arbitrary fragment of code within a stencil. class RawTextStencil : public StencilInterface { @@ -309,6 +324,10 @@ class AccessStencil : public StencilInterface { } } S = tooling::buildArrow(*E, *Match.Context); + } else if (const auto *Operand = isSmartDereference(*E, *Match.Context)) { + // `buildDot` already handles the built-in dereference operator, so we + // only need to catch overloaded `operator*`. + S = tooling::buildArrow(*Operand, *Match.Context); } else { S = tooling::buildDot(*E, *Match.Context); } diff --git a/clang/unittests/Tooling/StencilTest.cpp b/clang/unittests/Tooling/StencilTest.cpp index df05e6deeb577..0b5b9691dadc5 100644 --- a/clang/unittests/Tooling/StencilTest.cpp +++ b/clang/unittests/Tooling/StencilTest.cpp @@ -407,7 +407,7 @@ TEST_F(StencilTest, AccessOpSmartPointerDereference) { *x; )cc"; StringRef Id = "id"; - testExpr(Id, Snippet, access(Id, "field"), "(*x).field"); + testExpr(Id, Snippet, access(Id, "field"), "x->field"); } TEST_F(StencilTest, AccessOpSmartPointerMemberCall) {