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};
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;
14 declare_clippy_lint! {
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.
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
25 /// ### Known problems
26 /// https://github.com/rust-lang/rust-clippy/issues/886
33 #[clippy::version = "pre 1.29.0"]
34 pub INVALID_UPCAST_COMPARISONS,
36 "a comparison involving an upcast which is always true or false"
39 declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
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) {
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)),
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)),
73 fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
74 if let ExprKind::Cast(cast_val, _) = expr.kind {
77 INVALID_UPCAST_COMPARISONS,
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" },
88 fn upcast_comparison_bounds_err<'tcx>(
89 cx: &LateContext<'tcx>,
91 rel: comparisons::Rel,
92 lhs_bounds: Option<(FullInt, FullInt)>,
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);
103 } else if match rel {
118 Rel::Eq | Rel::Ne => unreachable!(),
120 err_upcast_comparison(cx, span, lhs, true);
121 } else if match rel {
136 Rel::Eq | Rel::Ne => unreachable!(),
138 err_upcast_comparison(cx, span, lhs, false);
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 {
154 let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
155 let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
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);