Skip to content

Commit 779e0f4

Browse files
committed
Do not lint unwrapping on ! or never-like enums
1 parent 78f5e0d commit 779e0f4

File tree

6 files changed

+137
-109
lines changed

6 files changed

+137
-109
lines changed

clippy_lints/src/methods/expect_used.rs

Lines changed: 0 additions & 44 deletions
This file was deleted.

clippy_lints/src/methods/mod.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ mod collapsible_str_replace;
1717
mod drain_collect;
1818
mod err_expect;
1919
mod expect_fun_call;
20-
mod expect_used;
2120
mod extend_with_drain;
2221
mod filetype_is_file;
2322
mod filter_map;
@@ -105,7 +104,7 @@ mod unnecessary_lazy_eval;
105104
mod unnecessary_literal_unwrap;
106105
mod unnecessary_sort_by;
107106
mod unnecessary_to_owned;
108-
mod unwrap_used;
107+
mod unwrap_expect_used;
109108
mod useless_asref;
110109
mod utils;
111110
mod vec_resize_to_zero;
@@ -3948,13 +3947,27 @@ impl Methods {
39483947
match method_call(recv) {
39493948
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
39503949
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
3951-
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
3950+
_ => unwrap_expect_used::check(
3951+
cx,
3952+
expr,
3953+
recv,
3954+
false,
3955+
self.allow_expect_in_tests,
3956+
unwrap_expect_used::Variant::Expect,
3957+
),
39523958
}
39533959
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
39543960
},
39553961
("expect_err", [_]) => {
39563962
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
3957-
expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests);
3963+
unwrap_expect_used::check(
3964+
cx,
3965+
expr,
3966+
recv,
3967+
true,
3968+
self.allow_expect_in_tests,
3969+
unwrap_expect_used::Variant::Expect,
3970+
);
39583971
},
39593972
("extend", [arg]) => {
39603973
string_extend_chars::check(cx, expr, recv, arg);
@@ -4180,11 +4193,25 @@ impl Methods {
41804193
_ => {},
41814194
}
41824195
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
4183-
unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
4196+
unwrap_expect_used::check(
4197+
cx,
4198+
expr,
4199+
recv,
4200+
false,
4201+
self.allow_unwrap_in_tests,
4202+
unwrap_expect_used::Variant::Unwrap,
4203+
);
41844204
},
41854205
("unwrap_err", []) => {
41864206
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
4187-
unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests);
4207+
unwrap_expect_used::check(
4208+
cx,
4209+
expr,
4210+
recv,
4211+
true,
4212+
self.allow_unwrap_in_tests,
4213+
unwrap_expect_used::Variant::Unwrap,
4214+
);
41884215
},
41894216
("unwrap_or", [u_arg]) => {
41904217
match method_call(recv) {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::ty::is_type_diagnostic_item;
3+
use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
4+
use rustc_hir::Expr;
5+
use rustc_lint::{LateContext, Lint};
6+
use rustc_middle::ty;
7+
use rustc_span::sym;
8+
9+
use super::{EXPECT_USED, UNWRAP_USED};
10+
11+
#[derive(Clone, Copy, Eq, PartialEq)]
12+
pub(super) enum Variant {
13+
Unwrap,
14+
Expect,
15+
}
16+
17+
impl Variant {
18+
fn method_name(self, is_err: bool) -> &'static str {
19+
match (self, is_err) {
20+
(Variant::Unwrap, true) => "unwrap_err",
21+
(Variant::Unwrap, false) => "unwrap",
22+
(Variant::Expect, true) => "expect_err",
23+
(Variant::Expect, false) => "expect",
24+
}
25+
}
26+
27+
fn lint(self) -> &'static Lint {
28+
match self {
29+
Variant::Unwrap => UNWRAP_USED,
30+
Variant::Expect => EXPECT_USED,
31+
}
32+
}
33+
}
34+
35+
/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
36+
pub(super) fn check(
37+
cx: &LateContext<'_>,
38+
expr: &Expr<'_>,
39+
recv: &Expr<'_>,
40+
is_err: bool,
41+
allow_unwrap_in_tests: bool,
42+
variant: Variant,
43+
) {
44+
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
45+
46+
let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err {
47+
("an `Option`", "None", "")
48+
} else if is_type_diagnostic_item(cx, ty, sym::Result)
49+
&& let ty::Adt(_, substs) = ty.kind()
50+
&& let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type()
51+
{
52+
// Issue #11245: Do not lint `!` or never-like enums
53+
if t_or_e_ty.is_never()
54+
|| (t_or_e_ty.is_enum() && t_or_e_ty.ty_adt_def().is_some_and(|def| def.variants().is_empty()))
55+
{
56+
return;
57+
}
58+
59+
("a `Result`", if is_err { "Ok" } else { "Err" }, "an ")
60+
} else {
61+
return;
62+
};
63+
64+
let method_suffix = if is_err { "_err" } else { "" };
65+
66+
if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
67+
return;
68+
}
69+
70+
let help = if variant == Variant::Unwrap && is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
71+
format!(
72+
"if you don't want to handle the `{none_value}` case gracefully, consider \
73+
using `expect{method_suffix}()` to provide a better panic message"
74+
)
75+
} else {
76+
format!("if this value is {none_prefix}`{none_value}`, it will panic")
77+
};
78+
79+
span_lint_and_help(
80+
cx,
81+
variant.lint(),
82+
expr.span,
83+
&format!("used `{}()` on {kind} value", variant.method_name(is_err)),
84+
None,
85+
&help,
86+
);
87+
}

clippy_lints/src/methods/unwrap_used.rs

Lines changed: 0 additions & 53 deletions
This file was deleted.

tests/ui/unwrap_expect_used.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#![warn(clippy::unwrap_used, clippy::expect_used)]
22
#![allow(clippy::unnecessary_literal_unwrap)]
3+
#![feature(never_type)]
4+
5+
use std::convert::Infallible;
36

47
trait OptionExt {
58
type Item;
@@ -28,6 +31,14 @@ fn main() {
2831
Some(3).unwrap_err();
2932
Some(3).expect_err("Hellow none!");
3033

34+
// Issue #11245: The `Err` variant can never be constructed so do not lint this.
35+
let x: Result<(), !> = Ok(());
36+
x.unwrap();
37+
x.expect("is `!` (never)");
38+
let x: Result<(), Infallible> = Ok(());
39+
x.unwrap();
40+
x.expect("is never-like (0 variants)");
41+
3142
let a: Result<i32, i32> = Ok(3);
3243
a.unwrap();
3344
a.expect("Hello world!");

tests/ui/unwrap_expect_used.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: used `unwrap()` on an `Option` value
2-
--> $DIR/unwrap_expect_used.rs:24:5
2+
--> $DIR/unwrap_expect_used.rs:27:5
33
|
44
LL | Some(3).unwrap();
55
| ^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | Some(3).unwrap();
88
= note: `-D clippy::unwrap-used` implied by `-D warnings`
99

1010
error: used `expect()` on an `Option` value
11-
--> $DIR/unwrap_expect_used.rs:25:5
11+
--> $DIR/unwrap_expect_used.rs:28:5
1212
|
1313
LL | Some(3).expect("Hello world!");
1414
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,31 +17,31 @@ LL | Some(3).expect("Hello world!");
1717
= note: `-D clippy::expect-used` implied by `-D warnings`
1818

1919
error: used `unwrap()` on a `Result` value
20-
--> $DIR/unwrap_expect_used.rs:32:5
20+
--> $DIR/unwrap_expect_used.rs:43:5
2121
|
2222
LL | a.unwrap();
2323
| ^^^^^^^^^^
2424
|
2525
= help: if this value is an `Err`, it will panic
2626

2727
error: used `expect()` on a `Result` value
28-
--> $DIR/unwrap_expect_used.rs:33:5
28+
--> $DIR/unwrap_expect_used.rs:44:5
2929
|
3030
LL | a.expect("Hello world!");
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^
3232
|
3333
= help: if this value is an `Err`, it will panic
3434

3535
error: used `unwrap_err()` on a `Result` value
36-
--> $DIR/unwrap_expect_used.rs:34:5
36+
--> $DIR/unwrap_expect_used.rs:45:5
3737
|
3838
LL | a.unwrap_err();
3939
| ^^^^^^^^^^^^^^
4040
|
4141
= help: if this value is an `Ok`, it will panic
4242

4343
error: used `expect_err()` on a `Result` value
44-
--> $DIR/unwrap_expect_used.rs:35:5
44+
--> $DIR/unwrap_expect_used.rs:46:5
4545
|
4646
LL | a.expect_err("Hello error!");
4747
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)