1 //===--- RedundantSmartptrGetCheck.cpp - clang-tidy -----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "RedundantSmartptrGetCheck.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/Lex/Lexer.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace readability { 19 20 namespace { 21 internal::Matcher<Expr> callToGet(internal::Matcher<Decl> OnClass) { 22 return cxxMemberCallExpr( 23 on(expr(anyOf(hasType(OnClass), 24 hasType(qualType( 25 pointsTo(decl(OnClass).bind("ptr_to_ptr")))))) 26 .bind("smart_pointer")), 27 unless(callee(memberExpr(hasObjectExpression(cxxThisExpr())))), 28 callee(cxxMethodDecl( 29 hasName("get"), 30 returns(qualType(pointsTo(type().bind("getType"))))))) 31 .bind("redundant_get"); 32 } 33 34 void registerMatchersForGetArrowStart(MatchFinder *Finder, 35 MatchFinder::MatchCallback *Callback) { 36 const auto QuacksLikeASmartptr = recordDecl( 37 recordDecl().bind("duck_typing"), 38 has(cxxMethodDecl(hasName("operator->"), 39 returns(qualType(pointsTo(type().bind("op->Type")))))), 40 has(cxxMethodDecl(hasName("operator*"), returns(qualType(references( 41 type().bind("op*Type"))))))); 42 43 // Catch 'ptr.get()->Foo()' 44 Finder->addMatcher(memberExpr(expr().bind("memberExpr"), isArrow(), 45 hasObjectExpression(ignoringImpCasts( 46 callToGet(QuacksLikeASmartptr)))), 47 Callback); 48 49 // Catch '*ptr.get()' or '*ptr->get()' 50 Finder->addMatcher( 51 unaryOperator(hasOperatorName("*"), 52 hasUnaryOperand(callToGet(QuacksLikeASmartptr))), 53 Callback); 54 } 55 56 void registerMatchersForGetEquals(MatchFinder *Finder, 57 MatchFinder::MatchCallback *Callback) { 58 // This one is harder to do with duck typing. 59 // The operator==/!= that we are looking for might be member or non-member, 60 // might be on global namespace or found by ADL, might be a template, etc. 61 // For now, lets keep a list of known standard types. 62 63 const auto IsAKnownSmartptr = recordDecl( 64 anyOf(hasName("::std::unique_ptr"), hasName("::std::shared_ptr"))); 65 66 // Matches against nullptr. 67 Finder->addMatcher( 68 binaryOperator( 69 anyOf(hasOperatorName("=="), hasOperatorName("!=")), 70 hasEitherOperand(ignoringImpCasts(cxxNullPtrLiteralExpr())), 71 hasEitherOperand(callToGet(IsAKnownSmartptr))), 72 Callback); 73 // TODO: Catch ptr.get() == other_ptr.get() 74 } 75 76 77 } // namespace 78 79 void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) { 80 // Only register the matchers for C++; the functionality currently does not 81 // provide any benefit to other languages, despite being benign. 82 if (!getLangOpts().CPlusPlus) 83 return; 84 85 registerMatchersForGetArrowStart(Finder, this); 86 registerMatchersForGetEquals(Finder, this); 87 } 88 89 namespace { 90 bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) { 91 if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr) 92 return true; 93 // Verify that the types match. 94 // We can't do this on the matcher because the type nodes can be different, 95 // even though they represent the same type. This difference comes from how 96 // the type is referenced (eg. through a typedef, a type trait, etc). 97 const Type *OpArrowType = 98 Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType(); 99 const Type *OpStarType = 100 Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType(); 101 const Type *GetType = 102 Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType(); 103 return OpArrowType == OpStarType && OpArrowType == GetType; 104 } 105 } // namespace 106 107 void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) { 108 if (!allReturnTypesMatch(Result)) return; 109 110 bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr; 111 bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr; 112 const Expr *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get"); 113 const Expr *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer"); 114 115 if (IsPtrToPtr && IsMemberExpr) { 116 // Ignore this case (eg. Foo->get()->DoSomething()); 117 return; 118 } 119 120 StringRef SmartptrText = Lexer::getSourceText( 121 CharSourceRange::getTokenRange(Smartptr->getSourceRange()), 122 *Result.SourceManager, Result.Context->getLangOpts()); 123 // Replace foo->get() with *foo, and foo.get() with foo. 124 std::string Replacement = Twine(IsPtrToPtr ? "*" : "", SmartptrText).str(); 125 diag(GetCall->getLocStart(), "redundant get() call on smart pointer") 126 << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement); 127 } 128 129 } // namespace readability 130 } // namespace tidy 131 } // namespace clang 132