From 622197bae545e73e093b502d8ede021fbd7ee595 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 28 Oct 2025 15:24:45 +0100 Subject: [PATCH 1/6] drive-by-cleanup: merge an early return into a match --- compiler/rustc_lint/src/unused.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 533ab67053095..0ce54b3ac8f40 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -279,11 +279,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); let is_uninhabited = |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); - if is_uninhabited(ty) { - return Some(MustUsePath::Suppressed); - } match *ty.kind() { + _ if is_uninhabited(ty) => Some(MustUsePath::Suppressed), ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { is_ty_must_use(cx, boxed, expr, span) .map(|inner| MustUsePath::Boxed(Box::new(inner))) From f6839c97b85e76078b3361842320f0899a0f655e Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 28 Oct 2025 15:48:49 +0100 Subject: [PATCH 2/6] add `must_use` tests for `Result` --- .../must_use-result-unit-uninhabited.rs | 15 ++++++++ .../must_use-result-unit-uninhabited.stderr | 38 +++++++++++++++---- 2 files changed, 46 insertions(+), 7 deletions(-) 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..0bddc9a7aa9b4 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 `Result` that must be used + result_struct_never(); //~ ERROR: unused `Result` that must be used 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..b8448465f924a 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(); | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,31 @@ LL | let _ = result_unit_unit(); | +++++++ error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:84:5 + --> $DIR/must_use-result-unit-uninhabited.rs:96:5 + | +LL | result_must_use_never(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = result_must_use_never(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:97:5 + | +LL | result_struct_never(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: this `Result` may be an `Err` variant, which should be handled +help: use `let _ = ...` to ignore the resulting value + | +LL | let _ = result_struct_never(); + | +++++++ + +error: unused `Result` that must be used + --> $DIR/must_use-result-unit-uninhabited.rs:99:5 | LL | result_unit_myuninhabited_nonexhaustive(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +52,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 +64,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 +76,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 +87,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 +98,5 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = self.generate(); | +++++++ -error: aborting due to 6 previous errors +error: aborting due to 8 previous errors From 17a892b7f336d0439c72145ac4e730cfb43ab1c2 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Tue, 28 Oct 2025 15:24:45 +0100 Subject: [PATCH 3/6] consider `Result`/`ControlFlow` the same as `T` for must_use lint (or more accurately `Result`/`ControlFlow`). This generalizes a previous change where we only did this for `T = ()`. --- compiler/rustc_lint/src/unused.rs | 44 ++++++++++++++++--- .../must_use-result-unit-uninhabited.rs | 4 +- .../must_use-result-unit-uninhabited.stderr | 22 +--------- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 0ce54b3ac8f40..511e16358326d 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -259,6 +259,10 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { Opaque(Box), TraitObject(Box), TupleElement(Vec<(usize, Self)>), + /// `Result` + Result(Box), + /// `ControlFlow` + ControlFlow(Box), Array(Box, u64), /// The root of the unused_closures lint. Closure(Span), @@ -291,21 +295,23 @@ 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<(), !>`). + // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. 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)) => { - Some(MustUsePath::Suppressed) + let ok_ty = args.type_at(0); + is_ty_must_use(cx, ok_ty, expr, span).map(Box::new).map(MustUsePath::Result) } - // Suppress warnings on `ControlFlow` (e.g. `ControlFlow`). + // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. 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)) => { - Some(MustUsePath::Suppressed) + let continue_ty = args.type_at(1); + is_ty_must_use(cx, continue_ty, expr, span) + .map(Box::new) + .map(MustUsePath::ControlFlow) } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { @@ -498,6 +504,32 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } } + MustUsePath::Result(path) => { + let descr_post = + &format!(" in a `Result` with an uninhabited error{descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::ControlFlow(path) => { + let descr_post = + &format!(" in a `ControlFlow` with an uninhabited break {descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } MustUsePath::Array(path, len) => { let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); emit_must_use_untranslated( 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 0bddc9a7aa9b4..d1b47374a11b8 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -93,8 +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 `Result` that must be used - result_struct_never(); //~ ERROR: unused `Result` that must be used + 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 b8448465f924a..b4c62c7690b49 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.stderr @@ -15,29 +15,11 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = result_unit_unit(); | +++++++ -error: unused `Result` that must be used +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(); | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: this `Result` may be an `Err` variant, which should be handled -help: use `let _ = ...` to ignore the resulting value - | -LL | let _ = result_must_use_never(); - | +++++++ - -error: unused `Result` that must be used - --> $DIR/must_use-result-unit-uninhabited.rs:97:5 - | -LL | result_struct_never(); - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: this `Result` may be an `Err` variant, which should be handled -help: use `let _ = ...` to ignore the resulting value - | -LL | let _ = result_struct_never(); - | +++++++ error: unused `Result` that must be used --> $DIR/must_use-result-unit-uninhabited.rs:99:5 @@ -98,5 +80,5 @@ help: use `let _ = ...` to ignore the resulting value LL | let _ = self.generate(); | +++++++ -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors From 3ab6b23975e3b9bdf19a02d5b81bbee8d8052b54 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 6 Nov 2025 12:35:07 +0100 Subject: [PATCH 4/6] move things outside god I actually hate nested functions now lol. just use modules >:( --- compiler/rustc_lint/src/unused.rs | 672 +++++++++++++++--------------- 1 file changed, 326 insertions(+), 346 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 511e16358326d..c41b3488d3545 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -208,378 +208,358 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); } + } +} - fn check_fn_must_use( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - expr_is_from_block: bool, - ) -> bool { - let maybe_def_id = match expr.kind { - hir::ExprKind::Call(callee, _) => { - match callee.kind { - hir::ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, callee.hir_id) { - Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), - // `Res::Local` if it was a closure, for which we - // do not currently support must-use linting - _ => None, - } - } +fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_block: bool) -> bool { + let maybe_def_id = match expr.kind { + hir::ExprKind::Call(callee, _) => { + match callee.kind { + hir::ExprKind::Path(ref qpath) => { + match cx.qpath_res(qpath, callee.hir_id) { + Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), + // `Res::Local` if it was a closure, for which we + // do not currently support must-use linting _ => None, } } - hir::ExprKind::MethodCall(..) => { - cx.typeck_results().type_dependent_def_id(expr.hir_id) - } _ => None, - }; - if let Some(def_id) = maybe_def_id { - check_must_use_def( - cx, - def_id, - expr.span, - "return value of ", - "", - expr_is_from_block, - ) - } else { - false } } + hir::ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), + _ => None, + }; + if let Some(def_id) = maybe_def_id { + check_must_use_def(cx, def_id, expr.span, "return value of ", "", expr_is_from_block) + } else { + false + } +} - /// A path through a type to a must_use source. Contains useful info for the lint. - #[derive(Debug)] - enum MustUsePath { - /// Suppress must_use checking. - Suppressed, - /// The root of the normal must_use lint with an optional message. - Def(Span, DefId, Option), - Boxed(Box), - Pinned(Box), - Opaque(Box), - TraitObject(Box), - TupleElement(Vec<(usize, Self)>), - /// `Result` - Result(Box), - /// `ControlFlow` - ControlFlow(Box), - Array(Box, u64), - /// The root of the unused_closures lint. - Closure(Span), - /// The root of the unused_coroutines lint. - Coroutine(Span), - } +/// A path through a type to a must_use source. Contains useful info for the lint. +#[derive(Debug)] +enum MustUsePath { + /// Suppress must_use checking. + Suppressed, + /// The root of the normal must_use lint with an optional message. + Def(Span, DefId, Option), + Boxed(Box), + Pinned(Box), + Opaque(Box), + TraitObject(Box), + TupleElement(Vec<(usize, Self)>), + /// `Result` + Result(Box), + /// `ControlFlow` + ControlFlow(Box), + Array(Box, u64), + /// The root of the unused_closures lint. + Closure(Span), + /// The root of the unused_coroutines lint. + Coroutine(Span), +} - #[instrument(skip(cx, expr), level = "debug", ret)] - fn is_ty_must_use<'tcx>( - cx: &LateContext<'tcx>, - ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - span: Span, - ) -> Option { - if ty.is_unit() { - return Some(MustUsePath::Suppressed); - } - let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); - let is_uninhabited = - |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); - - match *ty.kind() { - _ if is_uninhabited(ty) => Some(MustUsePath::Suppressed), - ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { - is_ty_must_use(cx, boxed, expr, span) - .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, span) - .map(|inner| MustUsePath::Pinned(Box::new(inner))) - } - // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. - ty::Adt(def, args) - 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, span).map(Box::new).map(MustUsePath::Result) - } - // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. - ty::Adt(def, args) - 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, span) - .map(Box::new) - .map(MustUsePath::ControlFlow) - } - 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()) - // We only care about self bounds for the impl-trait - .filter_only_self() - .find_map(|(pred, _span)| { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::ClauseKind::Trait(ref poly_trait_predicate) = - pred.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; - - is_def_must_use(cx, def_id, span) - } else { - None - } - }) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) - } - ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() +#[instrument(skip(cx, expr), level = "debug", ret)] +fn is_ty_must_use<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + span: Span, +) -> Option { + if ty.is_unit() { + return Some(MustUsePath::Suppressed); + } + let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id(); + let is_uninhabited = + |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env()); + + match *ty.kind() { + _ if is_uninhabited(ty) => Some(MustUsePath::Suppressed), + ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { + is_ty_must_use(cx, boxed, expr, span).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, span) + .map(|inner| MustUsePath::Pinned(Box::new(inner))) + } + // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. + ty::Adt(def, args) + 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, span).map(Box::new).map(MustUsePath::Result) + } + // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. + ty::Adt(def, args) + 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, span).map(Box::new).map(MustUsePath::ControlFlow) + } + 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()) + // We only care about self bounds for the impl-trait + .filter_only_self() + .find_map(|(pred, _span)| { + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::ClauseKind::Trait(ref poly_trait_predicate) = + pred.kind().skip_binder() { - let def_id = trait_ref.def_id; + let def_id = poly_trait_predicate.trait_ref.def_id; + is_def_must_use(cx, def_id, span) - .map(|inner| MustUsePath::TraitObject(Box::new(inner))) } else { None } - }), - ty::Tuple(tys) => { - let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { - debug_assert_eq!(elem_exprs.len(), tys.len()); - elem_exprs - } else { - &[] - }; + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) + } + ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| { + if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { + let def_id = trait_ref.def_id; + is_def_must_use(cx, def_id, span) + .map(|inner| MustUsePath::TraitObject(Box::new(inner))) + } else { + None + } + }), + ty::Tuple(tys) => { + let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { + debug_assert_eq!(elem_exprs.len(), tys.len()); + elem_exprs + } else { + &[] + }; - // Default to `expr`. - let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); + // Default to `expr`. + let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); - let nested_must_use = tys - .iter() - .zip(elem_exprs) - .enumerate() - .filter_map(|(i, (ty, expr))| { - is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) - }) - .collect::>(); + let nested_must_use = tys + .iter() + .zip(elem_exprs) + .enumerate() + .filter_map(|(i, (ty, expr))| { + is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) + }) + .collect::>(); - if !nested_must_use.is_empty() { - Some(MustUsePath::TupleElement(nested_must_use)) - } else { - None - } - } - ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { - // If the array is empty we don't lint, to avoid false positives - Some(0) | None => None, - // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(len) => is_ty_must_use(cx, ty, expr, span) - .map(|inner| MustUsePath::Array(Box::new(inner), len)), - }, - ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)), - ty::Coroutine(def_id, ..) => { - // async fn should be treated as "implementor of `Future`" - let must_use = if cx.tcx.coroutine_is_async(def_id) { - let def_id = cx.tcx.lang_items().future_trait()?; - is_def_must_use(cx, def_id, span) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) - } else { - None - }; - must_use.or(Some(MustUsePath::Coroutine(span))) - } - _ => None, + if !nested_must_use.is_empty() { + Some(MustUsePath::TupleElement(nested_must_use)) + } else { + None } } - - fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option { - if let Some(reason) = find_attr!( - cx.tcx.get_all_attrs(def_id), - AttributeKind::MustUse { reason, .. } => reason - ) { - // check for #[must_use = "..."] - Some(MustUsePath::Def(span, def_id, *reason)) + ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) { + // If the array is empty we don't lint, to avoid false positives + Some(0) | None => None, + // If the array is definitely non-empty, we can do `#[must_use]` checking. + Some(len) => is_ty_must_use(cx, ty, expr, span) + .map(|inner| MustUsePath::Array(Box::new(inner), len)), + }, + ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)), + ty::Coroutine(def_id, ..) => { + // async fn should be treated as "implementor of `Future`" + let must_use = if cx.tcx.coroutine_is_async(def_id) { + let def_id = cx.tcx.lang_items().future_trait()?; + is_def_must_use(cx, def_id, span).map(|inner| MustUsePath::Opaque(Box::new(inner))) } else { None - } + }; + must_use.or(Some(MustUsePath::Coroutine(span))) } + _ => None, + } +} - // Returns whether further errors should be suppressed because either a lint has been - // emitted or the type should be ignored. - fn check_must_use_def( - cx: &LateContext<'_>, - def_id: DefId, - span: Span, - descr_pre_path: &str, - descr_post_path: &str, - expr_is_from_block: bool, - ) -> bool { - is_def_must_use(cx, def_id, span) - .map(|must_use_path| { - emit_must_use_untranslated( - cx, - &must_use_path, - descr_pre_path, - descr_post_path, - 1, - false, - expr_is_from_block, - ) - }) - .is_some() - } +fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option { + if let Some(reason) = find_attr!( + cx.tcx.get_all_attrs(def_id), + AttributeKind::MustUse { reason, .. } => reason + ) { + // check for #[must_use = "..."] + Some(MustUsePath::Def(span, def_id, *reason)) + } else { + None + } +} - #[instrument(skip(cx), level = "debug")] - fn emit_must_use_untranslated( - cx: &LateContext<'_>, - path: &MustUsePath, - descr_pre: &str, - descr_post: &str, - plural_len: usize, - is_inner: bool, - expr_is_from_block: bool, - ) { - let plural_suffix = pluralize!(plural_len); - - match path { - MustUsePath::Suppressed => {} - MustUsePath::Boxed(path) => { - let descr_pre = &format!("{descr_pre}boxed "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::Pinned(path) => { - let descr_pre = &format!("{descr_pre}pinned "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::Opaque(path) => { - let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::TraitObject(path) => { - let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::TupleElement(elems) => { - for (index, path) in elems { - let descr_post = &format!(" in tuple element {index}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - } - MustUsePath::Result(path) => { - let descr_post = - &format!(" in a `Result` with an uninhabited error{descr_post}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::ControlFlow(path) => { - let descr_post = - &format!(" in a `ControlFlow` with an uninhabited break {descr_post}"); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len, - true, - expr_is_from_block, - ); - } - MustUsePath::Array(path, len) => { - let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); - emit_must_use_untranslated( - cx, - path, - descr_pre, - descr_post, - plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), - true, - expr_is_from_block, - ); - } - MustUsePath::Closure(span) => { - cx.emit_span_lint( - UNUSED_MUST_USE, - *span, - UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, - ); - } - MustUsePath::Coroutine(span) => { - cx.emit_span_lint( - UNUSED_MUST_USE, - *span, - UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post }, - ); - } - MustUsePath::Def(span, def_id, reason) => { - let span = span.find_ancestor_not_from_macro().unwrap_or(*span); - cx.emit_span_lint( - UNUSED_MUST_USE, - span, - UnusedDef { - pre: descr_pre, - post: descr_post, - cx, - def_id: *def_id, - note: *reason, - suggestion: (!is_inner).then_some(if expr_is_from_block { - UnusedDefSuggestion::BlockTailExpr { - before_span: span.shrink_to_lo(), - after_span: span.shrink_to_hi(), - } - } else { - UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() } - }), - }, - ); - } +// Returns whether further errors should be suppressed because either a lint has been +// emitted or the type should be ignored. +fn check_must_use_def( + cx: &LateContext<'_>, + def_id: DefId, + span: Span, + descr_pre_path: &str, + descr_post_path: &str, + expr_is_from_block: bool, +) -> bool { + is_def_must_use(cx, def_id, span) + .map(|must_use_path| { + emit_must_use_untranslated( + cx, + &must_use_path, + descr_pre_path, + descr_post_path, + 1, + false, + expr_is_from_block, + ) + }) + .is_some() +} + +#[instrument(skip(cx), level = "debug")] +fn emit_must_use_untranslated( + cx: &LateContext<'_>, + path: &MustUsePath, + descr_pre: &str, + descr_post: &str, + plural_len: usize, + is_inner: bool, + expr_is_from_block: bool, +) { + let plural_suffix = pluralize!(plural_len); + + match path { + MustUsePath::Suppressed => {} + MustUsePath::Boxed(path) => { + let descr_pre = &format!("{descr_pre}boxed "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Pinned(path) => { + let descr_pre = &format!("{descr_pre}pinned "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Opaque(path) => { + let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::TraitObject(path) => { + let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::TupleElement(elems) => { + for (index, path) in elems { + let descr_post = &format!(" in tuple element {index}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); } } + MustUsePath::Result(path) => { + let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::ControlFlow(path) => { + let descr_post = &format!(" in a `ControlFlow` with an uninhabited break {descr_post}"); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } + MustUsePath::Array(path, len) => { + let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), + true, + expr_is_from_block, + ); + } + MustUsePath::Closure(span) => { + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, + ); + } + MustUsePath::Coroutine(span) => { + cx.emit_span_lint( + UNUSED_MUST_USE, + *span, + UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post }, + ); + } + MustUsePath::Def(span, def_id, reason) => { + let span = span.find_ancestor_not_from_macro().unwrap_or(*span); + cx.emit_span_lint( + UNUSED_MUST_USE, + span, + UnusedDef { + pre: descr_pre, + post: descr_post, + cx, + def_id: *def_id, + note: *reason, + suggestion: (!is_inner).then_some(if expr_is_from_block { + UnusedDefSuggestion::BlockTailExpr { + before_span: span.shrink_to_lo(), + after_span: span.shrink_to_hi(), + } + } else { + UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() } + }), + }, + ); + } } } From 6bc667a8059c413e823ca37a6c02b0a563833a66 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 6 Nov 2025 12:42:04 +0100 Subject: [PATCH 5/6] add temporary lints for crater run(s), detecting proposed change in muse use behavior --- compiler/rustc_lint/src/unused.rs | 104 +++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index c41b3488d3545..c63739f60f7b8 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -53,6 +53,20 @@ declare_lint! { report_in_external_macro } +declare_lint! { + pub UNMUSTUSE_IN_ALWAYS_OK, + Warn, + "", + report_in_external_macro +} + +declare_lint! { + pub MUSTUSE_IN_ALWAYS_OK, + Warn, + "", + report_in_external_macro +} + declare_lint! { /// The `unused_results` lint checks for the unused result of an /// expression in a statement. @@ -92,7 +106,7 @@ declare_lint! { "unused result of an expression in a statement" } -declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); +declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS, UNMUSTUSE_IN_ALWAYS_OK, MUSTUSE_IN_ALWAYS_OK]); impl<'tcx> LateLintPass<'tcx> for UnusedResults { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { @@ -136,7 +150,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, expr.span); + let must_use_result = is_ty_must_use(cx, ty, expr, expr.span, false); let type_lint_emitted_or_suppressed = match must_use_result { Some(path) => { emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); @@ -209,6 +223,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); } } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + let ty = cx.typeck_results().expr_ty(expr); + + _ = is_ty_must_use(cx, ty, expr, expr.span, true); + } } fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expr_is_from_block: bool) -> bool { @@ -259,12 +279,33 @@ enum MustUsePath { Coroutine(Span), } +fn is_suppressed(p: &MustUsePath) -> bool { + match p { + MustUsePath::Suppressed => true, + + MustUsePath::Def(..) | MustUsePath::Closure(..) | MustUsePath::Coroutine(..) => false, + + MustUsePath::Boxed(must_use_path) + | MustUsePath::Pinned(must_use_path) + | MustUsePath::Opaque(must_use_path) + | MustUsePath::Array(must_use_path, _) + | MustUsePath::TraitObject(must_use_path) + | MustUsePath::Result(must_use_path) + | MustUsePath::ControlFlow(must_use_path) => is_suppressed(must_use_path), + + MustUsePath::TupleElement(items) => { + items.iter().all(|(_, must_use_path)| is_suppressed(must_use_path)) + } + } +} + #[instrument(skip(cx, expr), level = "debug", ret)] fn is_ty_must_use<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span, + tmp_lint: bool, ) -> Option { if ty.is_unit() { return Some(MustUsePath::Suppressed); @@ -276,11 +317,12 @@ fn is_ty_must_use<'tcx>( match *ty.kind() { _ if is_uninhabited(ty) => Some(MustUsePath::Suppressed), ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => { - is_ty_must_use(cx, boxed, expr, span).map(|inner| MustUsePath::Boxed(Box::new(inner))) + is_ty_must_use(cx, boxed, expr, span, tmp_lint) + .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, span) + is_ty_must_use(cx, pinned_ty, expr, span, tmp_lint) .map(|inner| MustUsePath::Pinned(Box::new(inner))) } // Consider `Result` (e.g. `Result<(), !>`) equivalent to `T`. @@ -289,7 +331,30 @@ fn is_ty_must_use<'tcx>( && is_uninhabited(args.type_at(1)) => { let ok_ty = args.type_at(0); - is_ty_must_use(cx, ok_ty, expr, span).map(Box::new).map(MustUsePath::Result) + let res = is_ty_must_use(cx, ok_ty, expr, span, tmp_lint) + .map(Box::new) + .map(MustUsePath::Result); + + if tmp_lint + && !matches!(ok_ty.kind(), ty::Param(_)) + && !ok_ty.is_unit() + && res.as_ref().is_none_or(is_suppressed) + { + cx.span_lint(UNMUSTUSE_IN_ALWAYS_OK, span, |d| { + d.primary_message(format!("this type will no longer be must used: {ty}")); + }); + } + + if tmp_lint + && !matches!(ok_ty.kind(), ty::Param(_)) + && res.as_ref().is_some_and(|x| !is_suppressed(x)) + { + cx.span_lint(MUSTUSE_IN_ALWAYS_OK, span, |d| { + d.primary_message(format!("this type continue be must used: {ty}")); + }); + } + + res } // Consider `ControlFlow` (e.g. `ControlFlow`) equivalent to `T`. ty::Adt(def, args) @@ -297,7 +362,30 @@ fn is_ty_must_use<'tcx>( && is_uninhabited(args.type_at(0)) => { let continue_ty = args.type_at(1); - is_ty_must_use(cx, continue_ty, expr, span).map(Box::new).map(MustUsePath::ControlFlow) + let res = is_ty_must_use(cx, continue_ty, expr, span, tmp_lint) + .map(Box::new) + .map(MustUsePath::ControlFlow); + + if tmp_lint + && !matches!(continue_ty.kind(), ty::Param(_)) + && !continue_ty.is_unit() + && res.as_ref().is_none_or(is_suppressed) + { + cx.span_lint(UNMUSTUSE_IN_ALWAYS_OK, span, |d| { + d.primary_message(format!("this type will no longer be must used: {ty}")); + }); + } + + if tmp_lint + && !matches!(continue_ty.kind(), ty::Param(_)) + && res.as_ref().is_some_and(|x| !is_suppressed(x)) + { + cx.span_lint(MUSTUSE_IN_ALWAYS_OK, span, |d| { + d.primary_message(format!("this type continue be must used: {ty}")); + }); + } + + res } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { @@ -343,7 +431,7 @@ fn is_ty_must_use<'tcx>( .zip(elem_exprs) .enumerate() .filter_map(|(i, (ty, expr))| { - is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) + is_ty_must_use(cx, ty, expr, expr.span, tmp_lint).map(|path| (i, path)) }) .collect::>(); @@ -357,7 +445,7 @@ fn is_ty_must_use<'tcx>( // If the array is empty we don't lint, to avoid false positives Some(0) | None => None, // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(len) => is_ty_must_use(cx, ty, expr, span) + Some(len) => is_ty_must_use(cx, ty, expr, span, tmp_lint) .map(|inner| MustUsePath::Array(Box::new(inner), len)), }, ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)), From 310b96396cecbe8d42da76f7e433675cc0c7fa3d Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 6 Nov 2025 13:04:44 +0100 Subject: [PATCH 6/6] #![expect([un]mustuse_in_always_ok)] --- library/alloc/src/lib.rs | 1 + library/core/src/lib.rs | 1 + library/std/src/lib.rs | 1 + src/bootstrap/src/core/builder/cargo.rs | 4 ++++ tests/ui/binding/empty-types-in-patterns.rs | 2 +- tests/ui/lint/unused/must_use-result-unit-uninhabited.rs | 2 +- .../lint/use-redundant/use-redundant-prelude-rust-2015.rs | 2 +- .../lint/use-redundant/use-redundant-prelude-rust-2021.rs | 2 +- tests/ui/never_type/never-result.rs | 2 +- tests/ui/never_type/never-type-rvalues.rs | 2 +- tests/ui/never_type/question_mark_from_never.rs | 2 +- tests/ui/never_type/try_from.rs | 2 +- tests/ui/pattern/usefulness/explain-unreachable-pats.rs | 2 +- .../ui/pattern/usefulness/rustfix-unreachable-pattern.fixed | 2 +- tests/ui/pattern/usefulness/rustfix-unreachable-pattern.rs | 2 +- tests/ui/print_type_sizes/uninhabited.rs | 2 +- tests/ui/reachable/unreachable-try-pattern.rs | 1 + tests/ui/reachable/unreachable-try-pattern.stderr | 6 +++--- tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs | 2 +- tests/ui/rfcs/rfc-0000-never_patterns/use-bindings.rs | 2 +- tests/ui/traits/const-traits/const-drop.rs | 1 + 21 files changed, 26 insertions(+), 17 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index fd54a375f3ea9..55a364837385c 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -82,6 +82,7 @@ #![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] #![warn(unreachable_pub)] +#![expect(unmustuse_in_always_ok)] // // Library features: // tidy-alphabetical-start diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e213e1d91a75d..336308475a5cc 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -89,6 +89,7 @@ #![allow(internal_features)] #![deny(ffi_unwind_calls)] #![warn(unreachable_pub)] +#![expect(unmustuse_in_always_ok)] // Do not check link redundancy on bootstrapping phase #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index a8c50cec01e0b..0b3a9acc6780c 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -249,6 +249,7 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] +#![expect(unmustuse_in_always_ok)] // Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind` #![deny(ffi_unwind_calls)] // std may use features in a platform-specific way diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index c2029f97391d4..3aa762b967440 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1169,6 +1169,10 @@ impl Builder<'_> { // Lints just for `compiler/` crates. if mode == Mode::Rustc { lint_flags.push("-Wrustc::internal"); + if compiler.stage > 0 { + lint_flags.push("-Aunmustuse_in_always_ok"); + lint_flags.push("-Amustuse_in_always_ok"); + } lint_flags.push("-Drustc::symbol_intern_string_literal"); // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all // of the individual lints are satisfied. diff --git a/tests/ui/binding/empty-types-in-patterns.rs b/tests/ui/binding/empty-types-in-patterns.rs index 48a8c4197241e..63d4a6f7666b4 100644 --- a/tests/ui/binding/empty-types-in-patterns.rs +++ b/tests/ui/binding/empty-types-in-patterns.rs @@ -5,7 +5,7 @@ #![allow(unreachable_patterns)] #![allow(unreachable_code)] -#![allow(unused_variables)] +#![expect(unused_variables, unmustuse_in_always_ok)] #[allow(dead_code)] fn foo(z: !) { 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 d1b47374a11b8..7f109f2d5129d 100644 --- a/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs +++ b/tests/ui/lint/unused/must_use-result-unit-uninhabited.rs @@ -3,7 +3,7 @@ #![deny(unused_must_use)] #![feature(never_type)] - +#![expect(unmustuse_in_always_ok, mustuse_in_always_ok)] use core::ops::{ControlFlow, ControlFlow::Continue}; use dep::{MyUninhabited, MyUninhabitedNonexhaustive}; diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs index 7dc9ba0afeae5..6ff5012ffcb9a 100644 --- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs +++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2015.rs @@ -1,7 +1,7 @@ //@ edition: 2015 //@ check-pass #![warn(redundant_imports)] - +#![expect(unmustuse_in_always_ok)] use std::option::Option::Some;//~ WARNING the item `Some` is imported redundantly use std::option::Option::None; //~ WARNING the item `None` is imported redundantly diff --git a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs index 236ee032b677b..7f46b9deb76cc 100644 --- a/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs +++ b/tests/ui/lint/use-redundant/use-redundant-prelude-rust-2021.rs @@ -1,7 +1,7 @@ //@ check-pass //@ edition:2021 #![warn(redundant_imports)] - +#![expect(unmustuse_in_always_ok)] use std::convert::TryFrom;//~ WARNING the item `TryFrom` is imported redundantly use std::convert::TryInto;//~ WARNING the item `TryInto` is imported redundantly diff --git a/tests/ui/never_type/never-result.rs b/tests/ui/never_type/never-result.rs index 98ad140466621..945043d2fb72f 100644 --- a/tests/ui/never_type/never-result.rs +++ b/tests/ui/never_type/never-result.rs @@ -5,7 +5,7 @@ #![allow(unreachable_patterns)] // Test that we can extract a ! through pattern matching then use it as several different types. #![feature(never_type)] - +#![expect(unmustuse_in_always_ok)] fn main() { let x: Result = Ok(123); match x { diff --git a/tests/ui/never_type/never-type-rvalues.rs b/tests/ui/never_type/never-type-rvalues.rs index d3f6f628e1a7b..add6c791d41fc 100644 --- a/tests/ui/never_type/never-type-rvalues.rs +++ b/tests/ui/never_type/never-type-rvalues.rs @@ -4,7 +4,7 @@ #![allow(dead_code)] #![allow(path_statements)] #![allow(unreachable_patterns)] - +#![expect(unmustuse_in_always_ok)] fn never_direct(x: !) { x; } diff --git a/tests/ui/never_type/question_mark_from_never.rs b/tests/ui/never_type/question_mark_from_never.rs index 06d2a1926ea90..1506b05edb1cc 100644 --- a/tests/ui/never_type/question_mark_from_never.rs +++ b/tests/ui/never_type/question_mark_from_never.rs @@ -4,7 +4,7 @@ // //@ revisions: unit never //@ check-pass -#![allow(internal_features)] +#![expect(internal_features, unmustuse_in_always_ok)] #![feature(rustc_attrs, never_type)] #![cfg_attr(unit, rustc_never_type_options(fallback = "unit"))] #![cfg_attr(never, rustc_never_type_options(fallback = "never"))] diff --git a/tests/ui/never_type/try_from.rs b/tests/ui/never_type/try_from.rs index acde524e98f5b..e978b108aa977 100644 --- a/tests/ui/never_type/try_from.rs +++ b/tests/ui/never_type/try_from.rs @@ -6,7 +6,7 @@ // over `TryFrom` being blanket impl for all `T: From` #![feature(never_type)] - +#![expect(unmustuse_in_always_ok)] use std::convert::{TryInto, Infallible}; struct Foo { diff --git a/tests/ui/pattern/usefulness/explain-unreachable-pats.rs b/tests/ui/pattern/usefulness/explain-unreachable-pats.rs index f1af7f294cbd8..90b9032172621 100644 --- a/tests/ui/pattern/usefulness/explain-unreachable-pats.rs +++ b/tests/ui/pattern/usefulness/explain-unreachable-pats.rs @@ -2,7 +2,7 @@ #![feature(exhaustive_patterns)] #![deny(unreachable_patterns)] //~^ NOTE lint level is defined here - +#![expect(unmustuse_in_always_ok)] #[rustfmt::skip] fn main() { match (0u8,) { diff --git a/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.fixed b/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.fixed index 18e3aecbd0db9..1a378dd6968f3 100644 --- a/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.fixed +++ b/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.fixed @@ -1,7 +1,7 @@ //@ run-rustfix #![feature(never_patterns)] #![feature(exhaustive_patterns)] -#![allow(incomplete_features)] +#![expect(incomplete_features, unmustuse_in_always_ok)] #![deny(unreachable_patterns)] enum Void {} diff --git a/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.rs b/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.rs index a81420d149690..d2dd21d224e3a 100644 --- a/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.rs +++ b/tests/ui/pattern/usefulness/rustfix-unreachable-pattern.rs @@ -1,7 +1,7 @@ //@ run-rustfix #![feature(never_patterns)] #![feature(exhaustive_patterns)] -#![allow(incomplete_features)] +#![expect(incomplete_features, unmustuse_in_always_ok)] #![deny(unreachable_patterns)] enum Void {} diff --git a/tests/ui/print_type_sizes/uninhabited.rs b/tests/ui/print_type_sizes/uninhabited.rs index 7cb3e5b33fa56..e6603d7e74b50 100644 --- a/tests/ui/print_type_sizes/uninhabited.rs +++ b/tests/ui/print_type_sizes/uninhabited.rs @@ -3,7 +3,7 @@ //@ ignore-pass // ^-- needed because `--pass check` does not emit the output needed. // FIXME: consider using an attribute instead of side-effects. - +#![expect(unmustuse_in_always_ok)] #![feature(never_type)] pub fn test() { diff --git a/tests/ui/reachable/unreachable-try-pattern.rs b/tests/ui/reachable/unreachable-try-pattern.rs index 1358722e229ce..5d17fb8ed92be 100644 --- a/tests/ui/reachable/unreachable-try-pattern.rs +++ b/tests/ui/reachable/unreachable-try-pattern.rs @@ -2,6 +2,7 @@ #![feature(never_type, exhaustive_patterns)] #![warn(unreachable_code)] #![warn(unreachable_patterns)] +#![expect(unmustuse_in_always_ok)] enum Void {} diff --git a/tests/ui/reachable/unreachable-try-pattern.stderr b/tests/ui/reachable/unreachable-try-pattern.stderr index 468af427249c1..5beb7072d0a86 100644 --- a/tests/ui/reachable/unreachable-try-pattern.stderr +++ b/tests/ui/reachable/unreachable-try-pattern.stderr @@ -1,5 +1,5 @@ warning: unreachable call - --> $DIR/unreachable-try-pattern.rs:19:33 + --> $DIR/unreachable-try-pattern.rs:20:33 | LL | let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; | ^^ - any code following this expression is unreachable @@ -13,7 +13,7 @@ LL | #![warn(unreachable_code)] | ^^^^^^^^^^^^^^^^ warning: unreachable pattern - --> $DIR/unreachable-try-pattern.rs:19:24 + --> $DIR/unreachable-try-pattern.rs:20:24 | LL | let y = (match x { Ok(n) => Ok(n as u32), Err(e) => Err(e) })?; | ^^^^^----------------- @@ -29,7 +29,7 @@ LL | #![warn(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ warning: unreachable pattern - --> $DIR/unreachable-try-pattern.rs:30:40 + --> $DIR/unreachable-try-pattern.rs:31:40 | LL | let y = (match x { Ok(n) => Ok(n), Err(e) => Err(e) })?; | ^^^^^^---------- diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs index 6d7815f7a9eb0..7dd556dc1e065 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs @@ -1,6 +1,6 @@ #![feature(exhaustive_patterns)] #![feature(never_patterns)] -#![allow(incomplete_features)] +#![expect(incomplete_features, unmustuse_in_always_ok)] #![allow(dead_code, unreachable_code)] #![deny(unreachable_patterns)] diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/use-bindings.rs b/tests/ui/rfcs/rfc-0000-never_patterns/use-bindings.rs index 33da6f02ce269..15e184eb1238b 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/use-bindings.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/use-bindings.rs @@ -1,6 +1,6 @@ //@ check-pass #![feature(never_patterns)] -#![allow(incomplete_features)] +#![expect(incomplete_features, unmustuse_in_always_ok)] #[derive(Copy, Clone)] enum Void {} diff --git a/tests/ui/traits/const-traits/const-drop.rs b/tests/ui/traits/const-traits/const-drop.rs index dc985a8f62073..9dac7d15a5d21 100644 --- a/tests/ui/traits/const-traits/const-drop.rs +++ b/tests/ui/traits/const-traits/const-drop.rs @@ -5,6 +5,7 @@ #![feature(const_trait_impl, const_destruct)] #![feature(never_type)] #![cfg_attr(precise, feature(const_precise_live_drops))] +#![expect(unmustuse_in_always_ok)] use std::marker::Destruct;