xref: /llvm-project/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp (revision 72a28a3bf0b539bcdfd8f41905675ce6a890c0ac)
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