1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::source::snippet_with_applicability;
3 use clippy_utils::{match_def_path, path_def_id};
4 use if_chain::if_chain;
6 use rustc_errors::Applicability;
8 use rustc_lint::LateContext;
9 use rustc_middle::ty::layout::LayoutOf;
14 arith_lhs: &hir::Expr<'_>,
15 arith_rhs: &hir::Expr<'_>,
16 unwrap_arg: &hir::Expr<'_>,
19 let ty = cx.typeck_results().expr_ty(arith_lhs);
20 if !ty.is_integral() {
24 let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return };
32 let Some(sign) = lit_sign(arith_rhs) else {
36 match (arith, sign, mm) {
37 ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
38 // "mul" is omitted because lhs can be negative.
43 (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
48 let mut applicability = Applicability::MachineApplicable;
51 super::MANUAL_SATURATING_ARITHMETIC,
53 "manual saturating arithmetic",
54 &format!("try using `saturating_{arith}`"),
56 "{}.saturating_{arith}({})",
57 snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
58 snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
64 #[derive(PartialEq, Eq)]
70 fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
71 // `T::max_value()` `T::min_value()` inherent methods
73 if let hir::ExprKind::Call(func, args) = &expr.kind;
75 if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
77 match segment.ident.as_str() {
78 "max_value" => return Some(MinMax::Max),
79 "min_value" => return Some(MinMax::Min),
85 let ty = cx.typeck_results().expr_ty(expr);
86 let ty_str = ty.to_string();
88 // `std::T::MAX` `std::T::MIN` constants
89 if let Some(id) = path_def_id(cx, expr) {
90 if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
91 return Some(MinMax::Max);
94 if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
95 return Some(MinMax::Min);
100 let bits = cx.layout_of(ty).unwrap().size.bits();
101 let (minval, maxval): (u128, u128) = if ty.is_signed() {
102 let minval = 1 << (bits - 1);
103 let mut maxval = !(1 << (bits - 1));
105 maxval &= (1 << bits) - 1;
109 (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
112 let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
113 if let hir::ExprKind::Lit(lit) = &expr.kind {
114 if let ast::LitKind::Int(value, _) = lit.node {
116 return Some(MinMax::Max);
119 if check_min && value == minval {
120 return Some(MinMax::Min);
128 if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
133 if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
134 return check_lit(val, true);
141 #[derive(PartialEq, Eq)]
147 fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
148 if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
149 if let hir::ExprKind::Lit(..) = &inner.kind {
150 return Some(Sign::Neg);
152 } else if let hir::ExprKind::Lit(..) = &expr.kind {
153 return Some(Sign::Pos);