]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/methods/manual_saturating_arithmetic.rs
Rollup merge of #82048 - mark-i-m:or-pat-type-ascription, r=petrochenkov
[rust.git] / clippy_lints / src / methods / manual_saturating_arithmetic.rs
1 use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg};
2 use if_chain::if_chain;
3 use rustc_ast::ast;
4 use rustc_errors::Applicability;
5 use rustc_hir as hir;
6 use rustc_lint::LateContext;
7 use rustc_target::abi::LayoutOf;
8
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];
13
14     let ty = cx.typeck_results().expr_ty(arith_lhs);
15     if !ty.is_integral() {
16         return;
17     }
18
19     let mm = if let Some(mm) = is_min_or_max(cx, unwrap_arg) {
20         mm
21     } else {
22         return;
23     };
24
25     if ty.is_signed() {
26         use self::{
27             MinMax::{Max, Min},
28             Sign::{Neg, Pos},
29         };
30
31         let sign = if let Some(sign) = lit_sign(arith_rhs) {
32             sign
33         } else {
34             return;
35         };
36
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.
40             _ => return,
41         }
42
43         let mut applicability = Applicability::MachineApplicable;
44         span_lint_and_sugg(
45             cx,
46             super::MANUAL_SATURATING_ARITHMETIC,
47             expr.span,
48             "manual saturating arithmetic",
49             &format!("try using `saturating_{}`", arith),
50             format!(
51                 "{}.saturating_{}({})",
52                 snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
53                 arith,
54                 snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
55             ),
56             applicability,
57         );
58     } else {
59         match (mm, arith) {
60             (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
61             _ => return,
62         }
63
64         let mut applicability = Applicability::MachineApplicable;
65         span_lint_and_sugg(
66             cx,
67             super::MANUAL_SATURATING_ARITHMETIC,
68             expr.span,
69             "manual saturating arithmetic",
70             &format!("try using `saturating_{}`", arith),
71             format!(
72                 "{}.saturating_{}({})",
73                 snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
74                 arith,
75                 snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
76             ),
77             applicability,
78         );
79     }
80 }
81
82 #[derive(PartialEq, Eq)]
83 enum MinMax {
84     Min,
85     Max,
86 }
87
88 fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
89     // `T::max_value()` `T::min_value()` inherent methods
90     if_chain! {
91         if let hir::ExprKind::Call(func, args) = &expr.kind;
92         if args.is_empty();
93         if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
94         then {
95             match &*segment.ident.as_str() {
96                 "max_value" => return Some(MinMax::Max),
97                 "min_value" => return Some(MinMax::Min),
98                 _ => {}
99             }
100         }
101     }
102
103     let ty = cx.typeck_results().expr_ty(expr);
104     let ty_str = ty.to_string();
105
106     // `std::T::MAX` `std::T::MIN` constants
107     if let hir::ExprKind::Path(path) = &expr.kind {
108         if match_qpath(path, &["core", &ty_str, "MAX"][..]) {
109             return Some(MinMax::Max);
110         }
111
112         if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
113             return Some(MinMax::Min);
114         }
115     }
116
117     // Literals
118     let bits = cx.layout_of(ty).unwrap().size.bits();
119     let (minval, maxval): (u128, u128) = if ty.is_signed() {
120         let minval = 1 << (bits - 1);
121         let mut maxval = !(1 << (bits - 1));
122         if bits != 128 {
123             maxval &= (1 << bits) - 1;
124         }
125         (minval, maxval)
126     } else {
127         (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
128     };
129
130     let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
131         if let hir::ExprKind::Lit(lit) = &expr.kind {
132             if let ast::LitKind::Int(value, _) = lit.node {
133                 if value == maxval {
134                     return Some(MinMax::Max);
135                 }
136
137                 if check_min && value == minval {
138                     return Some(MinMax::Min);
139                 }
140             }
141         }
142
143         None
144     };
145
146     if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
147         return r;
148     }
149
150     if ty.is_signed() {
151         if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
152             return check_lit(val, true);
153         }
154     }
155
156     None
157 }
158
159 #[derive(PartialEq, Eq)]
160 enum Sign {
161     Pos,
162     Neg,
163 }
164
165 fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
166     if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
167         if let hir::ExprKind::Lit(..) = &inner.kind {
168             return Some(Sign::Neg);
169         }
170     } else if let hir::ExprKind::Lit(..) = &expr.kind {
171         return Some(Sign::Pos);
172     }
173
174     None
175 }