| 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(RT: MD->getReturnType()); |
| 58 | break; |
| 59 | case OO_Arrow: |
| 60 | ArrowReturnType = getLikeReturnType(RT: 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(Str: 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(RD: Node); |
| 103 | if (RT.isNull()) |
| 104 | return false; |
| 105 | return getLikeReturnType(RT: findReturnType(RD: Node, MethodName)) == RT; |
| 106 | } |
| 107 | |
| 108 | AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithValueLike, |
| 109 | clang::StringRef, MethodName) { |
| 110 | auto RT = pointerLikeReturnType(RD: Node); |
| 111 | if (RT.isNull()) |
| 112 | return false; |
| 113 | return valueLikeReturnType(RT: findReturnType(RD: Node, MethodName)) == RT; |
| 114 | } |
| 115 | |
| 116 | AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) { |
| 117 | auto RT = pointerLikeReturnType(RD: Node); |
| 118 | if (RT.isNull()) |
| 119 | return false; |
| 120 | return getLikeReturnType(RT: findReturnType(RD: Node, MethodName: "get" )) == RT || |
| 121 | valueLikeReturnType(RT: findReturnType(RD: Node, MethodName: "value" )) == RT; |
| 122 | } |
| 123 | |
| 124 | AST_MATCHER(clang::CXXRecordDecl, pointerClass) { |
| 125 | return !pointerLikeReturnType(RD: Node).isNull(); |
| 126 | } |
| 127 | |
| 128 | } // namespace |
| 129 | |
| 130 | namespace clang::dataflow { |
| 131 | |
| 132 | ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() { |
| 133 | return cxxOperatorCallExpr( |
| 134 | hasOverloadedOperatorName(Name: "*" ), |
| 135 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
| 136 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())), |
| 137 | ofClass(InnerMatcher: smartPointerClassWithGetOrValue())))); |
| 138 | } |
| 139 | |
| 140 | ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() { |
| 141 | return cxxOperatorCallExpr( |
| 142 | hasOverloadedOperatorName(Name: "->" ), |
| 143 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
| 144 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())), |
| 145 | ofClass(InnerMatcher: smartPointerClassWithGetOrValue())))); |
| 146 | } |
| 147 | |
| 148 | ast_matchers::StatementMatcher isPointerLikeOperatorStar() { |
| 149 | return cxxOperatorCallExpr( |
| 150 | hasOverloadedOperatorName(Name: "*" ), |
| 151 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
| 152 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())), |
| 153 | ofClass(InnerMatcher: pointerClass())))); |
| 154 | } |
| 155 | |
| 156 | ast_matchers::StatementMatcher isPointerLikeOperatorArrow() { |
| 157 | return cxxOperatorCallExpr( |
| 158 | hasOverloadedOperatorName(Name: "->" ), |
| 159 | callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), |
| 160 | returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())), |
| 161 | ofClass(InnerMatcher: pointerClass())))); |
| 162 | } |
| 163 | |
| 164 | ast_matchers::StatementMatcher |
| 165 | isSmartPointerLikeValueMethodCall(clang::StringRef MethodName) { |
| 166 | return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl( |
| 167 | parameterCountIs(N: 0), returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())), |
| 168 | hasName(Name: MethodName), |
| 169 | ofClass(InnerMatcher: smartPointerClassWithValueLike(MethodName))))); |
| 170 | } |
| 171 | |
| 172 | ast_matchers::StatementMatcher |
| 173 | isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) { |
| 174 | return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl( |
| 175 | parameterCountIs(N: 0), returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())), |
| 176 | hasName(Name: MethodName), ofClass(InnerMatcher: 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>(Val: 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 | |