Skip to content

Commit 00e59cd

Browse files
committed
Auto merge of #9264 - guerinoni:instant-elapsed, r=Alexendoo
Add `elapsed_instant` lint Closes #8603 - \[x] Followed [lint naming conventions][lint_naming] - \[x] Added passing UI tests (including committed `.stderr` file) - \[x] `cargo test` passes locally - \[x] Executed `cargo dev update_lints` - \[x] Added lint documentation - \[x] Run `cargo dev fmt` --- changelog: [`elapsed_instant`]: Add lint
2 parents 4914833 + 0696624 commit 00e59cd

9 files changed

+146
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3686,6 +3686,7 @@ Released 2018-09-13
36863686
[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
36873687
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
36883688
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
3689+
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
36893690
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
36903691
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
36913692
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ store.register_lints(&[
244244
manual_assert::MANUAL_ASSERT,
245245
manual_async_fn::MANUAL_ASYNC_FN,
246246
manual_bits::MANUAL_BITS,
247+
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
247248
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
248249
manual_ok_or::MANUAL_OK_OR,
249250
manual_rem_euclid::MANUAL_REM_EUCLID,

clippy_lints/src/lib.register_pedantic.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
4949
LintId::of(loops::EXPLICIT_ITER_LOOP),
5050
LintId::of(macro_use::MACRO_USE_IMPORTS),
5151
LintId::of(manual_assert::MANUAL_ASSERT),
52+
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
5253
LintId::of(manual_ok_or::MANUAL_OK_OR),
5354
LintId::of(matches::MATCH_BOOL),
5455
LintId::of(matches::MATCH_ON_VEC_ITEMS),

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ mod main_recursion;
273273
mod manual_assert;
274274
mod manual_async_fn;
275275
mod manual_bits;
276+
mod manual_instant_elapsed;
276277
mod manual_non_exhaustive;
277278
mod manual_ok_or;
278279
mod manual_rem_euclid;
@@ -929,6 +930,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
929930
store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
930931
store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
931932
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
933+
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
932934
// add lints here, do not remove this comment, it's used in `new_lint`
933935
}
934936

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use rustc_errors::Applicability;
3+
use rustc_hir::{BinOpKind, Expr, ExprKind};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
use rustc_span::source_map::Spanned;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Lints subtraction between `Instant::now()` and another `Instant`.
11+
///
12+
/// ### Why is this bad?
13+
/// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
14+
/// as `Instant` subtraction saturates.
15+
///
16+
/// `prev_instant.elapsed()` also more clearly signals intention.
17+
///
18+
/// ### Example
19+
/// ```rust
20+
/// use std::time::Instant;
21+
/// let prev_instant = Instant::now();
22+
/// let duration = Instant::now() - prev_instant;
23+
/// ```
24+
/// Use instead:
25+
/// ```rust
26+
/// use std::time::Instant;
27+
/// let prev_instant = Instant::now();
28+
/// let duration = prev_instant.elapsed();
29+
/// ```
30+
#[clippy::version = "1.64.0"]
31+
pub MANUAL_INSTANT_ELAPSED,
32+
pedantic,
33+
"subtraction between `Instant::now()` and previous `Instant`"
34+
}
35+
36+
declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]);
37+
38+
impl LateLintPass<'_> for ManualInstantElapsed {
39+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
40+
if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
41+
&& check_instant_now_call(cx, lhs)
42+
&& let ty_resolved = cx.typeck_results().expr_ty(rhs)
43+
&& let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
44+
&& clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
45+
&& let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
46+
{
47+
span_lint_and_sugg(
48+
cx,
49+
MANUAL_INSTANT_ELAPSED,
50+
expr.span,
51+
"manual implementation of `Instant::elapsed`",
52+
"try",
53+
format!("{}.elapsed()", sugg.maybe_par()),
54+
Applicability::MachineApplicable,
55+
);
56+
}
57+
}
58+
}
59+
60+
fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
61+
if let ExprKind::Call(fn_expr, []) = expr_block.kind
62+
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
63+
&& clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
64+
{
65+
true
66+
} else {
67+
false
68+
}
69+
}

clippy_utils/src/paths.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,5 @@ pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
194194
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
195195
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
196196
pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
197+
pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
198+
pub const INSTANT: [&str; 3] = ["std", "time", "Instant"];

tests/ui/manual_instant_elapsed.fixed

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// run-rustfix
2+
#![warn(clippy::manual_instant_elapsed)]
3+
#![allow(clippy::unnecessary_operation)]
4+
#![allow(unused_variables)]
5+
#![allow(unused_must_use)]
6+
7+
use std::time::Instant;
8+
9+
fn main() {
10+
let prev_instant = Instant::now();
11+
12+
{
13+
// don't influence
14+
let another_instant = Instant::now();
15+
}
16+
17+
let duration = prev_instant.elapsed();
18+
19+
// don't catch
20+
let duration = prev_instant.elapsed();
21+
22+
Instant::now() - duration;
23+
24+
let ref_to_instant = &Instant::now();
25+
26+
(*ref_to_instant).elapsed(); // to ensure parens are added correctly
27+
}

tests/ui/manual_instant_elapsed.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// run-rustfix
2+
#![warn(clippy::manual_instant_elapsed)]
3+
#![allow(clippy::unnecessary_operation)]
4+
#![allow(unused_variables)]
5+
#![allow(unused_must_use)]
6+
7+
use std::time::Instant;
8+
9+
fn main() {
10+
let prev_instant = Instant::now();
11+
12+
{
13+
// don't influence
14+
let another_instant = Instant::now();
15+
}
16+
17+
let duration = Instant::now() - prev_instant;
18+
19+
// don't catch
20+
let duration = prev_instant.elapsed();
21+
22+
Instant::now() - duration;
23+
24+
let ref_to_instant = &Instant::now();
25+
26+
Instant::now() - *ref_to_instant; // to ensure parens are added correctly
27+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: manual implementation of `Instant::elapsed`
2+
--> $DIR/manual_instant_elapsed.rs:17:20
3+
|
4+
LL | let duration = Instant::now() - prev_instant;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `prev_instant.elapsed()`
6+
|
7+
= note: `-D clippy::manual-instant-elapsed` implied by `-D warnings`
8+
9+
error: manual implementation of `Instant::elapsed`
10+
--> $DIR/manual_instant_elapsed.rs:26:5
11+
|
12+
LL | Instant::now() - *ref_to_instant; // to ensure parens are added correctly
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*ref_to_instant).elapsed()`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)