diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs index 9eb45666442fc..a4a0a0def7eaa 100644 --- a/compiler/rustc_lint/src/unused/must_use.rs +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -193,6 +193,14 @@ pub fn is_ty_must_use<'tcx>( { IsTyMustUse::Trivial } + // Suppress warnings on `Result` (e.g. `Result`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::Result, def.did()) + && is_uninhabited(args.type_at(0)) + && args.type_at(1).is_unit() => + { + IsTyMustUse::Trivial + } // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). ty::Adt(def, args) if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) @@ -201,6 +209,14 @@ pub fn is_ty_must_use<'tcx>( { IsTyMustUse::Trivial } + // Suppress warnings on `ControlFlow<(), Uninhabited>` (e.g. `ControlFlow<(), !>`). + ty::Adt(def, args) + if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + && args.type_at(0).is_unit() + && is_uninhabited(args.type_at(1)) => + { + IsTyMustUse::Trivial + } ty::Adt(def, _) => { is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes) } diff --git a/tests/ui/lint/unused/must_use-result-uninhabited-unit.rs b/tests/ui/lint/unused/must_use-result-uninhabited-unit.rs new file mode 100644 index 0000000000000..ccd0f50f37014 --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-uninhabited-unit.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::Break}; +use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; + +fn result_unit_unit() -> Result<(), ()> { + Err(()) +} + +fn result_infallible_unit() -> Result { + Err(()) +} + +fn result_never_unit() -> Result { + Err(()) +} + +fn result_myuninhabited_unit() -> Result { + Err(()) +} + +fn result_myuninhabited_nonexhaustive_unit() -> Result { + Err(()) +} + +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 { + Err(()) +} + +trait UsesAssocType { + type Error; + fn method_use_assoc_type(&self) -> Result; +} + +impl UsesAssocType for S1 { + type Error = !; + fn method_use_assoc_type(&self) -> Result { + Err(()) + } +} + +impl UsesAssocType for S2 { + type Error = (); + fn method_use_assoc_type(&self) -> Result { + Ok(()) + } +} + +fn controlflow_unit_unit() -> ControlFlow<()> { + Break(()) +} + +fn controlflow_unit_infallible() -> ControlFlow<(), core::convert::Infallible> { + Break(()) +} + +fn controlflow_unit_never() -> ControlFlow<(), !> { + Break(()) +} + +fn main() { + result_unit_unit(); //~ ERROR: unused `Result` that must be used + result_infallible_unit(); + result_never_unit(); + result_myuninhabited_unit(); + result_myuninhabited_nonexhaustive_unit(); //~ 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_unit(); //~ ERROR: unused `ControlFlow` that must be used + controlflow_unit_infallible(); + controlflow_unit_never(); +} + +trait AssocTypeBeforeMonomorphisation { + type Error; + fn generate(&self) -> Result; + fn process(&self) { + self.generate(); //~ ERROR: unused `Result` that must be used + } +} diff --git a/tests/ui/lint/unused/must_use-result-uninhabited-unit.stderr b/tests/ui/lint/unused/must_use-result-uninhabited-unit.stderr new file mode 100644 index 0000000000000..70e249cb982bc --- /dev/null +++ b/tests/ui/lint/unused/must_use-result-uninhabited-unit.stderr @@ -0,0 +1,78 @@ +error: unused `Result` that must be used + --> $DIR/must_use-result-uninhabited-unit.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-uninhabited-unit.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-uninhabited-unit.rs:84:5 + | +LL | result_myuninhabited_nonexhaustive_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = result_myuninhabited_nonexhaustive_unit(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-uninhabited-unit.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-uninhabited-unit.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-uninhabited-unit.rs:90:5 + | +LL | controlflow_unit_unit(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = controlflow_unit_unit(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-uninhabited-unit.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 +