|
| 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 | + } |
| 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 | +ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() { |
| 136 | + return cxxMemberCallExpr(callee( |
| 137 | + cxxMethodDecl(parameterCountIs(0), returns(referenceType()), |
| 138 | + hasName("value"), ofClass(smartPointerClassWithValue())))); |
| 139 | +} |
| 140 | + |
| 141 | +ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall() { |
| 142 | + return cxxMemberCallExpr(callee( |
| 143 | + cxxMethodDecl(parameterCountIs(0), returns(pointerType()), hasName("get"), |
| 144 | + ofClass(smartPointerClassWithGet())))); |
| 145 | +} |
| 146 | + |
| 147 | +} // namespace clang::dataflow |
0 commit comments