1 #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h" 2 3 #include "clang/AST/CanonicalType.h" 4 #include "clang/AST/DeclCXX.h" 5 #include "clang/ASTMatchers/ASTMatchers.h" 6 #include "clang/ASTMatchers/ASTMatchersMacros.h" 7 #include "clang/Basic/OperatorKinds.h" 8 9 namespace clang::dataflow { 10 11 namespace { 12 13 using ast_matchers::callee; 14 using ast_matchers::cxxMemberCallExpr; 15 using ast_matchers::cxxMethodDecl; 16 using ast_matchers::cxxOperatorCallExpr; 17 using ast_matchers::hasName; 18 using ast_matchers::hasOverloadedOperatorName; 19 using ast_matchers::ofClass; 20 using ast_matchers::parameterCountIs; 21 using ast_matchers::pointerType; 22 using ast_matchers::referenceType; 23 using ast_matchers::returns; 24 25 bool hasSmartPointerClassShape(const CXXRecordDecl &RD, bool &HasGet, 26 bool &HasValue) { 27 // We may want to cache this search, but in current profiles it hasn't shown 28 // up as a hot spot (possibly because there aren't many hits, relatively). 29 bool HasArrow = false; 30 bool HasStar = false; 31 CanQualType StarReturnType, ArrowReturnType, GetReturnType, ValueReturnType; 32 for (const auto *MD : RD.methods()) { 33 // We only consider methods that are const and have zero parameters. 34 // It may be that there is a non-const overload for the method, but 35 // there should at least be a const overload as well. 36 if (!MD->isConst() || MD->getNumParams() != 0) 37 continue; 38 switch (MD->getOverloadedOperator()) { 39 case OO_Star: 40 if (MD->getReturnType()->isReferenceType()) { 41 HasStar = true; 42 StarReturnType = MD->getReturnType() 43 .getNonReferenceType() 44 ->getCanonicalTypeUnqualified(); 45 } 46 break; 47 case OO_Arrow: 48 if (MD->getReturnType()->isPointerType()) { 49 HasArrow = true; 50 ArrowReturnType = MD->getReturnType() 51 ->getPointeeType() 52 ->getCanonicalTypeUnqualified(); 53 } 54 break; 55 case OO_None: { 56 IdentifierInfo *II = MD->getIdentifier(); 57 if (II == nullptr) 58 continue; 59 if (II->isStr("get")) { 60 if (MD->getReturnType()->isPointerType()) { 61 HasGet = true; 62 GetReturnType = MD->getReturnType() 63 ->getPointeeType() 64 ->getCanonicalTypeUnqualified(); 65 } 66 } else if (II->isStr("value")) { 67 if (MD->getReturnType()->isReferenceType()) { 68 HasValue = true; 69 ValueReturnType = MD->getReturnType() 70 .getNonReferenceType() 71 ->getCanonicalTypeUnqualified(); 72 } 73 } 74 } break; 75 default: 76 break; 77 } 78 } 79 80 if (!HasStar || !HasArrow || StarReturnType != ArrowReturnType) 81 return false; 82 HasGet = HasGet && (GetReturnType == StarReturnType); 83 HasValue = HasValue && (ValueReturnType == StarReturnType); 84 return true; 85 } 86 87 } // namespace 88 } // namespace clang::dataflow 89 90 // AST_MATCHER macros create an "internal" namespace, so we put it in 91 // its own anonymous namespace instead of in clang::dataflow. 92 namespace { 93 94 AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGet) { 95 bool HasGet = false; 96 bool HasValue = false; 97 bool HasStarAndArrow = 98 clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue); 99 return HasStarAndArrow && HasGet; 100 } 101 102 AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithValue) { 103 bool HasGet = false; 104 bool HasValue = false; 105 bool HasStarAndArrow = 106 clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue); 107 return HasStarAndArrow && HasValue; 108 } 109 110 AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) { 111 bool HasGet = false; 112 bool HasValue = false; 113 bool HasStarAndArrow = 114 clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue); 115 return HasStarAndArrow && (HasGet || HasValue); 116 } 117 118 } // namespace 119 120 namespace clang::dataflow { 121 122 ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() { 123 return cxxOperatorCallExpr( 124 hasOverloadedOperatorName("*"), 125 callee(cxxMethodDecl(parameterCountIs(0), returns(referenceType()), 126 ofClass(smartPointerClassWithGetOrValue())))); 127 } 128 129 ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() { 130 return cxxOperatorCallExpr( 131 hasOverloadedOperatorName("->"), 132 callee(cxxMethodDecl(parameterCountIs(0), returns(pointerType()), 133 ofClass(smartPointerClassWithGetOrValue())))); 134 } 135 136 ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() { 137 return cxxMemberCallExpr(callee( 138 cxxMethodDecl(parameterCountIs(0), returns(referenceType()), 139 hasName("value"), ofClass(smartPointerClassWithValue())))); 140 } 141 142 ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall() { 143 return cxxMemberCallExpr(callee( 144 cxxMethodDecl(parameterCountIs(0), returns(pointerType()), hasName("get"), 145 ofClass(smartPointerClassWithGet())))); 146 } 147 148 const FunctionDecl * 149 getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) { 150 const FunctionDecl *CanonicalCallee = nullptr; 151 const CXXMethodDecl *Callee = 152 cast_or_null<CXXMethodDecl>(CE->getDirectCallee()); 153 if (Callee == nullptr) 154 return nullptr; 155 const CXXRecordDecl *RD = Callee->getParent(); 156 if (RD == nullptr) 157 return nullptr; 158 for (const auto *MD : RD->methods()) { 159 if (MD->getOverloadedOperator() == OO_Star && MD->isConst() && 160 MD->getNumParams() == 0 && MD->getReturnType()->isReferenceType()) { 161 CanonicalCallee = MD; 162 break; 163 } 164 } 165 return CanonicalCallee; 166 } 167 168 } // namespace clang::dataflow 169