diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index f4e41dc826b0..f1fc018450bd 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 - /// Lints subtraction between an `Instant` and a `Duration`. + /// Lints subtraction between an `Instant` or `Duration` and a `Duration`. /// /// ### Why is this bad? /// Unchecked subtraction could cause underflow on certain platforms, leading to @@ -51,17 +51,19 @@ declare_clippy_lint! { /// ```no_run /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now() - Duration::from_secs(5); + /// let time_delta = Duration::from_secs(1) - Duration::from_secs(5); /// ``` /// /// Use instead: /// ```no_run /// # use std::time::{Instant, Duration}; /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// let time_delta = Duration::from_secs(1).checked_sub(Duration::from_secs(5)); /// ``` #[clippy::version = "1.67.0"] pub UNCHECKED_DURATION_SUBTRACTION, pedantic, - "finds unchecked subtraction of a 'Duration' from an 'Instant'" + "finds unchecked subtraction of a 'Duration' from an 'Instant' or 'Duration'" } pub struct InstantSubtraction { @@ -88,7 +90,8 @@ impl LateLintPass<'_> for InstantSubtraction { rhs, ) = expr.kind && let typeck = cx.typeck_results() - && ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant) + && (ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant) + || ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Duration)) { let rhs_ty = typeck.expr_ty(rhs); @@ -139,16 +142,23 @@ fn print_unchecked_duration_subtraction_sugg( expr: &Expr<'_>, ) { let mut applicability = Applicability::MachineApplicable; + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + let (left_default, left_ty) = match ty::get_type_diagnostic_name(cx, ty) { + Some(v) => (format!("<{}>", v.as_str().to_lowercase()), v.as_str().to_string()), + None => { + return; + }, + }; let ctxt = expr.span.ctxt(); - let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "", &mut applicability).0; + let left_expr = snippet_with_context(cx, left_expr.span, ctxt, &left_default, &mut applicability).0; let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "", &mut applicability).0; span_lint_and_sugg( cx, UNCHECKED_DURATION_SUBTRACTION, expr.span, - "unchecked subtraction of a 'Duration' from an 'Instant'", + format!("unchecked subtraction of 'Duration' from '{left_ty}'"), "try", 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 a0c3330d1777..5426f0745ba6 100644 --- a/tests/ui/unchecked_duration_subtraction.fixed +++ b/tests/ui/unchecked_duration_subtraction.fixed @@ -3,14 +3,21 @@ use std::time::{Duration, Instant}; fn main() { - let _first = Instant::now(); - let second = Duration::from_secs(3); + let instant = Instant::now(); + let duration = Duration::from_secs(3); + let duration2 = Duration::from_secs(1); - let _ = _first.checked_sub(second).unwrap(); + let _ = instant.checked_sub(duration).unwrap(); let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap(); - let _ = _first.checked_sub(Duration::from_secs(5)).unwrap(); + let _ = instant.checked_sub(Duration::from_secs(5)).unwrap(); - let _ = Instant::now().checked_sub(second).unwrap(); + let _ = Instant::now().checked_sub(duration).unwrap(); + + let _ = Duration::from_secs(1).checked_sub(duration).unwrap(); + + let _ = duration2.checked_sub(duration).unwrap(); + + let _ = Instant::now().elapsed().checked_sub(duration).unwrap(); } diff --git a/tests/ui/unchecked_duration_subtraction.rs b/tests/ui/unchecked_duration_subtraction.rs index fff1d13720d9..21ea7cf36e22 100644 --- a/tests/ui/unchecked_duration_subtraction.rs +++ b/tests/ui/unchecked_duration_subtraction.rs @@ -3,14 +3,21 @@ use std::time::{Duration, Instant}; fn main() { - let _first = Instant::now(); - let second = Duration::from_secs(3); + let instant = Instant::now(); + let duration = Duration::from_secs(3); + let duration2 = Duration::from_secs(1); - let _ = _first - second; + let _ = instant - duration; let _ = Instant::now() - Duration::from_secs(5); - let _ = _first - Duration::from_secs(5); + let _ = instant - Duration::from_secs(5); - let _ = Instant::now() - second; + let _ = Instant::now() - duration; + + let _ = Duration::from_secs(1) - duration; + + let _ = duration2 - duration; + + let _ = Instant::now().elapsed() - duration; } diff --git a/tests/ui/unchecked_duration_subtraction.stderr b/tests/ui/unchecked_duration_subtraction.stderr index b5e9bab86b47..746c67eb3c05 100644 --- a/tests/ui/unchecked_duration_subtraction.stderr +++ b/tests/ui/unchecked_duration_subtraction.stderr @@ -1,29 +1,47 @@ -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:9:13 +error: unchecked subtraction of 'Duration' from 'Instant' + --> tests/ui/unchecked_duration_subtraction.rs:10:13 | -LL | let _ = _first - second; - | ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()` +LL | let _ = instant - duration; + | ^^^^^^^^^^^^^^^^^^ help: try: `instant.checked_sub(duration).unwrap()` | = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unchecked_duration_subtraction)]` -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:11:13 +error: unchecked subtraction of 'Duration' from 'Instant' + --> tests/ui/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' - --> tests/ui/unchecked_duration_subtraction.rs:13:13 +error: unchecked subtraction of 'Duration' from 'Instant' + --> tests/ui/unchecked_duration_subtraction.rs:14:13 | -LL | let _ = _first - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()` +LL | let _ = instant - Duration::from_secs(5); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `instant.checked_sub(Duration::from_secs(5)).unwrap()` -error: unchecked subtraction of a 'Duration' from an 'Instant' - --> tests/ui/unchecked_duration_subtraction.rs:15:13 +error: unchecked subtraction of 'Duration' from 'Instant' + --> tests/ui/unchecked_duration_subtraction.rs:16:13 | -LL | let _ = Instant::now() - second; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()` +LL | let _ = Instant::now() - duration; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(duration).unwrap()` -error: aborting due to 4 previous errors +error: unchecked subtraction of 'Duration' from 'Duration' + --> tests/ui/unchecked_duration_subtraction.rs:18:13 + | +LL | let _ = Duration::from_secs(1) - duration; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(1).checked_sub(duration).unwrap()` + +error: unchecked subtraction of 'Duration' from 'Duration' + --> tests/ui/unchecked_duration_subtraction.rs:20:13 + | +LL | let _ = duration2 - duration; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `duration2.checked_sub(duration).unwrap()` + +error: unchecked subtraction of 'Duration' from 'Duration' + --> tests/ui/unchecked_duration_subtraction.rs:22:13 + | +LL | let _ = Instant::now().elapsed() - duration; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().elapsed().checked_sub(duration).unwrap()` + +error: aborting due to 7 previous errors