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;
10 /// Lints subtraction between `Instant::now()` and another `Instant`.
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.
16 /// `prev_instant.elapsed()` also more clearly signals intention.
20 /// use std::time::Instant;
21 /// let prev_instant = Instant::now();
22 /// let duration = Instant::now() - prev_instant;
26 /// use std::time::Instant;
27 /// let prev_instant = Instant::now();
28 /// let duration = prev_instant.elapsed();
30 #[clippy::version = "1.64.0"]
31 pub MANUAL_INSTANT_ELAPSED,
33 "subtraction between `Instant::now()` and previous `Instant`"
36 declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]);
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)
49 MANUAL_INSTANT_ELAPSED,
51 "manual implementation of `Instant::elapsed`",
53 format!("{}.elapsed()", sugg.maybe_par()),
54 Applicability::MachineApplicable,
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)