From 6f89cecf3756c276dbd263810e86c13a652dbb5a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 5 Oct 2025 10:25:17 -0700 Subject: [PATCH] unused_must_use: Don't warn on `Result<(), Uninhabited>` This suppresses warnings on things like `Result<(), !>`, which helps simplify code using the common pattern of having an `Error` associated type: code will only have to check the error if there is a possibility of error. --- compiler/rustc_lint/src/unused.rs | 12 ++++ ...se_result_unit_uninhabited_extern_crate.rs | 4 ++ .../must_use-result-unit-uninhabited.rs | 55 +++++++++++++++++++ .../must_use-result-unit-uninhabited.stderr | 43 +++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs create mode 100644 tests/ui/lint/unused/must_use-result-unit-uninhabited.rs create mode 100644 tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index e70ec18340ad..0eb1e881896b 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -291,6 +291,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { is_ty_must_use(cx, pinned_ty, expr, span) .map(|inner| MustUsePath::Pinned(Box::new(inner))) } + // Suppress warnings on `Result<(), Uninhabited>` (e.g. `Result<(), !>`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::Result, def.did()) + && args.type_at(0).is_unit() + && !args.type_at(1).is_inhabited_from( + cx.tcx, + parent_mod_did, + cx.typing_env(), + ) => + { + Some(MustUsePath::Suppressed) + } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) diff --git a/tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs b/tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs new file mode 100644 index 000000000000..a193dfa8c47b --- /dev/null +++ b/tests/ui/lint/unused/auxiliary/must_use_result_unit_uninhabited_extern_crate.rs @@ -0,0 +1,4 @@ +pub enum MyUninhabited {} + +#[non_exhaustive] +pub enum MyUninhabitedNonexhaustive {} diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs new file mode 100644 index 000000000000..1a7facb91a9a --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -0,0 +1,55 @@ +//@ edition: 2024 +//@ aux-crate:dep=must_use_result_unit_uninhabited_extern_crate.rs + +#![deny(unused_must_use)] +#![feature(never_type)] + +use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; + +fn f1() -> Result<(), ()> { + Ok(()) +} + +fn f2() -> Result<(), core::convert::Infallible> { + Ok(()) +} + +fn f3() -> Result<(), !> { + Ok(()) +} + +fn f4() -> Result<(), MyUninhabited> { + Ok(()) +} + +fn f5() -> Result<(), MyUninhabitedNonexhaustive> { + Ok(()) +} + +trait AssocType { + type Error; +} + +struct S1; +impl AssocType for S1 { + type Error = !; +} + +struct S2; +impl AssocType for S2 { + type Error = (); +} + +fn f6(_: AT) -> Result<(), AT::Error> { + Ok(()) +} + +fn main() { + f1(); //~ ERROR: unused `Result` that must be used + f2(); + f3(); + f4(); + f5(); //~ ERROR: unused `Result` that must be used + f6(S1); + f6(S2); //~ ERROR: unused `Result` that must be used +} diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr new file mode 100644 index 000000000000..0ee06b0504ad --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -0,0 +1,43 @@ +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:48:5 + | +LL | f1(); + | ^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +note: the lint level is defined here + --> $DIR/must_use-result-unit-uninhabited.rs:4:9 + | +LL | #![deny(unused_must_use)] + | ^^^^^^^^^^^^^^^ +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = f1(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:52:5 + | +LL | f5(); + | ^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = f5(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:54:5 + | +LL | f6(S2); + | ^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = f6(S2); + | +++++++ + +error: aborting due to 3 previous errors +