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 pub INVALID_UPCAST_COMPARISONS,
35 "a comparison involving an upcast which is always true or false"
38 declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
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) {
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)),
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)),
72 fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
73 if let ExprKind::Cast(cast_val, _) = expr.kind {
76 INVALID_UPCAST_COMPARISONS,
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" },
87 fn upcast_comparison_bounds_err<'tcx>(
88 cx: &LateContext<'tcx>,
90 rel: comparisons::Rel,
91 lhs_bounds: Option<(FullInt, FullInt)>,
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);
102 } else if match rel {
117 Rel::Eq | Rel::Ne => unreachable!(),
119 err_upcast_comparison(cx, span, lhs, true);
120 } else if match rel {
135 Rel::Eq | Rel::Ne => unreachable!(),
137 err_upcast_comparison(cx, span, lhs, false);
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 {
153 let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
154 let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
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);