1 // RUN: %check_clang_tidy %s modernize-min-max-use-initializer-list %t 2 3 // CHECK-FIXES: #include <algorithm> 4 namespace utils { 5 template <typename T> 6 T max(T a, T b) { 7 return (a < b) ? b : a; 8 } 9 } // namespace utils 10 11 namespace std { 12 template< class T > 13 struct initializer_list { 14 const T *a, *b; 15 initializer_list()=default; 16 initializer_list(T*,int){} 17 const T* begin() const {return nullptr;} 18 const T* end() const {return nullptr;} 19 }; 20 21 template<class ForwardIt> 22 ForwardIt min_element(ForwardIt first, ForwardIt last) 23 { 24 if (first == last) 25 return last; 26 27 ForwardIt smallest = first; 28 29 while (++first != last) 30 if (*first < *smallest) 31 smallest = first; 32 33 return smallest; 34 } 35 36 template<class ForwardIt, class Compare> 37 ForwardIt min_element(ForwardIt first, ForwardIt last, Compare comp) 38 { 39 if (first == last) 40 return last; 41 42 ForwardIt smallest = first; 43 44 while (++first != last) 45 if (comp(*first, *smallest)) 46 smallest = first; 47 48 return smallest; 49 } 50 51 template<class ForwardIt> 52 ForwardIt max_element(ForwardIt first, ForwardIt last) 53 { 54 if (first == last) 55 return last; 56 57 ForwardIt largest = first; 58 59 while (++first != last) 60 if (*largest < *first) 61 largest = first; 62 63 return largest; 64 } 65 66 template<class ForwardIt, class Compare> 67 ForwardIt max_element(ForwardIt first, ForwardIt last, Compare comp) 68 { 69 if (first == last) 70 return last; 71 72 ForwardIt largest = first; 73 74 while(++first != last) 75 if (comp(*largest, *first)) 76 largest = first; 77 78 return largest; 79 } 80 81 template< class T > 82 const T& max( const T& a, const T& b ) { 83 return (a < b) ? b : a; 84 }; 85 86 template< class T > 87 T max(std::initializer_list<T> ilist) 88 { 89 return *std::max_element(ilist.begin(), ilist.end()); 90 } 91 92 template< class T, class Compare > 93 const T& max( const T& a, const T& b, Compare comp ) { 94 return (comp(a, b)) ? b : a; 95 }; 96 97 template< class T, class Compare > 98 T max(std::initializer_list<T> ilist, Compare comp) { 99 return *std::max_element(ilist.begin(), ilist.end(), comp); 100 }; 101 102 template< class T > 103 const T& min( const T& a, const T& b ) { 104 return (b < a) ? b : a; 105 }; 106 107 template< class T > 108 T min(std::initializer_list<T> ilist) 109 { 110 return *std::min_element(ilist.begin(), ilist.end()); 111 } 112 113 114 template< class T, class Compare > 115 const T& min( const T& a, const T& b, Compare comp ) { 116 return (comp(b, a)) ? b : a; 117 }; 118 119 template< class T, class Compare > 120 T min(std::initializer_list<T> ilist, Compare comp) { 121 return *std::min_element(ilist.begin(), ilist.end(), comp); 122 }; 123 124 } // namespace std 125 126 using namespace std; 127 128 namespace { 129 bool fless_than(int a, int b) { 130 return a < b; 131 } 132 133 bool fgreater_than(int a, int b) { 134 return a > b; 135 } 136 auto less_than = [](int a, int b) { return a < b; }; 137 auto greater_than = [](int a, int b) { return a > b; }; 138 139 int max1 = std::max(1, std::max(2, 3)); 140 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 141 // CHECK-FIXES: int max1 = std::max({1, 2, 3}); 142 143 int min1 = std::min(1, std::min(2, 3)); 144 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 145 // CHECK-FIXES: int min1 = std::min({1, 2, 3}); 146 147 int max2 = std::max(1, std::max(2, std::max(3, 4))); 148 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 149 // CHECK-FIXES: int max2 = std::max({1, 2, 3, 4}); 150 151 int max2b = std::max(std::max(std::max(1, 2), std::max(3, 4)), std::max(std::max(5, 6), std::max(7, 8))); 152 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 153 // CHECK-FIXES: int max2b = std::max({1, 2, 3, 4, 5, 6, 7, 8}); 154 155 int max2c = std::max(std::max(1, std::max(2, 3)), 4); 156 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 157 // CHECK-FIXES: int max2c = std::max({1, 2, 3, 4}); 158 159 int max2d = std::max(std::max({1, 2, 3}), 4); 160 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 161 // CHECK-FIXES: int max2d = std::max({1, 2, 3, 4}); 162 163 164 int max2e = std::max(1, max(2, max(3, 4))); 165 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 166 // CHECK-FIXES: int max2e = std::max({1, 2, 3, 4}); 167 168 int min2 = std::min(1, std::min(2, std::min(3, 4))); 169 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 170 // CHECK-FIXES: int min2 = std::min({1, 2, 3, 4}); 171 172 int max3 = std::max(std::max(4, 5), std::min(2, std::min(3, 1))); 173 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 174 // CHECK-MESSAGES: :[[@LINE-2]]:37: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 175 // CHECK-FIXES: int max3 = std::max({4, 5, std::min({2, 3, 1})}); 176 177 int min3 = std::min(std::min(4, 5), std::max(2, std::max(3, 1))); 178 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 179 // CHECK-MESSAGES: :[[@LINE-2]]:37: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 180 // CHECK-FIXES: int min3 = std::min({4, 5, std::max({2, 3, 1})}); 181 182 int max4 = std::max(1, std::max(2, 3, greater_than), less_than); 183 // CHECK-FIXES: int max4 = std::max(1, std::max(2, 3, greater_than), less_than); 184 185 int min4 = std::min(1, std::min(2, 3, greater_than), less_than); 186 // CHECK-FIXES: int min4 = std::min(1, std::min(2, 3, greater_than), less_than); 187 188 int max5 = std::max(1, std::max(2, 3), less_than); 189 // CHECK-FIXES: int max5 = std::max(1, std::max(2, 3), less_than); 190 191 int min5 = std::min(1, std::min(2, 3), less_than); 192 // CHECK-FIXES: int min5 = std::min(1, std::min(2, 3), less_than); 193 194 int max6 = std::max(1, std::max(2, 3, greater_than), greater_than); 195 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 196 // CHECK-FIXES: int max6 = std::max({1, 2, 3 }, greater_than); 197 198 int min6 = std::min(1, std::min(2, 3, greater_than), greater_than); 199 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 200 // CHECK-FIXES: int min6 = std::min({1, 2, 3 }, greater_than); 201 202 int max7 = std::max(1, std::max(2, 3, fless_than), fgreater_than); 203 // CHECK-FIXES: int max7 = std::max(1, std::max(2, 3, fless_than), fgreater_than); 204 205 int min7 = std::min(1, std::min(2, 3, fless_than), fgreater_than); 206 // CHECK-FIXES: int min7 = std::min(1, std::min(2, 3, fless_than), fgreater_than); 207 208 int max8 = std::max(1, std::max(2, 3, fless_than), less_than); 209 // CHECK-FIXES: int max8 = std::max(1, std::max(2, 3, fless_than), less_than) 210 211 int min8 = std::min(1, std::min(2, 3, fless_than), less_than); 212 // CHECK-FIXES: int min8 = std::min(1, std::min(2, 3, fless_than), less_than); 213 214 int max9 = std::max(1, std::max(2, 3, fless_than), fless_than); 215 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 216 // CHECK-FIXES: int max9 = std::max({1, 2, 3 }, fless_than); 217 218 int min9 = std::min(1, std::min(2, 3, fless_than), fless_than); 219 // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 220 // CHECK-FIXES: int min9 = std::min({1, 2, 3 }, fless_than); 221 222 int min10 = std::min(std::min(4, 5), std::max(2, utils::max(3, 1))); 223 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::min' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 224 // CHECK-FIXES: int min10 = std::min({4, 5, std::max(2, utils::max(3, 1))}); 225 226 int max10 = std::max({std::max(1, 2), std::max({5, 6, 1}), 2, std::min({1, 2, 4})}); 227 // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 228 // CHECK-FIXES: int max10 = std::max({1, 2, 5, 6, 1, 2, std::min({1, 2, 4})}); 229 230 int typecastTest = std::max(std::max<int>(0U, 0.0f), 0); 231 // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 232 // CHECK-FIXES: int typecastTest = std::max({static_cast<int>(0U), static_cast<int>(0.0f), 0}); 233 234 int typecastTest1 = std::max(std::max<long>(0U, 0.0f), 0L); 235 // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 236 // CHECK-FIXES: int typecastTest1 = std::max({static_cast<long>(0U), static_cast<long>(0.0f), 0L}); 237 238 int typecastTest2 = std::max(std::max<int>(10U, 20.0f), 30); 239 // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 240 // CHECK-FIXES: int typecastTest2 = std::max({static_cast<int>(10U), static_cast<int>(20.0f), 30}); 241 242 int typecastTest3 = std::max(std::max<int>(0U, std::max<int>(0.0f, 1.0f)), 0); 243 // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 244 // CHECK-FIXES: int typecastTest3 = std::max({static_cast<int>(0U), static_cast<int>(0.0f), static_cast<int>(1.0f), 0}); 245 246 #define max3f(a, b, c) std::max(a, std::max(b, c)) 247 // CHECK-FIXES: #define max3f(a, b, c) std::max(a, std::max(b, c)) 248 249 #define value 4545 250 int macroVarMax = std::max(value, std::max(1, 2)); 251 // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 252 // CHECK-FIXES: int macroVarMax = std::max({value, 1, 2}); 253 254 #define value2 45U 255 int macroVarMax2 = std::max(1, std::max<int>(value2, 2.0f)); 256 // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 257 // CHECK-FIXES: int macroVarMax2 = std::max({1, static_cast<int>(value2), static_cast<int>(2.0f)}); 258 259 // True-negative tests 260 int maxTN1 = std::max(1, 2); 261 // CHECK-FIXES: int maxTN1 = std::max(1, 2); 262 263 int maxTN2 = std::max({1, 2, 3}); 264 // CHECK-FIXES: int maxTN2 = std::max({1, 2, 3}); 265 266 int maxTN3 = std::max({1, 2, 3}, less_than); 267 // CHECK-FIXES: int maxTN3 = std::max({1, 2, 3}, less_than); 268 269 // non-trivial types 270 struct A { 271 int a; 272 A(int a) : a(a) {} 273 bool operator<(const A &rhs) const { return a < rhs.a; } 274 }; 275 276 A maxNT1 = std::max(A(1), A(2)); 277 // CHECK-FIXES: A maxNT1 = std::max(A(1), A(2)); 278 279 A maxNT2 = std::max(A(1), std::max(A(2), A(3))); 280 // CHECK-FIXES: A maxNT2 = std::max(A(1), std::max(A(2), A(3))); 281 282 A maxNT3 = std::max(A(1), std::max(A(2), A(3)), [](const A &lhs, const A &rhs) { return lhs.a < rhs.a; }); 283 // CHECK-FIXES: A maxNT3 = std::max(A(1), std::max(A(2), A(3)), [](const A &lhs, const A &rhs) { return lhs.a < rhs.a; }); 284 285 // Trivial type with size greater than 32 286 struct B { 287 // 9*4 = 36 bytes > 32 bytes 288 int a[9]; 289 290 bool operator<(const B& rhs) const { 291 return a[0] < rhs.a[0]; 292 } 293 }; 294 295 B maxTT1 = std::max(B(), B()); 296 // CHECK-FIXES: B maxTT1 = std::max(B(), B()); 297 298 B maxTT2 = std::max(B(), std::max(B(), B())); 299 // CHECK-FIXES: B maxTT2 = std::max(B(), std::max(B(), B())); 300 301 B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; }); 302 // CHECK-FIXES: B maxTT3 = std::max(B(), std::max(B(), B()), [](const B &lhs, const B &rhs) { return lhs.a[0] < rhs.a[0]; }); 303 304 struct GH91982 { 305 int fun0Args(); 306 int fun1Arg(int a); 307 int fun2Args(int a, int b); 308 int fun3Args(int a, int b, int c); 309 int fun4Args(int a, int b, int c, int d); 310 311 int foo() { 312 return std::max( 313 fun0Args(), 314 std::max(fun1Arg(0), 315 std::max(fun2Args(0, 1), 316 std::max(fun3Args(0, 1, 2), fun4Args(0, 1, 2, 3))))); 317 // CHECK-MESSAGES: :[[@LINE-5]]:12: warning: do not use nested 'std::max' calls, use an initializer list instead [modernize-min-max-use-initializer-list] 318 // CHECK-FIXES: return std::max( 319 // CHECK-FIXES-NEXT: {fun0Args(), 320 // CHECK-FIXES-NEXT: fun1Arg(0), 321 // CHECK-FIXES-NEXT: fun2Args(0, 1), 322 // CHECK-FIXES-NEXT: fun3Args(0, 1, 2), fun4Args(0, 1, 2, 3)}); 323 } 324 }; 325 326 struct GH107594 { 327 int foo(int a, int b, char c) { 328 return std::max<int>({a, b, c}); 329 } 330 }; 331 332 } // namespace 333 334