Skip to content

Commit 1391141

Browse files
committed
refactor: lint man. instant elapsed and unch. dur. subtr. in single pass
1 parent 9a1813f commit 1391141

File tree

7 files changed

+76
-88
lines changed

7 files changed

+76
-88
lines changed

clippy_lints/src/unchecked_duration_subtraction.rs renamed to clippy_lints/src/instant_subtraction.rs

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,48 @@
1-
use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty};
1+
use clippy_utils::{
2+
diagnostics::{self, span_lint_and_sugg},
3+
meets_msrv, msrvs, source, ty,
4+
};
25
use rustc_errors::Applicability;
3-
use rustc_hir::*;
6+
use rustc_hir::{BinOpKind, Expr, ExprKind};
47
use rustc_lint::{LateContext, LateLintPass};
58
use rustc_semver::RustcVersion;
69
use rustc_session::{declare_tool_lint, impl_lint_pass};
7-
use rustc_span::symbol::sym;
10+
use rustc_span::{source_map::Spanned, sym};
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// Lints subtraction between `Instant::now()` and another `Instant`.
15+
///
16+
/// ### Why is this bad?
17+
/// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
18+
/// as `Instant` subtraction saturates.
19+
///
20+
/// `prev_instant.elapsed()` also more clearly signals intention.
21+
///
22+
/// ### Example
23+
/// ```rust
24+
/// use std::time::Instant;
25+
/// let prev_instant = Instant::now();
26+
/// let duration = Instant::now() - prev_instant;
27+
/// ```
28+
/// Use instead:
29+
/// ```rust
30+
/// use std::time::Instant;
31+
/// let prev_instant = Instant::now();
32+
/// let duration = prev_instant.elapsed();
33+
/// ```
34+
#[clippy::version = "1.64.0"]
35+
pub MANUAL_INSTANT_ELAPSED,
36+
pedantic,
37+
"subtraction between `Instant::now()` and previous `Instant`"
38+
}
839

940
declare_clippy_lint! {
1041
/// ### What it does
1142
/// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
1243
///
1344
/// ### Why is this bad?
14-
/// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
45+
/// Unchecked subtraction could cause underflow on certain platforms, leading to
1546
/// unintentional panics.
1647
///
1748
/// ### Example
@@ -32,21 +63,39 @@ declare_clippy_lint! {
3263
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
3364
}
3465

35-
pub struct UncheckedDurationSubtraction {
66+
pub struct InstantSubtraction {
3667
msrv: Option<RustcVersion>,
3768
}
3869

39-
impl UncheckedDurationSubtraction {
70+
impl InstantSubtraction {
4071
#[must_use]
4172
pub fn new(msrv: Option<RustcVersion>) -> Self {
4273
Self { msrv }
4374
}
4475
}
4576

46-
impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]);
77+
impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]);
78+
79+
impl LateLintPass<'_> for InstantSubtraction {
80+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
81+
if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
82+
&& check_instant_now_call(cx, lhs)
83+
&& let ty_resolved = cx.typeck_results().expr_ty(rhs)
84+
&& let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
85+
&& clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
86+
&& let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
87+
{
88+
span_lint_and_sugg(
89+
cx,
90+
MANUAL_INSTANT_ELAPSED,
91+
expr.span,
92+
"manual implementation of `Instant::elapsed`",
93+
"try",
94+
format!("{}.elapsed()", sugg.maybe_par()),
95+
Applicability::MachineApplicable,
96+
);
97+
}
4798

48-
impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
49-
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
5099
if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) {
51100
return;
52101
}
@@ -67,6 +116,17 @@ impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
67116
}
68117
}
69118

119+
fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
120+
if let ExprKind::Call(fn_expr, []) = expr_block.kind
121+
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
122+
&& clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
123+
{
124+
true
125+
} else {
126+
false
127+
}
128+
}
129+
70130
fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
71131
let expr_ty = cx.typeck_results().expr_ty(expr);
72132

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
9191
LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
9292
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
9393
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
94+
LintId::of(instant_subtraction::UNCHECKED_DURATION_SUBTRACTION),
9495
LintId::of(int_plus_one::INT_PLUS_ONE),
9596
LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
9697
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
@@ -334,7 +335,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
334335
LintId::of(types::REDUNDANT_ALLOCATION),
335336
LintId::of(types::TYPE_COMPLEXITY),
336337
LintId::of(types::VEC_BOX),
337-
LintId::of(unchecked_duration_subtraction::UNCHECKED_DURATION_SUBTRACTION),
338338
LintId::of(unicode::INVISIBLE_CHARACTERS),
339339
LintId::of(uninit_vec::UNINIT_VEC),
340340
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),

clippy_lints/src/lib.register_lints.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ store.register_lints(&[
196196
inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
197197
init_numbered_fields::INIT_NUMBERED_FIELDS,
198198
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
199+
instant_subtraction::MANUAL_INSTANT_ELAPSED,
200+
instant_subtraction::UNCHECKED_DURATION_SUBTRACTION,
199201
int_plus_one::INT_PLUS_ONE,
200202
invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
201203
invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
@@ -245,7 +247,6 @@ store.register_lints(&[
245247
manual_assert::MANUAL_ASSERT,
246248
manual_async_fn::MANUAL_ASYNC_FN,
247249
manual_bits::MANUAL_BITS,
248-
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
249250
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
250251
manual_rem_euclid::MANUAL_REM_EUCLID,
251252
manual_retain::MANUAL_RETAIN,
@@ -566,7 +567,6 @@ store.register_lints(&[
566567
types::REDUNDANT_ALLOCATION,
567568
types::TYPE_COMPLEXITY,
568569
types::VEC_BOX,
569-
unchecked_duration_subtraction::UNCHECKED_DURATION_SUBTRACTION,
570570
undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
571571
unicode::INVISIBLE_CHARACTERS,
572572
unicode::NON_ASCII_LITERAL,

clippy_lints/src/lib.register_pedantic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
3737
LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
3838
LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
3939
LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
40+
LintId::of(instant_subtraction::MANUAL_INSTANT_ELAPSED),
4041
LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
4142
LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
4243
LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
@@ -48,7 +49,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
4849
LintId::of(loops::EXPLICIT_ITER_LOOP),
4950
LintId::of(macro_use::MACRO_USE_IMPORTS),
5051
LintId::of(manual_assert::MANUAL_ASSERT),
51-
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
5252
LintId::of(manual_string_new::MANUAL_STRING_NEW),
5353
LintId::of(matches::MATCH_BOOL),
5454
LintId::of(matches::MATCH_ON_VEC_ITEMS),

clippy_lints/src/lib.register_suspicious.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
2020
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
2121
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
2222
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
23+
LintId::of(instant_subtraction::UNCHECKED_DURATION_SUBTRACTION),
2324
LintId::of(loops::EMPTY_LOOP),
2425
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
2526
LintId::of(loops::MUT_RANGE_BOUND),
@@ -35,5 +36,4 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
3536
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
3637
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
3738
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
38-
LintId::of(unchecked_duration_subtraction::UNCHECKED_DURATION_SUBTRACTION),
3939
])

clippy_lints/src/lib.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ mod inherent_impl;
248248
mod inherent_to_string;
249249
mod init_numbered_fields;
250250
mod inline_fn_without_body;
251+
mod instant_subtraction;
251252
mod int_plus_one;
252253
mod invalid_upcast_comparisons;
253254
mod invalid_utf8_in_unchecked;
@@ -268,7 +269,6 @@ mod main_recursion;
268269
mod manual_assert;
269270
mod manual_async_fn;
270271
mod manual_bits;
271-
mod manual_instant_elapsed;
272272
mod manual_non_exhaustive;
273273
mod manual_rem_euclid;
274274
mod manual_retain;
@@ -370,7 +370,6 @@ mod trailing_empty_array;
370370
mod trait_bounds;
371371
mod transmute;
372372
mod types;
373-
mod unchecked_duration_subtraction;
374373
mod undocumented_unsafe_blocks;
375374
mod unicode;
376375
mod uninit_vec;
@@ -898,13 +897,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
898897
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
899898
store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
900899
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
901-
store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed));
900+
store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
902901
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
903902
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
904903
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
905904
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
906-
store
907-
.register_late_pass(move |_| Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv)));
908905
store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
909906
store.register_late_pass(|_| Box::new(box_default::BoxDefault));
910907
// add lints here, do not remove this comment, it's used in `new_lint`

clippy_lints/src/manual_instant_elapsed.rs

Lines changed: 0 additions & 69 deletions
This file was deleted.

0 commit comments

Comments
 (0)