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