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()
30 ->getCanonicalTypeUnqualified()
31 .getUnqualifiedType();
32 }
33 return {};
34}
35
36CanQualType valueLikeReturnType(QualType RT) {
37 if (!RT.isNull() && RT->isReferenceType()) {
38 return RT.getNonReferenceType()
39 ->getCanonicalTypeUnqualified()
40 .getUnqualifiedType();
41 }
42 return {};
43}
44
45CanQualType 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(RT: MD->getReturnType());
58 break;
59 case OO_Arrow:
60 ArrowReturnType = getLikeReturnType(RT: 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
73QualType 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(Str: 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.
93namespace {
94
95using clang::dataflow::findReturnType;
96using clang::dataflow::getLikeReturnType;
97using clang::dataflow::pointerLikeReturnType;
98using clang::dataflow::valueLikeReturnType;
99
100AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithGetLike,
101 clang::StringRef, MethodName) {
102 auto RT = pointerLikeReturnType(RD: Node);
103 if (RT.isNull())
104 return false;
105 return getLikeReturnType(RT: findReturnType(RD: Node, MethodName)) == RT;
106}
107
108AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithValueLike,
109 clang::StringRef, MethodName) {
110 auto RT = pointerLikeReturnType(RD: Node);
111 if (RT.isNull())
112 return false;
113 return valueLikeReturnType(RT: findReturnType(RD: Node, MethodName)) == RT;
114}
115
116AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
117 auto RT = pointerLikeReturnType(RD: Node);
118 if (RT.isNull())
119 return false;
120 return getLikeReturnType(RT: findReturnType(RD: Node, MethodName: "get")) == RT ||
121 valueLikeReturnType(RT: findReturnType(RD: Node, MethodName: "value")) == RT;
122}
123
124AST_MATCHER(clang::CXXRecordDecl, pointerClass) {
125 return !pointerLikeReturnType(RD: Node).isNull();
126}
127
128} // namespace
129
130namespace clang::dataflow {
131
132ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() {
133 return cxxOperatorCallExpr(
134 hasOverloadedOperatorName(Name: "*"),
135 callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0),
136 returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())),
137 ofClass(InnerMatcher: smartPointerClassWithGetOrValue()))));
138}
139
140ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {
141 return cxxOperatorCallExpr(
142 hasOverloadedOperatorName(Name: "->"),
143 callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0),
144 returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())),
145 ofClass(InnerMatcher: smartPointerClassWithGetOrValue()))));
146}
147
148ast_matchers::StatementMatcher isPointerLikeOperatorStar() {
149 return cxxOperatorCallExpr(
150 hasOverloadedOperatorName(Name: "*"),
151 callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0),
152 returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())),
153 ofClass(InnerMatcher: pointerClass()))));
154}
155
156ast_matchers::StatementMatcher isPointerLikeOperatorArrow() {
157 return cxxOperatorCallExpr(
158 hasOverloadedOperatorName(Name: "->"),
159 callee(InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0),
160 returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())),
161 ofClass(InnerMatcher: pointerClass()))));
162}
163
164ast_matchers::StatementMatcher
165isSmartPointerLikeValueMethodCall(clang::StringRef MethodName) {
166 return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl(
167 parameterCountIs(N: 0), returns(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType())),
168 hasName(Name: MethodName),
169 ofClass(InnerMatcher: smartPointerClassWithValueLike(MethodName)))));
170}
171
172ast_matchers::StatementMatcher
173isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) {
174 return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl(
175 parameterCountIs(N: 0), returns(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType())),
176 hasName(Name: MethodName), ofClass(InnerMatcher: smartPointerClassWithGetLike(MethodName)))));
177}
178
179const FunctionDecl *
180getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) {
181 const FunctionDecl *CanonicalCallee = nullptr;
182 const CXXMethodDecl *Callee =
183 cast_or_null<CXXMethodDecl>(Val: 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