1
- use clippy_utils:: { diagnostics, meets_msrv, msrvs, source, ty} ;
1
+ use clippy_utils:: {
2
+ diagnostics:: { self , span_lint_and_sugg} ,
3
+ meets_msrv, msrvs, source, ty,
4
+ } ;
2
5
use rustc_errors:: Applicability ;
3
- use rustc_hir:: * ;
6
+ use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
4
7
use rustc_lint:: { LateContext , LateLintPass } ;
5
8
use rustc_semver:: RustcVersion ;
6
9
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
7
- use rustc_span:: symbol:: sym;
10
+ use rustc_span:: { source_map:: Spanned , sym} ;
11
+
12
+ declare_clippy_lint ! {
13
+ /// ### What it does
14
+ /// Lints subtraction between `Instant::now()` and another `Instant`.
15
+ ///
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.
19
+ ///
20
+ /// `prev_instant.elapsed()` also more clearly signals intention.
21
+ ///
22
+ /// ### Example
23
+ /// ```rust
24
+ /// use std::time::Instant;
25
+ /// let prev_instant = Instant::now();
26
+ /// let duration = Instant::now() - prev_instant;
27
+ /// ```
28
+ /// Use instead:
29
+ /// ```rust
30
+ /// use std::time::Instant;
31
+ /// let prev_instant = Instant::now();
32
+ /// let duration = prev_instant.elapsed();
33
+ /// ```
34
+ #[ clippy:: version = "1.64.0" ]
35
+ pub MANUAL_INSTANT_ELAPSED ,
36
+ pedantic,
37
+ "subtraction between `Instant::now()` and previous `Instant`"
38
+ }
8
39
9
40
declare_clippy_lint ! {
10
41
/// ### What it does
11
42
/// Finds patterns of unchecked subtraction of [`Duration`] from [`Instant::now()`].
12
43
///
13
44
/// ### Why is this bad?
14
- /// Unchecked subtraction could cause underflow on certain platforms, leading to bugs and/or
45
+ /// Unchecked subtraction could cause underflow on certain platforms, leading to
15
46
/// unintentional panics.
16
47
///
17
48
/// ### Example
@@ -32,21 +63,39 @@ declare_clippy_lint! {
32
63
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
33
64
}
34
65
35
- pub struct UncheckedDurationSubtraction {
66
+ pub struct InstantSubtraction {
36
67
msrv : Option < RustcVersion > ,
37
68
}
38
69
39
- impl UncheckedDurationSubtraction {
70
+ impl InstantSubtraction {
40
71
#[ must_use]
41
72
pub fn new ( msrv : Option < RustcVersion > ) -> Self {
42
73
Self { msrv }
43
74
}
44
75
}
45
76
46
- impl_lint_pass ! ( UncheckedDurationSubtraction => [ UNCHECKED_DURATION_SUBTRACTION ] ) ;
77
+ impl_lint_pass ! ( InstantSubtraction => [ MANUAL_INSTANT_ELAPSED , UNCHECKED_DURATION_SUBTRACTION ] ) ;
78
+
79
+ impl LateLintPass < ' _ > for InstantSubtraction {
80
+ fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) {
81
+ if let ExprKind :: Binary ( Spanned { node : BinOpKind :: Sub , ..} , lhs, rhs) = expr. kind
82
+ && check_instant_now_call ( cx, lhs)
83
+ && let ty_resolved = cx. typeck_results ( ) . expr_ty ( rhs)
84
+ && let rustc_middle:: ty:: Adt ( def, _) = ty_resolved. kind ( )
85
+ && clippy_utils:: match_def_path ( cx, def. did ( ) , & clippy_utils:: paths:: INSTANT )
86
+ && let Some ( sugg) = clippy_utils:: sugg:: Sugg :: hir_opt ( cx, rhs)
87
+ {
88
+ span_lint_and_sugg (
89
+ cx,
90
+ MANUAL_INSTANT_ELAPSED ,
91
+ expr. span ,
92
+ "manual implementation of `Instant::elapsed`" ,
93
+ "try" ,
94
+ format ! ( "{}.elapsed()" , sugg. maybe_par( ) ) ,
95
+ Applicability :: MachineApplicable ,
96
+ ) ;
97
+ }
47
98
48
- impl < ' tcx > LateLintPass < ' tcx > for UncheckedDurationSubtraction {
49
- fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
50
99
if expr. span . from_expansion ( ) || !meets_msrv ( self . msrv , msrvs:: TRY_FROM ) {
51
100
return ;
52
101
}
@@ -67,6 +116,17 @@ impl<'tcx> LateLintPass<'tcx> for UncheckedDurationSubtraction {
67
116
}
68
117
}
69
118
119
+ fn check_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 )
123
+ {
124
+ true
125
+ } else {
126
+ false
127
+ }
128
+ }
129
+
70
130
fn is_an_instant ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
71
131
let expr_ty = cx. typeck_results ( ) . expr_ty ( expr) ;
72
132
0 commit comments