xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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 
getLikeReturnType(QualType RT)27 CanQualType getLikeReturnType(QualType RT) {
28   if (!RT.isNull() && RT->isPointerType()) {
29     return RT->getPointeeType()
30         ->getCanonicalTypeUnqualified()
31         .getUnqualifiedType();
32   }
33   return {};
34 }
35 
valueLikeReturnType(QualType RT)36 CanQualType valueLikeReturnType(QualType RT) {
37   if (!RT.isNull() && RT->isReferenceType()) {
38     return RT.getNonReferenceType()
39         ->getCanonicalTypeUnqualified()
40         .getUnqualifiedType();
41   }
42   return {};
43 }
44 
pointerLikeReturnType(const CXXRecordDecl & RD)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 
findReturnType(const CXXRecordDecl & RD,StringRef MethodName)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 
AST_MATCHER_P(clang::CXXRecordDecl,smartPointerClassWithGetLike,clang::StringRef,MethodName)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 
AST_MATCHER_P(clang::CXXRecordDecl,smartPointerClassWithValueLike,clang::StringRef,MethodName)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 
AST_MATCHER(clang::CXXRecordDecl,smartPointerClassWithGetOrValue)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 
AST_MATCHER(clang::CXXRecordDecl,pointerClass)124 AST_MATCHER(clang::CXXRecordDecl, pointerClass) {
125   return !pointerLikeReturnType(Node).isNull();
126 }
127 
128 } // namespace
129 
130 namespace clang::dataflow {
131 
isSmartPointerLikeOperatorStar()132 ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() {
133   return cxxOperatorCallExpr(
134       hasOverloadedOperatorName("*"),
135       callee(cxxMethodDecl(parameterCountIs(0),
136                            returns(hasCanonicalType(referenceType())),
137                            ofClass(smartPointerClassWithGetOrValue()))));
138 }
139 
isSmartPointerLikeOperatorArrow()140 ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {
141   return cxxOperatorCallExpr(
142       hasOverloadedOperatorName("->"),
143       callee(cxxMethodDecl(parameterCountIs(0),
144                            returns(hasCanonicalType(pointerType())),
145                            ofClass(smartPointerClassWithGetOrValue()))));
146 }
147 
isPointerLikeOperatorStar()148 ast_matchers::StatementMatcher isPointerLikeOperatorStar() {
149   return cxxOperatorCallExpr(
150       hasOverloadedOperatorName("*"),
151       callee(cxxMethodDecl(parameterCountIs(0),
152                            returns(hasCanonicalType(referenceType())),
153                            ofClass(pointerClass()))));
154 }
155 
isPointerLikeOperatorArrow()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
isSmartPointerLikeValueMethodCall(clang::StringRef MethodName)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
isSmartPointerLikeGetMethodCall(clang::StringRef MethodName)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 *
getCanonicalSmartPointerLikeOperatorCallee(const CallExpr * CE)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