]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / absurd_extreme_comparisons.rs
CommitLineData
cdc7bbd5
XL
1use rustc_hir::{BinOpKind, Expr, ExprKind};
2use rustc_lint::{LateContext, LateLintPass};
3use rustc_middle::ty;
4use rustc_session::{declare_lint_pass, declare_tool_lint};
5
cdc7bbd5 6use clippy_utils::comparisons::{normalize_comparison, Rel};
17df50a5 7use clippy_utils::consts::{constant, Constant};
cdc7bbd5
XL
8use clippy_utils::diagnostics::span_lint_and_help;
9use clippy_utils::source::snippet;
10use clippy_utils::ty::is_isize_or_usize;
11use clippy_utils::{clip, int_bits, unsext};
12
13declare_clippy_lint! {
94222f64
XL
14 /// ### What it does
15 /// Checks for comparisons where one side of the relation is
cdc7bbd5
XL
16 /// either the minimum or maximum value for its type and warns if it involves a
17 /// case that is always true or always false. Only integer and boolean types are
18 /// checked.
19 ///
94222f64
XL
20 /// ### Why is this bad?
21 /// An expression like `min <= x` may misleadingly imply
cdc7bbd5
XL
22 /// that it is possible for `x` to be less than the minimum. Expressions like
23 /// `max < x` are probably mistakes.
24 ///
94222f64
XL
25 /// ### Known problems
26 /// For `usize` the size of the current compile target will
cdc7bbd5
XL
27 /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
28 /// a comparison to detect target pointer width will trigger this lint. One can
29 /// use `mem::sizeof` and compare its value or conditional compilation
30 /// attributes
31 /// like `#[cfg(target_pointer_width = "64")] ..` instead.
32 ///
94222f64 33 /// ### Example
cdc7bbd5
XL
34 /// ```rust
35 /// let vec: Vec<isize> = Vec::new();
36 /// if vec.len() <= 0 {}
37 /// if 100 > i32::MAX {}
38 /// ```
a2a8927a 39 #[clippy::version = "pre 1.29.0"]
cdc7bbd5
XL
40 pub ABSURD_EXTREME_COMPARISONS,
41 correctness,
42 "a comparison with a maximum or minimum value that is always true or false"
43}
44
45declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
46
47impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons {
48 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
49 if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
50 if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
51 if !expr.span.from_expansion() {
52 let msg = "this comparison involving the minimum or maximum element for this \
53 type contains a case that is always true or always false";
54
55 let conclusion = match result {
56 AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
57 AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
58 AbsurdComparisonResult::InequalityImpossible => format!(
59 "the case where the two sides are not equal never occurs, consider using `{} == {}` \
60 instead",
61 snippet(cx, lhs.span, "lhs"),
62 snippet(cx, rhs.span, "rhs")
63 ),
64 };
65
66 let help = format!(
67 "because `{}` is the {} value for this type, {}",
68 snippet(cx, culprit.expr.span, "x"),
69 match culprit.which {
70 ExtremeType::Minimum => "minimum",
71 ExtremeType::Maximum => "maximum",
72 },
73 conclusion
74 );
75
76 span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
77 }
78 }
79 }
80 }
81}
82
83enum ExtremeType {
84 Minimum,
85 Maximum,
86}
87
88struct ExtremeExpr<'a> {
89 which: ExtremeType,
90 expr: &'a Expr<'a>,
91}
92
93enum AbsurdComparisonResult {
94 AlwaysFalse,
95 AlwaysTrue,
96 InequalityImpossible,
97}
98
99fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
100 if let ExprKind::Cast(cast_exp, _) = expr.kind {
101 let precast_ty = cx.typeck_results().expr_ty(cast_exp);
102 let cast_ty = cx.typeck_results().expr_ty(expr);
103
104 return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
105 }
106
107 false
108}
109
110fn detect_absurd_comparison<'tcx>(
111 cx: &LateContext<'tcx>,
112 op: BinOpKind,
113 lhs: &'tcx Expr<'_>,
114 rhs: &'tcx Expr<'_>,
115) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
116 use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
117 use ExtremeType::{Maximum, Minimum};
118 // absurd comparison only makes sense on primitive types
119 // primitive types don't implement comparison operators with each other
120 if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) {
121 return None;
122 }
123
124 // comparisons between fix sized types and target sized types are considered unanalyzable
125 if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
126 return None;
127 }
128
129 let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
130
131 let lx = detect_extreme_expr(cx, normalized_lhs);
132 let rx = detect_extreme_expr(cx, normalized_rhs);
133
134 Some(match rel {
135 Rel::Lt => {
136 match (lx, rx) {
137 (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
138 (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
139 _ => return None,
140 }
141 },
142 Rel::Le => {
143 match (lx, rx) {
144 (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
145 (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
146 (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
147 (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
148 _ => return None,
149 }
150 },
151 Rel::Ne | Rel::Eq => return None,
152 })
153}
154
155fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
156 let ty = cx.typeck_results().expr_ty(expr);
157
158 let cv = constant(cx, cx.typeck_results(), expr)?.0;
159
160 let which = match (ty.kind(), cv) {
161 (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
162 (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
163 ExtremeType::Minimum
164 },
165
166 (&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum,
167 (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
168 ExtremeType::Maximum
169 },
170 (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum,
171
172 _ => return None,
173 };
174 Some(ExtremeExpr { which, expr })
175}