diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 874c435402919..ad006083737dd 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -273,13 +273,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { expr: &hir::Expr<'_>, span: Span, ) -> Option { - if ty.is_unit() - || !ty.is_inhabited_from( - cx.tcx, - cx.tcx.parent_module(expr.hir_id).to_def_id(), - cx.typing_env(), - ) - { + if ty.is_unit() { + return Some(MustUsePath::Suppressed); + } + let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); + if !ty.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()) { return Some(MustUsePath::Suppressed); } @@ -293,6 +291,30 @@ 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<(), UninhabitedType>` (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) + } + // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + && args.type_at(1).is_unit() + && !args.type_at(0).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 0000000000000..a193dfa8c47bc --- /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 0000000000000..8f63e4a7f8323 --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -0,0 +1,101 @@ +//@ edition: 2024 +//@ aux-crate:dep=must_use_result_unit_uninhabited_extern_crate.rs + +#![deny(unused_must_use)] +#![feature(never_type)] + +use core::ops::{ControlFlow, ControlFlow::Continue}; +use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; + +fn result_unit_unit() -> Result<(), ()> { + Ok(()) +} + +fn result_unit_infallible() -> Result<(), core::convert::Infallible> { + Ok(()) +} + +fn result_unit_never() -> Result<(), !> { + Ok(()) +} + +fn result_unit_myuninhabited() -> Result<(), MyUninhabited> { + Ok(()) +} + +fn result_unit_myuninhabited_nonexhaustive() -> Result<(), MyUninhabitedNonexhaustive> { + Ok(()) +} + +trait AssocType { + type Error; +} + +struct S1; +impl AssocType for S1 { + type Error = !; +} + +struct S2; +impl AssocType for S2 { + type Error = (); +} + +fn result_unit_assoctype(_: AT) -> Result<(), AT::Error> { + Ok(()) +} + +trait UsesAssocType { + type Error; + fn method_use_assoc_type(&self) -> Result<(), Self::Error>; +} + +impl UsesAssocType for S1 { + type Error = !; + fn method_use_assoc_type(&self) -> Result<(), Self::Error> { + Ok(()) + } +} + +impl UsesAssocType for S2 { + type Error = (); + fn method_use_assoc_type(&self) -> Result<(), Self::Error> { + Err(()) + } +} + +fn controlflow_unit() -> ControlFlow<()> { + Continue(()) +} + +fn controlflow_infallible_unit() -> ControlFlow { + Continue(()) +} + +fn controlflow_never() -> ControlFlow { + Continue(()) +} + +fn main() { + result_unit_unit(); //~ ERROR: unused `Result` that must be used + result_unit_infallible(); + result_unit_never(); + result_unit_myuninhabited(); + result_unit_myuninhabited_nonexhaustive(); //~ ERROR: unused `Result` that must be used + result_unit_assoctype(S1); + result_unit_assoctype(S2); //~ ERROR: unused `Result` that must be used + S1.method_use_assoc_type(); + S2.method_use_assoc_type(); //~ ERROR: unused `Result` that must be used + + controlflow_unit(); //~ ERROR: unused `ControlFlow` that must be used + controlflow_infallible_unit(); + controlflow_never(); +} + +trait AssocTypeBeforeMonomorphisation { + type Error; + fn generate(&self) -> Result<(), Self::Error>; + fn process(&self) { + self.generate(); //~ 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 0000000000000..31d6f6bcf2bc7 --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -0,0 +1,78 @@ +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:80:5 + | +LL | result_unit_unit(); + | ^^^^^^^^^^^^^^^^^^ + | + = 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 _ = result_unit_unit(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:84:5 + | +LL | result_unit_myuninhabited_nonexhaustive(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = result_unit_myuninhabited_nonexhaustive(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:86:5 + | +LL | result_unit_assoctype(S2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = result_unit_assoctype(S2); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:88:5 + | +LL | S2.method_use_assoc_type(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = S2.method_use_assoc_type(); + | +++++++ + +error: unused `ControlFlow` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:90:5 + | +LL | controlflow_unit(); + | ^^^^^^^^^^^^^^^^^^ + | +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = controlflow_unit(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:99:9 + | +LL | self.generate(); + | ^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = self.generate(); + | +++++++ + +error: aborting due to 6 previous errors +