Skip to content

Commit 722aad6

Browse files
committed
extend UNCHECKED_DURATION_SUBTRACTION to check for Duration
Duration - Duration could panic in the same way as Instant - Duration
1 parent c4aeb32 commit 722aad6

File tree

4 files changed

+68
-26
lines changed

4 files changed

+68
-26
lines changed

clippy_lints/src/instant_subtraction.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ declare_clippy_lint! {
4141

4242
declare_clippy_lint! {
4343
/// ### What it does
44-
/// Lints subtraction between an `Instant` and a `Duration`.
44+
/// Lints subtraction between an `Instant` or `Duration` and a `Duration`.
4545
///
4646
/// ### Why is this bad?
4747
/// Unchecked subtraction could cause underflow on certain platforms, leading to
@@ -51,17 +51,19 @@ declare_clippy_lint! {
5151
/// ```no_run
5252
/// # use std::time::{Instant, Duration};
5353
/// let time_passed = Instant::now() - Duration::from_secs(5);
54+
/// let time_delta = Duration::from_secs(1) - Duration::from_secs(5);
5455
/// ```
5556
///
5657
/// Use instead:
5758
/// ```no_run
5859
/// # use std::time::{Instant, Duration};
5960
/// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
61+
/// let time_delta = Duration::from_secs(1).checked_sub(Duration::from_secs(5));
6062
/// ```
6163
#[clippy::version = "1.67.0"]
6264
pub UNCHECKED_DURATION_SUBTRACTION,
6365
pedantic,
64-
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
66+
"finds unchecked subtraction of a 'Duration' from an 'Instant' or 'Duration'"
6567
}
6668

6769
pub struct InstantSubtraction {
@@ -88,7 +90,8 @@ impl LateLintPass<'_> for InstantSubtraction {
8890
rhs,
8991
) = expr.kind
9092
&& let typeck = cx.typeck_results()
91-
&& ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant)
93+
&& (ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant)
94+
|| ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Duration))
9295
{
9396
let rhs_ty = typeck.expr_ty(rhs);
9497

@@ -139,16 +142,23 @@ fn print_unchecked_duration_subtraction_sugg(
139142
expr: &Expr<'_>,
140143
) {
141144
let mut applicability = Applicability::MachineApplicable;
145+
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
146+
let (left_default, left_ty) = match ty::get_type_diagnostic_name(cx, ty) {
147+
Some(v) => (format!("<{}>", v.as_str().to_lowercase()), v.as_str().to_string()),
148+
None => {
149+
return;
150+
},
151+
};
142152

143153
let ctxt = expr.span.ctxt();
144-
let left_expr = snippet_with_context(cx, left_expr.span, ctxt, "<instant>", &mut applicability).0;
154+
let left_expr = snippet_with_context(cx, left_expr.span, ctxt, &left_default, &mut applicability).0;
145155
let right_expr = snippet_with_context(cx, right_expr.span, ctxt, "<duration>", &mut applicability).0;
146156

147157
span_lint_and_sugg(
148158
cx,
149159
UNCHECKED_DURATION_SUBTRACTION,
150160
expr.span,
151-
"unchecked subtraction of a 'Duration' from an 'Instant'",
161+
format!("unchecked subtraction of 'Duration' from '{left_ty}'"),
152162
"try",
153163
format!("{left_expr}.checked_sub({right_expr}).unwrap()"),
154164
applicability,

tests/ui/unchecked_duration_subtraction.fixed

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@
33
use std::time::{Duration, Instant};
44

55
fn main() {
6-
let _first = Instant::now();
7-
let second = Duration::from_secs(3);
6+
let instant = Instant::now();
7+
let duration = Duration::from_secs(3);
8+
let duration2 = Duration::from_secs(1);
89

9-
let _ = _first.checked_sub(second).unwrap();
10+
let _ = instant.checked_sub(duration).unwrap();
1011

1112
let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();
1213

13-
let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();
14+
let _ = instant.checked_sub(Duration::from_secs(5)).unwrap();
1415

15-
let _ = Instant::now().checked_sub(second).unwrap();
16+
let _ = Instant::now().checked_sub(duration).unwrap();
17+
18+
let _ = Duration::from_secs(1).checked_sub(duration).unwrap();
19+
20+
let _ = duration2.checked_sub(duration).unwrap();
21+
22+
let _ = Instant::now().elapsed().checked_sub(duration).unwrap();
1623
}

tests/ui/unchecked_duration_subtraction.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@
33
use std::time::{Duration, Instant};
44

55
fn main() {
6-
let _first = Instant::now();
7-
let second = Duration::from_secs(3);
6+
let instant = Instant::now();
7+
let duration = Duration::from_secs(3);
8+
let duration2 = Duration::from_secs(1);
89

9-
let _ = _first - second;
10+
let _ = instant - duration;
1011

1112
let _ = Instant::now() - Duration::from_secs(5);
1213

13-
let _ = _first - Duration::from_secs(5);
14+
let _ = instant - Duration::from_secs(5);
1415

15-
let _ = Instant::now() - second;
16+
let _ = Instant::now() - duration;
17+
18+
let _ = Duration::from_secs(1) - duration;
19+
20+
let _ = duration2 - duration;
21+
22+
let _ = Instant::now().elapsed() - duration;
1623
}
Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,47 @@
11
error: unchecked subtraction of a 'Duration' from an 'Instant'
2-
--> tests/ui/unchecked_duration_subtraction.rs:9:13
2+
--> tests/ui/unchecked_duration_subtraction.rs:10:13
33
|
4-
LL | let _ = _first - second;
5-
| ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()`
4+
LL | let _ = instant - duration;
5+
| ^^^^^^^^^^^^^^^^^^ help: try: `instant.checked_sub(duration).unwrap()`
66
|
77
= note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings`
88
= help: to override `-D warnings` add `#[allow(clippy::unchecked_duration_subtraction)]`
99

1010
error: unchecked subtraction of a 'Duration' from an 'Instant'
11-
--> tests/ui/unchecked_duration_subtraction.rs:11:13
11+
--> tests/ui/unchecked_duration_subtraction.rs:12:13
1212
|
1313
LL | let _ = Instant::now() - Duration::from_secs(5);
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()`
1515

1616
error: unchecked subtraction of a 'Duration' from an 'Instant'
17-
--> tests/ui/unchecked_duration_subtraction.rs:13:13
17+
--> tests/ui/unchecked_duration_subtraction.rs:14:13
1818
|
19-
LL | let _ = _first - Duration::from_secs(5);
20-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()`
19+
LL | let _ = instant - Duration::from_secs(5);
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `instant.checked_sub(Duration::from_secs(5)).unwrap()`
2121

2222
error: unchecked subtraction of a 'Duration' from an 'Instant'
23-
--> tests/ui/unchecked_duration_subtraction.rs:15:13
23+
--> tests/ui/unchecked_duration_subtraction.rs:16:13
2424
|
25-
LL | let _ = Instant::now() - second;
26-
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()`
25+
LL | let _ = Instant::now() - duration;
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(duration).unwrap()`
2727

28-
error: aborting due to 4 previous errors
28+
error: unchecked subtraction of a 'Duration' from an 'Duration'
29+
--> tests/ui/unchecked_duration_subtraction.rs:18:13
30+
|
31+
LL | let _ = Duration::from_secs(1) - duration;
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(1).checked_sub(duration).unwrap()`
33+
34+
error: unchecked subtraction of a 'Duration' from an 'Duration'
35+
--> tests/ui/unchecked_duration_subtraction.rs:20:13
36+
|
37+
LL | let _ = duration2 - duration;
38+
| ^^^^^^^^^^^^^^^^^^^^ help: try: `duration2.checked_sub(duration).unwrap()`
39+
40+
error: unchecked subtraction of a 'Duration' from an 'Duration'
41+
--> tests/ui/unchecked_duration_subtraction.rs:22:13
42+
|
43+
LL | let _ = Instant::now().elapsed() - duration;
44+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().elapsed().checked_sub(duration).unwrap()`
45+
46+
error: aborting due to 7 previous errors
2947

0 commit comments

Comments
 (0)