ymandel created this revision. ymandel added reviewers: tdl-g, sbenza. ymandel requested review of this revision. Herald added a project: clang.
Stencils `maybeDeref` and `maybeAddressOf` are designed to handle nodes that may be pointers. Currently, they only handle native pointers. This patch extends the support to recognize smart pointers and handle them as well. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D93637 Files: clang/include/clang/Tooling/Transformer/Stencil.h clang/lib/Tooling/Transformer/Stencil.cpp clang/unittests/Tooling/StencilTest.cpp
Index: clang/unittests/Tooling/StencilTest.cpp =================================================================== --- clang/unittests/Tooling/StencilTest.cpp +++ clang/unittests/Tooling/StencilTest.cpp @@ -8,6 +8,7 @@ #include "clang/Tooling/Transformer/Stencil.h" #include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Expr.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/FixIt.h" #include "clang/Tooling/Tooling.h" @@ -30,11 +31,17 @@ // Create a valid translation-unit from a statement. static std::string wrapSnippet(StringRef StatementCode) { - return ("namespace N { class C {}; } " - "namespace { class AnonC {}; } " - "struct S { int field; }; auto stencil_test_snippet = []{" + - StatementCode + "};") - .str(); + constexpr char Preface[] = R"cc( + namespace N { class C {}; } + namespace { class AnonC {}; } + struct S { int Field; }; + struct Smart { + S* operator->() const; + S& operator*() const; + }; + auto stencil_test_snippet = []{ + )cc"; + return (Preface + StatementCode + "};").str(); } static DeclarationMatcher wrapMatcher(const StatementMatcher &Matcher) { @@ -260,6 +267,25 @@ testExpr(Id, "int x; &x;", maybeDeref(Id), "x"); } +TEST_F(StencilTest, MaybeDerefSmartPointer) { + StringRef Id = "id"; + std::string Snippet = R"cc( + Smart x; + x; + )cc"; + testExpr(Id, Snippet, maybeDeref(Id), "*x"); +} + +TEST_F(StencilTest, MaybeDerefSmartPointerFromMemberExpr) { + StringRef Id = "id"; + std::string Snippet = "Smart x; x->Field;"; + auto StmtMatch = + matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind(Id)))); + ASSERT_TRUE(StmtMatch); + const Stencil Stencil = maybeDeref(Id); + EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("*x")); +} + TEST_F(StencilTest, MaybeAddressOfPointer) { StringRef Id = "id"; testExpr(Id, "int *x; x;", maybeAddressOf(Id), "x"); @@ -280,6 +306,26 @@ testExpr(Id, "int *x; *x;", addressOf(Id), "x"); } +TEST_F(StencilTest, MaybeAddressOfSmartPointer) { + StringRef Id = "id"; + testExpr(Id, "Smart x; x;", maybeAddressOf(Id), "x"); +} + +TEST_F(StencilTest, MaybeAddressOfSmartPointerFromMemberCall) { + StringRef Id = "id"; + std::string Snippet = "Smart x; x->Field;"; + auto StmtMatch = + matchStmt(Snippet, memberExpr(hasObjectExpression(expr().bind(Id)))); + ASSERT_TRUE(StmtMatch); + const Stencil Stencil = maybeAddressOf(Id); + EXPECT_THAT_EXPECTED(Stencil->eval(StmtMatch->Result), HasValue("x")); +} + +TEST_F(StencilTest, MaybeAddressOfSmartPointerDerefNoCancel) { + StringRef Id = "id"; + testExpr(Id, "Smart x; *x;", maybeAddressOf(Id), "&*x"); +} + TEST_F(StencilTest, AccessOpValue) { StringRef Snippet = R"cc( S x; Index: clang/lib/Tooling/Transformer/Stencil.cpp =================================================================== --- clang/lib/Tooling/Transformer/Stencil.cpp +++ clang/lib/Tooling/Transformer/Stencil.cpp @@ -195,6 +195,22 @@ return printNode(Data.Id, Match, Result); } +// FIXME: Consider memoizing this function using the `ASTContext`. +static bool isSmartPointerType(QualType Ty, ASTContext &Context) { + using namespace ::clang::ast_matchers; + + auto KnownSmartPointer = + cxxRecordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr")); + const auto QuacksLikeASmartPointer = cxxRecordDecl( + hasMethod(cxxMethodDecl(hasOverloadedOperatorName("->"), + returns(qualType(pointsTo(type()))))), + hasMethod(cxxMethodDecl(hasOverloadedOperatorName("*"), + returns(qualType(references(type())))))); + const auto SmartPointer = qualType(hasDeclaration( + cxxRecordDecl(anyOf(KnownSmartPointer, QuacksLikeASmartPointer)))); + return match(SmartPointer, Ty, Context).size() > 0; +} + Error evalData(const UnaryOperationData &Data, const MatchFinder::MatchResult &Match, std::string *Result) { // The `Describe` operation can be applied to any node, not just expressions, @@ -215,17 +231,37 @@ Source = tooling::buildDereference(*E, *Match.Context); break; case UnaryNodeOperator::MaybeDeref: - if (!E->getType()->isAnyPointerType()) { - *Result += tooling::getText(*E, *Match.Context); - return Error::success(); + if (E->getType()->isAnyPointerType() || + isSmartPointerType(E->getType(), *Match.Context)) { + // Strip off any operator->. This can only occur inside an actual arrow + // member access, so we treat it as equivalent to an actual object + // expression. + if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { + if (OpCall->getOperator() == clang::OO_Arrow && + OpCall->getNumArgs() == 1) { + E = OpCall->getArg(0); + } + } + Source = tooling::buildDereference(*E, *Match.Context); + break; } - Source = tooling::buildDereference(*E, *Match.Context); - break; + *Result += tooling::getText(*E, *Match.Context); + return Error::success(); case UnaryNodeOperator::AddressOf: Source = tooling::buildAddressOf(*E, *Match.Context); break; case UnaryNodeOperator::MaybeAddressOf: - if (E->getType()->isAnyPointerType()) { + if (E->getType()->isAnyPointerType() || + isSmartPointerType(E->getType(), *Match.Context)) { + // Strip off any operator->. This can only occur inside an actual arrow + // member access, so we treat it as equivalent to an actual object + // expression. + if (const auto *OpCall = dyn_cast<clang::CXXOperatorCallExpr>(E)) { + if (OpCall->getOperator() == clang::OO_Arrow && + OpCall->getNumArgs() == 1) { + E = OpCall->getArg(0); + } + } *Result += tooling::getText(*E, *Match.Context); return Error::success(); } Index: clang/include/clang/Tooling/Transformer/Stencil.h =================================================================== --- clang/include/clang/Tooling/Transformer/Stencil.h +++ clang/include/clang/Tooling/Transformer/Stencil.h @@ -82,7 +82,6 @@ /// If \p ExprId is of pointer type, constructs an idiomatic dereferencing of /// the expression bound to \p ExprId, including wrapping it in parentheses, if /// needed. Otherwise, generates the original expression source. -/// FIXME: Identify smart-pointers as pointer types. Stencil maybeDeref(llvm::StringRef ExprId); /// Constructs an expression that idiomatically takes the address of the @@ -94,7 +93,6 @@ /// idiomatically takes the address of the expression bound to \p ExprId, /// including wrapping \p ExprId in parentheses, if needed. Otherwise, generates /// the original expression source. -/// FIXME: Identify smart-pointers as pointer types. Stencil maybeAddressOf(llvm::StringRef ExprId); /// Constructs a `MemberExpr` that accesses the named member (\p Member) of the
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits