]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
Rollup merge of #105615 - WaffleLapkin:remove_opt_scope_span_mention, r=compiler...
[rust.git] / src / tools / clippy / clippy_lints / src / methods / manual_saturating_arithmetic.rs
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;
5 use rustc_ast::ast;
6 use rustc_errors::Applicability;
7 use rustc_hir as hir;
8 use rustc_lint::LateContext;
9 use rustc_middle::ty::layout::LayoutOf;
10
11 pub fn check(
12     cx: &LateContext<'_>,
13     expr: &hir::Expr<'_>,
14     arith_lhs: &hir::Expr<'_>,
15     arith_rhs: &hir::Expr<'_>,
16     unwrap_arg: &hir::Expr<'_>,
17     arith: &str,
18 ) {
19     let ty = cx.typeck_results().expr_ty(arith_lhs);
20     if !ty.is_integral() {
21         return;
22     }
23
24     let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return };
25
26     if ty.is_signed() {
27         use self::{
28             MinMax::{Max, Min},
29             Sign::{Neg, Pos},
30         };
31
32         let Some(sign) = lit_sign(arith_rhs) else {
33             return;
34         };
35
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.
39             _ => return,
40         }
41     } else {
42         match (mm, arith) {
43             (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
44             _ => return,
45         }
46     }
47
48     let mut applicability = Applicability::MachineApplicable;
49     span_lint_and_sugg(
50         cx,
51         super::MANUAL_SATURATING_ARITHMETIC,
52         expr.span,
53         "manual saturating arithmetic",
54         &format!("try using `saturating_{arith}`"),
55         format!(
56             "{}.saturating_{arith}({})",
57             snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
58             snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
59         ),
60         applicability,
61     );
62 }
63
64 #[derive(PartialEq, Eq)]
65 enum MinMax {
66     Min,
67     Max,
68 }
69
70 fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
71     // `T::max_value()` `T::min_value()` inherent methods
72     if_chain! {
73         if let hir::ExprKind::Call(func, args) = &expr.kind;
74         if args.is_empty();
75         if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
76         then {
77             match segment.ident.as_str() {
78                 "max_value" => return Some(MinMax::Max),
79                 "min_value" => return Some(MinMax::Min),
80                 _ => {}
81             }
82         }
83     }
84
85     let ty = cx.typeck_results().expr_ty(expr);
86     let ty_str = ty.to_string();
87
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);
92         }
93
94         if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
95             return Some(MinMax::Min);
96         }
97     }
98
99     // Literals
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));
104         if bits != 128 {
105             maxval &= (1 << bits) - 1;
106         }
107         (minval, maxval)
108     } else {
109         (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
110     };
111
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 {
115                 if value == maxval {
116                     return Some(MinMax::Max);
117                 }
118
119                 if check_min && value == minval {
120                     return Some(MinMax::Min);
121                 }
122             }
123         }
124
125         None
126     };
127
128     if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
129         return r;
130     }
131
132     if ty.is_signed() {
133         if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
134             return check_lit(val, true);
135         }
136     }
137
138     None
139 }
140
141 #[derive(PartialEq, Eq)]
142 enum Sign {
143     Pos,
144     Neg,
145 }
146
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);
151         }
152     } else if let hir::ExprKind::Lit(..) = &expr.kind {
153         return Some(Sign::Pos);
154     }
155
156     None
157 }