1 use clippy_utils::{diagnostics, meets_msrv, msrvs, source, ty};
2 use rustc_errors::Applicability;
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_semver::RustcVersion;
6 use rustc_session::{declare_tool_lint, impl_lint_pass};
7 use rustc_span::symbol::sym;
11 /// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
13 /// ### Why is this bad?
14 /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
15 /// unintentional panics.
19 /// let time_passed = Instant::now() - Duration::from_secs(5);
24 /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
27 /// [`Duration`]: std::time::Duration
28 /// [`Instant::now()`]: std::time::Instant::now;
29 #[clippy::version = "1.65.0"]
30 pub UNCHECKED_DURATION_SUBTRACTION,
32 "finds unchecked subtraction of a 'Duration' from an 'Instant'"
35 pub struct UncheckedDurationSubtraction {
36 msrv: Option<RustcVersion>,
39 impl UncheckedDurationSubtraction {
41 pub fn new(msrv: Option<RustcVersion>) -> Self {
46 impl_lint_pass!(UncheckedDurationSubtraction => [UNCHECKED_DURATION_SUBTRACTION]);
48 impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
49 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
50 if expr.span.from_expansion() || !meets_msrv(self.msrv, msrvs::TRY_FROM) {
55 if let ExprKind::Binary(op, lhs, rhs) = expr.kind;
57 if let BinOpKind::Sub = op.node;
59 // get types of left and right side
60 if is_an_instant(cx, lhs);
61 if is_a_duration(cx, rhs);
64 print_lint_and_sugg(cx, lhs, rhs, expr)
70 fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
71 let expr_ty = cx.typeck_results().expr_ty(expr);
73 match expr_ty.kind() {
74 rustc_middle::ty::Adt(def, _) => {
75 clippy_utils::match_def_path(cx, dbg!(def).did(), &clippy_utils::paths::INSTANT)
81 fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
82 let expr_ty = cx.typeck_results().expr_ty(expr);
83 ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration)
86 fn print_lint_and_sugg(cx: &LateContext<'_>, left_expr: &Expr<'_>, right_expr: &Expr<'_>, expr: &Expr<'_>) {
87 let mut applicability = Applicability::MachineApplicable;
90 source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
91 let right_expr = source::snippet_with_applicability(
94 "std::time::Duration::from_secs(1)",
98 diagnostics::span_lint_and_sugg(
100 UNCHECKED_DURATION_SUBTRACTION,
102 "unchecked subtraction of a 'Duration' from an 'Instant'",
104 format!("{}.checked_sub({}).unwrap();", left_expr, right_expr),