From 708c2d95c14829e0acbe7f40d9bea39e7bb03450 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Sat, 1 Oct 2022 15:58:10 +0200
Subject: [PATCH 01/16] feat: add and implement unchecked_duration_subtraction
 lint

---
 CHANGELOG.md                                  |   1 +
 clippy_lints/src/lib.rs                       |   2 +
 .../src/unchecked_duration_subtraction.rs     | 107 ++++++++++++++++++
 clippy_lints/src/utils/conf.rs                |   2 +-
 4 files changed, 111 insertions(+), 1 deletion(-)
 create mode 100644 clippy_lints/src/unchecked_duration_subtraction.rs

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bd67617f5b..eb4ca980143 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4428,6 +4428,7 @@ Released 2018-09-13
 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
+[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction
 [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
 [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 [`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index a4bacb78034..a5407b65b88 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -279,6 +279,7 @@ mod trailing_empty_array;
 mod trait_bounds;
 mod transmute;
 mod types;
+mod unchecked_duration_subtraction;
 mod undocumented_unsafe_blocks;
 mod unicode;
 mod uninit_vec;
@@ -921,6 +922,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
     store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
     store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
+    store.register_late_pass(move || Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/unchecked_duration_subtraction.rs
new file mode 100644
index 00000000000..fb08067de8d
--- /dev/null
+++ b/clippy_lints/src/unchecked_duration_subtraction.rs
@@ -0,0 +1,107 @@
+use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty};
+use rustc_errors::Applicability;
+use rustc_hir::*;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
+    ///
+    /// ### Why is this bad?
+    /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
+    /// unintentional panics.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let time_passed = Instant::now() - Duration::from_secs(5);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
+    /// ```
+    ///
+    /// [`Duration`]: std::time::Duration
+    /// [`Instant::now()`]: std::time::Instant::now;
+    #[clippy::version = "1.65.0"]
+    pub UNCHECKED_DURATION_SUBTRACTION,
+    suspicious,
+    "finds unchecked subtraction of a 'Duration' from an 'Instant'"
+}
+
+pub struct UncheckedDurationSubtraction {
+    msrv: Option<RustcVersion>,
+}
+
+impl UncheckedDurationSubtraction {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]);
+
+impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) {
+            return;
+        }
+
+        if_chain! {
+            if let ExprKind::Binary(op, lhs, rhs) = expr.kind;
+
+            if let BinOpKind::Sub = op.node;
+
+            // get types of left and right side
+            if is_an_instant(cx, lhs);
+            if is_a_duration(cx, rhs);
+
+            then {
+                print_lint_and_sugg(cx, lhs, rhs, expr)
+            }
+        }
+    }
+}
+
+fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr);
+
+    match expr_ty.kind() {
+        rustc_middle::ty::Adt(def, _) => {
+            clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT)
+        },
+        _ => false,
+    }
+}
+
+fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr);
+    ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration)
+}
+
+fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) {
+    let mut applicability = Applicability::MachineApplicable;
+
+    let left_expr =
+        source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
+    let right_expr = source::snippet_with_applicability(
+        cx,
+        right_expr.span,
+        "std::time::Duration::from_secs(1)",
+        &mut applicability,
+    );
+
+    diagnostics::span_lint_and_sugg(
+        cx,
+        UNCHECKED_DURATION_SUBTRACTION,
+        expr.span,
+        "unchecked subtraction of a 'Duration' from an 'Instant'",
+        "try",
+        format!("{}.checked_sub({}).unwrap();", left_expr, right_expr),
+        applicability,
+    );
+}
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 0cf49ca6d42..ab58e9b8b68 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -213,7 +213,7 @@ define_Conf! {
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
-    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE.
+    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION.
     ///
     /// The minimum rust version that the project supports
     (msrv: Option<String> = None),

From 3b4e42b91b0df11c43983c6fe67bb971f472aa3f Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Sat, 1 Oct 2022 16:02:57 +0200
Subject: [PATCH 02/16] test: add tests for 'unchecked_duration_subtraction'
 lint

---
 tests/ui/unchecked_duration_subtraction.rs    | 16 ++++++++++
 .../ui/unchecked_duration_subtraction.stderr  | 32 +++++++++++++++++++
 2 files changed, 48 insertions(+)
 create mode 100644 tests/ui/unchecked_duration_subtraction.rs
 create mode 100644 tests/ui/unchecked_duration_subtraction.stderr

diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs
new file mode 100644
index 00000000000..fff1d13720d
--- /dev/null
+++ b/tests/ui/unchecked_duration_subtraction.rs
@@ -0,0 +1,16 @@
+#![warn(clippy::unchecked_duration_subtraction)]
+
+use std::time::{Duration, Instant};
+
+fn main() {
+    let _first = Instant::now();
+    let second = Duration::from_secs(3);
+
+    let _ = _first - second;
+
+    let _ = Instant::now() - Duration::from_secs(5);
+
+    let _ = _first - Duration::from_secs(5);
+
+    let _ = Instant::now() - second;
+}
diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr
new file mode 100644
index 00000000000..827b18a5a09
--- /dev/null
+++ b/tests/ui/unchecked_duration_subtraction.stderr
@@ -0,0 +1,32 @@
+[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
+error: unchecked subtraction of a 'Duration' from an 'Instant'
+  --> $DIR/unchecked_duration_subtraction.rs:9:13
+   |
+LL |     let _ = _first - second;
+   |             ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();`
+   |
+   = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings`
+
+[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
+error: unchecked subtraction of a 'Duration' from an 'Instant'
+  --> $DIR/unchecked_duration_subtraction.rs:11:13
+   |
+LL |     let _ = Instant::now() - Duration::from_secs(5);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();`
+
+[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
+error: unchecked subtraction of a 'Duration' from an 'Instant'
+  --> $DIR/unchecked_duration_subtraction.rs:13:13
+   |
+LL |     let _ = _first - Duration::from_secs(5);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();`
+
+[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
+error: unchecked subtraction of a 'Duration' from an 'Instant'
+  --> $DIR/unchecked_duration_subtraction.rs:15:13
+   |
+LL |     let _ = Instant::now() - second;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();`
+
+error: aborting due to 4 previous errors
+

From b280dbe5f73286a3f2a02e93c6c3e42181ec8aca Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Sat, 1 Oct 2022 16:50:45 +0200
Subject: [PATCH 03/16] docs: add docs for `unchecked_duration_subtraction`
 lint

---
 src/docs/unchecked_duration_subtraction.txt | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 src/docs/unchecked_duration_subtraction.txt

diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt
new file mode 100644
index 00000000000..6b9c9308c87
--- /dev/null
+++ b/src/docs/unchecked_duration_subtraction.txt
@@ -0,0 +1,19 @@
+### What it does
+Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
+
+### Why is this bad?
+Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
+unintentional panics.
+
+### Example
+```
+let time_passed = Instant::now() - Duration::from_secs(5);
+```
+
+Use instead:
+```
+let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
+```
+
+[`Duration`]: std::time::Duration
+[`Instant::now()`]: std::time::Instant::now;
\ No newline at end of file

From b485832b16a388dbcfd3c42030356959e7662a3d Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Sat, 1 Oct 2022 16:50:56 +0200
Subject: [PATCH 04/16] style: remove `dbg!` call

---
 clippy_lints/src/unchecked_duration_subtraction.rs | 4 +---
 tests/ui/unchecked_duration_subtraction.stderr     | 4 ----
 2 files changed, 1 insertion(+), 7 deletions(-)

diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/unchecked_duration_subtraction.rs
index fb08067de8d..ae796000662 100644
--- a/clippy_lints/src/unchecked_duration_subtraction.rs
+++ b/clippy_lints/src/unchecked_duration_subtraction.rs
@@ -71,9 +71,7 @@ fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let expr_ty = cx.typeck_results().expr_ty(expr);
 
     match expr_ty.kind() {
-        rustc_middle::ty::Adt(def, _) => {
-            clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT)
-        },
+        rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT),
         _ => false,
     }
 }
diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr
index 827b18a5a09..6b297a01c7b 100644
--- a/tests/ui/unchecked_duration_subtraction.stderr
+++ b/tests/ui/unchecked_duration_subtraction.stderr
@@ -1,4 +1,3 @@
-[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
 error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:9:13
    |
@@ -7,21 +6,18 @@ LL |     let _ = _first - second;
    |
    = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings`
 
-[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
 error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:11:13
    |
 LL |     let _ = Instant::now() - Duration::from_secs(5);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();`
 
-[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
 error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:13:13
    |
 LL |     let _ = _first - Duration::from_secs(5);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();`
 
-[clippy_lints/src/unchecked_duration_subtraction.rs:75] def = std::time::Instant
 error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:15:13
    |

From 2f2eb2e4baace68868af2f4ac3ec7aed073139ad Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 7 Nov 2022 20:58:49 +0100
Subject: [PATCH 05/16] refactor: lint man. instant elapsed and unch. dur.
 subtr. in single pass

---
 ..._subtraction.rs => instant_subtraction.rs} | 78 ++++++++++++++++---
 clippy_lints/src/lib.rs                       |  5 +-
 clippy_lints/src/manual_instant_elapsed.rs    | 69 ----------------
 3 files changed, 71 insertions(+), 81 deletions(-)
 rename clippy_lints/src/{unchecked_duration_subtraction.rs => instant_subtraction.rs} (52%)
 delete mode 100644 clippy_lints/src/manual_instant_elapsed.rs

diff --git a/clippy_lints/src/unchecked_duration_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
similarity index 52%
rename from clippy_lints/src/unchecked_duration_subtraction.rs
rename to clippy_lints/src/instant_subtraction.rs
index ae796000662..8cc5643c9a7 100644
--- a/clippy_lints/src/unchecked_duration_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -1,17 +1,48 @@
-use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty};
+use clippy_utils::{
+    diagnostics::{self, span_lint_and_sugg},
+    meets_msrv, msrvs, source, ty,
+};
 use rustc_errors::Applicability;
-use rustc_hir::*;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::sym;
+use rustc_span::{source_map::Spanned, sym};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Lints subtraction between `Instant::now()` and another `Instant`.
+    ///
+    /// ### Why is this bad?
+    /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
+    /// as `Instant` subtraction saturates.
+    ///
+    /// `prev_instant.elapsed()` also more clearly signals intention.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::time::Instant;
+    /// let prev_instant = Instant::now();
+    /// let duration = Instant::now() - prev_instant;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::time::Instant;
+    /// let prev_instant = Instant::now();
+    /// let duration = prev_instant.elapsed();
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub MANUAL_INSTANT_ELAPSED,
+    pedantic,
+    "subtraction between `Instant::now()` and previous `Instant`"
+}
 
 declare_clippy_lint! {
     /// ### What it does
     /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
     ///
     /// ### Why is this bad?
-    /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
+    /// Unchecked subtraction could cause underflow on certain platforms, leading to
     /// unintentional panics.
     ///
     /// ### Example
@@ -32,21 +63,39 @@ declare_clippy_lint! {
     "finds unchecked subtraction of a 'Duration' from an 'Instant'"
 }
 
-pub struct UncheckedDurationSubtraction {
+pub struct InstantSubtraction {
     msrv: Option<RustcVersion>,
 }
 
-impl UncheckedDurationSubtraction {
+impl InstantSubtraction {
     #[must_use]
     pub fn new(msrv: Option<RustcVersion>) -> Self {
         Self { msrv }
     }
 }
 
-impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]);
+impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]);
+
+impl LateLintPass<'_> for InstantSubtraction {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
+        if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
+            && check_instant_now_call(cx, lhs)
+            && let ty_resolved = cx.typeck_results().expr_ty(rhs)
+            && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
+            && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
+            && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
+        {
+            span_lint_and_sugg(
+                cx,
+                MANUAL_INSTANT_ELAPSED,
+                expr.span,
+                "manual implementation of `Instant::elapsed`",
+                "try",
+                format!("{}.elapsed()", sugg.maybe_par()),
+                Applicability::MachineApplicable,
+            );
+        }
 
-impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) {
             return;
         }
@@ -67,6 +116,17 @@ impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
     }
 }
 
+fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
+    if let ExprKind::Call(fn_expr, []) = expr_block.kind
+        && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
+        && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
+    {
+        true
+    } else {
+        false
+    }
+}
+
 fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let expr_ty = cx.typeck_results().expr_ty(expr);
 
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index a5407b65b88..ce0e7ebfbf1 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -151,6 +151,7 @@ mod inherent_impl;
 mod inherent_to_string;
 mod init_numbered_fields;
 mod inline_fn_without_body;
+mod instant_subtraction;
 mod int_plus_one;
 mod invalid_upcast_comparisons;
 mod invalid_utf8_in_unchecked;
@@ -172,7 +173,6 @@ mod manual_assert;
 mod manual_async_fn;
 mod manual_bits;
 mod manual_clamp;
-mod manual_instant_elapsed;
 mod manual_is_ascii_check;
 mod manual_let_else;
 mod manual_non_exhaustive;
@@ -279,7 +279,6 @@ mod trailing_empty_array;
 mod trait_bounds;
 mod transmute;
 mod types;
-mod unchecked_duration_subtraction;
 mod undocumented_unsafe_blocks;
 mod unicode;
 mod uninit_vec;
@@ -908,7 +907,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
     store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
     store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
-    store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed));
+    store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
     store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
     store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv)));
     store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
diff --git a/clippy_lints/src/manual_instant_elapsed.rs b/clippy_lints/src/manual_instant_elapsed.rs
deleted file mode 100644
index 1e60aa02d3c..00000000000
--- a/clippy_lints/src/manual_instant_elapsed.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Lints subtraction between `Instant::now()` and another `Instant`.
-    ///
-    /// ### Why is this bad?
-    /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
-    /// as `Instant` subtraction saturates.
-    ///
-    /// `prev_instant.elapsed()` also more clearly signals intention.
-    ///
-    /// ### Example
-    /// ```rust
-    /// use std::time::Instant;
-    /// let prev_instant = Instant::now();
-    /// let duration = Instant::now() - prev_instant;
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// use std::time::Instant;
-    /// let prev_instant = Instant::now();
-    /// let duration = prev_instant.elapsed();
-    /// ```
-    #[clippy::version = "1.65.0"]
-    pub MANUAL_INSTANT_ELAPSED,
-    pedantic,
-    "subtraction between `Instant::now()` and previous `Instant`"
-}
-
-declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]);
-
-impl LateLintPass<'_> for ManualInstantElapsed {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
-        if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
-            && check_instant_now_call(cx, lhs)
-            && let ty_resolved = cx.typeck_results().expr_ty(rhs)
-            && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
-            && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
-            && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
-        {
-            span_lint_and_sugg(
-                cx,
-                MANUAL_INSTANT_ELAPSED,
-                expr.span,
-                "manual implementation of `Instant::elapsed`",
-                "try",
-                format!("{}.elapsed()", sugg.maybe_par()),
-                Applicability::MachineApplicable,
-            );
-        }
-    }
-}
-
-fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
-    if let ExprKind::Call(fn_expr, []) = expr_block.kind
-        && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
-        && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
-    {
-        true
-    } else {
-        false
-    }
-}

From a566eb37659ecbdcfe1403f8ccab753714dbec60 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 7 Nov 2022 20:59:55 +0100
Subject: [PATCH 06/16] docs: update docs for unchecked duration subtr. lint

---
 src/docs/unchecked_duration_subtraction.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt
index 6b9c9308c87..15a4c02c606 100644
--- a/src/docs/unchecked_duration_subtraction.txt
+++ b/src/docs/unchecked_duration_subtraction.txt
@@ -2,7 +2,7 @@
 Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
 
 ### Why is this bad?
-Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
+Unchecked subtraction could cause underflow on certain platforms, leading to
 unintentional panics.
 
 ### Example

From 80e3553f290cd252646c61086e9461fc7618b694 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 7 Nov 2022 21:34:24 +0100
Subject: [PATCH 07/16] refactor: improve code re-use in InstantSubtraction
 lint pass

---
 clippy_lints/src/instant_subtraction.rs | 77 +++++++++++++++----------
 1 file changed, 46 insertions(+), 31 deletions(-)

diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index 8cc5643c9a7..3166c62dad4 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -1,6 +1,8 @@
 use clippy_utils::{
     diagnostics::{self, span_lint_and_sugg},
-    meets_msrv, msrvs, source, ty,
+    meets_msrv, msrvs, source,
+    sugg::Sugg,
+    ty,
 };
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
@@ -78,45 +80,41 @@ impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATIO
 
 impl LateLintPass<'_> for InstantSubtraction {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
-        if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
-            && check_instant_now_call(cx, lhs)
-            && let ty_resolved = cx.typeck_results().expr_ty(rhs)
-            && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
-            && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
-            && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Sub, ..
+            },
+            lhs,
+            rhs,
+        ) = expr.kind
         {
-            span_lint_and_sugg(
-                cx,
-                MANUAL_INSTANT_ELAPSED,
-                expr.span,
-                "manual implementation of `Instant::elapsed`",
-                "try",
-                format!("{}.elapsed()", sugg.maybe_par()),
-                Applicability::MachineApplicable,
-            );
-        }
+            if_chain! {
+                if is_instant_now_call(cx, lhs);
 
-        if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) {
-            return;
-        }
+                if is_an_instant(cx, rhs);
+                if let Some(sugg) = Sugg::hir_opt(cx, rhs);
 
-        if_chain! {
-            if let ExprKind::Binary(op, lhs, rhs) = expr.kind;
+                then {
+                    print_manual_instant_elapsed_sugg(cx, expr, sugg)
+                } else {
+                    if_chain! {
+                        if !expr.span.from_expansion();
+                        if meets_msrv(self.msrv, msrvs::TRY_FROM);
 
-            if let BinOpKind::Sub = op.node;
+                        if is_an_instant(cx, lhs);
+                        if is_a_duration(cx, rhs);
 
-            // get types of left and right side
-            if is_an_instant(cx, lhs);
-            if is_a_duration(cx, rhs);
-
-            then {
-                print_lint_and_sugg(cx, lhs, rhs, expr)
+                        then {
+                            print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr)
+                        }
+                    }
+                }
             }
         }
     }
 }
 
-fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
+fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
     if let ExprKind::Call(fn_expr, []) = expr_block.kind
         && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
         && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
@@ -141,7 +139,24 @@ fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration)
 }
 
-fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) {
+fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) {
+    span_lint_and_sugg(
+        cx,
+        MANUAL_INSTANT_ELAPSED,
+        expr.span,
+        "manual implementation of `Instant::elapsed`",
+        "try",
+        format!("{}.elapsed()", sugg.maybe_par()),
+        Applicability::MachineApplicable,
+    );
+}
+
+fn print_unchecked_duration_subtraction_sugg(
+    cx: &LateContext<'_>,
+    left_expr: &Expr<'_>,
+    right_expr: &Expr<'_>,
+    expr: &Expr<'_>,
+) {
     let mut applicability = Applicability::MachineApplicable;
 
     let left_expr =

From 4bd6d0beb0ef8ad72ad8ce6a28eb25a48f9cbc4b Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 7 Nov 2022 21:37:04 +0100
Subject: [PATCH 08/16] test: update tests for manual_instant_elapsed lint

---
 tests/ui/manual_instant_elapsed.fixed  | 1 +
 tests/ui/manual_instant_elapsed.rs     | 1 +
 tests/ui/manual_instant_elapsed.stderr | 4 ++--
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/tests/ui/manual_instant_elapsed.fixed b/tests/ui/manual_instant_elapsed.fixed
index 0fa776b7b2e..85a91543c89 100644
--- a/tests/ui/manual_instant_elapsed.fixed
+++ b/tests/ui/manual_instant_elapsed.fixed
@@ -1,6 +1,7 @@
 // run-rustfix
 #![warn(clippy::manual_instant_elapsed)]
 #![allow(clippy::unnecessary_operation)]
+#![allow(clippy::unchecked_duration_subtraction)]
 #![allow(unused_variables)]
 #![allow(unused_must_use)]
 
diff --git a/tests/ui/manual_instant_elapsed.rs b/tests/ui/manual_instant_elapsed.rs
index 5b11b84535d..c98cb15b916 100644
--- a/tests/ui/manual_instant_elapsed.rs
+++ b/tests/ui/manual_instant_elapsed.rs
@@ -1,6 +1,7 @@
 // run-rustfix
 #![warn(clippy::manual_instant_elapsed)]
 #![allow(clippy::unnecessary_operation)]
+#![allow(clippy::unchecked_duration_subtraction)]
 #![allow(unused_variables)]
 #![allow(unused_must_use)]
 
diff --git a/tests/ui/manual_instant_elapsed.stderr b/tests/ui/manual_instant_elapsed.stderr
index 5537f5642a2..4ce1f689107 100644
--- a/tests/ui/manual_instant_elapsed.stderr
+++ b/tests/ui/manual_instant_elapsed.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of `Instant::elapsed`
-  --> $DIR/manual_instant_elapsed.rs:17:20
+  --> $DIR/manual_instant_elapsed.rs:18:20
    |
 LL |     let duration = Instant::now() - prev_instant;
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `prev_instant.elapsed()`
@@ -7,7 +7,7 @@ LL |     let duration = Instant::now() - prev_instant;
    = note: `-D clippy::manual-instant-elapsed` implied by `-D warnings`
 
 error: manual implementation of `Instant::elapsed`
-  --> $DIR/manual_instant_elapsed.rs:26:5
+  --> $DIR/manual_instant_elapsed.rs:27:5
    |
 LL |     Instant::now() - *ref_to_instant; // to ensure parens are added correctly
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*ref_to_instant).elapsed()`

From 852d80245148cdd23c5516bea015b0f3bf277ed4 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 7 Nov 2022 22:21:32 +0100
Subject: [PATCH 09/16] style: inline variables in `format!`

---
 clippy_lints/src/instant_subtraction.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index 3166c62dad4..c039f1b5c6a 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -174,7 +174,7 @@ fn print_unchecked_duration_subtraction_sugg(
         expr.span,
         "unchecked subtraction of a 'Duration' from an 'Instant'",
         "try",
-        format!("{}.checked_sub({}).unwrap();", left_expr, right_expr),
+        format!("{left_expr}.checked_sub({right_expr}).unwrap();"),
         applicability,
     );
 }

From 62ab4fb9c265b9e25d61c99511cd26de0260f435 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Tue, 8 Nov 2022 10:22:58 +0100
Subject: [PATCH 10/16] fix: add rust-fix annotation to unckd. duration subtr.
 lint test

---
 tests/ui/unchecked_duration_subtraction.fixed  | 17 +++++++++++++++++
 tests/ui/unchecked_duration_subtraction.rs     |  1 +
 tests/ui/unchecked_duration_subtraction.stderr |  8 ++++----
 3 files changed, 22 insertions(+), 4 deletions(-)
 create mode 100644 tests/ui/unchecked_duration_subtraction.fixed

diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed
new file mode 100644
index 00000000000..bcc231d93b1
--- /dev/null
+++ b/tests/ui/unchecked_duration_subtraction.fixed
@@ -0,0 +1,17 @@
+// run-rustfix
+#![warn(clippy::unchecked_duration_subtraction)]
+
+use std::time::{Duration, Instant};
+
+fn main() {
+    let _first = Instant::now();
+    let second = Duration::from_secs(3);
+
+    let _ = _first.checked_sub(second).unwrap();;
+
+    let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();;
+
+    let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();;
+
+    let _ = Instant::now().checked_sub(second).unwrap();;
+}
diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs
index fff1d13720d..a14a7ea57cc 100644
--- a/tests/ui/unchecked_duration_subtraction.rs
+++ b/tests/ui/unchecked_duration_subtraction.rs
@@ -1,3 +1,4 @@
+// run-rustfix
 #![warn(clippy::unchecked_duration_subtraction)]
 
 use std::time::{Duration, Instant};
diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr
index 6b297a01c7b..b949dbf701a 100644
--- a/tests/ui/unchecked_duration_subtraction.stderr
+++ b/tests/ui/unchecked_duration_subtraction.stderr
@@ -1,5 +1,5 @@
 error: unchecked subtraction of a 'Duration' from an 'Instant'
-  --> $DIR/unchecked_duration_subtraction.rs:9:13
+  --> $DIR/unchecked_duration_subtraction.rs:10:13
    |
 LL |     let _ = _first - second;
    |             ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();`
@@ -7,19 +7,19 @@ LL |     let _ = _first - second;
    = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings`
 
 error: unchecked subtraction of a 'Duration' from an 'Instant'
-  --> $DIR/unchecked_duration_subtraction.rs:11:13
+  --> $DIR/unchecked_duration_subtraction.rs:12:13
    |
 LL |     let _ = Instant::now() - Duration::from_secs(5);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();`
 
 error: unchecked subtraction of a 'Duration' from an 'Instant'
-  --> $DIR/unchecked_duration_subtraction.rs:13:13
+  --> $DIR/unchecked_duration_subtraction.rs:14:13
    |
 LL |     let _ = _first - Duration::from_secs(5);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();`
 
 error: unchecked subtraction of a 'Duration' from an 'Instant'
-  --> $DIR/unchecked_duration_subtraction.rs:15:13
+  --> $DIR/unchecked_duration_subtraction.rs:16:13
    |
 LL |     let _ = Instant::now() - second;
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();`

From 862ac29192bd4e2c9db04e9763dfd1a35a00a9cb Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Tue, 8 Nov 2022 15:04:32 +0100
Subject: [PATCH 11/16] fix: remove (redundant) semicolon in lint suggestion

---
 clippy_lints/src/instant_subtraction.rs        | 2 +-
 tests/ui/unchecked_duration_subtraction.fixed  | 8 ++++----
 tests/ui/unchecked_duration_subtraction.stderr | 8 ++++----
 3 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index c039f1b5c6a..d83beb622cf 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -174,7 +174,7 @@ fn print_unchecked_duration_subtraction_sugg(
         expr.span,
         "unchecked subtraction of a 'Duration' from an 'Instant'",
         "try",
-        format!("{left_expr}.checked_sub({right_expr}).unwrap();"),
+        format!("{left_expr}.checked_sub({right_expr}).unwrap()"),
         applicability,
     );
 }
diff --git a/tests/ui/unchecked_duration_subtraction.fixed b/tests/ui/unchecked_duration_subtraction.fixed
index bcc231d93b1..a0e49a8beb1 100644
--- a/tests/ui/unchecked_duration_subtraction.fixed
+++ b/tests/ui/unchecked_duration_subtraction.fixed
@@ -7,11 +7,11 @@ fn main() {
     let _first = Instant::now();
     let second = Duration::from_secs(3);
 
-    let _ = _first.checked_sub(second).unwrap();;
+    let _ = _first.checked_sub(second).unwrap();
 
-    let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();;
+    let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();
 
-    let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();;
+    let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();
 
-    let _ = Instant::now().checked_sub(second).unwrap();;
+    let _ = Instant::now().checked_sub(second).unwrap();
 }
diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr
index b949dbf701a..a2e0aa1d7c0 100644
--- a/tests/ui/unchecked_duration_subtraction.stderr
+++ b/tests/ui/unchecked_duration_subtraction.stderr
@@ -2,7 +2,7 @@ error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:10:13
    |
 LL |     let _ = _first - second;
-   |             ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap();`
+   |             ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()`
    |
    = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings`
 
@@ -10,19 +10,19 @@ error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:12:13
    |
 LL |     let _ = Instant::now() - Duration::from_secs(5);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap();`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()`
 
 error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:14:13
    |
 LL |     let _ = _first - Duration::from_secs(5);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap();`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()`
 
 error: unchecked subtraction of a 'Duration' from an 'Instant'
   --> $DIR/unchecked_duration_subtraction.rs:16:13
    |
 LL |     let _ = Instant::now() - second;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap();`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()`
 
 error: aborting due to 4 previous errors
 

From 36eac0cb4ae2e372193f73c3181d5290431d3ae4 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Thu, 10 Nov 2022 15:55:45 +0100
Subject: [PATCH 12/16] fix: update lints

---
 clippy_lints/src/declared_lints.rs          |  3 ++-
 clippy_lints/src/lib.rs                     |  1 -
 src/docs/unchecked_duration_subtraction.txt | 19 -------------------
 3 files changed, 2 insertions(+), 21 deletions(-)
 delete mode 100644 src/docs/unchecked_duration_subtraction.txt

diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 98265410e84..7a705e563fc 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -202,6 +202,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO,
     crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO,
     crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO,
+    crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO,
+    crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO,
     crate::int_plus_one::INT_PLUS_ONE_INFO,
     crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO,
     crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO,
@@ -250,7 +252,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
     crate::manual_bits::MANUAL_BITS_INFO,
     crate::manual_clamp::MANUAL_CLAMP_INFO,
-    crate::manual_instant_elapsed::MANUAL_INSTANT_ELAPSED_INFO,
     crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
     crate::manual_let_else::MANUAL_LET_ELSE_INFO,
     crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ce0e7ebfbf1..3cfb2f491bc 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -921,7 +921,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
     store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
     store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv)));
-    store.register_late_pass(move || Box::new(unchecked_duration_subtraction::UncheckedDurationSubtraction::new(msrv)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/docs/unchecked_duration_subtraction.txt b/src/docs/unchecked_duration_subtraction.txt
deleted file mode 100644
index 15a4c02c606..00000000000
--- a/src/docs/unchecked_duration_subtraction.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-### What it does
-Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
-
-### Why is this bad?
-Unchecked subtraction could cause underflow on certain platforms, leading to
-unintentional panics.
-
-### Example
-```
-let time_passed = Instant::now() - Duration::from_secs(5);
-```
-
-Use instead:
-```
-let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
-```
-
-[`Duration`]: std::time::Duration
-[`Instant::now()`]: std::time::Instant::now;
\ No newline at end of file

From 3f89ab0618a1075f0c53c47d405c0f6e9bf43953 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 14 Nov 2022 22:00:09 +0100
Subject: [PATCH 13/16] chore: update lint version of MANUAL_INSTAN_ELAPSED to
 1.65

---
 clippy_lints/src/instant_subtraction.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index d83beb622cf..6d23a672856 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -33,7 +33,7 @@ declare_clippy_lint! {
     /// let prev_instant = Instant::now();
     /// let duration = prev_instant.elapsed();
     /// ```
-    #[clippy::version = "1.64.0"]
+    #[clippy::version = "1.65.0"]
     pub MANUAL_INSTANT_ELAPSED,
     pedantic,
     "subtraction between `Instant::now()` and previous `Instant`"

From 912dc919af03427288b64921bb4aea0010d071bf Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 14 Nov 2022 22:08:11 +0100
Subject: [PATCH 14/16] docs: update unchecked duration subtraction lint doc

---
 clippy_lints/src/instant_subtraction.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index 6d23a672856..3f72222f31b 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -41,7 +41,7 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
+    /// Lints subtraction between an [`Instant`] and a [`Duration`].
     ///
     /// ### Why is this bad?
     /// Unchecked subtraction could cause underflow on certain platforms, leading to

From 72ab91d8066ee399cff3e3c8293bf4dbccfb6358 Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 14 Nov 2022 22:25:56 +0100
Subject: [PATCH 15/16] fix: add extract_msrv_attr call to instan_subtraction
 lint pass

---
 clippy_lints/src/instant_subtraction.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index 3f72222f31b..716c11becd9 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -112,6 +112,8 @@ impl LateLintPass<'_> for InstantSubtraction {
             }
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {

From 0dd6ce0b1931e3e5c274daf4bbf50e96a322befd Mon Sep 17 00:00:00 2001
From: Nadir Fejzic <nadirfejzo@gmail.com>
Date: Mon, 14 Nov 2022 22:37:25 +0100
Subject: [PATCH 16/16] docs: import Instant and Duration in doctests

---
 clippy_lints/src/instant_subtraction.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs
index 716c11becd9..60754b224fc 100644
--- a/clippy_lints/src/instant_subtraction.rs
+++ b/clippy_lints/src/instant_subtraction.rs
@@ -49,11 +49,13 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```rust
+    /// # use std::time::{Instant, Duration};
     /// let time_passed = Instant::now() - Duration::from_secs(5);
     /// ```
     ///
     /// Use instead:
     /// ```rust
+    /// # use std::time::{Instant, Duration};
     /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
     /// ```
     ///