mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-23 21:23:56 +00:00
Auto merge of #9570 - nfejzic:lint-unchecked-duration-subtraction, r=llogiq
feat: lint unchecked subtraction of a 'Duration' from an 'Instant' Hello all, I tried to tackle the open issue #9371 and this is what I came up with. I have a difficulty currently - some tests are failing: ``` failures: [ui] ui/manual_instant_elapsed.rs ``` The `manual_instant_elapsed` is failing because of `Instant::now() - duration` test, this now gets also picked by `unchecked_duration_subtraction` lint. What is the correct way to proceed in this case? Simply update the `.stderr` file for `manual_instant_elapsed` lint? changelog: [`unchecked_duration_subtraction`]: Add lint for unchecked subtraction of a `Duration` from an `Instant`. fixes #9371
This commit is contained in:
commit
5b0d727bad
12 changed files with 256 additions and 75 deletions
|
@ -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
|
||||
|
|
|
@ -203,6 +203,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,
|
||||
|
@ -251,7 +253,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,
|
||||
|
|
184
clippy_lints/src/instant_subtraction.rs
Normal file
184
clippy_lints/src/instant_subtraction.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
use clippy_utils::{
|
||||
diagnostics::{self, span_lint_and_sugg},
|
||||
meets_msrv, msrvs, source,
|
||||
sugg::Sugg,
|
||||
ty,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
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::{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.65.0"]
|
||||
pub MANUAL_INSTANT_ELAPSED,
|
||||
pedantic,
|
||||
"subtraction between `Instant::now()` and previous `Instant`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints subtraction between an [`Instant`] and a [`Duration`].
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unchecked subtraction could cause underflow on certain platforms, leading to
|
||||
/// unintentional panics.
|
||||
///
|
||||
/// ### 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));
|
||||
/// ```
|
||||
///
|
||||
/// [`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 InstantSubtraction {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl InstantSubtraction {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if_chain! {
|
||||
if is_instant_now_call(cx, lhs);
|
||||
|
||||
if is_an_instant(cx, rhs);
|
||||
if let Some(sugg) = Sugg::hir_opt(cx, rhs);
|
||||
|
||||
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 is_an_instant(cx, lhs);
|
||||
if is_a_duration(cx, rhs);
|
||||
|
||||
then {
|
||||
print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
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, 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_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 =
|
||||
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!("{left_expr}.checked_sub({right_expr}).unwrap()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
|
@ -907,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));
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
|
|
|
@ -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)]
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
||||
|
|
|
@ -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()`
|
||||
|
|
17
tests/ui/unchecked_duration_subtraction.fixed
Normal file
17
tests/ui/unchecked_duration_subtraction.fixed
Normal file
|
@ -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();
|
||||
}
|
17
tests/ui/unchecked_duration_subtraction.rs
Normal file
17
tests/ui/unchecked_duration_subtraction.rs
Normal file
|
@ -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 - second;
|
||||
|
||||
let _ = Instant::now() - Duration::from_secs(5);
|
||||
|
||||
let _ = _first - Duration::from_secs(5);
|
||||
|
||||
let _ = Instant::now() - second;
|
||||
}
|
28
tests/ui/unchecked_duration_subtraction.stderr
Normal file
28
tests/ui/unchecked_duration_subtraction.stderr
Normal file
|
@ -0,0 +1,28 @@
|
|||
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()`
|
||||
|
|
||||
= note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings`
|
||||
|
||||
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()`
|
||||
|
||||
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()`
|
||||
|
||||
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()`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
Loading…
Reference in a new issue