1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
|
// RUN: %check_clang_tidy -std=c++20 %s readability-container-contains %t
// Some *very* simplified versions of `map` etc.
namespace std {
template <class Key, class T>
struct map {
unsigned count(const Key &K) const;
bool contains(const Key &K) const;
void *find(const Key &K);
void *end();
};
template <class Key>
struct set {
unsigned count(const Key &K) const;
bool contains(const Key &K) const;
};
template <class Key>
struct unordered_set {
unsigned count(const Key &K) const;
bool contains(const Key &K) const;
};
template <class Key, class T>
struct multimap {
unsigned count(const Key &K) const;
bool contains(const Key &K) const;
};
} // namespace std
// Check that we detect various common ways to check for membership
int testDifferentCheckTypes(std::map<int, int> &MyMap) {
if (MyMap.count(0))
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MyMap.contains(0))
return 1;
bool C1 = MyMap.count(1);
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C1 = MyMap.contains(1);
auto C2 = static_cast<bool>(MyMap.count(1));
// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C2 = static_cast<bool>(MyMap.contains(1));
auto C3 = MyMap.count(2) != 0;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C3 = MyMap.contains(2);
auto C4 = MyMap.count(3) > 0;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C4 = MyMap.contains(3);
auto C5 = MyMap.count(4) >= 1;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C5 = MyMap.contains(4);
auto C6 = MyMap.find(5) != MyMap.end();
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C6 = MyMap.contains(5);
return C1 + C2 + C3 + C4 + C5 + C6;
}
// Check that we detect various common ways to check for non-membership
int testNegativeChecks(std::map<int, int> &MyMap) {
bool C1 = !MyMap.count(-1);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C1 = !MyMap.contains(-1);
auto C2 = MyMap.count(-2) == 0;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C2 = !MyMap.contains(-2);
auto C3 = MyMap.count(-3) <= 0;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C3 = !MyMap.contains(-3);
auto C4 = MyMap.count(-4) < 1;
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C4 = !MyMap.contains(-4);
auto C5 = MyMap.find(-5) == MyMap.end();
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: auto C5 = !MyMap.contains(-5);
return C1 + C2 + C3 + C4 + C5;
}
// Check for various types
int testDifferentTypes(std::map<int, int> &M, std::unordered_set<int> &US, std::set<int> &S, std::multimap<int, int> &MM) {
bool C1 = M.count(1001);
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C1 = M.contains(1001);
bool C2 = US.count(1002);
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C2 = US.contains(1002);
bool C3 = S.count(1003);
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C3 = S.contains(1003);
bool C4 = MM.count(1004);
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C4 = MM.contains(1004);
return C1 + C2 + C3 + C4;
}
// The check detects all kind of `const`, reference, rvalue-reference and value types.
int testQualifiedTypes(std::map<int, int> ValueM, std::map<int, int> &RefM, const std::map<int, int> &ConstRefM, std::map<int, int> &&RValueM) {
bool C1 = ValueM.count(2001);
// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C1 = ValueM.contains(2001);
bool C2 = RefM.count(2002);
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C2 = RefM.contains(2002);
bool C3 = ConstRefM.count(2003);
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C3 = ConstRefM.contains(2003);
bool C4 = RValueM.count(2004);
// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C4 = RValueM.contains(2004);
return C1 + C2 + C3 + C4;
}
// This is effectively a membership check, as the result is implicitly casted
// to `bool`.
bool returnContains(std::map<int, int> &M) {
return M.count(42);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: return M.contains(42);
}
// This returns the actual count and should not be rewritten
int actualCount(std::multimap<int, int> &M) {
return M.count(21);
// NO-WARNING.
// CHECK-FIXES: return M.count(21);
}
// Check that we are not confused by aliases
namespace s2 = std;
using MyMapT = s2::map<int, int>;
int typeAliases(MyMapT &MyMap) {
bool C1 = MyMap.count(99);
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: bool C1 = MyMap.contains(99);
return C1;
}
// Check that the tests also trigger for a local variable and not only for
// function arguments.
bool localVar() {
using namespace std;
map<int, int> LocalM;
return LocalM.count(42);
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: return LocalM.contains(42);
}
// Check various usages of an actual `count` which isn't rewritten
int nonRewrittenCount(std::multimap<int, int> &MyMap) {
// This is an actual test if we have at least 2 usages. Shouldn't be rewritten.
bool C1 = MyMap.count(1) >= 2;
// NO-WARNING.
// CHECK-FIXES: bool C1 = MyMap.count(1) >= 2;
// "< 0" makes little sense and is always `false`. Still, let's ensure we
// don't accidentally rewrite it to 'contains'.
bool C2 = MyMap.count(2) < 0;
// NO-WARNING.
// CHECK-FIXES: bool C2 = MyMap.count(2) < 0;
// The `count` is used in some more complicated formula.
bool C3 = MyMap.count(1) + MyMap.count(2) * 2 + MyMap.count(3) / 3 >= 20;
// NO-WARNING.
// CHECK-FIXES: bool C3 = MyMap.count(1) + MyMap.count(2) * 2 + MyMap.count(3) / 3 >= 20;
// This could theoretically be rewritten into a 'contains' after removig the
// `4` on both sides of the comparison. For the time being, we don't detect
// this case.
bool C4 = MyMap.count(1) + 4 > 4;
// NO-WARNING.
// CHECK-FIXES: bool C4 = MyMap.count(1) + 4 > 4;
return C1 + C2 + C3 + C4;
}
// We don't want to rewrite if the `contains` call is from a macro expansion
int testMacroExpansion(std::unordered_set<int> &MySet) {
#define COUNT_ONES(SET) SET.count(1)
// Rewriting the macro would break the code
// CHECK-FIXES: #define COUNT_ONES(SET) SET.count(1)
// We still want to warn the user even if we don't offer a fixit
if (COUNT_ONES(MySet)) {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-MESSAGES: note: expanded from macro 'COUNT_ONES'
return COUNT_ONES(MySet);
}
#undef COUNT_ONES
#define COUNT_ONES count(1)
// Rewriting the macro would break the code
// CHECK-FIXES: #define COUNT_ONES count(1)
// We still want to warn the user even if we don't offer a fixit
if (MySet.COUNT_ONES) {
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-MESSAGES: note: expanded from macro 'COUNT_ONES'
return MySet.COUNT_ONES;
}
#undef COUNT_ONES
#define MY_SET MySet
// CHECK-FIXES: #define MY_SET MySet
// We still want to rewrite one of the two calls to `count`
if (MY_SET.count(1)) {
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'contains' to check for membership [readability-container-contains]
// CHECK-FIXES: if (MY_SET.contains(1)) {
return MY_SET.count(1);
}
#undef MY_SET
return 0;
}
// The following map has the same interface like `std::map`.
template <class Key, class T>
struct CustomMap {
unsigned count(const Key &K) const;
bool contains(const Key &K) const;
void *find(const Key &K);
void *end();
};
// The clang-tidy check is currently hard-coded against the `std::` containers
// and hence won't annotate the following instance. We might change this in the
// future and also detect the following case.
void *testDifferentCheckTypes(CustomMap<int, int> &MyMap) {
if (MyMap.count(0))
// NO-WARNING.
// CHECK-FIXES: if (MyMap.count(0))
return nullptr;
return MyMap.find(2);
}
|