diff --git a/compiler/rustc_lint/src/unused/must_use.rs b/compiler/rustc_lint/src/unused/must_use.rs index 5bbe84a51fa2f..62da7ed6a1226 100644 --- a/compiler/rustc_lint/src/unused/must_use.rs +++ b/compiler/rustc_lint/src/unused/must_use.rs @@ -133,18 +133,11 @@ pub enum MustUsePath { /// Returns `Some(path)` if `ty` should be considered as "`must_use`" in the context of `expr` /// (`expr` is used to get the parent module, which can affect which types are considered uninhabited). -/// -/// If `simplify_uninhabited` is true, this function considers `Result` and -/// `ControlFlow` the same as `T` (we don't set this *yet* in rustc, but expose this -/// so clippy can use this). -// -// FIXME: remove `simplify_uninhabited` once clippy had a release with the new semantics. #[instrument(skip(cx, expr), level = "debug", ret)] pub fn is_ty_must_use<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, - simplify_uninhabited: bool, ) -> IsTyMustUse { if ty.is_unit() { return IsTyMustUse::Trivial; @@ -157,50 +150,29 @@ pub fn is_ty_must_use<'tcx>( match *ty.kind() { _ if is_uninhabited(ty) => IsTyMustUse::Trivial, ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { - is_ty_must_use(cx, boxed, expr, simplify_uninhabited) - .map(|inner| MustUsePath::Boxed(Box::new(inner))) + is_ty_must_use(cx, boxed, expr).map(|inner| MustUsePath::Boxed(Box::new(inner))) } ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => { let pinned_ty = args.type_at(0); - is_ty_must_use(cx, pinned_ty, expr, simplify_uninhabited) - .map(|inner| MustUsePath::Pinned(Box::new(inner))) + is_ty_must_use(cx, pinned_ty, expr).map(|inner| MustUsePath::Pinned(Box::new(inner))) } // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. ty::Adt(def, args) - if simplify_uninhabited - && cx.tcx.is_diagnostic_item(sym::Result, def.did()) + if cx.tcx.is_diagnostic_item(sym::Result, def.did()) && is_uninhabited(args.type_at(1)) => { let ok_ty = args.type_at(0); - is_ty_must_use(cx, ok_ty, expr, simplify_uninhabited) - .map(|path| MustUsePath::Result(Box::new(path))) + is_ty_must_use(cx, ok_ty, expr).map(|path| MustUsePath::Result(Box::new(path))) } // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. ty::Adt(def, args) - if simplify_uninhabited - && cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) + if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did()) && is_uninhabited(args.type_at(0)) => { let continue_ty = args.type_at(1); - is_ty_must_use(cx, continue_ty, expr, simplify_uninhabited) + is_ty_must_use(cx, continue_ty, expr) .map(|path| MustUsePath::ControlFlow(Box::new(path))) } - // 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() - && is_uninhabited(args.type_at(1)) => - { - IsTyMustUse::Trivial - } - // 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() - && is_uninhabited(args.type_at(0)) => - { - IsTyMustUse::Trivial - } ty::Adt(def, _) => { is_def_must_use(cx, def.did(), expr.span).map_or(IsTyMustUse::No, IsTyMustUse::Yes) } @@ -254,7 +226,7 @@ pub fn is_ty_must_use<'tcx>( let mut nested_must_use = Vec::new(); tys.iter().zip(elem_exprs).enumerate().for_each(|(i, (ty, expr))| { - let must_use = is_ty_must_use(cx, ty, expr, simplify_uninhabited); + let must_use = is_ty_must_use(cx, ty, expr); all_trivial &= matches!(must_use, IsTyMustUse::Trivial); if let IsTyMustUse::Yes(path) = must_use { @@ -276,8 +248,9 @@ pub fn is_ty_must_use<'tcx>( // If the array is empty we don't lint, to avoid false positives Some(0) | None => IsTyMustUse::No, // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(len) => is_ty_must_use(cx, ty, expr, simplify_uninhabited) - .map(|inner| MustUsePath::Array(Box::new(inner), len)), + Some(len) => { + is_ty_must_use(cx, ty, expr).map(|inner| MustUsePath::Array(Box::new(inner), len)) + } }, ty::Closure(..) | ty::CoroutineClosure(..) => { IsTyMustUse::Yes(MustUsePath::Closure(expr.span)) @@ -341,7 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let ty = cx.typeck_results().expr_ty(expr); - let must_use_result = is_ty_must_use(cx, ty, expr, false); + let must_use_result = is_ty_must_use(cx, ty, expr); let type_lint_emitted_or_trivial = match must_use_result { IsTyMustUse::Yes(path) => { emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs index 8f63e4a7f8323..d1b47374a11b8 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -7,6 +7,11 @@ use core::ops::{ControlFlow, ControlFlow::Continue}; use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; +#[must_use] +struct MustUse; + +struct Struct; + fn result_unit_unit() -> Result<(), ()> { Ok(()) } @@ -19,6 +24,14 @@ fn result_unit_never() -> Result<(), !> { Ok(()) } +fn result_struct_never() -> Result { + Ok(Struct) +} + +fn result_must_use_never() -> Result { + Ok(MustUse) +} + fn result_unit_myuninhabited() -> Result<(), MyUninhabited> { Ok(()) } @@ -80,6 +93,8 @@ fn main() { result_unit_unit(); //~ ERROR: unused `Result` that must be used result_unit_infallible(); result_unit_never(); + result_must_use_never(); //~ ERROR: unused `MustUse` in a `Result` with an uninhabited error that must be used + result_struct_never(); result_unit_myuninhabited(); result_unit_myuninhabited_nonexhaustive(); //~ ERROR: unused `Result` that must be used result_unit_assoctype(S1); diff --git a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr index 31d6f6bcf2bc7..b4c62c7690b49 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -1,5 +1,5 @@ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:80:5 + --> $DIR/must_use-result-unit-uninhabited.rs:93:5 | LL | result_unit_unit(); | ^^^^^^^^^^^^^^^^^^ @@ -15,8 +15,14 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = result_unit_unit(); | +++++++ +error: unused `MustUse` in a `Result` with an uninhabited error that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:96:5 + | +LL | result_must_use_never(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:84:5 + --> $DIR/must_use-result-unit-uninhabited.rs:99:5 | LL | result_unit_myuninhabited_nonexhaustive(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +34,7 @@ LL | let _ = result_unit_myuninhabited_nonexhaustive(); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:86:5 + --> $DIR/must_use-result-unit-uninhabited.rs:101:5 | LL | result_unit_assoctype(S2); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +46,7 @@ LL | let _ = result_unit_assoctype(S2); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:88:5 + --> $DIR/must_use-result-unit-uninhabited.rs:103:5 | LL | S2.method_use_assoc_type(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +58,7 @@ LL | let _ = S2.method_use_assoc_type(); | +++++++ error: unused `ControlFlow` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:90:5 + --> $DIR/must_use-result-unit-uninhabited.rs:105:5 | LL | controlflow_unit(); | ^^^^^^^^^^^^^^^^^^ @@ -63,7 +69,7 @@ LL | let _ = controlflow_unit(); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:99:9 + --> $DIR/must_use-result-unit-uninhabited.rs:114:9 | LL | self.generate(); | ^^^^^^^^^^^^^^^ @@ -74,5 +80,5 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = self.generate(); | +++++++ -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors