rust-clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs

164 lines
4.3 KiB
Rust
Raw Normal View History

use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
2022-01-06 18:41:17 +00:00
use clippy_utils::{match_def_path, path_def_id};
2019-09-04 07:08:48 +00:00
use if_chain::if_chain;
2020-03-01 03:23:33 +00:00
use rustc_ast::ast;
2019-09-04 07:08:48 +00:00
use rustc_errors::Applicability;
2020-01-06 16:39:50 +00:00
use rustc_hir as hir;
2020-01-12 06:08:41 +00:00
use rustc_lint::LateContext;
use rustc_middle::ty::layout::LayoutOf;
2019-09-04 07:08:48 +00:00
pub fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
arith_lhs: &hir::Expr<'_>,
arith_rhs: &hir::Expr<'_>,
unwrap_arg: &hir::Expr<'_>,
arith: &str,
) {
2020-07-17 08:47:04 +00:00
let ty = cx.typeck_results().expr_ty(arith_lhs);
2019-09-04 07:08:48 +00:00
if !ty.is_integral() {
return;
}
let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) {
mm
} else {
return;
};
if ty.is_signed() {
2020-02-21 08:39:38 +00:00
use self::{
MinMax::{Max, Min},
Sign::{Neg, Pos},
};
2019-09-04 07:08:48 +00:00
let sign = if let Some(sign) = lit_sign(arith_rhs) {
sign
} else {
return;
};
match (arith, sign, mm) {
("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
// "mul" is omitted because lhs can be negative.
_ => return,
}
} else {
match (mm, arith) {
(MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
2019-09-04 07:08:48 +00:00
_ => return,
}
}
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
super::MANUAL_SATURATING_ARITHMETIC,
expr.span,
"manual saturating arithmetic",
&format!("try using `saturating_{arith}`"),
format!(
"{}.saturating_{arith}({})",
snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
),
applicability,
);
2019-09-04 07:08:48 +00:00
}
#[derive(PartialEq, Eq)]
enum MinMax {
Min,
Max,
}
fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
2019-09-04 07:08:48 +00:00
// `T::max_value()` `T::min_value()` inherent methods
if_chain! {
2019-09-27 15:16:06 +00:00
if let hir::ExprKind::Call(func, args) = &expr.kind;
2019-09-04 07:08:48 +00:00
if args.is_empty();
if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
2019-09-04 07:08:48 +00:00
then {
match segment.ident.as_str() {
2019-09-04 07:08:48 +00:00
"max_value" => return Some(MinMax::Max),
"min_value" => return Some(MinMax::Min),
_ => {}
}
}
}
2020-07-17 08:47:04 +00:00
let ty = cx.typeck_results().expr_ty(expr);
2019-09-04 07:08:48 +00:00
let ty_str = ty.to_string();
// `std::T::MAX` `std::T::MIN` constants
2022-01-06 18:41:17 +00:00
if let Some(id) = path_def_id(cx, expr) {
if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
2019-09-04 07:08:48 +00:00
return Some(MinMax::Max);
}
2022-01-06 18:41:17 +00:00
if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
2019-09-04 07:08:48 +00:00
return Some(MinMax::Min);
}
}
// Literals
let bits = cx.layout_of(ty).unwrap().size.bits();
let (minval, maxval): (u128, u128) = if ty.is_signed() {
let minval = 1 << (bits - 1);
let mut maxval = !(1 << (bits - 1));
if bits != 128 {
maxval &= (1 << bits) - 1;
}
(minval, maxval)
} else {
(0, if bits == 128 { !0 } else { (1 << bits) - 1 })
};
2019-12-27 07:12:26 +00:00
let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
2019-09-27 15:16:06 +00:00
if let hir::ExprKind::Lit(lit) = &expr.kind {
2019-09-04 07:08:48 +00:00
if let ast::LitKind::Int(value, _) = lit.node {
if value == maxval {
return Some(MinMax::Max);
}
if check_min && value == minval {
return Some(MinMax::Min);
}
}
}
None
};
if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
return r;
}
if ty.is_signed() {
if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
2019-09-04 07:08:48 +00:00
return check_lit(val, true);
}
}
None
}
#[derive(PartialEq, Eq)]
enum Sign {
Pos,
Neg,
}
2019-12-27 07:12:26 +00:00
fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
2019-09-27 15:16:06 +00:00
if let hir::ExprKind::Lit(..) = &inner.kind {
2019-09-04 07:08:48 +00:00
return Some(Sign::Neg);
}
2019-09-27 15:16:06 +00:00
} else if let hir::ExprKind::Lit(..) = &expr.kind {
2019-09-04 07:08:48 +00:00
return Some(Sign::Pos);
}
None
}