Skip to content

Commit caa49c8

Browse files
committed
Move absurd_extreme_comparisons to its own module
1 parent 63aca96 commit caa49c8

File tree

3 files changed

+183
-177
lines changed

3 files changed

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

clippy_lints/src/lib.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ mod consts;
164164
mod utils;
165165

166166
// begin lints modules, do not remove this comment, it’s used in `update_lints`
167+
mod absurd_extreme_comparisons;
167168
mod approx_const;
168169
mod arithmetic;
169170
mod as_conversions;
@@ -558,6 +559,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
558559
&utils::internal_lints::PRODUCE_ICE,
559560
#[cfg(feature = "internal-lints")]
560561
&utils::internal_lints::UNNECESSARY_SYMBOL_STR,
562+
&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
561563
&approx_const::APPROX_CONSTANT,
562564
&arithmetic::FLOAT_ARITHMETIC,
563565
&arithmetic::INTEGER_ARITHMETIC,
@@ -955,7 +957,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
955957
&transmute::WRONG_TRANSMUTE,
956958
&transmuting_null::TRANSMUTING_NULL,
957959
&try_err::TRY_ERR,
958-
&types::ABSURD_EXTREME_COMPARISONS,
959960
&types::BORROWED_BOX,
960961
&types::BOX_VEC,
961962
&types::IMPLICIT_HASHER,
@@ -1112,7 +1113,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11121113
store.register_late_pass(|| box get_last_with_len::GetLastWithLen);
11131114
store.register_late_pass(|| box drop_forget_ref::DropForgetRef);
11141115
store.register_late_pass(|| box empty_enum::EmptyEnum);
1115-
store.register_late_pass(|| box types::AbsurdExtremeComparisons);
1116+
store.register_late_pass(|| box absurd_extreme_comparisons::AbsurdExtremeComparisons);
11161117
store.register_late_pass(|| box types::InvalidUpcastComparisons);
11171118
store.register_late_pass(|| box regex::Regex::default());
11181119
store.register_late_pass(|| box copies::CopyAndPaste);
@@ -1442,6 +1443,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14421443
]);
14431444

14441445
store.register_group(true, "clippy::all", Some("clippy"), vec![
1446+
LintId::of(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
14451447
LintId::of(&approx_const::APPROX_CONSTANT),
14461448
LintId::of(&assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
14471449
LintId::of(&assign_ops::ASSIGN_OP_PATTERN),
@@ -1701,7 +1703,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
17011703
LintId::of(&transmute::WRONG_TRANSMUTE),
17021704
LintId::of(&transmuting_null::TRANSMUTING_NULL),
17031705
LintId::of(&try_err::TRY_ERR),
1704-
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
17051706
LintId::of(&types::BORROWED_BOX),
17061707
LintId::of(&types::BOX_VEC),
17071708
LintId::of(&types::REDUNDANT_ALLOCATION),
@@ -1941,6 +1942,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
19411942
]);
19421943

19431944
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
1945+
LintId::of(&absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
19441946
LintId::of(&approx_const::APPROX_CONSTANT),
19451947
LintId::of(&async_yields_async::ASYNC_YIELDS_ASYNC),
19461948
LintId::of(&atomic_ordering::INVALID_ATOMIC_ORDERING),
@@ -2002,7 +2004,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
20022004
LintId::of(&transmute::UNSOUND_COLLECTION_TRANSMUTE),
20032005
LintId::of(&transmute::WRONG_TRANSMUTE),
20042006
LintId::of(&transmuting_null::TRANSMUTING_NULL),
2005-
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
20062007
LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
20072008
LintId::of(&unicode::INVISIBLE_CHARACTERS),
20082009
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),

0 commit comments

Comments
 (0)