1
+ use std:: convert:: Infallible ;
1
2
use std:: ops:: ControlFlow ;
2
3
3
4
use clippy_utils:: consts:: { constant, Constant } ;
@@ -63,11 +64,14 @@ fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx
63
64
return false ;
64
65
}
65
66
66
- // We don't check for sums of all-positive or all-negative values, but we could.
67
67
if let Sign :: ZeroOrPositive = expr_muldiv_sign ( cx, cast_op) {
68
68
return false ;
69
69
}
70
70
71
+ if let Sign :: ZeroOrPositive = expr_add_sign ( cx, cast_op) {
72
+ return false ;
73
+ }
74
+
71
75
true
72
76
} ,
73
77
@@ -182,13 +186,13 @@ fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'
182
186
}
183
187
}
184
188
185
- /// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
186
- /// which the result could always be positive under certain conditions, ignoring overflow .
189
+ /// Peels binary operators such as [`BinOpKind::Mul`] or [`BinOpKind::Rem`],
190
+ /// where the result could always be positive. See [`exprs_with_muldiv_binop_peeled()`] for details .
187
191
///
188
192
/// Returns the sign of the list of peeled expressions.
189
193
fn expr_muldiv_sign ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Sign {
190
- let mut uncertain_count = 0 ;
191
194
let mut negative_count = 0 ;
195
+ let mut uncertain_count = 0 ;
192
196
193
197
// Peel off possible binary expressions, for example:
194
198
// x * x / y => [x, x, y]
@@ -215,18 +219,58 @@ fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
215
219
}
216
220
}
217
221
222
+ /// Peels binary operators such as [`BinOpKind::Add`], where the result could always be positive.
223
+ /// See [`exprs_with_add_binop_peeled()`] for details.
224
+ ///
225
+ /// Returns the sign of the list of peeled expressions.
226
+ fn expr_add_sign ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Sign {
227
+ let mut negative_count = 0 ;
228
+ let mut uncertain_count = 0 ;
229
+ let mut positive_count = 0 ;
230
+
231
+ // Peel off possible binary expressions, for example:
232
+ // a + b + c => [a, b, c]
233
+ let exprs = exprs_with_add_binop_peeled ( expr) ;
234
+ for expr in exprs {
235
+ match expr_sign ( cx, expr, None ) {
236
+ Sign :: Negative => negative_count += 1 ,
237
+ Sign :: Uncertain => uncertain_count += 1 ,
238
+ Sign :: ZeroOrPositive => positive_count += 1 ,
239
+ } ;
240
+ }
241
+
242
+ // A sum is:
243
+ // - uncertain if there are any uncertain values (because they could be negative or positive),
244
+ // - positive or zero if there are only positive (or zero) values,
245
+ // - negative if there are only negative (or zero) values.
246
+ // We could split Zero out into its own variant, but we don't yet.
247
+ if uncertain_count > 0 {
248
+ Sign :: Uncertain
249
+ } else if negative_count == 0 {
250
+ Sign :: ZeroOrPositive
251
+ } else if positive_count == 0 {
252
+ Sign :: Negative
253
+ } else {
254
+ Sign :: Uncertain
255
+ }
256
+ }
257
+
218
258
/// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
219
- /// which the result could always be positive under certain conditions, ignoring overflow.
259
+ /// where the result depends on:
260
+ /// - the number of negative values in the entire expression, or
261
+ /// - the number of negative values on the left hand side of the expression.
262
+ /// Ignores overflow.
263
+ ///
220
264
///
221
265
/// Expressions using other operators are preserved, so we can try to evaluate them later.
222
266
fn exprs_with_muldiv_binop_peeled < ' e > ( expr : & ' e Expr < ' _ > ) -> Vec < & ' e Expr < ' e > > {
223
267
let mut res = vec ! [ ] ;
224
268
225
- for_each_expr ( expr, |sub_expr| {
269
+ for_each_expr ( expr, |sub_expr| -> ControlFlow < Infallible , Descend > {
226
270
// We don't check for mul/div/rem methods here, but we could.
227
271
if let ExprKind :: Binary ( op, lhs, _rhs) = sub_expr. kind {
228
272
if matches ! ( op. node, BinOpKind :: Mul | BinOpKind :: Div ) {
229
- // For binary operators which both contribute to the sign of the result,
273
+ // For binary operators where both sides contribute to the sign of the result,
230
274
// collect all their operands, recursively. This ignores overflow.
231
275
ControlFlow :: Continue ( Descend :: Yes )
232
276
} else if matches ! ( op. node, BinOpKind :: Rem | BinOpKind :: Shr ) {
@@ -259,3 +303,35 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
259
303
260
304
res
261
305
}
306
+
307
+ /// Peels binary operators such as [`BinOpKind::Add`], where the result depends on:
308
+ /// - all the expressions being positive, or
309
+ /// - all the expressions being negative.
310
+ /// Ignores overflow.
311
+ ///
312
+ /// Expressions using other operators are preserved, so we can try to evaluate them later.
313
+ fn exprs_with_add_binop_peeled < ' e > ( expr : & ' e Expr < ' _ > ) -> Vec < & ' e Expr < ' e > > {
314
+ let mut res = vec ! [ ] ;
315
+
316
+ for_each_expr ( expr, |sub_expr| -> ControlFlow < Infallible , Descend > {
317
+ // We don't check for add methods here, but we could.
318
+ if let ExprKind :: Binary ( op, _lhs, _rhs) = sub_expr. kind {
319
+ if matches ! ( op. node, BinOpKind :: Add ) {
320
+ // For binary operators where both sides contribute to the sign of the result,
321
+ // collect all their operands, recursively. This ignores overflow.
322
+ ControlFlow :: Continue ( Descend :: Yes )
323
+ } else {
324
+ // The sign of the result of other binary operators depends on the values of the operands,
325
+ // so try to evaluate the expression.
326
+ res. push ( sub_expr) ;
327
+ ControlFlow :: Continue ( Descend :: No )
328
+ }
329
+ } else {
330
+ // For other expressions, including unary operators and constants, try to evaluate the expression.
331
+ res. push ( sub_expr) ;
332
+ ControlFlow :: Continue ( Descend :: No )
333
+ }
334
+ } ) ;
335
+
336
+ res
337
+ }
0 commit comments