Skip to content

[WIP] Trigger absurd_extreme_comparisons if Duration is less than zero #14817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 74 additions & 2 deletions clippy_lints/src/operators/absurd_extreme_comparisons.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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;

Expand Down Expand Up @@ -121,6 +121,78 @@ fn detect_absurd_comparison<'tcx>(
fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
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) {
Expand Down
67 changes: 67 additions & 0 deletions tests/ui/absurd-extreme-comparisons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
clippy::needless_pass_by_value
)]

use std::time::Duration;

#[rustfmt::skip]
fn main() {
const Z: u32 = 0;
Expand Down Expand Up @@ -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};
Expand All @@ -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)
}
Loading
Loading