1 | //===- unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp ==// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h" |
10 | |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | #include "clang/Testing/TestAST.h" |
13 | #include "llvm/ADT/StringRef.h" |
14 | #include "gtest/gtest.h" |
15 | |
16 | namespace clang::dataflow { |
17 | namespace { |
18 | |
19 | using clang::ast_matchers::match; |
20 | |
21 | template <typename MatcherT> |
22 | bool matches(llvm::StringRef Decls, llvm::StringRef TestInput, |
23 | MatcherT Matcher) { |
24 | TestAST InputAST(Decls.str() + TestInput.str()); |
25 | return !match(Matcher, InputAST.context()).empty(); |
26 | } |
27 | |
28 | TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) { |
29 | llvm::StringRef Decls(R"cc( |
30 | namespace std { |
31 | template <class T> |
32 | struct unique_ptr { |
33 | T* operator->() const; |
34 | T& operator*() const; |
35 | T* get() const; |
36 | }; |
37 | } // namespace std |
38 | |
39 | template <class T> |
40 | using UniquePtrAlias = std::unique_ptr<T>; |
41 | |
42 | struct S { int i; }; |
43 | )cc" ); |
44 | |
45 | EXPECT_TRUE(matches(Decls, |
46 | "int target(std::unique_ptr<S> P) { return (*P).i; }" , |
47 | isSmartPointerLikeOperatorStar())); |
48 | EXPECT_TRUE(matches(Decls, |
49 | "int target(std::unique_ptr<S> P) { return (*P).i; }" , |
50 | isPointerLikeOperatorStar())); |
51 | |
52 | EXPECT_TRUE(matches(Decls, |
53 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
54 | isSmartPointerLikeOperatorArrow())); |
55 | EXPECT_TRUE(matches(Decls, |
56 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
57 | isPointerLikeOperatorArrow())); |
58 | |
59 | EXPECT_TRUE(matches(Decls, |
60 | "int target(std::unique_ptr<S> P) { return P.get()->i; }" , |
61 | isSmartPointerLikeGetMethodCall())); |
62 | |
63 | EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }" , |
64 | 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())); |
104 | } |
105 | |
106 | TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) { |
107 | llvm::StringRef Decls(R"cc( |
108 | namespace std { |
109 | // unique_ptr isn't really like this, but we aren't matching by name |
110 | template <class T, class U> |
111 | struct unique_ptr { |
112 | U* operator->() const; |
113 | T& operator*() const; |
114 | T* get() const; |
115 | }; |
116 | } // namespace std |
117 | |
118 | struct S { int i; }; |
119 | struct T { int j; }; |
120 | )cc" ); |
121 | |
122 | EXPECT_FALSE(matches(Decls, |
123 | "int target(std::unique_ptr<S, T> P) { return (*P).i; }" , |
124 | isSmartPointerLikeOperatorStar())); |
125 | EXPECT_FALSE(matches(Decls, |
126 | "int target(std::unique_ptr<S, T> P) { return (*P).i; }" , |
127 | isPointerLikeOperatorStar())); |
128 | |
129 | EXPECT_FALSE(matches(Decls, |
130 | "int target(std::unique_ptr<S, T> P) { return P->j; }" , |
131 | isSmartPointerLikeOperatorArrow())); |
132 | EXPECT_FALSE(matches(Decls, |
133 | "int target(std::unique_ptr<S, T> P) { return P->j; }" , |
134 | isPointerLikeOperatorArrow())); |
135 | // The class matching arguably accidentally matches, just because the |
136 | // instantiation is with S, S. Hopefully doesn't happen too much in real code |
137 | // with such operator* and operator-> overloads. |
138 | EXPECT_TRUE(matches(Decls, |
139 | "int target(std::unique_ptr<S, S> P) { return P->i; }" , |
140 | isSmartPointerLikeOperatorArrow())); |
141 | EXPECT_TRUE(matches(Decls, |
142 | "int target(std::unique_ptr<S, S> P) { return P->i; }" , |
143 | isPointerLikeOperatorArrow())); |
144 | } |
145 | |
146 | TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) { |
147 | llvm::StringRef Decls(R"cc( |
148 | namespace std { |
149 | template <class T> |
150 | struct unique_ptr { |
151 | T* operator->() const; |
152 | T& operator*(int x) const; |
153 | T* get() const; |
154 | }; |
155 | } // namespace std |
156 | |
157 | struct S { int i; }; |
158 | )cc" ); |
159 | |
160 | EXPECT_FALSE( |
161 | matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }" , |
162 | isSmartPointerLikeOperatorStar())); |
163 | EXPECT_FALSE( |
164 | matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }" , |
165 | isPointerLikeOperatorStar())); |
166 | } |
167 | |
168 | TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) { |
169 | llvm::StringRef Decls(R"cc( |
170 | namespace std { |
171 | template <class T> |
172 | struct unique_ptr { |
173 | T* operator->(); |
174 | T& operator*(); |
175 | T* get(); |
176 | }; |
177 | } // namespace std |
178 | |
179 | struct S { int i; }; |
180 | )cc" ); |
181 | |
182 | EXPECT_FALSE(matches(Decls, |
183 | "int target(std::unique_ptr<S> P) { return (*P).i; }" , |
184 | isSmartPointerLikeOperatorStar())); |
185 | EXPECT_FALSE(matches(Decls, |
186 | "int target(std::unique_ptr<S> P) { return (*P).i; }" , |
187 | isPointerLikeOperatorStar())); |
188 | |
189 | EXPECT_FALSE(matches(Decls, |
190 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
191 | isSmartPointerLikeOperatorArrow())); |
192 | EXPECT_FALSE(matches(Decls, |
193 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
194 | isPointerLikeOperatorArrow())); |
195 | |
196 | EXPECT_FALSE( |
197 | matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }" , |
198 | isSmartPointerLikeGetMethodCall())); |
199 | } |
200 | |
201 | TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) { |
202 | llvm::StringRef Decls(R"cc( |
203 | namespace std { |
204 | template <class T> |
205 | struct unique_ptr { |
206 | T* operator->(); |
207 | T* get(); |
208 | }; |
209 | } // namespace std |
210 | |
211 | struct S { int i; }; |
212 | )cc" ); |
213 | |
214 | EXPECT_FALSE(matches(Decls, |
215 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
216 | isSmartPointerLikeOperatorArrow())); |
217 | EXPECT_FALSE(matches(Decls, |
218 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
219 | isPointerLikeOperatorArrow())); |
220 | |
221 | EXPECT_FALSE(matches(Decls, |
222 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
223 | isSmartPointerLikeGetMethodCall())); |
224 | } |
225 | |
226 | TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) { |
227 | llvm::StringRef Decls(R"cc( |
228 | namespace std { |
229 | template <class T> |
230 | struct optional { |
231 | const T* operator->() const; |
232 | T* operator->(); |
233 | const T& operator*() const; |
234 | T& operator*(); |
235 | const T& value() const; |
236 | T& value(); |
237 | }; |
238 | } // namespace std |
239 | |
240 | struct S { int i; }; |
241 | )cc" ); |
242 | |
243 | EXPECT_TRUE(matches( |
244 | Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }" , |
245 | isSmartPointerLikeOperatorStar())); |
246 | EXPECT_TRUE(matches( |
247 | Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }" , |
248 | isPointerLikeOperatorStar())); |
249 | |
250 | EXPECT_TRUE(matches( |
251 | Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }" , |
252 | isSmartPointerLikeOperatorStar())); |
253 | EXPECT_TRUE(matches( |
254 | Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }" , |
255 | isPointerLikeOperatorStar())); |
256 | |
257 | EXPECT_TRUE(matches( |
258 | Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }" , |
259 | isSmartPointerLikeOperatorArrow())); |
260 | EXPECT_TRUE(matches( |
261 | Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }" , |
262 | isPointerLikeOperatorArrow())); |
263 | |
264 | EXPECT_TRUE(matches( |
265 | Decls, "int target(const std::optional<S> &Const) { return Const->i; }" , |
266 | isSmartPointerLikeOperatorArrow())); |
267 | EXPECT_TRUE(matches( |
268 | Decls, "int target(const std::optional<S> &Const) { return Const->i; }" , |
269 | isPointerLikeOperatorArrow())); |
270 | |
271 | EXPECT_TRUE(matches( |
272 | Decls, |
273 | "int target(std::optional<S> &NonConst) { return NonConst.value().i; }" , |
274 | isSmartPointerLikeValueMethodCall())); |
275 | EXPECT_TRUE(matches( |
276 | Decls, |
277 | "int target(const std::optional<S> &Const) { return Const.value().i; }" , |
278 | isSmartPointerLikeValueMethodCall())); |
279 | } |
280 | |
281 | TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) { |
282 | llvm::StringRef Decls(R"cc( |
283 | template <class T> |
284 | struct HasGetAndValue { |
285 | using pointer_t = T*; |
286 | using reference_t = T&; |
287 | |
288 | const pointer_t operator->() const; |
289 | pointer_t operator->(); |
290 | const reference_t operator*() const; |
291 | reference_t operator*(); |
292 | const reference_t value() const; |
293 | reference_t value(); |
294 | const pointer_t get() const; |
295 | pointer_t get(); |
296 | }; |
297 | |
298 | struct S { int i; }; |
299 | )cc" ); |
300 | |
301 | EXPECT_TRUE(matches( |
302 | Decls, |
303 | "int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }" , |
304 | isSmartPointerLikeOperatorStar())); |
305 | EXPECT_TRUE(matches( |
306 | Decls, |
307 | "int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }" , |
308 | isPointerLikeOperatorStar())); |
309 | |
310 | EXPECT_TRUE(matches( |
311 | Decls, |
312 | "int target(const HasGetAndValue<S> &Const) { return (*Const).i; }" , |
313 | isSmartPointerLikeOperatorStar())); |
314 | EXPECT_TRUE(matches( |
315 | Decls, |
316 | "int target(const HasGetAndValue<S> &Const) { return (*Const).i; }" , |
317 | isPointerLikeOperatorStar())); |
318 | |
319 | EXPECT_TRUE(matches( |
320 | Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }" , |
321 | isSmartPointerLikeOperatorArrow())); |
322 | EXPECT_TRUE(matches( |
323 | Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }" , |
324 | isPointerLikeOperatorArrow())); |
325 | |
326 | EXPECT_TRUE(matches( |
327 | Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }" , |
328 | isSmartPointerLikeOperatorArrow())); |
329 | EXPECT_TRUE(matches( |
330 | Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }" , |
331 | isPointerLikeOperatorArrow())); |
332 | |
333 | EXPECT_TRUE(matches( |
334 | Decls, |
335 | "int target(HasGetAndValue<S> &NonConst) { return NonConst.value().i; }" , |
336 | isSmartPointerLikeValueMethodCall())); |
337 | EXPECT_TRUE(matches( |
338 | Decls, |
339 | "int target(const HasGetAndValue<S> &Const) { return Const.value().i; }" , |
340 | isSmartPointerLikeValueMethodCall())); |
341 | EXPECT_TRUE(matches( |
342 | Decls, |
343 | "int target(HasGetAndValue<S> &NonConst) { return NonConst.get()->i; }" , |
344 | isSmartPointerLikeGetMethodCall())); |
345 | EXPECT_TRUE(matches( |
346 | Decls, |
347 | "int target(const HasGetAndValue<S> &Const) { return Const.get()->i; }" , |
348 | isSmartPointerLikeGetMethodCall())); |
349 | } |
350 | |
351 | TEST(SmartPointerAccessorCachingTest, Renamed) { |
352 | llvm::StringRef Decls(R"cc( |
353 | namespace std { |
354 | template <class T> |
355 | struct unique_ptr { |
356 | T* operator->() const; |
357 | T& operator*() const; |
358 | T* Get() const; |
359 | T& Value() const; |
360 | }; |
361 | } // namespace std |
362 | |
363 | template <class T> |
364 | using UniquePtrAlias = std::unique_ptr<T>; |
365 | |
366 | struct S { int i; }; |
367 | )cc" ); |
368 | |
369 | EXPECT_TRUE(matches(Decls, |
370 | "int target(std::unique_ptr<S> P) { return (*P).i; }" , |
371 | isPointerLikeOperatorStar())); |
372 | |
373 | EXPECT_TRUE(matches(Decls, |
374 | "int target(std::unique_ptr<S> P) { return P->i; }" , |
375 | isPointerLikeOperatorArrow())); |
376 | |
377 | EXPECT_TRUE(matches(Decls, |
378 | "int target(std::unique_ptr<S> P) { return P.Get()->i; }" , |
379 | isSmartPointerLikeGetMethodCall("Get" ))); |
380 | EXPECT_TRUE(matches(Decls, |
381 | "int target(UniquePtrAlias<S> P) { return P.Get()->i; }" , |
382 | isSmartPointerLikeGetMethodCall("Get" ))); |
383 | |
384 | EXPECT_TRUE( |
385 | matches(Decls, "int target(std::unique_ptr<S> P) { return P.Value().i; }" , |
386 | isSmartPointerLikeValueMethodCall("Value" ))); |
387 | EXPECT_TRUE(matches(Decls, |
388 | "int target(UniquePtrAlias<S> P) { return P.Value().i; }" , |
389 | isSmartPointerLikeValueMethodCall("Value" ))); |
390 | |
391 | EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }" , |
392 | isPointerLikeOperatorArrow())); |
393 | } |
394 | |
395 | } // namespace |
396 | } // namespace clang::dataflow |
397 | |