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