feat(ok_expect): add autofix

This commit is contained in:
Ada Alakbarova 2025-10-12 01:07:05 +02:00
parent a4924e2727
commit 98164377d7
No known key found for this signature in database
5 changed files with 163 additions and 17 deletions

View file

@ -5115,7 +5115,7 @@ impl Methods {
},
(sym::expect, [_]) => {
match method_call(recv) {
Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv),
Some((sym::ok, recv_inner, [], _, _)) => ok_expect::check(cx, expr, recv, recv_inner),
Some((sym::err, recv, [], err_span, _)) => {
err_expect::check(cx, expr, recv, span, err_span, self.msrv);
},

View file

@ -1,26 +1,35 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::has_debug_impl;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::{self, Ty};
use rustc_span::sym;
use super::OK_EXPECT;
/// lint use of `ok().expect()` for `Result`s
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
let result_ty = cx.typeck_results().expr_ty(recv);
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, recv_inner: &hir::Expr<'_>) {
let result_ty = cx.typeck_results().expr_ty(recv_inner);
// lint if the caller of `ok()` is a `Result`
if let Some(error_type) = get_error_type(cx, result_ty)
&& has_debug_impl(cx, error_type)
&& let Some(span) = recv.span.trim_start(recv_inner.span)
{
span_lint_and_help(
span_lint_and_then(
cx,
OK_EXPECT,
expr.span,
"called `ok().expect()` on a `Result` value",
None,
"you can call `expect()` directly on the `Result`",
|diag| {
let span = cx.sess().source_map().span_extend_while_whitespace(span);
diag.span_suggestion_verbose(
span,
"call `expect()` directly on the `Result`",
String::new(),
Applicability::MachineApplicable,
);
},
);
}
}

51
tests/ui/ok_expect.fixed Normal file
View file

@ -0,0 +1,51 @@
#![allow(clippy::unnecessary_literal_unwrap)]
use std::io;
struct MyError(()); // doesn't implement Debug
#[derive(Debug)]
struct MyErrorWithParam<T> {
x: T,
}
fn main() {
let res: Result<i32, ()> = Ok(0);
let _ = res.unwrap();
res.expect("disaster!");
//~^ ok_expect
res.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
//~^^ ok_expect
let resres = res;
resres.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
//~^^^ ok_expect
// this one has a suboptimal suggestion, but oh well
std::process::Command::new("rustc")
.arg("-vV")
.output().expect("failed to get rustc version");
//~^^^^^ ok_expect
// the following should not warn, since `expect` isn't implemented unless
// the error type implements `Debug`
let res2: Result<i32, MyError> = Ok(0);
res2.ok().expect("oh noes!");
let res3: Result<u32, MyErrorWithParam<u8>> = Ok(0);
res3.expect("whoof");
//~^ ok_expect
let res4: Result<u32, io::Error> = Ok(0);
res4.expect("argh");
//~^ ok_expect
let res5: io::Result<u32> = Ok(0);
res5.expect("oops");
//~^ ok_expect
let res6: Result<u32, &str> = Ok(0);
res6.expect("meh");
//~^ ok_expect
}

View file

@ -16,6 +16,24 @@ fn main() {
res.ok().expect("disaster!");
//~^ ok_expect
res.ok()
.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
//~^^ ok_expect
let resres = res;
resres
.ok()
.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
//~^^^ ok_expect
// this one has a suboptimal suggestion, but oh well
std::process::Command::new("rustc")
.arg("-vV")
.output()
.ok()
.expect("failed to get rustc version");
//~^^^^^ ok_expect
// the following should not warn, since `expect` isn't implemented unless
// the error type implements `Debug`
let res2: Result<i32, MyError> = Ok(0);

View file

@ -4,41 +4,109 @@ error: called `ok().expect()` on a `Result` value
LL | res.ok().expect("disaster!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: you can call `expect()` directly on the `Result`
= note: `-D clippy::ok-expect` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::ok_expect)]`
help: call `expect()` directly on the `Result`
|
LL - res.ok().expect("disaster!");
LL + res.expect("disaster!");
|
error: called `ok().expect()` on a `Result` value
--> tests/ui/ok_expect.rs:19:5
|
LL | / res.ok()
LL | | .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
| |___________________________________________________________________________^
|
help: call `expect()` directly on the `Result`
|
LL - res.ok()
LL - .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
LL + res.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
|
error: called `ok().expect()` on a `Result` value
--> tests/ui/ok_expect.rs:24:5
|
LL | / resres
LL | | .ok()
LL | | .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
| |___________________________________________________________________________^
|
help: call `expect()` directly on the `Result`
|
LL - resres
LL - .ok()
LL - .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
LL + resres.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong");
|
error: called `ok().expect()` on a `Result` value
--> tests/ui/ok_expect.rs:30:5
|
LL | / std::process::Command::new("rustc")
LL | | .arg("-vV")
LL | | .output()
LL | | .ok()
LL | | .expect("failed to get rustc version");
| |______________________________________________^
|
help: call `expect()` directly on the `Result`
|
LL - .output()
LL - .ok()
LL - .expect("failed to get rustc version");
LL + .output().expect("failed to get rustc version");
|
error: called `ok().expect()` on a `Result` value
--> tests/ui/ok_expect.rs:42:5
|
LL | res3.ok().expect("whoof");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: you can call `expect()` directly on the `Result`
help: call `expect()` directly on the `Result`
|
LL - res3.ok().expect("whoof");
LL + res3.expect("whoof");
|
error: called `ok().expect()` on a `Result` value
--> tests/ui/ok_expect.rs:28:5
--> tests/ui/ok_expect.rs:46:5
|
LL | res4.ok().expect("argh");
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: you can call `expect()` directly on the `Result`
help: call `expect()` directly on the `Result`
|
LL - res4.ok().expect("argh");
LL + res4.expect("argh");
|
error: called `ok().expect()` on a `Result` value
--> tests/ui/ok_expect.rs:32:5
--> tests/ui/ok_expect.rs:50:5
|
LL | res5.ok().expect("oops");
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: you can call `expect()` directly on the `Result`
help: call `expect()` directly on the `Result`
|
LL - res5.ok().expect("oops");
LL + res5.expect("oops");
|
error: called `ok().expect()` on a `Result` value
--> tests/ui/ok_expect.rs:36:5
--> tests/ui/ok_expect.rs:54:5
|
LL | res6.ok().expect("meh");
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: you can call `expect()` directly on the `Result`
help: call `expect()` directly on the `Result`
|
LL - res6.ok().expect("meh");
LL + res6.expect("meh");
|
error: aborting due to 5 previous errors
error: aborting due to 8 previous errors