Skip to content

Commit d761ba7

Browse files
authored
Merge pull request #2837 from fanzier/panicking_unwrap
Implement lint checking for `unwrap`s that will always panic.
2 parents 4839c79 + 817da4c commit d761ba7

File tree

4 files changed

+308
-91
lines changed

4 files changed

+308
-91
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
941941
mutex_atomic::MUTEX_INTEGER,
942942
needless_borrow::NEEDLESS_BORROW,
943943
ranges::RANGE_PLUS_ONE,
944+
unwrap::PANICKING_UNWRAP,
944945
unwrap::UNNECESSARY_UNWRAP,
945946
]);
946947
}

clippy_lints/src/unwrap.rs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,27 @@ declare_clippy_lint! {
3232
"checks for calls of unwrap[_err]() that cannot fail"
3333
}
3434

35+
/// **What it does:** Checks for calls of `unwrap[_err]()` that will always fail.
36+
///
37+
/// **Why is this bad?** If panicking is desired, an explicit `panic!()` should be used.
38+
///
39+
/// **Known problems:** This lint only checks `if` conditions not assignments.
40+
/// So something like `let x: Option<()> = None; x.unwrap();` will not be recognized.
41+
///
42+
/// **Example:**
43+
/// ```rust
44+
/// if option.is_none() {
45+
/// do_something_with(option.unwrap())
46+
/// }
47+
/// ```
48+
///
49+
/// This code will always panic. The if condition should probably be inverted.
50+
declare_clippy_lint! {
51+
pub PANICKING_UNWRAP,
52+
nursery,
53+
"checks for calls of unwrap[_err]() that will always fail"
54+
}
55+
3556
pub struct Pass;
3657

3758
/// Visitor that keeps track of which variables are unwrappable.
@@ -124,17 +145,28 @@ impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
124145
if ["unwrap", "unwrap_err"].contains(&&*method_name.name.as_str());
125146
let call_to_unwrap = method_name.name == "unwrap";
126147
if let Some(unwrappable) = self.unwrappables.iter()
127-
.find(|u| u.ident.def == path.def && call_to_unwrap == u.safe_to_unwrap);
148+
.find(|u| u.ident.def == path.def);
128149
then {
129-
span_lint_and_then(
130-
self.cx,
131-
UNNECESSARY_UNWRAP,
132-
expr.span,
133-
&format!("You checked before that `{}()` cannot fail. \
134-
Instead of checking and unwrapping, it's better to use `if let` or `match`.",
135-
method_name.name),
136-
|db| { db.span_label(unwrappable.check.span, "the check is happening here"); },
137-
);
150+
if call_to_unwrap == unwrappable.safe_to_unwrap {
151+
span_lint_and_then(
152+
self.cx,
153+
UNNECESSARY_UNWRAP,
154+
expr.span,
155+
&format!("You checked before that `{}()` cannot fail. \
156+
Instead of checking and unwrapping, it's better to use `if let` or `match`.",
157+
method_name.name),
158+
|db| { db.span_label(unwrappable.check.span, "the check is happening here"); },
159+
);
160+
} else {
161+
span_lint_and_then(
162+
self.cx,
163+
PANICKING_UNWRAP,
164+
expr.span,
165+
&format!("This call to `{}()` will always panic.",
166+
method_name.name),
167+
|db| { db.span_label(unwrappable.check.span, "because of this check"); },
168+
);
169+
}
138170
}
139171
}
140172
walk_expr(self, expr);
@@ -148,7 +180,7 @@ impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
148180

149181
impl<'a> LintPass for Pass {
150182
fn get_lints(&self) -> LintArray {
151-
lint_array!(UNNECESSARY_UNWRAP)
183+
lint_array!(PANICKING_UNWRAP, UNNECESSARY_UNWRAP)
152184
}
153185
}
154186

tests/ui/checked_unwrap.rs

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,101 @@
1-
#![deny(unnecessary_unwrap)]
1+
#![deny(panicking_unwrap, unnecessary_unwrap)]
2+
#![allow(if_same_then_else)]
23

34
fn main() {
45
let x = Some(());
56
if x.is_some() {
6-
x.unwrap();
7+
x.unwrap(); // unnecessary
8+
} else {
9+
x.unwrap(); // will panic
710
}
811
if x.is_none() {
9-
// nothing to do here
12+
x.unwrap(); // will panic
1013
} else {
11-
x.unwrap();
14+
x.unwrap(); // unnecessary
1215
}
1316
let mut x: Result<(), ()> = Ok(());
1417
if x.is_ok() {
15-
x.unwrap();
18+
x.unwrap(); // unnecessary
19+
x.unwrap_err(); // will panic
1620
} else {
17-
x.unwrap_err();
21+
x.unwrap(); // will panic
22+
x.unwrap_err(); // unnecessary
1823
}
1924
if x.is_err() {
20-
x.unwrap_err();
25+
x.unwrap(); // will panic
26+
x.unwrap_err(); // unnecessary
2127
} else {
22-
x.unwrap();
28+
x.unwrap(); // unnecessary
29+
x.unwrap_err(); // will panic
2330
}
2431
if x.is_ok() {
2532
x = Err(());
26-
x.unwrap();
33+
x.unwrap(); // not unnecessary because of mutation of x
34+
// it will always panic but the lint is not smart enoguh to see this (it only checks if conditions).
2735
} else {
2836
x = Ok(());
29-
x.unwrap_err();
37+
x.unwrap_err(); // not unnecessary because of mutation of x
38+
// it will always panic but the lint is not smart enoguh to see this (it only checks if conditions).
3039
}
3140
}
3241

3342
fn test_complex_conditions() {
3443
let x: Result<(), ()> = Ok(());
3544
let y: Result<(), ()> = Ok(());
3645
if x.is_ok() && y.is_err() {
37-
x.unwrap();
38-
y.unwrap_err();
46+
x.unwrap(); // unnecessary
47+
x.unwrap_err(); // will panic
48+
y.unwrap(); // will panic
49+
y.unwrap_err(); // unnecessary
3950
} else {
40-
// not clear whether unwrappable:
51+
// not statically determinable whether any of the following will always succeed or always fail:
52+
x.unwrap();
4153
x.unwrap_err();
4254
y.unwrap();
55+
y.unwrap_err();
4356
}
4457

4558
if x.is_ok() || y.is_ok() {
46-
// not clear whether unwrappable:
59+
// not statically determinable whether any of the following will always succeed or always fail:
4760
x.unwrap();
4861
y.unwrap();
4962
} else {
50-
x.unwrap_err();
51-
y.unwrap_err();
63+
x.unwrap(); // will panic
64+
x.unwrap_err(); // unnecessary
65+
y.unwrap(); // will panic
66+
y.unwrap_err(); // unnecessary
5267
}
5368
let z: Result<(), ()> = Ok(());
5469
if x.is_ok() && !(y.is_ok() || z.is_err()) {
55-
x.unwrap();
56-
y.unwrap_err();
57-
z.unwrap();
70+
x.unwrap(); // unnecessary
71+
x.unwrap_err(); // will panic
72+
y.unwrap(); // will panic
73+
y.unwrap_err(); // unnecessary
74+
z.unwrap(); // unnecessary
75+
z.unwrap_err(); // will panic
5876
}
5977
if x.is_ok() || !(y.is_ok() && z.is_err()) {
60-
// not clear what's unwrappable
61-
} else {
62-
x.unwrap_err();
78+
// not statically determinable whether any of the following will always succeed or always fail:
79+
x.unwrap();
6380
y.unwrap();
64-
z.unwrap_err();
81+
z.unwrap();
82+
} else {
83+
x.unwrap(); // will panic
84+
x.unwrap_err(); // unnecessary
85+
y.unwrap(); // unnecessary
86+
y.unwrap_err(); // will panic
87+
z.unwrap(); // will panic
88+
z.unwrap_err(); // unnecessary
6589
}
6690
}
6791

6892
fn test_nested() {
6993
fn nested() {
7094
let x = Some(());
7195
if x.is_some() {
72-
x.unwrap();
96+
x.unwrap(); // unnecessary
97+
} else {
98+
x.unwrap(); // will panic
7399
}
74100
}
75101
}

0 commit comments

Comments
 (0)