]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
New upstream version 1.75.0+dfsg1
[rustc.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 /// ```no_run
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(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) {
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 Some((rel, normalized_lhs, normalized_rhs)) = normalized else {
149 return;
150 };
151
152 let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
153 let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
154
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);
157 }
158 }
159 }