]> git.lizzy.rs Git - rust.git/commitdiff
Add `Operators` lint pass
authorJason Newcomb <jsnewcomb@pm.me>
Tue, 31 May 2022 18:07:50 +0000 (14:07 -0400)
committerJason Newcomb <jsnewcomb@pm.me>
Tue, 28 Jun 2022 16:50:33 +0000 (12:50 -0400)
clippy_lints/src/absurd_extreme_comparisons.rs [deleted file]
clippy_lints/src/lib.register_all.rs
clippy_lints/src/lib.register_correctness.rs
clippy_lints/src/lib.register_lints.rs
clippy_lints/src/lib.rs
clippy_lints/src/operators/absurd_extreme_comparisons.rs [new file with mode: 0644]
clippy_lints/src/operators/mod.rs [new file with mode: 0644]

diff --git a/clippy_lints/src/absurd_extreme_comparisons.rs b/clippy_lints/src/absurd_extreme_comparisons.rs
deleted file mode 100644 (file)
index 7665aa8..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-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 })
-}
index b4edb8ea90ede1ab0e65325d7006383e3d159435..7728157d2282dbc18c6664c6a3aa36668ae4b424 100644 (file)
@@ -3,7 +3,6 @@
 // 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),
index 92a3a0aabf1cabe7f846f2acb85fadfb3caec6bd..26852445fdcdf56743fdaa7a8fe28fc34244c104 100644 (file)
@@ -3,7 +3,6 @@
 // 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),
@@ -51,6 +50,7 @@
     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),
index 29a5f368f23b5b1bed29375a70219e65e364bcf5..f0ce0db65a7a4e9cd17497eba82a028758e82723 100644 (file)
@@ -35,7 +35,6 @@
     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,
index 6c5b5ed98bc42335298c66577a5863b2d8cd3613..16e72400fbf134d9e35433af9f18c5da18aa84ae 100644 (file)
@@ -168,7 +168,6 @@ macro_rules! declare_clippy_lint {
 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;
@@ -336,6 +335,7 @@ macro_rules! declare_clippy_lint {
 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;
@@ -683,7 +683,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     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));
@@ -941,6 +940,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     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`
 }
 
diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs
new file mode 100644 (file)
index 0000000..1ec4240
--- /dev/null
@@ -0,0 +1,142 @@
+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 })
+}
diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs
new file mode 100644 (file)
index 0000000..6f5a164
--- /dev/null
@@ -0,0 +1,51 @@
+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);
+            }
+        }
+    }
+}