Skip to content

Commit 1cbcaa4

Browse files
authored
[clang][dataflow] Add matcher for pointer-like types to be cached (#132314)
This is used e.g. for iterators.
1 parent b3e64fb commit 1cbcaa4

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ namespace clang::dataflow {
5858
/// for `std::optional`, we assume the (Matcher, TransferFunction) case
5959
/// with custom handling is ordered early so that these generic cases
6060
/// do not trigger.
61+
ast_matchers::StatementMatcher isPointerLikeOperatorStar();
6162
ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
63+
ast_matchers::StatementMatcher isPointerLikeOperatorArrow();
6264
ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow();
6365
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall();
6466
ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall();

clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
120120
return HasStarAndArrow && (HasGet || HasValue);
121121
}
122122

123+
AST_MATCHER(clang::CXXRecordDecl, pointerClass) {
124+
bool HasGet = false;
125+
bool HasValue = false;
126+
bool HasStarAndArrow =
127+
clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
128+
return HasStarAndArrow;
129+
}
130+
123131
} // namespace
124132

125133
namespace clang::dataflow {
@@ -140,6 +148,22 @@ ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {
140148
ofClass(smartPointerClassWithGetOrValue()))));
141149
}
142150

151+
ast_matchers::StatementMatcher isPointerLikeOperatorStar() {
152+
return cxxOperatorCallExpr(
153+
hasOverloadedOperatorName("*"),
154+
callee(cxxMethodDecl(parameterCountIs(0),
155+
returns(hasCanonicalType(referenceType())),
156+
ofClass(pointerClass()))));
157+
}
158+
159+
ast_matchers::StatementMatcher isPointerLikeOperatorArrow() {
160+
return cxxOperatorCallExpr(
161+
hasOverloadedOperatorName("->"),
162+
callee(cxxMethodDecl(parameterCountIs(0),
163+
returns(hasCanonicalType(pointerType())),
164+
ofClass(pointerClass()))));
165+
}
166+
143167
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() {
144168
return cxxMemberCallExpr(callee(cxxMethodDecl(
145169
parameterCountIs(0), returns(hasCanonicalType(referenceType())),

clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,62 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) {
4545
EXPECT_TRUE(matches(Decls,
4646
"int target(std::unique_ptr<S> P) { return (*P).i; }",
4747
isSmartPointerLikeOperatorStar()));
48+
EXPECT_TRUE(matches(Decls,
49+
"int target(std::unique_ptr<S> P) { return (*P).i; }",
50+
isPointerLikeOperatorStar()));
51+
4852
EXPECT_TRUE(matches(Decls,
4953
"int target(std::unique_ptr<S> P) { return P->i; }",
5054
isSmartPointerLikeOperatorArrow()));
55+
EXPECT_TRUE(matches(Decls,
56+
"int target(std::unique_ptr<S> P) { return P->i; }",
57+
isPointerLikeOperatorArrow()));
58+
5159
EXPECT_TRUE(matches(Decls,
5260
"int target(std::unique_ptr<S> P) { return P.get()->i; }",
5361
isSmartPointerLikeGetMethodCall()));
5462

5563
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
5664
isSmartPointerLikeOperatorArrow()));
65+
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
66+
isPointerLikeOperatorArrow()));
67+
}
68+
69+
TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) {
70+
llvm::StringRef Decls(R"cc(
71+
namespace std {
72+
template <class T>
73+
struct unique_ptr {
74+
T* operator->() const;
75+
T& operator*() const;
76+
};
77+
} // namespace std
78+
79+
template <class T>
80+
using UniquePtrAlias = std::unique_ptr<T>;
81+
82+
struct S { int i; };
83+
)cc");
84+
85+
EXPECT_FALSE(matches(Decls,
86+
"int target(std::unique_ptr<S> P) { return (*P).i; }",
87+
isSmartPointerLikeOperatorStar()));
88+
EXPECT_TRUE(matches(Decls,
89+
"int target(std::unique_ptr<S> P) { return (*P).i; }",
90+
isPointerLikeOperatorStar()));
91+
92+
EXPECT_FALSE(matches(Decls,
93+
"int target(std::unique_ptr<S> P) { return P->i; }",
94+
isSmartPointerLikeOperatorArrow()));
95+
EXPECT_TRUE(matches(Decls,
96+
"int target(std::unique_ptr<S> P) { return P->i; }",
97+
isPointerLikeOperatorArrow()));
98+
99+
EXPECT_FALSE(matches(Decls,
100+
"int target(UniquePtrAlias<S> P) { return P->i; }",
101+
isSmartPointerLikeOperatorArrow()));
102+
EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
103+
isPointerLikeOperatorArrow()));
57104
}
58105

59106
TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
@@ -75,15 +122,25 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
75122
EXPECT_FALSE(matches(Decls,
76123
"int target(std::unique_ptr<S, T> P) { return (*P).i; }",
77124
isSmartPointerLikeOperatorStar()));
125+
EXPECT_FALSE(matches(Decls,
126+
"int target(std::unique_ptr<S, T> P) { return (*P).i; }",
127+
isPointerLikeOperatorStar()));
128+
78129
EXPECT_FALSE(matches(Decls,
79130
"int target(std::unique_ptr<S, T> P) { return P->j; }",
80131
isSmartPointerLikeOperatorArrow()));
132+
EXPECT_FALSE(matches(Decls,
133+
"int target(std::unique_ptr<S, T> P) { return P->j; }",
134+
isPointerLikeOperatorArrow()));
81135
// The class matching arguably accidentally matches, just because the
82136
// instantiation is with S, S. Hopefully doesn't happen too much in real code
83137
// with such operator* and operator-> overloads.
84138
EXPECT_TRUE(matches(Decls,
85139
"int target(std::unique_ptr<S, S> P) { return P->i; }",
86140
isSmartPointerLikeOperatorArrow()));
141+
EXPECT_TRUE(matches(Decls,
142+
"int target(std::unique_ptr<S, S> P) { return P->i; }",
143+
isPointerLikeOperatorArrow()));
87144
}
88145

89146
TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) {
@@ -103,6 +160,9 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) {
103160
EXPECT_FALSE(
104161
matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }",
105162
isSmartPointerLikeOperatorStar()));
163+
EXPECT_FALSE(
164+
matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }",
165+
isPointerLikeOperatorStar()));
106166
}
107167

108168
TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
@@ -122,9 +182,17 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
122182
EXPECT_FALSE(matches(Decls,
123183
"int target(std::unique_ptr<S> P) { return (*P).i; }",
124184
isSmartPointerLikeOperatorStar()));
185+
EXPECT_FALSE(matches(Decls,
186+
"int target(std::unique_ptr<S> P) { return (*P).i; }",
187+
isPointerLikeOperatorStar()));
188+
125189
EXPECT_FALSE(matches(Decls,
126190
"int target(std::unique_ptr<S> P) { return P->i; }",
127191
isSmartPointerLikeOperatorArrow()));
192+
EXPECT_FALSE(matches(Decls,
193+
"int target(std::unique_ptr<S> P) { return P->i; }",
194+
isPointerLikeOperatorArrow()));
195+
128196
EXPECT_FALSE(
129197
matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }",
130198
isSmartPointerLikeGetMethodCall()));
@@ -146,6 +214,10 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) {
146214
EXPECT_FALSE(matches(Decls,
147215
"int target(std::unique_ptr<S> P) { return P->i; }",
148216
isSmartPointerLikeOperatorArrow()));
217+
EXPECT_FALSE(matches(Decls,
218+
"int target(std::unique_ptr<S> P) { return P->i; }",
219+
isPointerLikeOperatorArrow()));
220+
149221
EXPECT_FALSE(matches(Decls,
150222
"int target(std::unique_ptr<S> P) { return P->i; }",
151223
isSmartPointerLikeGetMethodCall()));
@@ -171,15 +243,31 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
171243
EXPECT_TRUE(matches(
172244
Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }",
173245
isSmartPointerLikeOperatorStar()));
246+
EXPECT_TRUE(matches(
247+
Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }",
248+
isPointerLikeOperatorStar()));
249+
174250
EXPECT_TRUE(matches(
175251
Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }",
176252
isSmartPointerLikeOperatorStar()));
253+
EXPECT_TRUE(matches(
254+
Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }",
255+
isPointerLikeOperatorStar()));
256+
177257
EXPECT_TRUE(matches(
178258
Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }",
179259
isSmartPointerLikeOperatorArrow()));
260+
EXPECT_TRUE(matches(
261+
Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }",
262+
isPointerLikeOperatorArrow()));
263+
180264
EXPECT_TRUE(matches(
181265
Decls, "int target(const std::optional<S> &Const) { return Const->i; }",
182266
isSmartPointerLikeOperatorArrow()));
267+
EXPECT_TRUE(matches(
268+
Decls, "int target(const std::optional<S> &Const) { return Const->i; }",
269+
isPointerLikeOperatorArrow()));
270+
183271
EXPECT_TRUE(matches(
184272
Decls,
185273
"int target(std::optional<S> &NonConst) { return NonConst.value().i; }",
@@ -214,16 +302,34 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
214302
Decls,
215303
"int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }",
216304
isSmartPointerLikeOperatorStar()));
305+
EXPECT_TRUE(matches(
306+
Decls,
307+
"int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }",
308+
isPointerLikeOperatorStar()));
309+
217310
EXPECT_TRUE(matches(
218311
Decls,
219312
"int target(const HasGetAndValue<S> &Const) { return (*Const).i; }",
220313
isSmartPointerLikeOperatorStar()));
314+
EXPECT_TRUE(matches(
315+
Decls,
316+
"int target(const HasGetAndValue<S> &Const) { return (*Const).i; }",
317+
isPointerLikeOperatorStar()));
318+
221319
EXPECT_TRUE(matches(
222320
Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }",
223321
isSmartPointerLikeOperatorArrow()));
322+
EXPECT_TRUE(matches(
323+
Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }",
324+
isPointerLikeOperatorArrow()));
325+
224326
EXPECT_TRUE(matches(
225327
Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }",
226328
isSmartPointerLikeOperatorArrow()));
329+
EXPECT_TRUE(matches(
330+
Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }",
331+
isPointerLikeOperatorArrow()));
332+
227333
EXPECT_TRUE(matches(
228334
Decls,
229335
"int target(HasGetAndValue<S> &NonConst) { return NonConst.value().i; }",

0 commit comments

Comments
 (0)