From a4924e2727d4f358396ea71f66da16e686a7038c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 12 Oct 2025 00:36:54 +0200 Subject: [PATCH 1/2] clean-up --- clippy_lints/src/methods/err_expect.rs | 13 +++++-------- clippy_lints/src/methods/ok_expect.rs | 10 ++++------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 6e9aebcf18ae..4353f6302c4b 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,7 +1,6 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; use clippy_utils::ty::has_debug_impl; use rustc_errors::Applicability; use rustc_lint::LateContext; @@ -17,12 +16,10 @@ pub(super) fn check( err_span: Span, msrv: Msrv, ) { - if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - // Grabs the `Result` type - && let result_type = cx.typeck_results().expr_ty(recv) - // Tests if the T type in a `Result` is not None - && let Some(data_type) = get_data_type(cx, result_type) - // Tests if the T type in a `Result` implements debug + let result_ty = cx.typeck_results().expr_ty(recv); + // Grabs the `Result` type + if let Some(data_type) = get_data_type(cx, result_ty) + // Tests if the T type in a `Result` implements Debug && has_debug_impl(cx, data_type) && msrv.meets(cx, msrvs::EXPECT_ERR) { @@ -41,7 +38,7 @@ pub(super) fn check( /// Given a `Result` type, return its data (`T`). fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(), + ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().next(), _ => None, } } diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index c9c1f4865b81..400f22580582 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::res::MaybeDef; use clippy_utils::ty::has_debug_impl; use rustc_hir as hir; use rustc_lint::LateContext; @@ -10,10 +9,9 @@ use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - // lint if the caller of `ok()` is a `Result` - && let result_type = cx.typeck_results().expr_ty(recv) - && let Some(error_type) = get_error_type(cx, result_type) + let result_ty = cx.typeck_results().expr_ty(recv); + // 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) { span_lint_and_help( @@ -30,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1), + ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().nth(1), _ => None, } } From 98164377d7608795c3830dcf60477b60435abf32 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 12 Oct 2025 01:07:05 +0200 Subject: [PATCH 2/2] feat(ok_expect): add autofix --- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/ok_expect.rs | 23 ++++--- tests/ui/ok_expect.fixed | 51 ++++++++++++++++ tests/ui/ok_expect.rs | 18 ++++++ tests/ui/ok_expect.stderr | 86 ++++++++++++++++++++++++--- 5 files changed, 163 insertions(+), 17 deletions(-) create mode 100644 tests/ui/ok_expect.fixed diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index c9066be51c44..8e0733eff87b 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -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); }, diff --git a/clippy_lints/src/methods/ok_expect.rs b/clippy_lints/src/methods/ok_expect.rs index 400f22580582..5f1cae130dae 100644 --- a/clippy_lints/src/methods/ok_expect.rs +++ b/clippy_lints/src/methods/ok_expect.rs @@ -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, + ); + }, ); } } diff --git a/tests/ui/ok_expect.fixed b/tests/ui/ok_expect.fixed new file mode 100644 index 000000000000..2a05b8805e4d --- /dev/null +++ b/tests/ui/ok_expect.fixed @@ -0,0 +1,51 @@ +#![allow(clippy::unnecessary_literal_unwrap)] + +use std::io; + +struct MyError(()); // doesn't implement Debug + +#[derive(Debug)] +struct MyErrorWithParam { + x: T, +} + +fn main() { + let res: Result = 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 = Ok(0); + res2.ok().expect("oh noes!"); + let res3: Result> = Ok(0); + res3.expect("whoof"); + //~^ ok_expect + + let res4: Result = Ok(0); + res4.expect("argh"); + //~^ ok_expect + + let res5: io::Result = Ok(0); + res5.expect("oops"); + //~^ ok_expect + + let res6: Result = Ok(0); + res6.expect("meh"); + //~^ ok_expect +} diff --git a/tests/ui/ok_expect.rs b/tests/ui/ok_expect.rs index efb56f242a74..3761aa26f6e8 100644 --- a/tests/ui/ok_expect.rs +++ b/tests/ui/ok_expect.rs @@ -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 = Ok(0); diff --git a/tests/ui/ok_expect.stderr b/tests/ui/ok_expect.stderr index a9e3533d8ca1..848a10e671db 100644 --- a/tests/ui/ok_expect.stderr +++ b/tests/ui/ok_expect.stderr @@ -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