]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
Add 'src/tools/clippy/' from commit 'd2708873ef711ec8ab45df1e984ecf24a96cd369'
[rust.git] / src / tools / clippy / 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.tables.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") | (MinMax::Max, "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(path) = &func.kind;
94         if let hir::QPath::TypeRelative(_, segment) = path;
95         then {
96             match &*segment.ident.as_str() {
97                 "max_value" => return Some(MinMax::Max),
98                 "min_value" => return Some(MinMax::Min),
99                 _ => {}
100             }
101         }
102     }
103
104     let ty = cx.tables.expr_ty(expr);
105     let ty_str = ty.to_string();
106
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);
111         }
112
113         if match_qpath(path, &["core", &ty_str, "MIN"][..]) {
114             return Some(MinMax::Min);
115         }
116     }
117
118     // Literals
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));
123         if bits != 128 {
124             maxval &= (1 << bits) - 1;
125         }
126         (minval, maxval)
127     } else {
128         (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
129     };
130
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 {
134                 if value == maxval {
135                     return Some(MinMax::Max);
136                 }
137
138                 if check_min && value == minval {
139                     return Some(MinMax::Min);
140                 }
141             }
142         }
143
144         None
145     };
146
147     if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
148         return r;
149     }
150
151     if ty.is_signed() {
152         if let hir::ExprKind::Unary(hir::UnOp::UnNeg, val) = &expr.kind {
153             return check_lit(val, true);
154         }
155     }
156
157     None
158 }
159
160 #[derive(PartialEq, Eq)]
161 enum Sign {
162     Pos,
163     Neg,
164 }
165
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);
170         }
171     } else if let hir::ExprKind::Lit(..) = &expr.kind {
172         return Some(Sign::Pos);
173     }
174
175     None
176 }