1 use clippy_utils
::diagnostics
::{self, span_lint_and_sugg}
;
2 use clippy_utils
::msrvs
::{self, Msrv}
;
3 use clippy_utils
::source
;
4 use clippy_utils
::sugg
::Sugg
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{BinOpKind, Expr, ExprKind}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
9 use rustc_session
::{declare_tool_lint, impl_lint_pass}
;
10 use rustc_span
::{source_map::Spanned, sym}
;
12 declare_clippy_lint
! {
14 /// Lints subtraction between `Instant::now()` and another `Instant`.
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.
20 /// `prev_instant.elapsed()` also more clearly signals intention.
24 /// use std::time::Instant;
25 /// let prev_instant = Instant::now();
26 /// let duration = Instant::now() - prev_instant;
30 /// use std::time::Instant;
31 /// let prev_instant = Instant::now();
32 /// let duration = prev_instant.elapsed();
34 #[clippy::version = "1.65.0"]
35 pub MANUAL_INSTANT_ELAPSED
,
37 "subtraction between `Instant::now()` and previous `Instant`"
40 declare_clippy_lint
! {
42 /// Lints subtraction between an [`Instant`] and a [`Duration`].
44 /// ### Why is this bad?
45 /// Unchecked subtraction could cause underflow on certain platforms, leading to
46 /// unintentional panics.
50 /// # use std::time::{Instant, Duration};
51 /// let time_passed = Instant::now() - Duration::from_secs(5);
56 /// # use std::time::{Instant, Duration};
57 /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
60 /// [`Duration`]: std::time::Duration
61 /// [`Instant::now()`]: std::time::Instant::now;
62 #[clippy::version = "1.65.0"]
63 pub UNCHECKED_DURATION_SUBTRACTION
,
65 "finds unchecked subtraction of a 'Duration' from an 'Instant'"
68 pub struct InstantSubtraction
{
72 impl InstantSubtraction
{
74 pub fn new(msrv
: Msrv
) -> Self {
79 impl_lint_pass
!(InstantSubtraction
=> [MANUAL_INSTANT_ELAPSED
, UNCHECKED_DURATION_SUBTRACTION
]);
81 impl LateLintPass
<'_
> for InstantSubtraction
{
82 fn check_expr(&mut self, cx
: &LateContext
<'_
>, expr
: &'_ Expr
<'_
>) {
83 if let ExprKind
::Binary(
85 node
: BinOpKind
::Sub
, ..
92 if is_instant_now_call(cx
, lhs
);
94 if is_an_instant(cx
, rhs
);
95 if let Some(sugg
) = Sugg
::hir_opt(cx
, rhs
);
98 print_manual_instant_elapsed_sugg(cx
, expr
, sugg
)
101 if !expr
.span
.from_expansion();
102 if self.msrv
.meets(msrvs
::TRY_FROM
);
104 if is_an_instant(cx
, lhs
);
105 if is_a_duration(cx
, rhs
);
108 print_unchecked_duration_subtraction_sugg(cx
, lhs
, rhs
, expr
)
116 extract_msrv_attr
!(LateContext
);
119 fn is_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
)
130 fn is_an_instant(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
131 let expr_ty
= cx
.typeck_results().expr_ty(expr
);
133 match expr_ty
.kind() {
134 rustc_middle
::ty
::Adt(def
, _
) => clippy_utils
::match_def_path(cx
, def
.did(), &clippy_utils
::paths
::INSTANT
),
139 fn is_a_duration(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
140 let expr_ty
= cx
.typeck_results().expr_ty(expr
);
141 ty
::is_type_diagnostic_item(cx
, expr_ty
, sym
::Duration
)
144 fn print_manual_instant_elapsed_sugg(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>, sugg
: Sugg
<'_
>) {
147 MANUAL_INSTANT_ELAPSED
,
149 "manual implementation of `Instant::elapsed`",
151 format
!("{}.elapsed()", sugg
.maybe_par()),
152 Applicability
::MachineApplicable
,
156 fn print_unchecked_duration_subtraction_sugg(
157 cx
: &LateContext
<'_
>,
158 left_expr
: &Expr
<'_
>,
159 right_expr
: &Expr
<'_
>,
162 let mut applicability
= Applicability
::MachineApplicable
;
165 source
::snippet_with_applicability(cx
, left_expr
.span
, "std::time::Instant::now()", &mut applicability
);
166 let right_expr
= source
::snippet_with_applicability(
169 "std::time::Duration::from_secs(1)",
173 diagnostics
::span_lint_and_sugg(
175 UNCHECKED_DURATION_SUBTRACTION
,
177 "unchecked subtraction of a 'Duration' from an 'Instant'",
179 format
!("{left_expr}.checked_sub({right_expr}).unwrap()"),