Skip to content

Commit 540e2f4

Browse files
authored
feat(ok_expect): add autofix (#15867)
changelog: [`ok_expect`]: add autofix
2 parents 663ef9b + 9816437 commit 540e2f4

File tree

6 files changed

+171
-30
lines changed

6 files changed

+171
-30
lines changed

clippy_lints/src/methods/err_expect.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use super::ERR_EXPECT;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::msrvs::{self, Msrv};
4-
use clippy_utils::res::MaybeDef;
54
use clippy_utils::ty::has_debug_impl;
65
use rustc_errors::Applicability;
76
use rustc_lint::LateContext;
@@ -17,12 +16,10 @@ pub(super) fn check(
1716
err_span: Span,
1817
msrv: Msrv,
1918
) {
20-
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
21-
// Grabs the `Result<T, E>` type
22-
&& let result_type = cx.typeck_results().expr_ty(recv)
23-
// Tests if the T type in a `Result<T, E>` is not None
24-
&& let Some(data_type) = get_data_type(cx, result_type)
25-
// Tests if the T type in a `Result<T, E>` implements debug
19+
let result_ty = cx.typeck_results().expr_ty(recv);
20+
// Grabs the `Result<T, E>` type
21+
if let Some(data_type) = get_data_type(cx, result_ty)
22+
// Tests if the T type in a `Result<T, E>` implements Debug
2623
&& has_debug_impl(cx, data_type)
2724
&& msrv.meets(cx, msrvs::EXPECT_ERR)
2825
{
@@ -41,7 +38,7 @@ pub(super) fn check(
4138
/// Given a `Result<T, E>` type, return its data (`T`).
4239
fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
4340
match ty.kind() {
44-
ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(),
41+
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().next(),
4542
_ => None,
4643
}
4744
}

clippy_lints/src/methods/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5167,7 +5167,7 @@ impl Methods {
51675167
},
51685168
(sym::expect, [_]) => {
51695169
match method_call(recv) {
5170-
Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv),
5170+
Some((sym::ok, recv_inner, [], _, _)) => ok_expect::check(cx, expr, recv, recv_inner),
51715171
Some((sym::err, recv, [], err_span, _)) => {
51725172
err_expect::check(cx, expr, recv, span, err_span, self.msrv);
51735173
},
Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,43 @@
1-
use clippy_utils::diagnostics::span_lint_and_help;
2-
use clippy_utils::res::MaybeDef;
1+
use clippy_utils::diagnostics::span_lint_and_then;
32
use clippy_utils::ty::has_debug_impl;
3+
use rustc_errors::Applicability;
44
use rustc_hir as hir;
5-
use rustc_lint::LateContext;
5+
use rustc_lint::{LateContext, LintContext};
66
use rustc_middle::ty::{self, Ty};
77
use rustc_span::sym;
88

99
use super::OK_EXPECT;
1010

1111
/// lint use of `ok().expect()` for `Result`s
12-
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
13-
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
14-
// lint if the caller of `ok()` is a `Result`
15-
&& let result_type = cx.typeck_results().expr_ty(recv)
16-
&& let Some(error_type) = get_error_type(cx, result_type)
12+
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, recv_inner: &hir::Expr<'_>) {
13+
let result_ty = cx.typeck_results().expr_ty(recv_inner);
14+
// lint if the caller of `ok()` is a `Result`
15+
if let Some(error_type) = get_error_type(cx, result_ty)
1716
&& has_debug_impl(cx, error_type)
17+
&& let Some(span) = recv.span.trim_start(recv_inner.span)
1818
{
19-
span_lint_and_help(
19+
span_lint_and_then(
2020
cx,
2121
OK_EXPECT,
2222
expr.span,
2323
"called `ok().expect()` on a `Result` value",
24-
None,
25-
"you can call `expect()` directly on the `Result`",
24+
|diag| {
25+
let span = cx.sess().source_map().span_extend_while_whitespace(span);
26+
diag.span_suggestion_verbose(
27+
span,
28+
"call `expect()` directly on the `Result`",
29+
String::new(),
30+
Applicability::MachineApplicable,
31+
);
32+
},
2633
);
2734
}
2835
}
2936

3037
/// Given a `Result<T, E>` type, return its error type (`E`).
3138
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
3239
match ty.kind() {
33-
ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1),
40+
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().nth(1),
3441
_ => None,
3542
}
3643
}

tests/ui/ok_expect.fixed

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#![allow(clippy::unnecessary_literal_unwrap)]
2+
3+
use std::io;
4+
5+
struct MyError(()); // doesn't implement Debug
6+
7+
#[derive(Debug)]
8+
struct MyErrorWithParam<T> {
9+
x: T,
10+
}
11+
12+
fn main() {
13+
let res: Result<i32, ()> = Ok(0);
14+
let _ = res.unwrap();
15+
16+
res.expect("disaster!");
17+
//~^ ok_expect
18+
19+
res.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
20+
//~^^ ok_expect
21+
22+
let resres = res;
23+
resres.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
24+
//~^^^ ok_expect
25+
26+
// this one has a suboptimal suggestion, but oh well
27+
std::process::Command::new("rustc")
28+
.arg("-vV")
29+
.output().expect("failed to get rustc version");
30+
//~^^^^^ ok_expect
31+
32+
// the following should not warn, since `expect` isn't implemented unless
33+
// the error type implements `Debug`
34+
let res2: Result<i32, MyError> = Ok(0);
35+
res2.ok().expect("oh noes!");
36+
let res3: Result<u32, MyErrorWithParam<u8>> = Ok(0);
37+
res3.expect("whoof");
38+
//~^ ok_expect
39+
40+
let res4: Result<u32, io::Error> = Ok(0);
41+
res4.expect("argh");
42+
//~^ ok_expect
43+
44+
let res5: io::Result<u32> = Ok(0);
45+
res5.expect("oops");
46+
//~^ ok_expect
47+
48+
let res6: Result<u32, &str> = Ok(0);
49+
res6.expect("meh");
50+
//~^ ok_expect
51+
}

tests/ui/ok_expect.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ fn main() {
1616
res.ok().expect("disaster!");
1717
//~^ ok_expect
1818

19+
res.ok()
20+
.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
21+
//~^^ ok_expect
22+
23+
let resres = res;
24+
resres
25+
.ok()
26+
.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
27+
//~^^^ ok_expect
28+
29+
// this one has a suboptimal suggestion, but oh well
30+
std::process::Command::new("rustc")
31+
.arg("-vV")
32+
.output()
33+
.ok()
34+
.expect("failed to get rustc version");
35+
//~^^^^^ ok_expect
36+
1937
// the following should not warn, since `expect` isn't implemented unless
2038
// the error type implements `Debug`
2139
let res2: Result<i32, MyError> = Ok(0);

tests/ui/ok_expect.stderr

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,109 @@ error: called `ok().expect()` on a `Result` value
44
LL | res.ok().expect("disaster!");
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
= help: you can call `expect()` directly on the `Result`
87
= note: `-D clippy::ok-expect` implied by `-D warnings`
98
= help: to override `-D warnings` add `#[allow(clippy::ok_expect)]`
9+
help: call `expect()` directly on the `Result`
10+
|
11+
LL - res.ok().expect("disaster!");
12+
LL + res.expect("disaster!");
13+
|
14+
15+
error: called `ok().expect()` on a `Result` value
16+
--> tests/ui/ok_expect.rs:19:5
17+
|
18+
LL | / res.ok()
19+
LL | | .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
20+
| |___________________________________________________________________________^
21+
|
22+
help: call `expect()` directly on the `Result`
23+
|
24+
LL - res.ok()
25+
LL - .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
26+
LL + res.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
27+
|
1028

1129
error: called `ok().expect()` on a `Result` value
1230
--> tests/ui/ok_expect.rs:24:5
1331
|
32+
LL | / resres
33+
LL | | .ok()
34+
LL | | .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
35+
| |___________________________________________________________________________^
36+
|
37+
help: call `expect()` directly on the `Result`
38+
|
39+
LL - resres
40+
LL - .ok()
41+
LL - .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
42+
LL + resres.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
43+
|
44+
45+
error: called `ok().expect()` on a `Result` value
46+
--> tests/ui/ok_expect.rs:30:5
47+
|
48+
LL | / std::process::Command::new("rustc")
49+
LL | | .arg("-vV")
50+
LL | | .output()
51+
LL | | .ok()
52+
LL | | .expect("failed to get rustc version");
53+
| |______________________________________________^
54+
|
55+
help: call `expect()` directly on the `Result`
56+
|
57+
LL - .output()
58+
LL - .ok()
59+
LL - .expect("failed to get rustc version");
60+
LL + .output().expect("failed to get rustc version");
61+
|
62+
63+
error: called `ok().expect()` on a `Result` value
64+
--> tests/ui/ok_expect.rs:42:5
65+
|
1466
LL | res3.ok().expect("whoof");
1567
| ^^^^^^^^^^^^^^^^^^^^^^^^^
1668
|
17-
= help: you can call `expect()` directly on the `Result`
69+
help: call `expect()` directly on the `Result`
70+
|
71+
LL - res3.ok().expect("whoof");
72+
LL + res3.expect("whoof");
73+
|
1874

1975
error: called `ok().expect()` on a `Result` value
20-
--> tests/ui/ok_expect.rs:28:5
76+
--> tests/ui/ok_expect.rs:46:5
2177
|
2278
LL | res4.ok().expect("argh");
2379
| ^^^^^^^^^^^^^^^^^^^^^^^^
2480
|
25-
= help: you can call `expect()` directly on the `Result`
81+
help: call `expect()` directly on the `Result`
82+
|
83+
LL - res4.ok().expect("argh");
84+
LL + res4.expect("argh");
85+
|
2686

2787
error: called `ok().expect()` on a `Result` value
28-
--> tests/ui/ok_expect.rs:32:5
88+
--> tests/ui/ok_expect.rs:50:5
2989
|
3090
LL | res5.ok().expect("oops");
3191
| ^^^^^^^^^^^^^^^^^^^^^^^^
3292
|
33-
= help: you can call `expect()` directly on the `Result`
93+
help: call `expect()` directly on the `Result`
94+
|
95+
LL - res5.ok().expect("oops");
96+
LL + res5.expect("oops");
97+
|
3498

3599
error: called `ok().expect()` on a `Result` value
36-
--> tests/ui/ok_expect.rs:36:5
100+
--> tests/ui/ok_expect.rs:54:5
37101
|
38102
LL | res6.ok().expect("meh");
39103
| ^^^^^^^^^^^^^^^^^^^^^^^
40104
|
41-
= help: you can call `expect()` directly on the `Result`
105+
help: call `expect()` directly on the `Result`
106+
|
107+
LL - res6.ok().expect("meh");
108+
LL + res6.expect("meh");
109+
|
42110

43-
error: aborting due to 5 previous errors
111+
error: aborting due to 8 previous errors
44112

0 commit comments

Comments
 (0)