]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/manual_instant_elapsed.rs
Simplify `clippy` fix.
[rust.git] / clippy_lints / src / manual_instant_elapsed.rs
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 }