Skip to content

Commit d6feb1f

Browse files
committed
Trigger absurd_extreme_comparisons on if Duration is less than zero
1 parent 689e62b commit d6feb1f

File tree

3 files changed

+288
-21
lines changed

3 files changed

+288
-21
lines changed

clippy_lints/src/operators/absurd_extreme_comparisons.rs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_hir::{BinOpKind, Expr, ExprKind};
1+
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
22
use rustc_lint::LateContext;
33
use rustc_middle::ty;
44

@@ -7,7 +7,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant};
77
use clippy_utils::diagnostics::span_lint_and_help;
88
use clippy_utils::source::snippet;
99
use clippy_utils::ty::is_isize_or_usize;
10-
use clippy_utils::{clip, int_bits, unsext};
10+
use clippy_utils::{clip, int_bits, sym, unsext};
1111

1212
use super::ABSURD_EXTREME_COMPARISONS;
1313

@@ -121,6 +121,78 @@ fn detect_absurd_comparison<'tcx>(
121121
fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
122122
let ty = cx.typeck_results().expr_ty(expr);
123123

124+
// Detect Duration zero values
125+
if let ty::Adt(adt_def, _) = *ty.kind()
126+
&& cx.tcx.is_diagnostic_item(sym::Duration, adt_def.did())
127+
{
128+
if let ExprKind::Call(func, args) = &expr.kind {
129+
if let ExprKind::Path(qpath) = &func.kind {
130+
let method_name = match qpath {
131+
QPath::Resolved(_, path) => path.segments.last().map(|seg| seg.ident.name.as_str()),
132+
QPath::TypeRelative(_, seg) => Some(seg.ident.name.as_str()),
133+
_ => None,
134+
};
135+
136+
// Handle constructors like from_secs(0), from_millis(0), etc.
137+
if args.len() == 1 {
138+
let int_methods = ["from_secs", "from_millis", "from_micros", "from_nanos"];
139+
if int_methods.iter().any(|&m| Some(m) == method_name) {
140+
if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(&args[0]) {
141+
return Some(ExtremeExpr {
142+
which: ExtremeType::Minimum,
143+
expr,
144+
});
145+
}
146+
}
147+
148+
// Handle float constructors
149+
let float_methods = ["from_secs_f32", "from_secs_f64"];
150+
if float_methods.iter().any(|&m| Some(m) == method_name) {
151+
if let ExprKind::Lit(lit) = &args[0].kind {
152+
let lit_str = snippet(cx, lit.span, "");
153+
if lit_str == "0.0" || lit_str == "0" {
154+
return Some(ExtremeExpr {
155+
which: ExtremeType::Minimum,
156+
expr,
157+
});
158+
}
159+
}
160+
}
161+
}
162+
// Handle new(0, 0)
163+
else if args.len() == 2 && method_name == Some("new") {
164+
let first_arg_const = ConstEvalCtxt::new(cx).eval(&args[0]);
165+
let second_arg_const = ConstEvalCtxt::new(cx).eval(&args[1]);
166+
167+
if let (Some(Constant::Int(0)), Some(Constant::Int(0))) = (first_arg_const, second_arg_const) {
168+
return Some(ExtremeExpr {
169+
which: ExtremeType::Minimum,
170+
expr,
171+
});
172+
}
173+
174+
if let (ExprKind::Path(_), ExprKind::Path(_)) = (&args[0].kind, &args[1].kind) {
175+
if snippet(cx, args[0].span, "").contains("zero")
176+
&& snippet(cx, args[1].span, "").contains("zero")
177+
{
178+
return Some(ExtremeExpr {
179+
which: ExtremeType::Minimum,
180+
expr,
181+
});
182+
}
183+
}
184+
}
185+
// Handle constructor default()
186+
else if args.is_empty() && method_name == Some("default") {
187+
return Some(ExtremeExpr {
188+
which: ExtremeType::Minimum,
189+
expr,
190+
});
191+
}
192+
}
193+
}
194+
}
195+
124196
let cv = ConstEvalCtxt::new(cx).eval(expr)?;
125197

126198
let which = match (ty.kind(), cv) {

tests/ui/absurd-extreme-comparisons.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
clippy::needless_pass_by_value
88
)]
99

10+
use std::time::Duration;
11+
1012
#[rustfmt::skip]
1113
fn main() {
1214
const Z: u32 = 0;
@@ -69,7 +71,68 @@ fn main() {
6971
() < {};
7072
//~^ unit_cmp
7173

74+
Duration::from_secs(0) > Duration::new(5, 0);
75+
//~^ absurd_extreme_comparisons
76+
Duration::new(5, 0) < Duration::from_secs(0);
77+
//~^ absurd_extreme_comparisons
78+
Duration::from_secs(0) < Duration::new(5, 0); // ok
79+
80+
let d = Duration::new(5, 0);
7281

82+
d < Duration::from_secs(0);
83+
//~^ absurd_extreme_comparisons
84+
Duration::from_secs(0) > d;
85+
//~^ absurd_extreme_comparisons
86+
87+
d < Duration::from_millis(0);
88+
//~^ absurd_extreme_comparisons
89+
Duration::from_micros(0) > d;
90+
//~^ absurd_extreme_comparisons
91+
92+
d <= Duration::from_nanos(0);
93+
//~^ absurd_extreme_comparisons
94+
Duration::from_nanos(0) >= d;
95+
//~^ absurd_extreme_comparisons
96+
97+
d < Duration::default();
98+
//~^ absurd_extreme_comparisons
99+
Duration::default() > d;
100+
//~^ absurd_extreme_comparisons
101+
102+
d < Duration::from_secs_f32(0.0);
103+
//~^ absurd_extreme_comparisons
104+
Duration::from_secs_f64(0.0) > d;
105+
//~^ absurd_extreme_comparisons
106+
107+
let zero_secs: u64 = 0;
108+
let zero_nanos: u32 = 0;
109+
d < Duration::new(zero_secs, zero_nanos);
110+
//~^ absurd_extreme_comparisons
111+
Duration::new(zero_secs, zero_nanos) > d;
112+
//~^ absurd_extreme_comparisons
113+
114+
d > Duration::from_secs(0); // OK
115+
Duration::from_secs(0) < d; // OK
116+
d == Duration::from_secs(0); // OK
117+
118+
let d = Duration::new(5, 0);
119+
d < one_second_plus(0); // OK
120+
one_second_plus(0) > d; // OK
121+
122+
let n = 0;
123+
d < one_second_plus(n); // OK
124+
125+
d < Duration::from_secs(0);
126+
//~^ absurd_extreme_comparisons
127+
Duration::from_secs(0) > d;
128+
//~^ absurd_extreme_comparisons
129+
130+
131+
let d_zero = Duration::from_secs(0);
132+
let d_one = one_second_plus(0);
133+
d_zero < d_one; // OK
134+
d_one > d_zero; // OK
135+
d_zero < d_zero; // OK
73136
}
74137

75138
use std::cmp::{Ordering, PartialEq, PartialOrd};
@@ -96,3 +159,7 @@ pub fn bar(len: u64) -> bool {
96159
// This is OK as we are casting from target sized to fixed size
97160
len >= usize::MAX as u64
98161
}
162+
163+
fn one_second_plus(n: u64) -> Duration {
164+
Duration::from_secs(n + 1)
165+
}

0 commit comments

Comments
 (0)