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