1+ use std:: convert:: Infallible ;
12use std:: ops:: ControlFlow ;
23
34use clippy_utils:: consts:: { constant, Constant } ;
@@ -63,11 +64,14 @@ fn should_lint<'cx>(cx: &LateContext<'cx>, cast_op: &Expr<'_>, cast_from: Ty<'cx
6364 return false ;
6465 }
6566
66- // We don't check for sums of all-positive or all-negative values, but we could.
6767 if let Sign :: ZeroOrPositive = expr_muldiv_sign ( cx, cast_op) {
6868 return false ;
6969 }
7070
71+ if let Sign :: ZeroOrPositive = expr_add_sign ( cx, cast_op) {
72+ return false ;
73+ }
74+
7175 true
7276 } ,
7377
@@ -182,13 +186,13 @@ fn pow_call_result_sign(cx: &LateContext<'_>, base: &Expr<'_>, exponent: &Expr<'
182186 }
183187}
184188
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 .
187191///
188192/// Returns the sign of the list of peeled expressions.
189193fn expr_muldiv_sign ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Sign {
190- let mut uncertain_count = 0 ;
191194 let mut negative_count = 0 ;
195+ let mut uncertain_count = 0 ;
192196
193197 // Peel off possible binary expressions, for example:
194198 // x * x / y => [x, x, y]
@@ -215,18 +219,58 @@ fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
215219 }
216220}
217221
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+
218258/// 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+ ///
220264///
221265/// Expressions using other operators are preserved, so we can try to evaluate them later.
222266fn exprs_with_muldiv_binop_peeled < ' e > ( expr : & ' e Expr < ' _ > ) -> Vec < & ' e Expr < ' e > > {
223267 let mut res = vec ! [ ] ;
224268
225- for_each_expr ( expr, |sub_expr| {
269+ for_each_expr ( expr, |sub_expr| -> ControlFlow < Infallible , Descend > {
226270 // We don't check for mul/div/rem methods here, but we could.
227271 if let ExprKind :: Binary ( op, lhs, _rhs) = sub_expr. kind {
228272 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,
230274 // collect all their operands, recursively. This ignores overflow.
231275 ControlFlow :: Continue ( Descend :: Yes )
232276 } 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>> {
259303
260304 res
261305}
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