]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
Merge commit 'ea199bacef07213dbe008841b89c450e3bf0c638' into rustfmt-sync
[rust.git] / src / tools / clippy / clippy_lints / src / invalid_upcast_comparisons.rs
1 use rustc_hir::{Expr, ExprKind};
2 use rustc_lint::{LateContext, LateLintPass};
3 use rustc_middle::ty::layout::LayoutOf;
4 use rustc_middle::ty::{self, IntTy, UintTy};
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
6 use rustc_span::Span;
7
8 use clippy_utils::comparisons;
9 use clippy_utils::comparisons::Rel;
10 use clippy_utils::consts::{constant_full_int, FullInt};
11 use clippy_utils::diagnostics::span_lint;
12 use clippy_utils::source::snippet;
13
14 declare_clippy_lint! {
15     /// ### What it does
16     /// Checks for comparisons where the relation is always either
17     /// true or false, but where one side has been upcast so that the comparison is
18     /// necessary. Only integer types are checked.
19     ///
20     /// ### Why is this bad?
21     /// An expression like `let x : u8 = ...; (x as u32) > 300`
22     /// will mistakenly imply that it is possible for `x` to be outside the range of
23     /// `u8`.
24     ///
25     /// ### Known problems
26     /// https://github.com/rust-lang/rust-clippy/issues/886
27     ///
28     /// ### Example
29     /// ```rust
30     /// let x: u8 = 1;
31     /// (x as u32) > 300;
32     /// ```
33     pub INVALID_UPCAST_COMPARISONS,
34     pedantic,
35     "a comparison involving an upcast which is always true or false"
36 }
37
38 declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
39
40 fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
41     if let ExprKind::Cast(cast_exp, _) = expr.kind {
42         let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp);
43         let cast_ty = cx.typeck_results().expr_ty(expr);
44         // if it's a cast from i32 to u32 wrapping will invalidate all these checks
45         if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
46             return None;
47         }
48         match pre_cast_ty.kind() {
49             ty::Int(int_ty) => Some(match int_ty {
50                 IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
51                 IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
52                 IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))),
53                 IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))),
54                 IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)),
55                 IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)),
56             }),
57             ty::Uint(uint_ty) => Some(match uint_ty {
58                 UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))),
59                 UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))),
60                 UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))),
61                 UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))),
62                 UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)),
63                 UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)),
64             }),
65             _ => None,
66         }
67     } else {
68         None
69     }
70 }
71
72 fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
73     if let ExprKind::Cast(cast_val, _) = expr.kind {
74         span_lint(
75             cx,
76             INVALID_UPCAST_COMPARISONS,
77             span,
78             &format!(
79                 "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
80                 snippet(cx, cast_val.span, "the expression"),
81                 if always { "true" } else { "false" },
82             ),
83         );
84     }
85 }
86
87 fn upcast_comparison_bounds_err<'tcx>(
88     cx: &LateContext<'tcx>,
89     span: Span,
90     rel: comparisons::Rel,
91     lhs_bounds: Option<(FullInt, FullInt)>,
92     lhs: &'tcx Expr<'_>,
93     rhs: &'tcx Expr<'_>,
94     invert: bool,
95 ) {
96     if let Some((lb, ub)) = lhs_bounds {
97         if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) {
98             if rel == Rel::Eq || rel == Rel::Ne {
99                 if norm_rhs_val < lb || norm_rhs_val > ub {
100                     err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
101                 }
102             } else if match rel {
103                 Rel::Lt => {
104                     if invert {
105                         norm_rhs_val < lb
106                     } else {
107                         ub < norm_rhs_val
108                     }
109                 },
110                 Rel::Le => {
111                     if invert {
112                         norm_rhs_val <= lb
113                     } else {
114                         ub <= norm_rhs_val
115                     }
116                 },
117                 Rel::Eq | Rel::Ne => unreachable!(),
118             } {
119                 err_upcast_comparison(cx, span, lhs, true);
120             } else if match rel {
121                 Rel::Lt => {
122                     if invert {
123                         norm_rhs_val >= ub
124                     } else {
125                         lb >= norm_rhs_val
126                     }
127                 },
128                 Rel::Le => {
129                     if invert {
130                         norm_rhs_val > ub
131                     } else {
132                         lb > norm_rhs_val
133                     }
134                 },
135                 Rel::Eq | Rel::Ne => unreachable!(),
136             } {
137                 err_upcast_comparison(cx, span, lhs, false);
138             }
139         }
140     }
141 }
142
143 impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons {
144     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
145         if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
146             let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
147             let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
148                 val
149             } else {
150                 return;
151             };
152
153             let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
154             let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
155
156             upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
157             upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
158         }
159     }
160 }