Skip to content

Commit ff8d1fc

Browse files
committed
recognize x.saturating_sub(y).unwrap_or_default() as well
1 parent 06a3826 commit ff8d1fc

File tree

5 files changed

+77
-11
lines changed

5 files changed

+77
-11
lines changed

clippy_lints/src/methods/manual_saturating_arithmetic.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ use clippy_utils::source::snippet_with_applicability;
33
use clippy_utils::{path_res, sym};
44
use rustc_ast::ast;
55
use rustc_errors::Applicability;
6-
use rustc_hir as hir;
76
use rustc_hir::def::Res;
7+
use rustc_hir::{self as hir, Expr};
88
use rustc_lint::LateContext;
9+
use rustc_middle::ty::Ty;
910
use rustc_middle::ty::layout::LayoutOf;
1011
use rustc_span::Symbol;
1112

12-
pub fn check(
13+
pub fn check_unwrap_or(
1314
cx: &LateContext<'_>,
14-
expr: &hir::Expr<'_>,
15-
arith_lhs: &hir::Expr<'_>,
16-
arith_rhs: &hir::Expr<'_>,
17-
unwrap_arg: &hir::Expr<'_>,
15+
expr: &Expr<'_>,
16+
arith_lhs: &Expr<'_>,
17+
arith_rhs: &Expr<'_>,
18+
unwrap_arg: &Expr<'_>,
1819
arith: Symbol,
1920
) {
2021
let ty = cx.typeck_results().expr_ty(arith_lhs);
@@ -30,6 +31,42 @@ pub fn check(
3031
return;
3132
};
3233

34+
check(cx, expr, arith_lhs, arith_rhs, ty, mm, checked_arith);
35+
}
36+
37+
/// Precondition: should be called on `x.saturating_sub(y).unwrap_or_default()`
38+
pub(super) fn check_unwrap_or_default(
39+
cx: &LateContext<'_>,
40+
expr: &Expr<'_>,
41+
arith_lhs: &Expr<'_>,
42+
arith_rhs: &Expr<'_>,
43+
) {
44+
let ty = cx.typeck_results().expr_ty(arith_lhs);
45+
if !ty.is_integral() {
46+
return;
47+
}
48+
49+
let mm = if ty.is_signed() {
50+
return; // iN::default() is 0, which is neither MIN nor MAX
51+
} else {
52+
MinMax::Min // uN::default() is 0, which is also the MIN
53+
};
54+
55+
// See precondition
56+
let checked_arith = CheckedArith::Sub;
57+
58+
check(cx, expr, arith_lhs, arith_rhs, ty, mm, checked_arith);
59+
}
60+
61+
fn check(
62+
cx: &LateContext<'_>,
63+
expr: &Expr<'_>,
64+
arith_lhs: &Expr<'_>,
65+
arith_rhs: &Expr<'_>,
66+
ty: Ty<'_>,
67+
mm: MinMax,
68+
checked_arith: CheckedArith,
69+
) {
3370
if ty.is_signed() {
3471
use self::MinMax::{Max, Min};
3572
use self::Sign::{Neg, Pos};
@@ -102,7 +139,7 @@ enum MinMax {
102139
Max,
103140
}
104141

105-
fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
142+
fn is_min_or_max(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<MinMax> {
106143
// `T::max_value()` `T::min_value()` inherent methods
107144
if let hir::ExprKind::Call(func, []) = &expr.kind
108145
&& let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind
@@ -140,7 +177,7 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
140177
(0, if bits == 128 { !0 } else { (1 << bits) - 1 })
141178
};
142179

143-
let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
180+
let check_lit = |expr: &Expr<'_>, check_min: bool| {
144181
if let hir::ExprKind::Lit(lit) = &expr.kind
145182
&& let ast::LitKind::Int(value, _) = lit.node
146183
{
@@ -175,7 +212,7 @@ enum Sign {
175212
Neg,
176213
}
177214

178-
fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
215+
fn lit_sign(expr: &Expr<'_>) -> Option<Sign> {
179216
if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
180217
if let hir::ExprKind::Lit(..) = &inner.kind {
181218
return Some(Sign::Neg);

clippy_lints/src/methods/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5488,7 +5488,7 @@ impl Methods {
54885488
(sym::unwrap_or, [u_arg]) => {
54895489
match method_call(recv) {
54905490
Some((arith @ (sym::checked_add | sym::checked_sub | sym::checked_mul), lhs, [rhs], _, _)) => {
5491-
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, arith);
5491+
manual_saturating_arithmetic::check_unwrap_or(cx, expr, lhs, rhs, u_arg, arith);
54925492
},
54935493
Some((sym::map, m_recv, [m_arg], span, _)) => {
54945494
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
@@ -5502,6 +5502,9 @@ impl Methods {
55025502
},
55035503
(sym::unwrap_or_default, []) => {
55045504
match method_call(recv) {
5505+
Some((sym::checked_sub, lhs, [rhs], _, _)) => {
5506+
manual_saturating_arithmetic::check_unwrap_or_default(cx, expr, lhs, rhs);
5507+
},
55055508
Some((sym::map, m_recv, [arg], span, _)) => {
55065509
manual_is_variant_and::check(cx, expr, m_recv, arg, span, self.msrv);
55075510
},

tests/ui/manual_saturating_arithmetic.fixed

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,13 @@ fn main() {
5858
let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
5959
let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
6060
}
61+
62+
fn issue15655() {
63+
let _ = 5u32.saturating_sub(1u32); //~ manual_saturating_arithmetic
64+
let _ = 5u32.checked_add(1u32).unwrap_or_default(); // ok
65+
let _ = 5u32.checked_mul(1u32).unwrap_or_default(); // ok
66+
67+
let _ = 5i32.checked_sub(1i32).unwrap_or_default(); // ok
68+
let _ = 5i32.checked_add(1i32).unwrap_or_default(); // ok
69+
let _ = 5i32.checked_mul(1i32).unwrap_or_default(); // ok
70+
}

tests/ui/manual_saturating_arithmetic.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,13 @@ fn main() {
7373
let _ = 1i8.checked_sub(1).unwrap_or(127); // ok
7474
let _ = 1i8.checked_sub(-1).unwrap_or(-128); // ok
7575
}
76+
77+
fn issue15655() {
78+
let _ = 5u32.checked_sub(1u32).unwrap_or_default(); //~ manual_saturating_arithmetic
79+
let _ = 5u32.checked_add(1u32).unwrap_or_default(); // ok
80+
let _ = 5u32.checked_mul(1u32).unwrap_or_default(); // ok
81+
82+
let _ = 5i32.checked_sub(1i32).unwrap_or_default(); // ok
83+
let _ = 5i32.checked_add(1i32).unwrap_or_default(); // ok
84+
let _ = 5i32.checked_mul(1i32).unwrap_or_default(); // ok
85+
}

tests/ui/manual_saturating_arithmetic.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,5 +165,11 @@ LL | | .checked_sub(-1)
165165
LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
166166
| |_______________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(-1)`
167167

168-
error: aborting due to 24 previous errors
168+
error: manual saturating arithmetic
169+
--> tests/ui/manual_saturating_arithmetic.rs:78:13
170+
|
171+
LL | let _ = 5u32.checked_sub(1u32).unwrap_or_default();
172+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `5u32.saturating_sub(1u32)`
173+
174+
error: aborting due to 25 previous errors
169175

0 commit comments

Comments
 (0)