+++ /dev/null
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-use clippy_utils::comparisons::{normalize_comparison, Rel};
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::source::snippet;
-use clippy_utils::ty::is_isize_or_usize;
-use clippy_utils::{clip, int_bits, unsext};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for comparisons where one side of the relation is
- /// either the minimum or maximum value for its type and warns if it involves a
- /// case that is always true or always false. Only integer and boolean types are
- /// checked.
- ///
- /// ### Why is this bad?
- /// An expression like `min <= x` may misleadingly imply
- /// that it is possible for `x` to be less than the minimum. Expressions like
- /// `max < x` are probably mistakes.
- ///
- /// ### Known problems
- /// For `usize` the size of the current compile target will
- /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
- /// a comparison to detect target pointer width will trigger this lint. One can
- /// use `mem::sizeof` and compare its value or conditional compilation
- /// attributes
- /// like `#[cfg(target_pointer_width = "64")] ..` instead.
- ///
- /// ### Example
- /// ```rust
- /// let vec: Vec<isize> = Vec::new();
- /// if vec.len() <= 0 {}
- /// if 100 > i32::MAX {}
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub ABSURD_EXTREME_COMPARISONS,
- correctness,
- "a comparison with a maximum or minimum value that is always true or false"
-}
-
-declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
-
-impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
- if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
- if !expr.span.from_expansion() {
- let msg = "this comparison involving the minimum or maximum element for this \
- type contains a case that is always true or always false";
-
- let conclusion = match result {
- AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
- AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
- AbsurdComparisonResult::InequalityImpossible => format!(
- "the case where the two sides are not equal never occurs, consider using `{} == {}` \
- instead",
- snippet(cx, lhs.span, "lhs"),
- snippet(cx, rhs.span, "rhs")
- ),
- };
-
- let help = format!(
- "because `{}` is the {} value for this type, {}",
- snippet(cx, culprit.expr.span, "x"),
- match culprit.which {
- ExtremeType::Minimum => "minimum",
- ExtremeType::Maximum => "maximum",
- },
- conclusion
- );
-
- span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
- }
- }
- }
- }
-}
-
-enum ExtremeType {
- Minimum,
- Maximum,
-}
-
-struct ExtremeExpr<'a> {
- which: ExtremeType,
- expr: &'a Expr<'a>,
-}
-
-enum AbsurdComparisonResult {
- AlwaysFalse,
- AlwaysTrue,
- InequalityImpossible,
-}
-
-fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- if let ExprKind::Cast(cast_exp, _) = expr.kind {
- let precast_ty = cx.typeck_results().expr_ty(cast_exp);
- let cast_ty = cx.typeck_results().expr_ty(expr);
-
- return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
- }
-
- false
-}
-
-fn detect_absurd_comparison<'tcx>(
- cx: &LateContext<'tcx>,
- op: BinOpKind,
- lhs: &'tcx Expr<'_>,
- rhs: &'tcx Expr<'_>,
-) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
- use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
- use ExtremeType::{Maximum, Minimum};
- // absurd comparison only makes sense on primitive types
- // primitive types don't implement comparison operators with each other
- if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) {
- return None;
- }
-
- // comparisons between fix sized types and target sized types are considered unanalyzable
- if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
- return None;
- }
-
- let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
-
- let lx = detect_extreme_expr(cx, normalized_lhs);
- let rx = detect_extreme_expr(cx, normalized_rhs);
-
- Some(match rel {
- Rel::Lt => {
- match (lx, rx) {
- (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
- (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
- _ => return None,
- }
- },
- Rel::Le => {
- match (lx, rx) {
- (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
- (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
- (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
- (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
- _ => return None,
- }
- },
- Rel::Ne | Rel::Eq => return None,
- })
-}
-
-fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
- let ty = cx.typeck_results().expr_ty(expr);
-
- let cv = constant(cx, cx.typeck_results(), expr)?.0;
-
- let which = match (ty.kind(), cv) {
- (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
- (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
- ExtremeType::Minimum
- },
-
- (&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum,
- (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
- ExtremeType::Maximum
- },
- (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum,
-
- _ => return None,
- };
- Some(ExtremeExpr { which, expr })
-}
// Manual edits will be overwritten.
store.register_group(true, "clippy::all", Some("clippy_all"), vec![
- LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
LintId::of(approx_const::APPROX_CONSTANT),
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+ LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
// Manual edits will be overwritten.
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
- LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
LintId::of(approx_const::APPROX_CONSTANT),
LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
LintId::of(attrs::DEPRECATED_SEMVER),
LintId::of(misc::MODULO_ONE),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+ LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
LintId::of(ptr::MUT_FROM_REF),
utils::internal_lints::PRODUCE_ICE,
#[cfg(feature = "internal")]
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
- absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
approx_const::APPROX_CONSTANT,
as_conversions::AS_CONVERSIONS,
octal_escapes::OCTAL_ESCAPES,
only_used_in_recursion::ONLY_USED_IN_RECURSION,
open_options::NONSENSICAL_OPEN_OPTIONS,
+ operators::ABSURD_EXTREME_COMPARISONS,
option_env_unwrap::OPTION_ENV_UNWRAP,
option_if_let_else::OPTION_IF_LET_ELSE,
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
mod renamed_lints;
// begin lints modules, do not remove this comment, it’s used in `update_lints`
-mod absurd_extreme_comparisons;
mod almost_complete_letter_range;
mod approx_const;
mod as_conversions;
mod octal_escapes;
mod only_used_in_recursion;
mod open_options;
+mod operators;
mod option_env_unwrap;
mod option_if_let_else;
mod overflow_check_conditional;
store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
- store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
store.register_late_pass(|| Box::new(regex::Regex));
store.register_late_pass(|| Box::new(copies::CopyAndPaste));
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
+ store.register_late_pass(|| Box::new(operators::Operators));
// add lints here, do not remove this comment, it's used in `new_lint`
}
--- /dev/null
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use clippy_utils::comparisons::{normalize_comparison, Rel};
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_isize_or_usize;
+use clippy_utils::{clip, int_bits, unsext};
+
+use super::ABSURD_EXTREME_COMPARISONS;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ op: BinOpKind,
+ lhs: &'tcx Expr<'_>,
+ rhs: &'tcx Expr<'_>,
+) {
+ if let Some((culprit, result)) = detect_absurd_comparison(cx, op, lhs, rhs) {
+ let msg = "this comparison involving the minimum or maximum element for this \
+ type contains a case that is always true or always false";
+
+ let conclusion = match result {
+ AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
+ AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
+ AbsurdComparisonResult::InequalityImpossible => format!(
+ "the case where the two sides are not equal never occurs, consider using `{} == {}` \
+ instead",
+ snippet(cx, lhs.span, "lhs"),
+ snippet(cx, rhs.span, "rhs")
+ ),
+ };
+
+ let help = format!(
+ "because `{}` is the {} value for this type, {}",
+ snippet(cx, culprit.expr.span, "x"),
+ match culprit.which {
+ ExtremeType::Minimum => "minimum",
+ ExtremeType::Maximum => "maximum",
+ },
+ conclusion
+ );
+
+ span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
+ }
+}
+
+enum ExtremeType {
+ Minimum,
+ Maximum,
+}
+
+struct ExtremeExpr<'a> {
+ which: ExtremeType,
+ expr: &'a Expr<'a>,
+}
+
+enum AbsurdComparisonResult {
+ AlwaysFalse,
+ AlwaysTrue,
+ InequalityImpossible,
+}
+
+fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ if let ExprKind::Cast(cast_exp, _) = expr.kind {
+ let precast_ty = cx.typeck_results().expr_ty(cast_exp);
+ let cast_ty = cx.typeck_results().expr_ty(expr);
+
+ return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
+ }
+
+ false
+}
+
+fn detect_absurd_comparison<'tcx>(
+ cx: &LateContext<'tcx>,
+ op: BinOpKind,
+ lhs: &'tcx Expr<'_>,
+ rhs: &'tcx Expr<'_>,
+) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
+ use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
+ use ExtremeType::{Maximum, Minimum};
+ // absurd comparison only makes sense on primitive types
+ // primitive types don't implement comparison operators with each other
+ if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) {
+ return None;
+ }
+
+ // comparisons between fix sized types and target sized types are considered unanalyzable
+ if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
+ return None;
+ }
+
+ let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
+
+ let lx = detect_extreme_expr(cx, normalized_lhs);
+ let rx = detect_extreme_expr(cx, normalized_rhs);
+
+ Some(match rel {
+ Rel::Lt => {
+ match (lx, rx) {
+ (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
+ (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
+ _ => return None,
+ }
+ },
+ Rel::Le => {
+ match (lx, rx) {
+ (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
+ (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
+ (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
+ (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
+ _ => return None,
+ }
+ },
+ Rel::Ne | Rel::Eq => return None,
+ })
+}
+
+fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
+ let ty = cx.typeck_results().expr_ty(expr);
+
+ let cv = constant(cx, cx.typeck_results(), expr)?.0;
+
+ let which = match (ty.kind(), cv) {
+ (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
+ (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
+ ExtremeType::Minimum
+ },
+
+ (&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum,
+ (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
+ ExtremeType::Maximum
+ },
+ (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum,
+
+ _ => return None,
+ };
+ Some(ExtremeExpr { which, expr })
+}
--- /dev/null
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+mod absurd_extreme_comparisons;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for comparisons where one side of the relation is
+ /// either the minimum or maximum value for its type and warns if it involves a
+ /// case that is always true or always false. Only integer and boolean types are
+ /// checked.
+ ///
+ /// ### Why is this bad?
+ /// An expression like `min <= x` may misleadingly imply
+ /// that it is possible for `x` to be less than the minimum. Expressions like
+ /// `max < x` are probably mistakes.
+ ///
+ /// ### Known problems
+ /// For `usize` the size of the current compile target will
+ /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
+ /// a comparison to detect target pointer width will trigger this lint. One can
+ /// use `mem::sizeof` and compare its value or conditional compilation
+ /// attributes
+ /// like `#[cfg(target_pointer_width = "64")] ..` instead.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let vec: Vec<isize> = Vec::new();
+ /// if vec.len() <= 0 {}
+ /// if 100 > i32::MAX {}
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub ABSURD_EXTREME_COMPARISONS,
+ correctness,
+ "a comparison with a maximum or minimum value that is always true or false"
+}
+
+pub struct Operators;
+impl_lint_pass!(Operators => [
+ ABSURD_EXTREME_COMPARISONS,
+]);
+impl<'tcx> LateLintPass<'tcx> for Operators {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if let ExprKind::Binary(op, lhs, rhs) = e.kind {
+ if !e.span.from_expansion() {
+ absurd_extreme_comparisons::check(cx, e, op.node, lhs, rhs);
+ }
+ }
+ }
+}