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