1 | // RUN: %check_clang_tidy %s misc-redundant-expression %t -- -- -fno-delayed-template-parsing |
2 | |
3 | typedef __INT64_TYPE__ I64; |
4 | |
5 | struct Point { |
6 | int x; |
7 | int y; |
8 | int a[5]; |
9 | } P; |
10 | |
11 | extern Point P1; |
12 | extern Point P2; |
13 | |
14 | extern int foo(int x); |
15 | extern int bar(int x); |
16 | extern int bat(int x, int y); |
17 | |
18 | int TestSimpleEquivalent(int X, int Y) { |
19 | if (X - X) return 1; |
20 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent [misc-redundant-expression] |
21 | if (X / X) return 1; |
22 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
23 | if (X % X) return 1; |
24 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
25 | |
26 | if (X & X) return 1; |
27 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
28 | if (X | X) return 1; |
29 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
30 | if (X ^ X) return 1; |
31 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
32 | |
33 | if (X < X) return 1; |
34 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
35 | if (X <= X) return 1; |
36 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
37 | if (X > X) return 1; |
38 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
39 | if (X >= X) return 1; |
40 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
41 | |
42 | if (X && X) return 1; |
43 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
44 | if (X || X) return 1; |
45 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
46 | |
47 | if (X != (((X)))) return 1; |
48 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of operator are equivalent |
49 | |
50 | if (X + 1 == X + 1) return 1; |
51 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent |
52 | if (X + 1 != X + 1) return 1; |
53 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent |
54 | if (X + 1 <= X + 1) return 1; |
55 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent |
56 | if (X + 1 >= X + 1) return 1; |
57 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent |
58 | |
59 | if ((X != 1 || Y != 1) && (X != 1 || Y != 1)) return 1; |
60 | // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both sides of operator are equivalent |
61 | if (P.a[X - P.x] != P.a[X - P.x]) return 1; |
62 | // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: both sides of operator are equivalent |
63 | |
64 | if ((int)X < (int)X) return 1; |
65 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent |
66 | if (int(X) < int(X)) return 1; |
67 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent |
68 | |
69 | if ( + "dummy" == + "dummy" ) return 1; |
70 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both sides of operator are equivalent |
71 | if (L"abc" == L"abc" ) return 1; |
72 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent |
73 | |
74 | if (foo(x: 0) - 2 < foo(x: 0) - 2) return 1; |
75 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: both sides of operator are equivalent |
76 | if (foo(x: bar(x: 0)) < (foo(x: bar(x: (0))))) return 1; |
77 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both sides of operator are equivalent |
78 | |
79 | if (P1.x < P2.x && P1.x < P2.x) return 1; |
80 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: both sides of operator are equivalent |
81 | if (P2.a[P1.x + 2] < P2.x && P2.a[(P1.x) + (2)] < (P2.x)) return 1; |
82 | // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: both sides of operator are equivalent |
83 | |
84 | if (X && Y && X) return 1; |
85 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: operator has equivalent nested operands |
86 | if (X || (Y || X)) return 1; |
87 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: operator has equivalent nested operands |
88 | if ((X ^ Y) ^ (Y ^ X)) return 1; |
89 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: operator has equivalent nested operands |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | template <int DX> |
95 | int TestSimpleEquivalentDependent() { |
96 | if (DX > 0 && DX > 0) return 1; |
97 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | int Valid(int X, int Y) { |
103 | if (X != Y) return 1; |
104 | if (X == Y + 0) return 1; |
105 | if (P.x == P.y) return 1; |
106 | if (P.a[P.x] < P.a[P.y]) return 1; |
107 | if (P.a[0] < P.a[1]) return 1; |
108 | |
109 | if (P.a[0] < P.a[0ULL]) return 1; |
110 | if (0 < 0ULL) return 1; |
111 | if ((int)0 < (int)0ULL) return 1; |
112 | |
113 | if (++X != ++X) return 1; |
114 | if (P.a[X]++ != P.a[X]++) return 1; |
115 | if (P.a[X++] != P.a[X++]) return 1; |
116 | if (X && X++ && X) return 1; |
117 | |
118 | if ("abc" == "ABC" ) return 1; |
119 | if (foo(x: bar(x: 0)) < (foo(x: bat(x: 0, y: 1)))) return 1; |
120 | return 0; |
121 | } |
122 | |
123 | #define COND_OP_MACRO 9 |
124 | #define COND_OP_OTHER_MACRO 9 |
125 | #define COND_OP_THIRD_MACRO COND_OP_MACRO |
126 | int TestConditional(int x, int y) { |
127 | int k = 0; |
128 | k += (y < 0) ? x : x; |
129 | // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: 'true' and 'false' expressions are equivalent |
130 | k += (y < 0) ? x + 1 : x + 1; |
131 | // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: 'true' and 'false' expressions are equivalent |
132 | k += (y < 0) ? COND_OP_MACRO : COND_OP_MACRO; |
133 | // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: 'true' and 'false' expressions are equivalent |
134 | k += (y < 0) ? COND_OP_MACRO + COND_OP_OTHER_MACRO : COND_OP_MACRO + COND_OP_OTHER_MACRO; |
135 | // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: 'true' and 'false' expressions are equivalent |
136 | |
137 | // Do not match for conditional operators with a macro and a const. |
138 | k += (y < 0) ? COND_OP_MACRO : 9; |
139 | // Do not match for conditional operators with expressions from different macros. |
140 | k += (y < 0) ? COND_OP_MACRO : COND_OP_OTHER_MACRO; |
141 | // Do not match for conditional operators when a macro is defined to another macro |
142 | k += (y < 0) ? COND_OP_MACRO : COND_OP_THIRD_MACRO; |
143 | #undef COND_OP_THIRD_MACRO |
144 | #define COND_OP_THIRD_MACRO 8 |
145 | k += (y < 0) ? COND_OP_MACRO : COND_OP_THIRD_MACRO; |
146 | #undef COND_OP_THIRD_MACRO |
147 | |
148 | k += (y < 0) ? sizeof(I64) : sizeof(I64); |
149 | // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: 'true' and 'false' expressions are equivalent |
150 | k += (y < 0) ? sizeof(TestConditional(x: k,y)) : sizeof(TestConditional(x: k,y)); |
151 | // CHECK-MESSAGES: :[[@LINE-1]]:47: warning: 'true' and 'false' expressions are equivalent |
152 | // No warning if the expression arguments are different. |
153 | k += (y < 0) ? sizeof(TestConditional(x: k,y)) : sizeof(Valid(X: k,Y: y)); |
154 | |
155 | return k; |
156 | } |
157 | #undef COND_OP_MACRO |
158 | #undef COND_OP_OTHER_MACRO |
159 | |
160 | // Overloaded operators that compare two instances of a struct. |
161 | struct MyStruct { |
162 | int x; |
163 | bool operator==(const MyStruct& rhs) const {return this->x == rhs.x; } // not modifing |
164 | bool operator>=(const MyStruct& rhs) const { return this->x >= rhs.x; } // not modifing |
165 | bool operator<=(MyStruct& rhs) const { return this->x <= rhs.x; } |
166 | bool operator&&(const MyStruct& rhs){ this->x++; return this->x && rhs.x; } |
167 | } Q; |
168 | |
169 | bool operator!=(const MyStruct& lhs, const MyStruct& rhs) { return lhs.x == rhs.x; } // not modifing |
170 | bool operator<(const MyStruct& lhs, const MyStruct& rhs) { return lhs.x < rhs.x; } // not modifing |
171 | bool operator>(const MyStruct& lhs, MyStruct& rhs) { rhs.x--; return lhs.x > rhs.x; } |
172 | bool operator||(MyStruct& lhs, const MyStruct& rhs) { lhs.x++; return lhs.x || rhs.x; } |
173 | |
174 | struct MyStruct1 { |
175 | bool x; |
176 | MyStruct1(bool x) : x(x) {}; |
177 | operator bool() { return x; } |
178 | }; |
179 | |
180 | MyStruct1 operator&&(const MyStruct1& lhs, const MyStruct1& rhs) { return lhs.x && rhs.x; } |
181 | MyStruct1 operator||(MyStruct1& lhs, MyStruct1& rhs) { return lhs.x && rhs.x; } |
182 | |
183 | bool TestOverloadedOperator(MyStruct& S) { |
184 | if (S == Q) return false; |
185 | |
186 | if (S <= S) return false; |
187 | if (S && S) return false; |
188 | if (S > S) return false; |
189 | if (S || S) return false; |
190 | |
191 | if (S == S) return true; |
192 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of overloaded operator are equivalent |
193 | if (S < S) return true; |
194 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of overloaded operator are equivalent |
195 | if (S != S) return true; |
196 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of overloaded operator are equivalent |
197 | if (S >= S) return true; |
198 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: both sides of overloaded operator are equivalent |
199 | |
200 | MyStruct1 U(false); |
201 | MyStruct1 V(true); |
202 | |
203 | // valid because the operator is not const |
204 | if ((U || V) || U) return true; |
205 | |
206 | if (U && V && U && V) return true; |
207 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: overloaded operator has equivalent nested operands |
208 | |
209 | return true; |
210 | } |
211 | |
212 | #define LT(x, y) (void)((x) < (y)) |
213 | #define COND(x, y, z) ((x)?(y):(z)) |
214 | #define EQUALS(x, y) (x) == (y) |
215 | |
216 | int TestMacro(int X, int Y) { |
217 | LT(0, 0); |
218 | LT(1, 0); |
219 | LT(X, X); |
220 | LT(X+1, X + 1); |
221 | COND(X < Y, X, X); |
222 | EQUALS(Q, Q); |
223 | return 0; |
224 | } |
225 | |
226 | int TestFalsePositive(int* A, int X, float F) { |
227 | // Produced by bison. |
228 | X = A[(2) - (2)]; |
229 | X = A['a' - 'a']; |
230 | |
231 | // Testing NaN. |
232 | if (F != F && F == F) return 1; |
233 | return 0; |
234 | } |
235 | |
236 | int TestBannedMacros() { |
237 | #define EAGAIN 3 |
238 | #define NOT_EAGAIN 3 |
239 | if (EAGAIN == 0 | EAGAIN == 0) return 0; |
240 | if (NOT_EAGAIN == 0 | NOT_EAGAIN == 0) return 0; |
241 | // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: both sides of operator are equivalent |
242 | return 0; |
243 | } |
244 | |
245 | struct MyClass { |
246 | static const int Value = 42; |
247 | }; |
248 | template <typename T, typename U> |
249 | void TemplateCheck() { |
250 | static_assert(T::Value == U::Value, "should be identical" ); |
251 | static_assert(T::Value == T::Value, "should be identical" ); |
252 | // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: both sides of operator are equivalent |
253 | } |
254 | void TestTemplate() { TemplateCheck<MyClass, MyClass>(); } |
255 | |
256 | int TestArithmetic(int X, int Y) { |
257 | if (X + 1 == X) return 1; |
258 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false |
259 | if (X + 1 != X) return 1; |
260 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
261 | if (X - 1 == X) return 1; |
262 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false |
263 | if (X - 1 != X) return 1; |
264 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
265 | |
266 | if (X + 1LL == X) return 1; |
267 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
268 | if (X + 1ULL == X) return 1; |
269 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false |
270 | |
271 | if (X == X + 1) return 1; |
272 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false |
273 | if (X != X + 1) return 1; |
274 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true |
275 | if (X == X - 1) return 1; |
276 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false |
277 | if (X != X - 1) return 1; |
278 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true |
279 | |
280 | if (X != X - 1U) return 1; |
281 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true |
282 | if (X != X - 1LL) return 1; |
283 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always true |
284 | |
285 | if ((X+X) != (X+X) - 1) return 1; |
286 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
287 | |
288 | if (X + 1 == X + 2) return 1; |
289 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false |
290 | if (X + 1 != X + 2) return 1; |
291 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
292 | |
293 | if (X - 1 == X - 2) return 1; |
294 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false |
295 | if (X - 1 != X - 2) return 1; |
296 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
297 | |
298 | if (X + 1 == X - -1) return 1; |
299 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
300 | if (X + 1 != X - -1) return 1; |
301 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false |
302 | if (X + 1 == X - -2) return 1; |
303 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false |
304 | if (X + 1 != X - -2) return 1; |
305 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
306 | |
307 | if (X + 1 == X - (~0)) return 1; |
308 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
309 | if (X + 1 == X - (~0U)) return 1; |
310 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
311 | |
312 | if (X + 1 == X - (~0ULL)) return 1; |
313 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
314 | |
315 | // Should not match. |
316 | if (X + 0.5 == X) return 1; |
317 | if (X + 1 == Y) return 1; |
318 | if (X + 1 == Y + 1) return 1; |
319 | if (X + 1 == Y + 2) return 1; |
320 | |
321 | return 0; |
322 | } |
323 | |
324 | int TestBitwise(int X, int Y) { |
325 | |
326 | if ((X & 0xFF) == 0xF00) return 1; |
327 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
328 | if ((X & 0xFF) != 0xF00) return 1; |
329 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true |
330 | if ((X | 0xFF) == 0xF00) return 1; |
331 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
332 | if ((X | 0xFF) != 0xF00) return 1; |
333 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true |
334 | |
335 | if ((X | 0xFFULL) != 0xF00) return 1; |
336 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: logical expression is always true |
337 | if ((X | 0xFF) != 0xF00ULL) return 1; |
338 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true |
339 | |
340 | if ((0xFF & X) == 0xF00) return 1; |
341 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
342 | if ((0xFF & X) != 0xF00) return 1; |
343 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true |
344 | if ((0xFF & X) == 0xF00) return 1; |
345 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
346 | if ((0xFF & X) != 0xF00) return 1; |
347 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always true |
348 | |
349 | if ((0xFFLL & X) == 0xF00) return 1; |
350 | // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: logical expression is always false |
351 | if ((0xFF & X) == 0xF00ULL) return 1; |
352 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
353 | |
354 | return 0; |
355 | } |
356 | |
357 | // Overloaded operators that compare an instance of a struct and an integer |
358 | // constant. |
359 | struct S { |
360 | S() { x = 1; } |
361 | int x; |
362 | // Overloaded comparison operators without any possible side effect. |
363 | bool operator==(const int &i) const { return x == i; } // not modifying |
364 | bool operator!=(int i) const { return x != i; } // not modifying |
365 | bool operator>(const int &i) const { return x > i; } // not modifying |
366 | bool operator<(int i) const { return x < i; } // not modifying |
367 | }; |
368 | |
369 | bool operator<=(const S &s, int i) { return s.x <= i; } // not modifying |
370 | bool operator>=(const S &s, const int &i) { return s.x >= i; } // not modifying |
371 | |
372 | bool operator==(int i, const S &s) { return s == i; } // not modifying |
373 | bool operator<(const int &i, const S &s) { return s > i; } // not modifying |
374 | bool operator<=(const int &i, const S &s) { return s >= i; } // not modifying |
375 | bool operator>(const int &i, const S &s) { return s < i; } // not modifying |
376 | |
377 | struct S2 { |
378 | S2() { x = 1; } |
379 | int x; |
380 | // Overloaded comparison operators that are able to modify their params. |
381 | bool operator==(const int &i) { |
382 | this->x++; |
383 | return x == i; |
384 | } |
385 | bool operator!=(int i) { return x != i; } |
386 | bool operator>(const int &i) { return x > i; } |
387 | bool operator<(int i) { |
388 | this->x--; |
389 | return x < i; |
390 | } |
391 | }; |
392 | |
393 | bool operator>=(S2 &s, const int &i) { return s.x >= i; } |
394 | bool operator<=(S2 &s, int i) { |
395 | s.x++; |
396 | return s.x <= i; |
397 | } |
398 | |
399 | int TestLogical(int X, int Y){ |
400 | #define CONFIG 0 |
401 | if (CONFIG && X) return 1; |
402 | #undef CONFIG |
403 | #define CONFIG 1 |
404 | if (CONFIG || X) return 1; |
405 | #undef CONFIG |
406 | |
407 | if (X == 10 && X != 10) return 1; |
408 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
409 | if (X == 10 && (X != 10)) return 1; |
410 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
411 | if (X == 10 && !(X == 10)) return 1; |
412 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
413 | if (!(X != 10) && !(X == 10)) return 1; |
414 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
415 | |
416 | if (X == 10ULL && X != 10ULL) return 1; |
417 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
418 | if (!(X != 10U) && !(X == 10)) return 1; |
419 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false |
420 | if (!(X != 10LL) && !(X == 10)) return 1; |
421 | // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: logical expression is always false |
422 | if (!(X != 10ULL) && !(X == 10)) return 1; |
423 | // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: logical expression is always false |
424 | |
425 | if (X == 0 && X) return 1; |
426 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
427 | if (X != 0 && !X) return 1; |
428 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
429 | if (X && !X) return 1; |
430 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: logical expression is always false |
431 | |
432 | if (X && !!X) return 1; |
433 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: equivalent expression on both sides of logical operator |
434 | if (X != 0 && X) return 1; |
435 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator |
436 | if (X != 0 && !!X) return 1; |
437 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator |
438 | if (X == 0 && !X) return 1; |
439 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator |
440 | |
441 | // Should not match. |
442 | if (X == 10 && Y == 10) return 1; |
443 | if (X != 10 && X != 12) return 1; |
444 | if (X == 10 || X == 12) return 1; |
445 | if (!X && !Y) return 1; |
446 | if (!X && Y) return 1; |
447 | if (!X && Y == 0) return 1; |
448 | if (X == 10 && Y != 10) return 1; |
449 | |
450 | // Test for overloaded operators with constant params. |
451 | S s1; |
452 | if (s1 == 1 && s1 == 1) return true; |
453 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: equivalent expression on both sides of logical operator |
454 | if (s1 == 1 || s1 != 1) return true; |
455 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true |
456 | if (s1 > 1 && s1 < 1) return true; |
457 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
458 | if (s1 >= 1 || s1 <= 1) return true; |
459 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true |
460 | if (s1 >= 2 && s1 <= 0) return true; |
461 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
462 | |
463 | // Same test as above but with swapped LHS/RHS on one side of the logical operator. |
464 | if (1 == s1 && s1 == 1) return true; |
465 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: equivalent expression on both sides of logical operator |
466 | if (1 == s1 || s1 != 1) return true; |
467 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true |
468 | if (1 < s1 && s1 < 1) return true; |
469 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
470 | if (1 <= s1 || s1 <= 1) return true; |
471 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true |
472 | if (2 < s1 && 0 > s1) return true; |
473 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
474 | |
475 | // Test for absence of false positives (issue #54011). |
476 | if (s1 == 1 || s1 == 2) return true; |
477 | if (s1 > 1 && s1 < 3) return true; |
478 | if (s1 >= 2 || s1 <= 0) return true; |
479 | |
480 | // Test for overloaded operators that may modify their params. |
481 | S2 s2; |
482 | if (s2 == 1 || s2 != 1) return true; |
483 | if (s2 == 1 || s2 == 1) return true; |
484 | if (s2 > 1 && s2 < 1) return true; |
485 | if (s2 >= 1 || s2 <= 1) return true; |
486 | } |
487 | |
488 | int TestRelational(int X, int Y) { |
489 | if (X == 10 && X > 10) return 1; |
490 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
491 | if (X == 10 && X < 10) return 1; |
492 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
493 | if (X < 10 && X > 10) return 1; |
494 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
495 | if (X <= 10 && X > 10) return 1; |
496 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always false |
497 | if (X < 10 && X >= 10) return 1; |
498 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
499 | if (X < 10 && X == 10) return 1; |
500 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
501 | |
502 | if (X > 5 && X <= 5) return 1; |
503 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always false |
504 | if (X > -5 && X <= -5) return 1; |
505 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always false |
506 | |
507 | if (X < 10 || X >= 10) return 1; |
508 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true |
509 | if (X <= 10 || X > 10) return 1; |
510 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true |
511 | if (X <= 10 || X >= 11) return 1; |
512 | // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: logical expression is always true |
513 | if (X != 7 || X != 14) return 1; |
514 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true |
515 | if (X == 7 || X != 5) return 1; |
516 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
517 | if (X != 7 || X == 7) return 1; |
518 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: logical expression is always true |
519 | |
520 | if (X < 7 && X < 6) return 1; |
521 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
522 | if (X < 7 && X < 7) return 1; |
523 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent |
524 | if (X < 7 && X < 8) return 1; |
525 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant |
526 | |
527 | if (X < 7 && X <= 5) return 1; |
528 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
529 | if (X < 7 && X <= 6) return 1; |
530 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: equivalent expression on both sides of logical operator |
531 | if (X < 7 && X <= 7) return 1; |
532 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant |
533 | if (X < 7 && X <= 8) return 1; |
534 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant |
535 | |
536 | if (X <= 7 && X < 6) return 1; |
537 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
538 | if (X <= 7 && X < 7) return 1; |
539 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
540 | if (X <= 7 && X < 8) return 1; |
541 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator |
542 | |
543 | if (X >= 7 && X > 6) return 1; |
544 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: equivalent expression on both sides of logical operator |
545 | if (X >= 7 && X > 7) return 1; |
546 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
547 | if (X >= 7 && X > 8) return 1; |
548 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
549 | |
550 | if (X <= 7 && X <= 5) return 1; |
551 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
552 | if (X <= 7 && X <= 6) return 1; |
553 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
554 | if (X <= 7 && X <= 7) return 1; |
555 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: both sides of operator are equivalent |
556 | if (X <= 7 && X <= 8) return 1; |
557 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: expression is redundant |
558 | |
559 | if (X == 11 && X > 10) return 1; |
560 | // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: expression is redundant |
561 | if (X == 11 && X < 12) return 1; |
562 | // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: expression is redundant |
563 | if (X > 10 && X == 11) return 1; |
564 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
565 | if (X < 12 && X == 11) return 1; |
566 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
567 | |
568 | if (X != 11 && X == 42) return 1; |
569 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
570 | if (X != 11 && X > 11) return 1; |
571 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
572 | if (X != 11 && X < 11) return 1; |
573 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
574 | if (X != 11 && X < 8) return 1; |
575 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
576 | if (X != 11 && X > 14) return 1; |
577 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
578 | |
579 | if (X < 7 || X < 6) return 1; |
580 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant |
581 | if (X < 7 || X < 7) return 1; |
582 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent |
583 | if (X < 7 || X < 8) return 1; |
584 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
585 | |
586 | if (X > 7 || X > 6) return 1; |
587 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
588 | if (X > 7 || X > 7) return 1; |
589 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: both sides of operator are equivalent |
590 | if (X > 7 || X > 8) return 1; |
591 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: expression is redundant |
592 | |
593 | // Should not match. |
594 | if (X < 10 || X > 12) return 1; |
595 | if (X > 10 && X < 12) return 1; |
596 | if (X < 10 || X >= 12) return 1; |
597 | if (X > 10 && X <= 12) return 1; |
598 | if (X <= 10 || X > 12) return 1; |
599 | if (X >= 10 && X < 12) return 1; |
600 | if (X <= 10 || X >= 12) return 1; |
601 | if (X >= 10 && X <= 12) return 1; |
602 | if (X >= 10 && X <= 11) return 1; |
603 | if (X >= 10 && X < 11) return 1; |
604 | if (X > 10 && X <= 11) return 1; |
605 | if (X > 10 && X != 11) return 1; |
606 | if (X >= 10 && X <= 10) return 1; |
607 | if (X <= 10 && X >= 10) return 1; |
608 | if (X < 0 || X > 0) return 1; |
609 | } |
610 | |
611 | int TestRelationalMacros(int X){ |
612 | #define SOME_MACRO 3 |
613 | #define SOME_MACRO_SAME_VALUE 3 |
614 | #define SOME_OTHER_MACRO 9 |
615 | // Do not match for redundant relational macro expressions that can be |
616 | // considered intentional, and for some particular values, non redundant. |
617 | |
618 | // Test cases for expressions with the same macro on both sides. |
619 | if (X < SOME_MACRO && X > SOME_MACRO) return 1; |
620 | // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always false |
621 | if (X < SOME_MACRO && X == SOME_MACRO) return 1; |
622 | // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always false |
623 | if (X < SOME_MACRO || X >= SOME_MACRO) return 1; |
624 | // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always true |
625 | if (X <= SOME_MACRO || X > SOME_MACRO) return 1; |
626 | // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: logical expression is always true |
627 | if (X != SOME_MACRO && X > SOME_MACRO) return 1; |
628 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
629 | if (X != SOME_MACRO && X < SOME_MACRO) return 1; |
630 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: expression is redundant |
631 | |
632 | // Test cases for two different macros. |
633 | if (X < SOME_MACRO && X > SOME_OTHER_MACRO) return 1; |
634 | if (X != SOME_MACRO && X >= SOME_OTHER_MACRO) return 1; |
635 | if (X != SOME_MACRO && X != SOME_OTHER_MACRO) return 1; |
636 | if (X == SOME_MACRO || X == SOME_MACRO_SAME_VALUE) return 1; |
637 | if (X == SOME_MACRO || X <= SOME_MACRO_SAME_VALUE) return 1; |
638 | if (X == SOME_MACRO || X > SOME_MACRO_SAME_VALUE) return 1; |
639 | if (X < SOME_MACRO && X <= SOME_OTHER_MACRO) return 1; |
640 | if (X == SOME_MACRO && X > SOME_OTHER_MACRO) return 1; |
641 | if (X == SOME_MACRO && X != SOME_OTHER_MACRO) return 1; |
642 | if (X == SOME_MACRO && X != SOME_MACRO_SAME_VALUE) return 1; |
643 | if (X == SOME_MACRO_SAME_VALUE && X == SOME_MACRO ) return 1; |
644 | |
645 | // Test cases for a macro and a const. |
646 | if (X < SOME_MACRO && X > 9) return 1; |
647 | if (X != SOME_MACRO && X >= 9) return 1; |
648 | if (X != SOME_MACRO && X != 9) return 1; |
649 | if (X == SOME_MACRO || X == 3) return 1; |
650 | if (X == SOME_MACRO || X <= 3) return 1; |
651 | if (X < SOME_MACRO && X <= 9) return 1; |
652 | if (X == SOME_MACRO && X != 9) return 1; |
653 | if (X == SOME_MACRO && X == 9) return 1; |
654 | |
655 | #undef SOME_OTHER_MACRO |
656 | #undef SOME_MACRO_SAME_VALUE |
657 | #undef SOME_MACRO |
658 | return 0; |
659 | } |
660 | |
661 | int TestValidExpression(int X) { |
662 | if (X - 1 == 1 - X) return 1; |
663 | if (2 * X == X) return 1; |
664 | if ((X << 1) == X) return 1; |
665 | |
666 | return 0; |
667 | } |
668 | |
669 | enum Color { Red, Yellow, Green }; |
670 | int TestRelationalWithEnum(enum Color C) { |
671 | if (C == Red && C == Yellow) return 1; |
672 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false |
673 | if (C == Red && C != Red) return 1; |
674 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always false |
675 | if (C != Red || C != Yellow) return 1; |
676 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: logical expression is always true |
677 | |
678 | // Should not match. |
679 | if (C == Red || C == Yellow) return 1; |
680 | if (C != Red && C != Yellow) return 1; |
681 | |
682 | return 0; |
683 | } |
684 | |
685 | template<class T> |
686 | int TestRelationalTemplated(int X) { |
687 | // This test causes a corner case with |isIntegerConstantExpr| where the type |
688 | // is dependent. There is an assert failing when evaluating |
689 | // sizeof(<incomplet-type>). |
690 | if (sizeof(T) == 4 || sizeof(T) == 8) return 1; |
691 | |
692 | if (X + 0 == -X) return 1; |
693 | if (X + 0 < X) return 1; |
694 | |
695 | return 0; |
696 | } |
697 | |
698 | int TestWithSignedUnsigned(int X) { |
699 | if (X + 1 == X + 1ULL) return 1; |
700 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: logical expression is always true |
701 | |
702 | if ((X & 0xFFU) == 0xF00) return 1; |
703 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false |
704 | |
705 | if ((X & 0xFF) == 0xF00U) return 1; |
706 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: logical expression is always false |
707 | |
708 | if ((X & 0xFFU) == 0xF00U) return 1; |
709 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: logical expression is always false |
710 | |
711 | return 0; |
712 | } |
713 | |
714 | int TestWithLong(int X, I64 Y) { |
715 | if (X + 0ULL == -X) return 1; |
716 | if (Y + 0 == -Y) return 1; |
717 | if (Y <= 10 && X >= 10LL) return 1; |
718 | if (Y <= 10 && X >= 10ULL) return 1; |
719 | if (X <= 10 || X > 12LL) return 1; |
720 | if (X <= 10 || X > 12ULL) return 1; |
721 | if (Y <= 10 || Y > 12) return 1; |
722 | |
723 | return 0; |
724 | } |
725 | |
726 | int TestWithMinMaxInt(int X) { |
727 | if (X <= X + 0xFFFFFFFFU) return 1; |
728 | if (X <= X + 0x7FFFFFFF) return 1; |
729 | if (X <= X + 0x80000000) return 1; |
730 | |
731 | if (X <= 0xFFFFFFFFU && X > 0) return 1; |
732 | if (X <= 0xFFFFFFFFU && X > 0U) return 1; |
733 | |
734 | if (X + 0x80000000 == X - 0x80000000) return 1; |
735 | // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: logical expression is always true |
736 | |
737 | if (X > 0x7FFFFFFF || X < ((-0x7FFFFFFF)-1)) return 1; |
738 | if (X <= 0x7FFFFFFF && X >= ((-0x7FFFFFFF)-1)) return 1; |
739 | |
740 | return 0; |
741 | } |
742 | |
743 | #define FLAG1 1 |
744 | #define FLAG2 2 |
745 | #define FLAG3 4 |
746 | #define FLAGS (FLAG1 | FLAG2 | FLAG3) |
747 | #define NOTFLAGS !(FLAG1 | FLAG2 | FLAG3) |
748 | int TestOperatorConfusion(int X, int Y, long Z) |
749 | { |
750 | // Ineffective & expressions. |
751 | Y = (Y << 8) & 0xff; |
752 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: ineffective bitwise and operation |
753 | Y = (Y << 12) & 0xfff; |
754 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: ineffective bitwise and |
755 | Y = (Y << 12) & 0xff; |
756 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: ineffective bitwise and |
757 | Y = (Y << 8) & 0x77; |
758 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: ineffective bitwise and |
759 | Y = (Y << 5) & 0x11; |
760 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: ineffective bitwise and |
761 | |
762 | // Tests for unmatched types |
763 | Z = (Z << 8) & 0xff; |
764 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: ineffective bitwise and operation |
765 | Y = (Y << 12) & 0xfffL; |
766 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: ineffective bitwise and |
767 | Z = (Y << 12) & 0xffLL; |
768 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: ineffective bitwise and |
769 | Y = (Z << 8L) & 0x77L; |
770 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: ineffective bitwise and |
771 | |
772 | Y = (Y << 8) & 0; |
773 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: ineffective bitwise and |
774 | |
775 | Y = (Y << 8) & -1; |
776 | |
777 | // Effective expressions. Do not check. |
778 | Y = (Y << 4) & 0x15; |
779 | Y = (Y << 3) & 0x250; |
780 | Y = (Y << 9) & 0xF33; |
781 | |
782 | int K = !(1 | 2 | 4); |
783 | // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: ineffective logical negation operator used; did you mean '~'? |
784 | // CHECK-FIXES: {{^}} int K = ~(1 | 2 | 4);{{$}} |
785 | K = !(FLAG1 & FLAG2 & FLAG3); |
786 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: ineffective logical negation operator |
787 | // CHECK-FIXES: {{^}} K = ~(FLAG1 & FLAG2 & FLAG3);{{$}} |
788 | K = !(3 | 4); |
789 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: ineffective logical negation operator |
790 | // CHECK-FIXES: {{^}} K = ~(3 | 4);{{$}} |
791 | int NotFlags = !FLAGS; |
792 | // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: ineffective logical negation operator |
793 | // CHECK-FIXES: {{^}} int NotFlags = ~FLAGS;{{$}} |
794 | NotFlags = NOTFLAGS; |
795 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: ineffective logical negation operator |
796 | return !(1 | 2 | 4); |
797 | // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: ineffective logical negation operator |
798 | // CHECK-FIXES: {{^}} return ~(1 | 2 | 4);{{$}} |
799 | } |
800 | |
801 | template <int Shift, int Mask> |
802 | int TestOperatorConfusionDependent(int Y) { |
803 | int r1 = (Y << Shift) & 0xff; |
804 | int r2 = (Y << 8) & Mask; |
805 | } |
806 | #undef FLAG1 |
807 | #undef FLAG2 |
808 | #undef FLAG3 |
809 | |
810 | namespace no_crash { |
811 | struct Foo {}; |
812 | bool operator<(const Foo&, const Foo&); |
813 | template <class T> |
814 | struct Bar { |
815 | static const Foo &GetFoo(); |
816 | static bool Test(const T & maybe_foo, const Foo& foo) { |
817 | return foo < GetFoo() && foo < maybe_foo; |
818 | } |
819 | }; |
820 | |
821 | template <class... Values> |
822 | struct Bar2 { |
823 | static_assert((... && (sizeof(Values) > 0)) == (... && (sizeof(Values) > 0))); |
824 | // FIXME: It's not clear that we should be diagnosing this. The `&&` operator |
825 | // here is unresolved and could resolve to an overloaded operator that might |
826 | // have side-effects on its operands. For other constructs with the same |
827 | // property (eg, the `S2` cases above) we suppress this diagnostic. This |
828 | // started failing when Clang started properly modeling the fold-expression as |
829 | // containing an unresolved operator name. |
830 | // FIXME-MESSAGES: :[[@LINE-1]]:47: warning: both sides of operator are equivalent [misc-redundant-expression] |
831 | }; |
832 | |
833 | } // namespace no_crash |
834 | |
835 | int TestAssignSideEffect(int i) { |
836 | int k = i; |
837 | |
838 | if ((k = k + 1) != 1 || (k = k + 1) != 2) |
839 | return 0; |
840 | |
841 | if ((k = foo(x: 0)) != 1 || (k = foo(x: 0)) != 2) |
842 | return 1; |
843 | |
844 | return 2; |
845 | } |
846 | |
847 | namespace PR63096 { |
848 | |
849 | struct alignas(sizeof(int)) X { |
850 | int x; |
851 | }; |
852 | |
853 | static_assert(alignof(X) == sizeof(X)); |
854 | static_assert(sizeof(X) == sizeof(X)); |
855 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: both sides of operator are equivalent |
856 | |
857 | } |
858 | |
859 | namespace PR35857 { |
860 | void test() { |
861 | int x = 0; |
862 | int y = 0; |
863 | decltype(x + y - (x + y)) z = 10; |
864 | } |
865 | } |
866 | |