1 use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg};
2 use if_chain::if_chain;
4 use rustc_errors::Applicability;
6 use rustc_lint::LateContext;
7 use rustc_target::abi::LayoutOf;
9 pub fn lint(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) {
10 let unwrap_arg = &args[0][1];
11 let arith_lhs = &args[1][0];
12 let arith_rhs = &args[1][1];
14 let ty = cx.typeck_results().expr_ty(arith_lhs);
15 if !ty.is_integral() {
19 let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) {
31 let sign = if let Some(sign) = lit_sign(arith_rhs) {
37 match (arith, sign, mm) {
38 ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
39 // "mul" is omitted because lhs can be negative.
43 let mut applicability = Applicability::MachineApplicable;
46 super::MANUAL_SATURATING_ARITHMETIC,
48 "manual saturating arithmetic",
49 &format!("try using `saturating_{}`", arith),
51 "{}.saturating_{}({})",
52 snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
54 snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
60 (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
64 let mut applicability = Applicability::MachineApplicable;
67 super::MANUAL_SATURATING_ARITHMETIC,
69 "manual saturating arithmetic",
70 &format!("try using `saturating_{}`", arith),
72 "{}.saturating_{}({})",
73 snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
75 snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
82 #[derive(PartialEq, Eq)]
88 fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
89 // `T::max_value()` `T::min_value()` inherent methods
91 if let hir::ExprKind::Call(func, args) = &expr.kind;
93 if let hir::ExprKind::Path(path) = &func.kind;
94 if let hir::QPath::TypeRelative(_, segment) = path;
96 match &*segment.ident.as_str() {
97 "max_value" => return Some(MinMax::Max),
98 "min_value" => return Some(MinMax::Min),
104 let ty = cx.typeck_results().expr_ty(expr);
105 let ty_str = ty.to_string();
107 // `std::T::MAX` `std::T::MIN` constants
108 if let hir::ExprKind::Path(path) = &expr.kind {
109 if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
110 return Some(MinMax::Max);
113 if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
114 return Some(MinMax::Min);
119 let bits = cx.layout_of(ty).unwrap().size.bits();
120 let (minval, maxval): (u128, u128) = if ty.is_signed() {
121 let minval = 1 << (bits - 1);
122 let mut maxval = !(1 << (bits - 1));
124 maxval &= (1 << bits) - 1;
128 (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
131 let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
132 if let hir::ExprKind::Lit(lit) = &expr.kind {
133 if let ast::LitKind::Int(value, _) = lit.node {
135 return Some(MinMax::Max);
138 if check_min && value == minval {
139 return Some(MinMax::Min);
147 if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
152 if let hir::ExprKind::Unary(hir::UnOp::UnNeg, val) = &expr.kind {
153 return check_lit(val, true);
160 #[derive(PartialEq, Eq)]
166 fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
167 if let hir::ExprKind::Unary(hir::UnOp::UnNeg, inner) = &expr.kind {
168 if let hir::ExprKind::Lit(..) = &inner.kind {
169 return Some(Sign::Neg);
171 } else if let hir::ExprKind::Lit(..) = &expr.kind {
172 return Some(Sign::Pos);