]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/invalid_upcast_comparisons.rs
Auto merge of #101709 - nnethercote:simplify-visitors-more, r=cjgillot
[rust.git] / 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     #[clippy::version = "pre 1.29.0"]
34     pub INVALID_UPCAST_COMPARISONS,
35     pedantic,
36     "a comparison involving an upcast which is always true or false"
37 }
38
39 declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
40
41 fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
42     if let ExprKind::Cast(cast_exp, _) = expr.kind {
43         let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp);
44         let cast_ty = cx.typeck_results().expr_ty(expr);
45         // if it's a cast from i32 to u32 wrapping will invalidate all these checks
46         if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
47             return None;
48         }
49         match pre_cast_ty.kind() {
50             ty::Int(int_ty) => Some(match int_ty {
51                 IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
52                 IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
53                 IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))),
54                 IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))),
55                 IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)),
56                 IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)),
57             }),
58             ty::Uint(uint_ty) => Some(match uint_ty {
59                 UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))),
60                 UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))),
61                 UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))),
62                 UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))),
63                 UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)),
64                 UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)),
65             }),
66             _ => None,
67         }
68     } else {
69         None
70     }
71 }
72
73 fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
74     if let ExprKind::Cast(cast_val, _) = expr.kind {
75         span_lint(
76             cx,
77             INVALID_UPCAST_COMPARISONS,
78             span,
79             &format!(
80                 "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
81                 snippet(cx, cast_val.span, "the expression"),
82                 if always { "true" } else { "false" },
83             ),
84         );
85     }
86 }
87
88 fn upcast_comparison_bounds_err<'tcx>(
89     cx: &LateContext<'tcx>,
90     span: Span,
91     rel: comparisons::Rel,
92     lhs_bounds: Option<(FullInt, FullInt)>,
93     lhs: &'tcx Expr<'_>,
94     rhs: &'tcx Expr<'_>,
95     invert: bool,
96 ) {
97     if let Some((lb, ub)) = lhs_bounds {
98         if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) {
99             if rel == Rel::Eq || rel == Rel::Ne {
100                 if norm_rhs_val < lb || norm_rhs_val > ub {
101                     err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
102                 }
103             } else if match rel {
104                 Rel::Lt => {
105                     if invert {
106                         norm_rhs_val < lb
107                     } else {
108                         ub < norm_rhs_val
109                     }
110                 },
111                 Rel::Le => {
112                     if invert {
113                         norm_rhs_val <= lb
114                     } else {
115                         ub <= norm_rhs_val
116                     }
117                 },
118                 Rel::Eq | Rel::Ne => unreachable!(),
119             } {
120                 err_upcast_comparison(cx, span, lhs, true);
121             } else if match rel {
122                 Rel::Lt => {
123                     if invert {
124                         norm_rhs_val >= ub
125                     } else {
126                         lb >= norm_rhs_val
127                     }
128                 },
129                 Rel::Le => {
130                     if invert {
131                         norm_rhs_val > ub
132                     } else {
133                         lb > norm_rhs_val
134                     }
135                 },
136                 Rel::Eq | Rel::Ne => unreachable!(),
137             } {
138                 err_upcast_comparison(cx, span, lhs, false);
139             }
140         }
141     }
142 }
143
144 impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons {
145     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
146         if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
147             let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
148             let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
149                 val
150             } else {
151                 return;
152             };
153
154             let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
155             let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
156
157             upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
158             upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
159         }
160     }
161 }