xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/readability/container-contains.cpp (revision 3605d9a456185f4af78c01a2684b822b57bca9b0)
1*3605d9a4SNicolas van Kempen // RUN: %check_clang_tidy -std=c++11-or-later %s readability-container-contains %t
289a1d03eSRichard 
389a1d03eSRichard // Some *very* simplified versions of `map` etc.
489a1d03eSRichard namespace std {
589a1d03eSRichard 
689a1d03eSRichard template <class Key, class T>
789a1d03eSRichard struct map {
8*3605d9a4SNicolas van Kempen   struct iterator {
9*3605d9a4SNicolas van Kempen     bool operator==(const iterator &Other) const;
10*3605d9a4SNicolas van Kempen     bool operator!=(const iterator &Other) const;
11*3605d9a4SNicolas van Kempen   };
12*3605d9a4SNicolas van Kempen 
1389a1d03eSRichard   unsigned count(const Key &K) const;
1489a1d03eSRichard   bool contains(const Key &K) const;
15*3605d9a4SNicolas van Kempen   iterator find(const Key &K);
16*3605d9a4SNicolas van Kempen   iterator end();
1789a1d03eSRichard };
1889a1d03eSRichard 
1989a1d03eSRichard template <class Key>
2089a1d03eSRichard struct set {
2189a1d03eSRichard   unsigned count(const Key &K) const;
2289a1d03eSRichard   bool contains(const Key &K) const;
2389a1d03eSRichard };
2489a1d03eSRichard 
2589a1d03eSRichard template <class Key>
2689a1d03eSRichard struct unordered_set {
2789a1d03eSRichard   unsigned count(const Key &K) const;
2889a1d03eSRichard   bool contains(const Key &K) const;
2989a1d03eSRichard };
3089a1d03eSRichard 
3189a1d03eSRichard template <class Key, class T>
3289a1d03eSRichard struct multimap {
3389a1d03eSRichard   unsigned count(const Key &K) const;
3489a1d03eSRichard   bool contains(const Key &K) const;
3589a1d03eSRichard };
3689a1d03eSRichard 
3789a1d03eSRichard } // namespace std
3889a1d03eSRichard 
3989a1d03eSRichard // Check that we detect various common ways to check for membership
4089a1d03eSRichard int testDifferentCheckTypes(std::map<int, int> &MyMap) {
4189a1d03eSRichard   if (MyMap.count(0))
4289a1d03eSRichard     // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use 'contains' to check for membership [readability-container-contains]
4389a1d03eSRichard     // CHECK-FIXES: if (MyMap.contains(0))
4489a1d03eSRichard     return 1;
4589a1d03eSRichard   bool C1 = MyMap.count(1);
4689a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
4789a1d03eSRichard   // CHECK-FIXES: bool C1 = MyMap.contains(1);
4889a1d03eSRichard   auto C2 = static_cast<bool>(MyMap.count(1));
4989a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: use 'contains' to check for membership [readability-container-contains]
5089a1d03eSRichard   // CHECK-FIXES: auto C2 = static_cast<bool>(MyMap.contains(1));
5189a1d03eSRichard   auto C3 = MyMap.count(2) != 0;
5289a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
5389a1d03eSRichard   // CHECK-FIXES: auto C3 = MyMap.contains(2);
5489a1d03eSRichard   auto C4 = MyMap.count(3) > 0;
5589a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
5689a1d03eSRichard   // CHECK-FIXES: auto C4 = MyMap.contains(3);
5789a1d03eSRichard   auto C5 = MyMap.count(4) >= 1;
5889a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
5989a1d03eSRichard   // CHECK-FIXES: auto C5 = MyMap.contains(4);
6089a1d03eSRichard   auto C6 = MyMap.find(5) != MyMap.end();
6189a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
6289a1d03eSRichard   // CHECK-FIXES: auto C6 = MyMap.contains(5);
6389a1d03eSRichard   return C1 + C2 + C3 + C4 + C5 + C6;
6489a1d03eSRichard }
6589a1d03eSRichard 
6689a1d03eSRichard // Check that we detect various common ways to check for non-membership
6789a1d03eSRichard int testNegativeChecks(std::map<int, int> &MyMap) {
6889a1d03eSRichard   bool C1 = !MyMap.count(-1);
6989a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'contains' to check for membership [readability-container-contains]
7089a1d03eSRichard   // CHECK-FIXES: bool C1 = !MyMap.contains(-1);
7189a1d03eSRichard   auto C2 = MyMap.count(-2) == 0;
7289a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
7389a1d03eSRichard   // CHECK-FIXES: auto C2 = !MyMap.contains(-2);
7489a1d03eSRichard   auto C3 = MyMap.count(-3) <= 0;
7589a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
7689a1d03eSRichard   // CHECK-FIXES: auto C3 = !MyMap.contains(-3);
7789a1d03eSRichard   auto C4 = MyMap.count(-4) < 1;
7889a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
7989a1d03eSRichard   // CHECK-FIXES: auto C4 = !MyMap.contains(-4);
8089a1d03eSRichard   auto C5 = MyMap.find(-5) == MyMap.end();
8189a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
8289a1d03eSRichard   // CHECK-FIXES: auto C5 = !MyMap.contains(-5);
8389a1d03eSRichard   return C1 + C2 + C3 + C4 + C5;
8489a1d03eSRichard }
8589a1d03eSRichard 
8689a1d03eSRichard // Check for various types
8789a1d03eSRichard int testDifferentTypes(std::map<int, int> &M, std::unordered_set<int> &US, std::set<int> &S, std::multimap<int, int> &MM) {
8889a1d03eSRichard   bool C1 = M.count(1001);
8989a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'contains' to check for membership [readability-container-contains]
9089a1d03eSRichard   // CHECK-FIXES: bool C1 = M.contains(1001);
9189a1d03eSRichard   bool C2 = US.count(1002);
9289a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use 'contains' to check for membership [readability-container-contains]
9389a1d03eSRichard   // CHECK-FIXES: bool C2 = US.contains(1002);
9489a1d03eSRichard   bool C3 = S.count(1003);
9589a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'contains' to check for membership [readability-container-contains]
9689a1d03eSRichard   // CHECK-FIXES: bool C3 = S.contains(1003);
9789a1d03eSRichard   bool C4 = MM.count(1004);
9889a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use 'contains' to check for membership [readability-container-contains]
9989a1d03eSRichard   // CHECK-FIXES: bool C4 = MM.contains(1004);
10089a1d03eSRichard   return C1 + C2 + C3 + C4;
10189a1d03eSRichard }
10289a1d03eSRichard 
10389a1d03eSRichard // The check detects all kind of `const`, reference, rvalue-reference and value types.
10489a1d03eSRichard int testQualifiedTypes(std::map<int, int> ValueM, std::map<int, int> &RefM, const std::map<int, int> &ConstRefM, std::map<int, int> &&RValueM) {
10589a1d03eSRichard   bool C1 = ValueM.count(2001);
10689a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'contains' to check for membership [readability-container-contains]
10789a1d03eSRichard   // CHECK-FIXES: bool C1 = ValueM.contains(2001);
10889a1d03eSRichard   bool C2 = RefM.count(2002);
10989a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use 'contains' to check for membership [readability-container-contains]
11089a1d03eSRichard   // CHECK-FIXES: bool C2 = RefM.contains(2002);
11189a1d03eSRichard   bool C3 = ConstRefM.count(2003);
11289a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use 'contains' to check for membership [readability-container-contains]
11389a1d03eSRichard   // CHECK-FIXES: bool C3 = ConstRefM.contains(2003);
11489a1d03eSRichard   bool C4 = RValueM.count(2004);
11589a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'contains' to check for membership [readability-container-contains]
11689a1d03eSRichard   // CHECK-FIXES: bool C4 = RValueM.contains(2004);
11789a1d03eSRichard   return C1 + C2 + C3 + C4;
11889a1d03eSRichard }
11989a1d03eSRichard 
12089a1d03eSRichard // This is effectively a membership check, as the result is implicitly casted
12189a1d03eSRichard // to `bool`.
12289a1d03eSRichard bool returnContains(std::map<int, int> &M) {
12389a1d03eSRichard   return M.count(42);
12489a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'contains' to check for membership [readability-container-contains]
12589a1d03eSRichard   // CHECK-FIXES: return M.contains(42);
12689a1d03eSRichard }
12789a1d03eSRichard 
12889a1d03eSRichard // This returns the actual count and should not be rewritten
12989a1d03eSRichard int actualCount(std::multimap<int, int> &M) {
13089a1d03eSRichard   return M.count(21);
13189a1d03eSRichard   // NO-WARNING.
13289a1d03eSRichard   // CHECK-FIXES: return M.count(21);
13389a1d03eSRichard }
13489a1d03eSRichard 
13589a1d03eSRichard // Check that we are not confused by aliases
13689a1d03eSRichard namespace s2 = std;
13789a1d03eSRichard using MyMapT = s2::map<int, int>;
13889a1d03eSRichard int typeAliases(MyMapT &MyMap) {
13989a1d03eSRichard   bool C1 = MyMap.count(99);
14089a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
14189a1d03eSRichard   // CHECK-FIXES: bool C1 = MyMap.contains(99);
14289a1d03eSRichard   return C1;
14389a1d03eSRichard }
14489a1d03eSRichard 
14589a1d03eSRichard // Check that the tests also trigger for a local variable and not only for
14689a1d03eSRichard // function arguments.
14789a1d03eSRichard bool localVar() {
14889a1d03eSRichard   using namespace std;
14989a1d03eSRichard   map<int, int> LocalM;
15089a1d03eSRichard   return LocalM.count(42);
15189a1d03eSRichard   // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use 'contains' to check for membership [readability-container-contains]
15289a1d03eSRichard   // CHECK-FIXES: return LocalM.contains(42);
15389a1d03eSRichard }
15489a1d03eSRichard 
15589a1d03eSRichard // Check various usages of an actual `count` which isn't rewritten
15689a1d03eSRichard int nonRewrittenCount(std::multimap<int, int> &MyMap) {
15789a1d03eSRichard   // This is an actual test if we have at least 2 usages. Shouldn't be rewritten.
15889a1d03eSRichard   bool C1 = MyMap.count(1) >= 2;
15989a1d03eSRichard   // NO-WARNING.
16089a1d03eSRichard   // CHECK-FIXES: bool C1 = MyMap.count(1) >= 2;
16189a1d03eSRichard 
16289a1d03eSRichard   // "< 0" makes little sense and is always `false`. Still, let's ensure we
16389a1d03eSRichard   // don't accidentally rewrite it to 'contains'.
16489a1d03eSRichard   bool C2 = MyMap.count(2) < 0;
16589a1d03eSRichard   // NO-WARNING.
16689a1d03eSRichard   // CHECK-FIXES: bool C2 = MyMap.count(2) < 0;
16789a1d03eSRichard 
16889a1d03eSRichard   // The `count` is used in some more complicated formula.
16989a1d03eSRichard   bool C3 = MyMap.count(1) + MyMap.count(2) * 2 + MyMap.count(3) / 3 >= 20;
17089a1d03eSRichard   // NO-WARNING.
17189a1d03eSRichard   // CHECK-FIXES: bool C3 = MyMap.count(1) + MyMap.count(2) * 2 + MyMap.count(3) / 3 >= 20;
17289a1d03eSRichard 
17389a1d03eSRichard   // This could theoretically be rewritten into a 'contains' after removig the
17489a1d03eSRichard   // `4` on both sides of the comparison. For the time being, we don't detect
17589a1d03eSRichard   // this case.
17689a1d03eSRichard   bool C4 = MyMap.count(1) + 4 > 4;
17789a1d03eSRichard   // NO-WARNING.
17889a1d03eSRichard   // CHECK-FIXES: bool C4 = MyMap.count(1) + 4 > 4;
17989a1d03eSRichard 
18089a1d03eSRichard   return C1 + C2 + C3 + C4;
18189a1d03eSRichard }
18289a1d03eSRichard 
1838ac84a95SThomas Schenker // Check different integer literal suffixes
1848ac84a95SThomas Schenker int testDifferentIntegerLiteralSuffixes(std::map<int, int> &MyMap) {
1858ac84a95SThomas Schenker 
1868ac84a95SThomas Schenker   auto C1 = MyMap.count(2) != 0U;
1878ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
1888ac84a95SThomas Schenker   // CHECK-FIXES: auto C1 = MyMap.contains(2);
1898ac84a95SThomas Schenker   auto C2 = MyMap.count(2) != 0UL;
1908ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
1918ac84a95SThomas Schenker   // CHECK-FIXES: auto C2 = MyMap.contains(2);
1928ac84a95SThomas Schenker   auto C3 = 0U != MyMap.count(2);
1938ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains]
1948ac84a95SThomas Schenker   // CHECK-FIXES: auto C3 = MyMap.contains(2);
1958ac84a95SThomas Schenker   auto C4 = 0UL != MyMap.count(2);
1968ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: use 'contains' to check for membership [readability-container-contains]
1978ac84a95SThomas Schenker   // CHECK-FIXES: auto C4 = MyMap.contains(2);
1988ac84a95SThomas Schenker   auto C5 = MyMap.count(2) < 1U;
1998ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
2008ac84a95SThomas Schenker   // CHECK-FIXES: auto C5 = !MyMap.contains(2);
2018ac84a95SThomas Schenker   auto C6 = MyMap.count(2) < 1UL;
2028ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
2038ac84a95SThomas Schenker   // CHECK-FIXES: auto C6 = !MyMap.contains(2);
2048ac84a95SThomas Schenker   auto C7 = 1U > MyMap.count(2);
2058ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: use 'contains' to check for membership [readability-container-contains]
2068ac84a95SThomas Schenker   // CHECK-FIXES: auto C7 = !MyMap.contains(2);
2078ac84a95SThomas Schenker   auto C8 = 1UL > MyMap.count(2);
2088ac84a95SThomas Schenker   // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: use 'contains' to check for membership [readability-container-contains]
2098ac84a95SThomas Schenker   // CHECK-FIXES: auto C8 = !MyMap.contains(2);
2108ac84a95SThomas Schenker 
2118ac84a95SThomas Schenker   return C1 + C2 + C3 + C4 + C5 + C6 + C7 + C8;
2128ac84a95SThomas Schenker }
2138ac84a95SThomas Schenker 
21489a1d03eSRichard // We don't want to rewrite if the `contains` call is from a macro expansion
21589a1d03eSRichard int testMacroExpansion(std::unordered_set<int> &MySet) {
21689a1d03eSRichard #define COUNT_ONES(SET) SET.count(1)
21789a1d03eSRichard   // Rewriting the macro would break the code
21889a1d03eSRichard   // CHECK-FIXES: #define COUNT_ONES(SET) SET.count(1)
21989a1d03eSRichard   // We still want to warn the user even if we don't offer a fixit
22089a1d03eSRichard   if (COUNT_ONES(MySet)) {
22189a1d03eSRichard     // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'contains' to check for membership [readability-container-contains]
22289a1d03eSRichard     // CHECK-MESSAGES: note: expanded from macro 'COUNT_ONES'
22389a1d03eSRichard     return COUNT_ONES(MySet);
22489a1d03eSRichard   }
22589a1d03eSRichard #undef COUNT_ONES
22689a1d03eSRichard #define COUNT_ONES count(1)
22789a1d03eSRichard   // Rewriting the macro would break the code
22889a1d03eSRichard   // CHECK-FIXES: #define COUNT_ONES count(1)
22989a1d03eSRichard   // We still want to warn the user even if we don't offer a fixit
23089a1d03eSRichard   if (MySet.COUNT_ONES) {
23189a1d03eSRichard     // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use 'contains' to check for membership [readability-container-contains]
23289a1d03eSRichard     // CHECK-MESSAGES: note: expanded from macro 'COUNT_ONES'
23389a1d03eSRichard     return MySet.COUNT_ONES;
23489a1d03eSRichard   }
23589a1d03eSRichard #undef COUNT_ONES
23689a1d03eSRichard #define MY_SET MySet
23789a1d03eSRichard   // CHECK-FIXES: #define MY_SET MySet
23889a1d03eSRichard   // We still want to rewrite one of the two calls to `count`
23989a1d03eSRichard   if (MY_SET.count(1)) {
24089a1d03eSRichard     // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'contains' to check for membership [readability-container-contains]
24189a1d03eSRichard     // CHECK-FIXES: if (MY_SET.contains(1)) {
24289a1d03eSRichard     return MY_SET.count(1);
24389a1d03eSRichard   }
24489a1d03eSRichard #undef MY_SET
24589a1d03eSRichard   return 0;
24689a1d03eSRichard }
24789a1d03eSRichard 
2481be4c971SNicolas van Kempen // The following map has the same interface as `std::map`.
24989a1d03eSRichard template <class Key, class T>
25089a1d03eSRichard struct CustomMap {
25189a1d03eSRichard   unsigned count(const Key &K) const;
25289a1d03eSRichard   bool contains(const Key &K) const;
25389a1d03eSRichard   void *find(const Key &K);
25489a1d03eSRichard   void *end();
25589a1d03eSRichard };
25689a1d03eSRichard 
2571be4c971SNicolas van Kempen void testDifferentCheckTypes(CustomMap<int, int> &MyMap) {
2581be4c971SNicolas van Kempen   if (MyMap.count(0)) {};
2591be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
2601be4c971SNicolas van Kempen   // CHECK-FIXES: if (MyMap.contains(0)) {};
2611be4c971SNicolas van Kempen 
2621be4c971SNicolas van Kempen   MyMap.find(0) != MyMap.end();
2631be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
2641be4c971SNicolas van Kempen   // CHECK-FIXES: MyMap.contains(0);
2651be4c971SNicolas van Kempen }
2661be4c971SNicolas van Kempen 
2671be4c971SNicolas van Kempen struct MySubmap : public CustomMap<int, int> {};
2681be4c971SNicolas van Kempen 
2691be4c971SNicolas van Kempen void testSubclass(MySubmap& MyMap) {
2701be4c971SNicolas van Kempen   if (MyMap.count(0)) {};
2711be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
2721be4c971SNicolas van Kempen   // CHECK-FIXES: if (MyMap.contains(0)) {};
2731be4c971SNicolas van Kempen }
2741be4c971SNicolas van Kempen 
2751be4c971SNicolas van Kempen using UsingMap = CustomMap<int, int>;
2761be4c971SNicolas van Kempen struct MySubmap2 : public UsingMap {};
2771be4c971SNicolas van Kempen using UsingMap2 = MySubmap2;
2781be4c971SNicolas van Kempen 
2791be4c971SNicolas van Kempen void testUsing(UsingMap2& MyMap) {
2801be4c971SNicolas van Kempen   if (MyMap.count(0)) {};
2811be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
2821be4c971SNicolas van Kempen   // CHECK-FIXES: if (MyMap.contains(0)) {};
2831be4c971SNicolas van Kempen }
2841be4c971SNicolas van Kempen 
2851be4c971SNicolas van Kempen template <class Key, class T>
2861be4c971SNicolas van Kempen struct CustomMapContainsDeleted {
2871be4c971SNicolas van Kempen   unsigned count(const Key &K) const;
2881be4c971SNicolas van Kempen   bool contains(const Key &K) const = delete;
2891be4c971SNicolas van Kempen   void *find(const Key &K);
2901be4c971SNicolas van Kempen   void *end();
2911be4c971SNicolas van Kempen };
2921be4c971SNicolas van Kempen 
2931be4c971SNicolas van Kempen struct SubmapContainsDeleted : public CustomMapContainsDeleted<int, int> {};
2941be4c971SNicolas van Kempen 
2951be4c971SNicolas van Kempen void testContainsDeleted(CustomMapContainsDeleted<int, int> &MyMap,
2961be4c971SNicolas van Kempen                          SubmapContainsDeleted &MyMap2) {
2971be4c971SNicolas van Kempen   // No warning if the `contains` method is deleted.
2981be4c971SNicolas van Kempen   if (MyMap.count(0)) {};
2991be4c971SNicolas van Kempen   if (MyMap2.count(0)) {};
3001be4c971SNicolas van Kempen }
3011be4c971SNicolas van Kempen 
3021be4c971SNicolas van Kempen template <class Key, class T>
3031be4c971SNicolas van Kempen struct CustomMapPrivateContains {
3041be4c971SNicolas van Kempen   unsigned count(const Key &K) const;
3051be4c971SNicolas van Kempen   void *find(const Key &K);
3061be4c971SNicolas van Kempen   void *end();
3071be4c971SNicolas van Kempen 
3081be4c971SNicolas van Kempen private:
3091be4c971SNicolas van Kempen   bool contains(const Key &K) const;
3101be4c971SNicolas van Kempen };
3111be4c971SNicolas van Kempen 
3121be4c971SNicolas van Kempen struct SubmapPrivateContains : public CustomMapPrivateContains<int, int> {};
3131be4c971SNicolas van Kempen 
3141be4c971SNicolas van Kempen void testPrivateContains(CustomMapPrivateContains<int, int> &MyMap,
3151be4c971SNicolas van Kempen                          SubmapPrivateContains &MyMap2) {
3161be4c971SNicolas van Kempen   // No warning if the `contains` method is not public.
3171be4c971SNicolas van Kempen   if (MyMap.count(0)) {};
3181be4c971SNicolas van Kempen   if (MyMap2.count(0)) {};
3191be4c971SNicolas van Kempen }
3201be4c971SNicolas van Kempen 
3211be4c971SNicolas van Kempen struct MyString {};
3221be4c971SNicolas van Kempen 
3231be4c971SNicolas van Kempen struct WeirdNonMatchingContains {
3241be4c971SNicolas van Kempen   unsigned count(char) const;
3251be4c971SNicolas van Kempen   bool contains(const MyString&) const;
3261be4c971SNicolas van Kempen };
3271be4c971SNicolas van Kempen 
3281be4c971SNicolas van Kempen void testWeirdNonMatchingContains(WeirdNonMatchingContains &MyMap) {
3291be4c971SNicolas van Kempen   // No warning if there is no `contains` method with the right type.
3301be4c971SNicolas van Kempen   if (MyMap.count('a')) {};
3311be4c971SNicolas van Kempen }
3321be4c971SNicolas van Kempen 
3331be4c971SNicolas van Kempen template <class T>
3341be4c971SNicolas van Kempen struct SmallPtrSet {
3351be4c971SNicolas van Kempen   using ConstPtrType = const T*;
3361be4c971SNicolas van Kempen   unsigned count(ConstPtrType Ptr) const;
3371be4c971SNicolas van Kempen   bool contains(ConstPtrType Ptr) const;
3381be4c971SNicolas van Kempen };
3391be4c971SNicolas van Kempen 
3401be4c971SNicolas van Kempen template <class T>
3411be4c971SNicolas van Kempen struct SmallPtrPtrSet {
3421be4c971SNicolas van Kempen   using ConstPtrType = const T**;
3431be4c971SNicolas van Kempen   unsigned count(ConstPtrType Ptr) const;
3441be4c971SNicolas van Kempen   bool contains(ConstPtrType Ptr) const;
3451be4c971SNicolas van Kempen };
3461be4c971SNicolas van Kempen 
3471be4c971SNicolas van Kempen template <class T>
3481be4c971SNicolas van Kempen struct SmallPtrPtrPtrSet {
3491be4c971SNicolas van Kempen   using ConstPtrType = const T***;
3501be4c971SNicolas van Kempen   unsigned count(ConstPtrType Ptr) const;
3511be4c971SNicolas van Kempen   bool contains(ConstPtrType Ptr) const;
3521be4c971SNicolas van Kempen };
3531be4c971SNicolas van Kempen 
3541be4c971SNicolas van Kempen void testSmallPtrSet(const int ***Ptr,
3551be4c971SNicolas van Kempen                      SmallPtrSet<int> &MySet,
3561be4c971SNicolas van Kempen                      SmallPtrPtrSet<int> &MySet2,
3571be4c971SNicolas van Kempen                      SmallPtrPtrPtrSet<int> &MySet3) {
3581be4c971SNicolas van Kempen   if (MySet.count(**Ptr)) {};
3591be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
3601be4c971SNicolas van Kempen   // CHECK-FIXES: if (MySet.contains(**Ptr)) {};
3611be4c971SNicolas van Kempen   if (MySet2.count(*Ptr)) {};
3621be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
3631be4c971SNicolas van Kempen   // CHECK-FIXES: if (MySet2.contains(*Ptr)) {};
3641be4c971SNicolas van Kempen   if (MySet3.count(Ptr)) {};
3651be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
3661be4c971SNicolas van Kempen   // CHECK-FIXES: if (MySet3.contains(Ptr)) {};
3671be4c971SNicolas van Kempen }
3681be4c971SNicolas van Kempen 
3691be4c971SNicolas van Kempen struct X {};
3701be4c971SNicolas van Kempen struct Y : public X {};
3711be4c971SNicolas van Kempen 
3721be4c971SNicolas van Kempen void testSubclassEntry(SmallPtrSet<X>& Set, Y* Entry) {
3731be4c971SNicolas van Kempen   if (Set.count(Entry)) {}
3741be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
3751be4c971SNicolas van Kempen   // CHECK-FIXES: if (Set.contains(Entry)) {}
3761be4c971SNicolas van Kempen }
3771be4c971SNicolas van Kempen 
3781be4c971SNicolas van Kempen struct WeirdPointerApi {
3791be4c971SNicolas van Kempen   unsigned count(int** Ptr) const;
3801be4c971SNicolas van Kempen   bool contains(int* Ptr) const;
3811be4c971SNicolas van Kempen };
3821be4c971SNicolas van Kempen 
3831be4c971SNicolas van Kempen void testWeirdApi(WeirdPointerApi& Set, int* E) {
3841be4c971SNicolas van Kempen   if (Set.count(&E)) {}
3851be4c971SNicolas van Kempen }
3861be4c971SNicolas van Kempen 
3871be4c971SNicolas van Kempen void testIntUnsigned(std::set<int>& S, unsigned U) {
3881be4c971SNicolas van Kempen   if (S.count(U)) {}
3891be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
3901be4c971SNicolas van Kempen   // CHECK-FIXES: if (S.contains(U)) {}
3911be4c971SNicolas van Kempen }
3921be4c971SNicolas van Kempen 
3931be4c971SNicolas van Kempen template <class T>
3941be4c971SNicolas van Kempen struct CustomSetConvertible {
3951be4c971SNicolas van Kempen   unsigned count(const T &K) const;
3961be4c971SNicolas van Kempen   bool contains(const T &K) const;
3971be4c971SNicolas van Kempen };
3981be4c971SNicolas van Kempen 
3991be4c971SNicolas van Kempen struct A {};
4001be4c971SNicolas van Kempen struct B { B() = default; B(const A&) {} };
4011be4c971SNicolas van Kempen struct C { operator A() const; };
4021be4c971SNicolas van Kempen 
4031be4c971SNicolas van Kempen void testConvertibleTypes() {
4041be4c971SNicolas van Kempen   CustomSetConvertible<B> MyMap;
4051be4c971SNicolas van Kempen   if (MyMap.count(A())) {};
4061be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
4071be4c971SNicolas van Kempen   // CHECK-FIXES: if (MyMap.contains(A())) {};
4081be4c971SNicolas van Kempen 
4091be4c971SNicolas van Kempen   CustomSetConvertible<A> MyMap2;
4101be4c971SNicolas van Kempen   if (MyMap2.count(C())) {};
4111be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
4121be4c971SNicolas van Kempen   // CHECK-FIXES: if (MyMap2.contains(C())) {};
4131be4c971SNicolas van Kempen 
4141be4c971SNicolas van Kempen   if (MyMap2.count(C()) != 0) {};
4151be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
4161be4c971SNicolas van Kempen   // CHECK-FIXES: if (MyMap2.contains(C())) {};
4171be4c971SNicolas van Kempen }
4181be4c971SNicolas van Kempen 
4191be4c971SNicolas van Kempen template<class U>
4201be4c971SNicolas van Kempen using Box = const U& ;
4211be4c971SNicolas van Kempen 
4221be4c971SNicolas van Kempen template <class T>
4231be4c971SNicolas van Kempen struct CustomBoxedSet {
4241be4c971SNicolas van Kempen   unsigned count(Box<T> K) const;
4251be4c971SNicolas van Kempen   bool contains(Box<T> K) const;
4261be4c971SNicolas van Kempen };
4271be4c971SNicolas van Kempen 
4281be4c971SNicolas van Kempen void testBox() {
4291be4c971SNicolas van Kempen   CustomBoxedSet<int> Set;
4301be4c971SNicolas van Kempen   if (Set.count(0)) {};
4311be4c971SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
4321be4c971SNicolas van Kempen   // CHECK-FIXES: if (Set.contains(0)) {};
43389a1d03eSRichard }
43414c76321SNicolas van Kempen 
43514c76321SNicolas van Kempen void testOperandPermutations(std::map<int, int>& Map) {
43614c76321SNicolas van Kempen   if (Map.count(0) != 0) {};
43714c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
43814c76321SNicolas van Kempen   // CHECK-FIXES: if (Map.contains(0)) {};
43914c76321SNicolas van Kempen   if (0 != Map.count(0)) {};
44014c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
44114c76321SNicolas van Kempen   // CHECK-FIXES: if (Map.contains(0)) {};
44214c76321SNicolas van Kempen   if (Map.count(0) == 0) {};
44314c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
44414c76321SNicolas van Kempen   // CHECK-FIXES: if (!Map.contains(0)) {};
44514c76321SNicolas van Kempen   if (0 == Map.count(0)) {};
44614c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
44714c76321SNicolas van Kempen   // CHECK-FIXES: if (!Map.contains(0)) {};
44814c76321SNicolas van Kempen   if (Map.find(0) != Map.end()) {};
44914c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
45014c76321SNicolas van Kempen   // CHECK-FIXES: if (Map.contains(0)) {};
45114c76321SNicolas van Kempen   if (Map.end() != Map.find(0)) {};
45214c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
45314c76321SNicolas van Kempen   // CHECK-FIXES: if (Map.contains(0)) {};
45414c76321SNicolas van Kempen   if (Map.find(0) == Map.end()) {};
45514c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
45614c76321SNicolas van Kempen   // CHECK-FIXES: if (!Map.contains(0)) {};
45714c76321SNicolas van Kempen   if (Map.end() == Map.find(0)) {};
45814c76321SNicolas van Kempen   // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use 'contains' to check for membership [readability-container-contains]
45914c76321SNicolas van Kempen   // CHECK-FIXES: if (!Map.contains(0)) {};
46014c76321SNicolas van Kempen }
461