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(cx
: &LateContext
<'_
>, expr
: &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 Some((rel
, normalized_lhs
, normalized_rhs
)) = normalized
else {
152 let lhs_bounds
= numeric_cast_precast_bounds(cx
, normalized_lhs
);
153 let rhs_bounds
= numeric_cast_precast_bounds(cx
, normalized_rhs
);
155 upcast_comparison_bounds_err(cx
, expr
.span
, rel
, lhs_bounds
, normalized_lhs
, normalized_rhs
, false);
156 upcast_comparison_bounds_err(cx
, expr
.span
, rel
, rhs_bounds
, normalized_rhs
, normalized_lhs
, true);