1// RUN: %check_clang_tidy -std=c++11-or-later %s readability-container-contains %t
2
3// Some *very* simplified versions of `map` etc.
4namespace std {
5
6template <class Key, class T>
7struct map {
8 struct iterator {
9 bool operator==(const iterator &Other) const;
10 bool operator!=(const iterator &Other) const;
11 };
12
13 unsigned count(const Key &K) const;
14 bool contains(const Key &K) const;
15 iterator find(const Key &K);
16 iterator end();
17};
18
19template <class Key>
20struct set {
21 unsigned count(const Key &K) const;
22 bool contains(const Key &K) const;
23};
24
25template <class Key>
26struct unordered_set {
27 unsigned count(const Key &K) const;
28 bool contains(const Key &K) const;
29};
30
31template <class Key, class T>
32struct multimap {
33 unsigned count(const Key &K) const;
34 bool contains(const Key &K) const;
35};
36
37} // namespace std
38
39// Check that we detect various common ways to check for membership
40int testDifferentCheckTypes(std::map<int, int> &MyMap) {
41 if (MyMap.count(K: 0))
42 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use 'contains' to check for membership [readability-container-contains]
43 // CHECK-FIXES: if (MyMap.contains(0))
44 return 1;
45 bool C1 = MyMap.count(K: 1);
46 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
47 // CHECK-FIXES: bool C1 = MyMap.contains(1);
48 auto C2 = static_cast<bool>(MyMap.count(K: 1));
49 // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: use 'contains' to check for membership [readability-container-contains]
50 // CHECK-FIXES: auto C2 = static_cast<bool>(MyMap.contains(1));
51 auto C3 = MyMap.count(K: 2) != 0;
52 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
53 // CHECK-FIXES: auto C3 = MyMap.contains(2);
54 auto C4 = MyMap.count(K: 3) > 0;
55 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
56 // CHECK-FIXES: auto C4 = MyMap.contains(3);
57 auto C5 = MyMap.count(K: 4) >= 1;
58 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
59 // CHECK-FIXES: auto C5 = MyMap.contains(4);
60 auto C6 = MyMap.find(K: 5) != MyMap.end();
61 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
62 // CHECK-FIXES: auto C6 = MyMap.contains(5);
63 return C1 + C2 + C3 + C4 + C5 + C6;
64}
65
66// Check that we detect various common ways to check for non-membership
67int testNegativeChecks(std::map<int, int> &MyMap) {
68 bool C1 = !MyMap.count(K: -1);
69 // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'contains' to check for membership [readability-container-contains]
70 // CHECK-FIXES: bool C1 = !MyMap.contains(-1);
71 auto C2 = MyMap.count(K: -2) == 0;
72 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
73 // CHECK-FIXES: auto C2 = !MyMap.contains(-2);
74 auto C3 = MyMap.count(K: -3) <= 0;
75 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
76 // CHECK-FIXES: auto C3 = !MyMap.contains(-3);
77 auto C4 = MyMap.count(K: -4) < 1;
78 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
79 // CHECK-FIXES: auto C4 = !MyMap.contains(-4);
80 auto C5 = MyMap.find(K: -5) == MyMap.end();
81 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
82 // CHECK-FIXES: auto C5 = !MyMap.contains(-5);
83 return C1 + C2 + C3 + C4 + C5;
84}
85
86// Check for various types
87int testDifferentTypes(std::map<int, int> &M, std::unordered_set<int> &US, std::set<int> &S, std::multimap<int, int> &MM) {
88 bool C1 = M.count(K: 1001);
89 // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'contains' to check for membership [readability-container-contains]
90 // CHECK-FIXES: bool C1 = M.contains(1001);
91 bool C2 = US.count(K: 1002);
92 // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use 'contains' to check for membership [readability-container-contains]
93 // CHECK-FIXES: bool C2 = US.contains(1002);
94 bool C3 = S.count(K: 1003);
95 // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'contains' to check for membership [readability-container-contains]
96 // CHECK-FIXES: bool C3 = S.contains(1003);
97 bool C4 = MM.count(K: 1004);
98 // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use 'contains' to check for membership [readability-container-contains]
99 // CHECK-FIXES: bool C4 = MM.contains(1004);
100 return C1 + C2 + C3 + C4;
101}
102
103// The check detects all kind of `const`, reference, rvalue-reference and value types.
104int testQualifiedTypes(std::map<int, int> ValueM, std::map<int, int> &RefM, const std::map<int, int> &ConstRefM, std::map<int, int> &&RValueM) {
105 bool C1 = ValueM.count(K: 2001);
106 // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'contains' to check for membership [readability-container-contains]
107 // CHECK-FIXES: bool C1 = ValueM.contains(2001);
108 bool C2 = RefM.count(K: 2002);
109 // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use 'contains' to check for membership [readability-container-contains]
110 // CHECK-FIXES: bool C2 = RefM.contains(2002);
111 bool C3 = ConstRefM.count(K: 2003);
112 // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use 'contains' to check for membership [readability-container-contains]
113 // CHECK-FIXES: bool C3 = ConstRefM.contains(2003);
114 bool C4 = RValueM.count(K: 2004);
115 // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'contains' to check for membership [readability-container-contains]
116 // CHECK-FIXES: bool C4 = RValueM.contains(2004);
117 return C1 + C2 + C3 + C4;
118}
119
120// This is effectively a membership check, as the result is implicitly casted
121// to `bool`.
122bool returnContains(std::map<int, int> &M) {
123 return M.count(K: 42);
124 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'contains' to check for membership [readability-container-contains]
125 // CHECK-FIXES: return M.contains(42);
126}
127
128// This returns the actual count and should not be rewritten
129int actualCount(std::multimap<int, int> &M) {
130 return M.count(K: 21);
131 // NO-WARNING.
132 // CHECK-FIXES: return M.count(21);
133}
134
135// Check that we are not confused by aliases
136namespace s2 = std;
137using MyMapT = s2::map<int, int>;
138int typeAliases(MyMapT &MyMap) {
139 bool C1 = MyMap.count(K: 99);
140 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
141 // CHECK-FIXES: bool C1 = MyMap.contains(99);
142 return C1;
143}
144
145// Check that the tests also trigger for a local variable and not only for
146// function arguments.
147bool localVar() {
148 using namespace std;
149 map<int, int> LocalM;
150 return LocalM.count(K: 42);
151 // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use 'contains' to check for membership [readability-container-contains]
152 // CHECK-FIXES: return LocalM.contains(42);
153}
154
155// Check various usages of an actual `count` which isn't rewritten
156int nonRewrittenCount(std::multimap<int, int> &MyMap) {
157 // This is an actual test if we have at least 2 usages. Shouldn't be rewritten.
158 bool C1 = MyMap.count(K: 1) >= 2;
159 // NO-WARNING.
160 // CHECK-FIXES: bool C1 = MyMap.count(1) >= 2;
161
162 // "< 0" makes little sense and is always `false`. Still, let's ensure we
163 // don't accidentally rewrite it to 'contains'.
164 bool C2 = MyMap.count(K: 2) < 0;
165 // NO-WARNING.
166 // CHECK-FIXES: bool C2 = MyMap.count(2) < 0;
167
168 // The `count` is used in some more complicated formula.
169 bool C3 = MyMap.count(K: 1) + MyMap.count(K: 2) * 2 + MyMap.count(K: 3) / 3 >= 20;
170 // NO-WARNING.
171 // CHECK-FIXES: bool C3 = MyMap.count(1) + MyMap.count(2) * 2 + MyMap.count(3) / 3 >= 20;
172
173 // This could theoretically be rewritten into a 'contains' after removig the
174 // `4` on both sides of the comparison. For the time being, we don't detect
175 // this case.
176 bool C4 = MyMap.count(K: 1) + 4 > 4;
177 // NO-WARNING.
178 // CHECK-FIXES: bool C4 = MyMap.count(1) + 4 > 4;
179
180 return C1 + C2 + C3 + C4;
181}
182
183// Check different integer literal suffixes
184int testDifferentIntegerLiteralSuffixes(std::map<int, int> &MyMap) {
185
186 auto C1 = MyMap.count(K: 2) != 0U;
187 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
188 // CHECK-FIXES: auto C1 = MyMap.contains(2);
189 auto C2 = MyMap.count(K: 2) != 0UL;
190 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
191 // CHECK-FIXES: auto C2 = MyMap.contains(2);
192 auto C3 = 0U != MyMap.count(K: 2);
193 // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains]
194 // CHECK-FIXES: auto C3 = MyMap.contains(2);
195 auto C4 = 0UL != MyMap.count(K: 2);
196 // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'contains' to check for membership [readability-container-contains]
197 // CHECK-FIXES: auto C4 = MyMap.contains(2);
198 auto C5 = MyMap.count(K: 2) < 1U;
199 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
200 // CHECK-FIXES: auto C5 = !MyMap.contains(2);
201 auto C6 = MyMap.count(K: 2) < 1UL;
202 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
203 // CHECK-FIXES: auto C6 = !MyMap.contains(2);
204 auto C7 = 1U > MyMap.count(K: 2);
205 // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'contains' to check for membership [readability-container-contains]
206 // CHECK-FIXES: auto C7 = !MyMap.contains(2);
207 auto C8 = 1UL > MyMap.count(K: 2);
208 // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains]
209 // CHECK-FIXES: auto C8 = !MyMap.contains(2);
210
211 return C1 + C2 + C3 + C4 + C5 + C6 + C7 + C8;
212}
213
214// We don't want to rewrite if the `contains` call is from a macro expansion
215int testMacroExpansion(std::unordered_set<int> &MySet) {
216#define COUNT_ONES(SET) SET.count(1)
217 // Rewriting the macro would break the code
218 // CHECK-FIXES: #define COUNT_ONES(SET) SET.count(1)
219 // We still want to warn the user even if we don't offer a fixit
220 if (COUNT_ONES(MySet)) {
221 // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'contains' to check for membership [readability-container-contains]
222 // CHECK-MESSAGES: note: expanded from macro 'COUNT_ONES'
223 return COUNT_ONES(MySet);
224 }
225#undef COUNT_ONES
226#define COUNT_ONES count(1)
227 // Rewriting the macro would break the code
228 // CHECK-FIXES: #define COUNT_ONES count(1)
229 // We still want to warn the user even if we don't offer a fixit
230 if (MySet.COUNT_ONES) {
231 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use 'contains' to check for membership [readability-container-contains]
232 // CHECK-MESSAGES: note: expanded from macro 'COUNT_ONES'
233 return MySet.COUNT_ONES;
234 }
235#undef COUNT_ONES
236#define MY_SET MySet
237 // CHECK-FIXES: #define MY_SET MySet
238 // We still want to rewrite one of the two calls to `count`
239 if (MY_SET.count(K: 1)) {
240 // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'contains' to check for membership [readability-container-contains]
241 // CHECK-FIXES: if (MY_SET.contains(1)) {
242 return MY_SET.count(K: 1);
243 }
244#undef MY_SET
245 return 0;
246}
247
248// The following map has the same interface as `std::map`.
249template <class Key, class T>
250struct CustomMap {
251 unsigned count(const Key &K) const;
252 bool contains(const Key &K) const;
253 void *find(const Key &K);
254 void *end();
255};
256
257void testDifferentCheckTypes(CustomMap<int, int> &MyMap) {
258 if (MyMap.count(K: 0)) {};
259 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
260 // CHECK-FIXES: if (MyMap.contains(0)) {};
261
262 MyMap.find(K: 0) != MyMap.end();
263 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
264 // CHECK-FIXES: MyMap.contains(0);
265}
266
267struct MySubmap : public CustomMap<int, int> {};
268
269void testSubclass(MySubmap& MyMap) {
270 if (MyMap.count(K: 0)) {};
271 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
272 // CHECK-FIXES: if (MyMap.contains(0)) {};
273}
274
275using UsingMap = CustomMap<int, int>;
276struct MySubmap2 : public UsingMap {};
277using UsingMap2 = MySubmap2;
278
279void testUsing(UsingMap2& MyMap) {
280 if (MyMap.count(K: 0)) {};
281 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
282 // CHECK-FIXES: if (MyMap.contains(0)) {};
283}
284
285template <class Key, class T>
286struct CustomMapContainsDeleted {
287 unsigned count(const Key &K) const;
288 bool contains(const Key &K) const = delete;
289 void *find(const Key &K);
290 void *end();
291};
292
293struct SubmapContainsDeleted : public CustomMapContainsDeleted<int, int> {};
294
295void testContainsDeleted(CustomMapContainsDeleted<int, int> &MyMap,
296 SubmapContainsDeleted &MyMap2) {
297 // No warning if the `contains` method is deleted.
298 if (MyMap.count(K: 0)) {};
299 if (MyMap2.count(K: 0)) {};
300}
301
302template <class Key, class T>
303struct CustomMapPrivateContains {
304 unsigned count(const Key &K) const;
305 void *find(const Key &K);
306 void *end();
307
308private:
309 bool contains(const Key &K) const;
310};
311
312struct SubmapPrivateContains : public CustomMapPrivateContains<int, int> {};
313
314void testPrivateContains(CustomMapPrivateContains<int, int> &MyMap,
315 SubmapPrivateContains &MyMap2) {
316 // No warning if the `contains` method is not public.
317 if (MyMap.count(K: 0)) {};
318 if (MyMap2.count(K: 0)) {};
319}
320
321struct MyString {};
322
323struct WeirdNonMatchingContains {
324 unsigned count(char) const;
325 bool contains(const MyString&) const;
326};
327
328void testWeirdNonMatchingContains(WeirdNonMatchingContains &MyMap) {
329 // No warning if there is no `contains` method with the right type.
330 if (MyMap.count('a')) {};
331}
332
333template <class T>
334struct SmallPtrSet {
335 using ConstPtrType = const T*;
336 unsigned count(ConstPtrType Ptr) const;
337 bool contains(ConstPtrType Ptr) const;
338};
339
340template <class T>
341struct SmallPtrPtrSet {
342 using ConstPtrType = const T**;
343 unsigned count(ConstPtrType Ptr) const;
344 bool contains(ConstPtrType Ptr) const;
345};
346
347template <class T>
348struct SmallPtrPtrPtrSet {
349 using ConstPtrType = const T***;
350 unsigned count(ConstPtrType Ptr) const;
351 bool contains(ConstPtrType Ptr) const;
352};
353
354void testSmallPtrSet(const int ***Ptr,
355 SmallPtrSet<int> &MySet,
356 SmallPtrPtrSet<int> &MySet2,
357 SmallPtrPtrPtrSet<int> &MySet3) {
358 if (MySet.count(Ptr: **Ptr)) {};
359 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
360 // CHECK-FIXES: if (MySet.contains(**Ptr)) {};
361 if (MySet2.count(Ptr: *Ptr)) {};
362 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
363 // CHECK-FIXES: if (MySet2.contains(*Ptr)) {};
364 if (MySet3.count(Ptr)) {};
365 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
366 // CHECK-FIXES: if (MySet3.contains(Ptr)) {};
367}
368
369struct X {};
370struct Y : public X {};
371
372void testSubclassEntry(SmallPtrSet<X>& Set, Y* Entry) {
373 if (Set.count(Ptr: Entry)) {}
374 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
375 // CHECK-FIXES: if (Set.contains(Entry)) {}
376}
377
378struct WeirdPointerApi {
379 unsigned count(int** Ptr) const;
380 bool contains(int* Ptr) const;
381};
382
383void testWeirdApi(WeirdPointerApi& Set, int* E) {
384 if (Set.count(Ptr: &E)) {}
385}
386
387void testIntUnsigned(std::set<int>& S, unsigned U) {
388 if (S.count(K: U)) {}
389 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
390 // CHECK-FIXES: if (S.contains(U)) {}
391}
392
393template <class T>
394struct CustomSetConvertible {
395 unsigned count(const T &K) const;
396 bool contains(const T &K) const;
397};
398
399struct A {};
400struct B { B() = default; B(const A&) {} };
401struct C { operator A() const; };
402
403void testConvertibleTypes() {
404 CustomSetConvertible<B> MyMap;
405 if (MyMap.count(K: A())) {};
406 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
407 // CHECK-FIXES: if (MyMap.contains(A())) {};
408
409 CustomSetConvertible<A> MyMap2;
410 if (MyMap2.count(K: C())) {};
411 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
412 // CHECK-FIXES: if (MyMap2.contains(C())) {};
413
414 if (MyMap2.count(K: C()) != 0) {};
415 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
416 // CHECK-FIXES: if (MyMap2.contains(C())) {};
417}
418
419template<class U>
420using Box = const U& ;
421
422template <class T>
423struct CustomBoxedSet {
424 unsigned count(Box<T> K) const;
425 bool contains(Box<T> K) const;
426};
427
428void testBox() {
429 CustomBoxedSet<int> Set;
430 if (Set.count(K: 0)) {};
431 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
432 // CHECK-FIXES: if (Set.contains(0)) {};
433}
434
435void testOperandPermutations(std::map<int, int>& Map) {
436 if (Map.count(K: 0) != 0) {};
437 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
438 // CHECK-FIXES: if (Map.contains(0)) {};
439 if (0 != Map.count(K: 0)) {};
440 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
441 // CHECK-FIXES: if (Map.contains(0)) {};
442 if (Map.count(K: 0) == 0) {};
443 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
444 // CHECK-FIXES: if (!Map.contains(0)) {};
445 if (0 == Map.count(K: 0)) {};
446 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
447 // CHECK-FIXES: if (!Map.contains(0)) {};
448 if (Map.find(K: 0) != Map.end()) {};
449 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
450 // CHECK-FIXES: if (Map.contains(0)) {};
451 if (Map.end() != Map.find(K: 0)) {};
452 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
453 // CHECK-FIXES: if (Map.contains(0)) {};
454 if (Map.find(K: 0) == Map.end()) {};
455 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
456 // CHECK-FIXES: if (!Map.contains(0)) {};
457 if (Map.end() == Map.find(K: 0)) {};
458 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
459 // CHECK-FIXES: if (!Map.contains(0)) {};
460}
461

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp