diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs index dbc9948eeedc..8172ccd78706 100644 --- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -1,4 +1,4 @@ -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -7,7 +7,7 @@ use clippy_utils::consts::{ConstEvalCtxt, 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 clippy_utils::{clip, int_bits, sym, unsext}; use super::ABSURD_EXTREME_COMPARISONS; @@ -121,6 +121,78 @@ fn detect_absurd_comparison<'tcx>( fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { let ty = cx.typeck_results().expr_ty(expr); + // Detect Duration zero values + if let ty::Adt(adt_def, _) = *ty.kind() + && cx.tcx.is_diagnostic_item(sym::Duration, adt_def.did()) + { + if let ExprKind::Call(func, args) = &expr.kind { + if let ExprKind::Path(qpath) = &func.kind { + let method_name = match qpath { + QPath::Resolved(_, path) => path.segments.last().map(|seg| seg.ident.name.as_str()), + QPath::TypeRelative(_, seg) => Some(seg.ident.name.as_str()), + _ => None, + }; + + // Handle constructors like from_secs(0), from_millis(0), etc. + if args.len() == 1 { + let int_methods = ["from_secs", "from_millis", "from_micros", "from_nanos"]; + if int_methods.iter().any(|&m| Some(m) == method_name) { + if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(&args[0]) { + return Some(ExtremeExpr { + which: ExtremeType::Minimum, + expr, + }); + } + } + + // Handle float constructors + let float_methods = ["from_secs_f32", "from_secs_f64"]; + if float_methods.iter().any(|&m| Some(m) == method_name) { + if let ExprKind::Lit(lit) = &args[0].kind { + let lit_str = snippet(cx, lit.span, ""); + if lit_str == "0.0" || lit_str == "0" { + return Some(ExtremeExpr { + which: ExtremeType::Minimum, + expr, + }); + } + } + } + } + // Handle new(0, 0) + else if args.len() == 2 && method_name == Some("new") { + let first_arg_const = ConstEvalCtxt::new(cx).eval(&args[0]); + let second_arg_const = ConstEvalCtxt::new(cx).eval(&args[1]); + + if let (Some(Constant::Int(0)), Some(Constant::Int(0))) = (first_arg_const, second_arg_const) { + return Some(ExtremeExpr { + which: ExtremeType::Minimum, + expr, + }); + } + + if let (ExprKind::Path(_), ExprKind::Path(_)) = (&args[0].kind, &args[1].kind) { + if snippet(cx, args[0].span, "").contains("zero") + && snippet(cx, args[1].span, "").contains("zero") + { + return Some(ExtremeExpr { + which: ExtremeType::Minimum, + expr, + }); + } + } + } + // Handle constructor default() + else if args.is_empty() && method_name == Some("default") { + return Some(ExtremeExpr { + which: ExtremeType::Minimum, + expr, + }); + } + } + } + } + let cv = ConstEvalCtxt::new(cx).eval(expr)?; let which = match (ty.kind(), cv) { diff --git a/tests/ui/absurd-extreme-comparisons.rs b/tests/ui/absurd-extreme-comparisons.rs index 793961d30f0d..9f58060f559f 100644 --- a/tests/ui/absurd-extreme-comparisons.rs +++ b/tests/ui/absurd-extreme-comparisons.rs @@ -7,6 +7,8 @@ clippy::needless_pass_by_value )] +use std::time::Duration; + #[rustfmt::skip] fn main() { const Z: u32 = 0; @@ -69,7 +71,68 @@ fn main() { () < {}; //~^ unit_cmp + Duration::from_secs(0) > Duration::new(5, 0); + //~^ absurd_extreme_comparisons + Duration::new(5, 0) < Duration::from_secs(0); + //~^ absurd_extreme_comparisons + Duration::from_secs(0) < Duration::new(5, 0); // ok + + let d = Duration::new(5, 0); + d < Duration::from_secs(0); + //~^ absurd_extreme_comparisons + Duration::from_secs(0) > d; + //~^ absurd_extreme_comparisons + + d < Duration::from_millis(0); + //~^ absurd_extreme_comparisons + Duration::from_micros(0) > d; + //~^ absurd_extreme_comparisons + + d <= Duration::from_nanos(0); + //~^ absurd_extreme_comparisons + Duration::from_nanos(0) >= d; + //~^ absurd_extreme_comparisons + + d < Duration::default(); + //~^ absurd_extreme_comparisons + Duration::default() > d; + //~^ absurd_extreme_comparisons + + d < Duration::from_secs_f32(0.0); + //~^ absurd_extreme_comparisons + Duration::from_secs_f64(0.0) > d; + //~^ absurd_extreme_comparisons + + let zero_secs: u64 = 0; + let zero_nanos: u32 = 0; + d < Duration::new(zero_secs, zero_nanos); + //~^ absurd_extreme_comparisons + Duration::new(zero_secs, zero_nanos) > d; + //~^ absurd_extreme_comparisons + + d > Duration::from_secs(0); // OK + Duration::from_secs(0) < d; // OK + d == Duration::from_secs(0); // OK + + let d = Duration::new(5, 0); + d < one_second_plus(0); // OK + one_second_plus(0) > d; // OK + + let n = 0; + d < one_second_plus(n); // OK + + d < Duration::from_secs(0); + //~^ absurd_extreme_comparisons + Duration::from_secs(0) > d; + //~^ absurd_extreme_comparisons + + + let d_zero = Duration::from_secs(0); + let d_one = one_second_plus(0); + d_zero < d_one; // OK + d_one > d_zero; // OK + d_zero < d_zero; // OK } use std::cmp::{Ordering, PartialEq, PartialOrd}; @@ -96,3 +159,7 @@ pub fn bar(len: u64) -> bool { // This is OK as we are casting from target sized to fixed size len >= usize::MAX as u64 } + +fn one_second_plus(n: u64) -> Duration { + Duration::from_secs(n + 1) +} diff --git a/tests/ui/absurd-extreme-comparisons.stderr b/tests/ui/absurd-extreme-comparisons.stderr index 6df50c15e8cd..bb9ff06045d0 100644 --- a/tests/ui/absurd-extreme-comparisons.stderr +++ b/tests/ui/absurd-extreme-comparisons.stderr @@ -1,5 +1,5 @@ error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:14:5 + --> tests/ui/absurd-extreme-comparisons.rs:16:5 | LL | u <= 0; | ^^^^^^ @@ -9,7 +9,7 @@ LL | u <= 0; = help: to override `-D warnings` add `#[allow(clippy::absurd_extreme_comparisons)]` error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:17:5 + --> tests/ui/absurd-extreme-comparisons.rs:19:5 | LL | u <= Z; | ^^^^^^ @@ -17,7 +17,7 @@ LL | u <= Z; = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == Z` instead error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:20:5 + --> tests/ui/absurd-extreme-comparisons.rs:22:5 | LL | u < Z; | ^^^^^ @@ -25,7 +25,7 @@ LL | u < Z; = help: because `Z` is the minimum value for this type, this comparison is always false error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:23:5 + --> tests/ui/absurd-extreme-comparisons.rs:25:5 | LL | Z >= u; | ^^^^^^ @@ -33,7 +33,7 @@ LL | Z >= u; = help: because `Z` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `Z == u` instead error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:26:5 + --> tests/ui/absurd-extreme-comparisons.rs:28:5 | LL | Z > u; | ^^^^^ @@ -41,7 +41,7 @@ LL | Z > u; = help: because `Z` is the minimum value for this type, this comparison is always false error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:29:5 + --> tests/ui/absurd-extreme-comparisons.rs:31:5 | LL | u > u32::MAX; | ^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | u > u32::MAX; = help: because `u32::MAX` is the maximum value for this type, this comparison is always false error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:32:5 + --> tests/ui/absurd-extreme-comparisons.rs:34:5 | LL | u >= u32::MAX; | ^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | u >= u32::MAX; = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == u32::MAX` instead error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:35:5 + --> tests/ui/absurd-extreme-comparisons.rs:37:5 | LL | u32::MAX < u; | ^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | u32::MAX < u; = help: because `u32::MAX` is the maximum value for this type, this comparison is always false error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:38:5 + --> tests/ui/absurd-extreme-comparisons.rs:40:5 | LL | u32::MAX <= u; | ^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | u32::MAX <= u; = help: because `u32::MAX` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u32::MAX == u` instead error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:41:5 + --> tests/ui/absurd-extreme-comparisons.rs:43:5 | LL | 1-1 > u; | ^^^^^^^ @@ -81,7 +81,7 @@ LL | 1-1 > u; = help: because `1-1` is the minimum value for this type, this comparison is always false error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:44:5 + --> tests/ui/absurd-extreme-comparisons.rs:46:5 | LL | u >= !0; | ^^^^^^^ @@ -89,7 +89,7 @@ LL | u >= !0; = help: because `!0` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `u == !0` instead error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:47:5 + --> tests/ui/absurd-extreme-comparisons.rs:49:5 | LL | u <= 12 - 2*6; | ^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | u <= 12 - 2*6; = help: because `12 - 2*6` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `u == 12 - 2*6` instead error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:51:5 + --> tests/ui/absurd-extreme-comparisons.rs:53:5 | LL | i < -127 - 1; | ^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | i < -127 - 1; = help: because `-127 - 1` is the minimum value for this type, this comparison is always false error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:54:5 + --> tests/ui/absurd-extreme-comparisons.rs:56:5 | LL | i8::MAX >= i; | ^^^^^^^^^^^^ @@ -113,7 +113,7 @@ LL | i8::MAX >= i; = help: because `i8::MAX` is the maximum value for this type, this comparison is always true error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:57:5 + --> tests/ui/absurd-extreme-comparisons.rs:59:5 | LL | 3-7 < i32::MIN; | ^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | 3-7 < i32::MIN; = help: because `i32::MIN` is the minimum value for this type, this comparison is always false error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:61:5 + --> tests/ui/absurd-extreme-comparisons.rs:63:5 | LL | b >= true; | ^^^^^^^^^ @@ -129,7 +129,7 @@ LL | b >= true; = help: because `true` is the maximum value for this type, the case where the two sides are not equal never occurs, consider using `b == true` instead error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false - --> tests/ui/absurd-extreme-comparisons.rs:64:5 + --> tests/ui/absurd-extreme-comparisons.rs:66:5 | LL | false > b; | ^^^^^^^^^ @@ -137,12 +137,140 @@ LL | false > b; = help: because `false` is the minimum value for this type, this comparison is always false error: <-comparison of unit values detected. This will always be false - --> tests/ui/absurd-extreme-comparisons.rs:69:5 + --> tests/ui/absurd-extreme-comparisons.rs:71:5 | LL | () < {}; | ^^^^^^^ | = note: `#[deny(clippy::unit_cmp)]` on by default -error: aborting due to 18 previous errors +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:74:5 + | +LL | Duration::from_secs(0) > Duration::new(5, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs(0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:76:5 + | +LL | Duration::new(5, 0) < Duration::from_secs(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs(0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:82:5 + | +LL | d < Duration::from_secs(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs(0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:84:5 + | +LL | Duration::from_secs(0) > d; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs(0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:87:5 + | +LL | d < Duration::from_millis(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_millis(0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:89:5 + | +LL | Duration::from_micros(0) > d; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_micros(0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:92:5 + | +LL | d <= Duration::from_nanos(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_nanos(0)` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `d == Duration::from_nanos(0)` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:94:5 + | +LL | Duration::from_nanos(0) >= d; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_nanos(0)` is the minimum value for this type, the case where the two sides are not equal never occurs, consider using `Duration::from_nanos(0) == d` instead + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:97:5 + | +LL | d < Duration::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::default()` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:99:5 + | +LL | Duration::default() > d; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::default()` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:102:5 + | +LL | d < Duration::from_secs_f32(0.0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs_f32(0.0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:104:5 + | +LL | Duration::from_secs_f64(0.0) > d; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs_f64(0.0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:109:5 + | +LL | d < Duration::new(zero_secs, zero_nanos); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::new(zero_secs, zero_nanos)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:111:5 + | +LL | Duration::new(zero_secs, zero_nanos) > d; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::new(zero_secs, zero_nanos)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:125:5 + | +LL | d < Duration::from_secs(0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs(0)` is the minimum value for this type, this comparison is always false + +error: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false + --> tests/ui/absurd-extreme-comparisons.rs:127:5 + | +LL | Duration::from_secs(0) > d; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: because `Duration::from_secs(0)` is the minimum value for this type, this comparison is always false + +error: aborting due to 34 previous errors