From f9db7f46136468995d791b90475989f0e610dfdf Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 30 Jul 2026 22:53:09 -0700 Subject: [PATCH 1/2] When issuing suggestions for missing trait items, label unstable items When we suggest implementing a missing trait item (or group of items from `must_implement_one_of`), label any unstable items, so the user doesn't get confused by suggestions to implement an unstable item parallel to suggestions to implement stable items. --- compiler/rustc_hir_analysis/src/check/mod.rs | 60 +++++++++++++++---- .../rustc_hir_analysis/src/diagnostics.rs | 19 ++++++ .../async-await/async-fn/impl-header.stderr | 2 +- tests/ui/lifetimes/issue-95023.stderr | 2 +- tests/ui/traits/issue-87558.stderr | 2 +- 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 4729168b5efb6..aba8a9ba250f3 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -84,6 +84,7 @@ use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::DenseBitSet; use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_types_for_signature; @@ -105,6 +106,7 @@ use self::compare_impl_item::collect_return_position_impl_trait_in_trait_tys; use self::region::region_scope_tree; use crate::diagnostics::{ MissingTraitItemLabel, MissingTraitItemSuggestion, MissingTraitItemSuggestionNone, + MissingTraitItemSuggestionUnstable, }; use crate::{check_c_variadic_abi, diagnostics}; @@ -229,6 +231,7 @@ fn missing_items_suggestions( String, Vec, Vec, + Vec, Vec, ) { let missing_items = @@ -244,8 +247,12 @@ fn missing_items_suggestions( // Obtain the level of indentation ending in `sugg_sp`. let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new); - let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) = - (Vec::new(), Vec::new(), Vec::new()); + let ( + mut missing_trait_item, + mut missing_trait_item_none, + mut missing_trait_item_unstable, + mut missing_trait_item_label, + ) = (Vec::new(), Vec::new(), Vec::new(), Vec::new()); for &trait_item in missing_items { let snippet = with_types_for_signature!(suggestion_signature( @@ -263,20 +270,42 @@ fn missing_items_suggestions( snippet, }); } else { - missing_trait_item_none.push(diagnostics::MissingTraitItemSuggestionNone { - span: sugg_sp, - code, - snippet, - }) + if let EvalResult::Deny { feature, .. } = + tcx.eval_stability(trait_item.def_id, None, sugg_sp, None) + { + missing_trait_item_unstable.push(diagnostics::MissingTraitItemSuggestionUnstable { + span: sugg_sp, + code, + snippet, + feature, + }); + } else { + missing_trait_item_none.push(diagnostics::MissingTraitItemSuggestionNone { + span: sugg_sp, + code, + snippet, + }); + } } } - (missing_items_msg, missing_trait_item, missing_trait_item_none, missing_trait_item_label) + ( + missing_items_msg, + missing_trait_item, + missing_trait_item_none, + missing_trait_item_unstable, + missing_trait_item_label, + ) } fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ty::AssocItem]) { - let (missing_items_msg, missing_trait_item, missing_trait_item_none, missing_trait_item_label) = - missing_items_suggestions(tcx, impl_def_id, missing_items); + let ( + missing_items_msg, + missing_trait_item, + missing_trait_item_none, + missing_trait_item_unstable, + missing_trait_item_label, + ) = missing_items_suggestions(tcx, impl_def_id, missing_items); tcx.dcx().emit_err(diagnostics::MissingTraitItem { span: tcx.span_of_impl(impl_def_id.to_def_id()).unwrap(), @@ -284,6 +313,7 @@ fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ missing_trait_item_label, missing_trait_item, missing_trait_item_none, + missing_trait_item_unstable, }); } @@ -301,8 +331,13 @@ fn missing_items_must_implement_one_of_err( .cloned() .collect::>(); - let (missing_items_msg, missing_trait_item, missing_trait_item_none, missing_trait_item_label) = - missing_items_suggestions(tcx, impl_def_id, &missing_items); + let ( + missing_items_msg, + missing_trait_item, + missing_trait_item_none, + missing_trait_item_unstable, + missing_trait_item_label, + ) = missing_items_suggestions(tcx, impl_def_id, &missing_items); tcx.dcx().emit_err(diagnostics::MissingOneOfTraitItem { span: tcx.def_span(impl_def_id), @@ -310,6 +345,7 @@ fn missing_items_must_implement_one_of_err( missing_items_msg, missing_trait_item_label, missing_trait_item, + missing_trait_item_unstable, missing_trait_item_none, }) } diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index eb0dcb3a346b2..ab6fa34be9fbb 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -919,6 +919,8 @@ pub(crate) struct MissingTraitItem { pub missing_trait_item: Vec, #[subdiagnostic] pub missing_trait_item_none: Vec, + #[subdiagnostic] + pub missing_trait_item_unstable: Vec, pub missing_items_msg: String, } @@ -944,6 +946,21 @@ pub(crate) struct MissingTraitItemSuggestion { pub snippet: String, } +#[derive(Subdiagnostic)] +#[suggestion( + "implement the missing item: `{$snippet}` (unstable, requires feature `{$feature}`)", + style = "hidden", + applicability = "has-placeholders", + code = "{code}" +)] +pub(crate) struct MissingTraitItemSuggestionUnstable { + #[primary_span] + pub span: Span, + pub code: String, + pub snippet: String, + pub feature: Symbol, +} + #[derive(Subdiagnostic)] #[suggestion( "implement the missing item: `{$snippet}`", @@ -972,6 +989,8 @@ pub(crate) struct MissingOneOfTraitItem { pub missing_trait_item: Vec, #[subdiagnostic] pub missing_trait_item_none: Vec, + #[subdiagnostic] + pub missing_trait_item_unstable: Vec, pub missing_items_msg: String, } diff --git a/tests/ui/async-await/async-fn/impl-header.stderr b/tests/ui/async-await/async-fn/impl-header.stderr index d1e3f884d02b3..3f35b9d99d1e5 100644 --- a/tests/ui/async-await/async-fn/impl-header.stderr +++ b/tests/ui/async-await/async-fn/impl-header.stderr @@ -28,7 +28,7 @@ error[E0046]: not all trait items implemented, missing: `call` LL | impl async Fn<()> for F {} | ^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation | - = help: implement the missing item: `fn call(&self, _: ()) -> >::Output { todo!() }` + = help: implement the missing item: `fn call(&self, _: ()) -> >::Output { todo!() }` (unstable, requires feature `fn_traits`) error[E0277]: expected an `FnMut()` closure, found `F` --> $DIR/impl-header.rs:5:23 diff --git a/tests/ui/lifetimes/issue-95023.stderr b/tests/ui/lifetimes/issue-95023.stderr index afb627de1c1bb..c05cea0dbe533 100644 --- a/tests/ui/lifetimes/issue-95023.stderr +++ b/tests/ui/lifetimes/issue-95023.stderr @@ -38,7 +38,7 @@ error[E0046]: not all trait items implemented, missing: `call` LL | impl Fn(&isize) for Error { | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation | - = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` + = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` (unstable, requires feature `fn_traits`) error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-95023.rs:3:21 diff --git a/tests/ui/traits/issue-87558.stderr b/tests/ui/traits/issue-87558.stderr index b8b9ea57612b4..0238e4ae18685 100644 --- a/tests/ui/traits/issue-87558.stderr +++ b/tests/ui/traits/issue-87558.stderr @@ -30,7 +30,7 @@ error[E0046]: not all trait items implemented, missing: `call` LL | impl Fn(&isize) for Error { | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `call` in implementation | - = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` + = help: implement the missing item: `fn call(&self, _: (&isize,)) -> >::Output { todo!() }` (unstable, requires feature `fn_traits`) error[E0277]: expected an `FnMut(&isize)` closure, found `Error` --> $DIR/issue-87558.rs:3:21 From c4058be5ab4d474a3a86cf8d317d1f8776bfac9c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 30 Jul 2026 23:17:12 -0700 Subject: [PATCH 2/2] Add test for suggestions on `must_implement_one_of` Includes a test for unstable trait methods. --- .../traits/default-method/auxiliary/mioou.rs | 31 +++++++++++++++++++ .../rustc_must_implement_one_of-unstable.rs | 16 ++++++++++ ...ustc_must_implement_one_of-unstable.stderr | 21 +++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 tests/ui/traits/default-method/auxiliary/mioou.rs create mode 100644 tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.rs create mode 100644 tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.stderr diff --git a/tests/ui/traits/default-method/auxiliary/mioou.rs b/tests/ui/traits/default-method/auxiliary/mioou.rs new file mode 100644 index 0000000000000..f0cb013ad3348 --- /dev/null +++ b/tests/ui/traits/default-method/auxiliary/mioou.rs @@ -0,0 +1,31 @@ +#![feature(rustc_attrs)] +#![feature(staged_api)] +#![stable(feature="s", since="1.0.0")] + +#[rustc_must_implement_one_of(a1, b1)] +#[stable(feature="s", since="1.0.0")] +pub trait Trait1 { + #[stable(feature="s", since="1.0.0")] + fn a1(&self) -> u64 { + self.a1() + 1 + } + + #[stable(feature="s", since="1.0.0")] + fn b1(&self) -> u64 { + self.b1() + 1 + } +} + +#[rustc_must_implement_one_of(a2, b2)] +#[stable(feature="s", since="1.0.0")] +pub trait Trait2 { + #[stable(feature="s", since="1.0.0")] + fn a2(&self) -> u64 { + self.b2() + 1 + } + + #[unstable(feature="trait2_b2", issue="none")] + fn b2(&self) -> u64 { + self.a2() + 1 + } +} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.rs new file mode 100644 index 0000000000000..86e03e19e7835 --- /dev/null +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.rs @@ -0,0 +1,16 @@ +//@ edition:2024 +//@ aux-crate:mioou=mioou.rs + +use mioou::*; + +struct A; + +impl Trait1 for A { +//~^ ERROR not all trait items implemented, missing one of: `a1`, `b1` +} + +impl Trait2 for A { +//~^ ERROR not all trait items implemented, missing one of: `a2`, `b2` +} + +fn main() {} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.stderr b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.stderr new file mode 100644 index 0000000000000..6338fd1e45f27 --- /dev/null +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of-unstable.stderr @@ -0,0 +1,21 @@ +error[E0046]: not all trait items implemented, missing one of: `a1`, `b1` + --> $DIR/rustc_must_implement_one_of-unstable.rs:8:1 + | +LL | impl Trait1 for A { + | ^^^^^^^^^^^^^^^^^ missing one of `a1`, `b1` in implementation + | + = help: implement the missing item: `fn a1(&self) -> u64 { todo!() }` + = help: implement the missing item: `fn b1(&self) -> u64 { todo!() }` + +error[E0046]: not all trait items implemented, missing one of: `a2`, `b2` + --> $DIR/rustc_must_implement_one_of-unstable.rs:12:1 + | +LL | impl Trait2 for A { + | ^^^^^^^^^^^^^^^^^ missing one of `a2`, `b2` in implementation + | + = help: implement the missing item: `fn a2(&self) -> u64 { todo!() }` + = help: implement the missing item: `fn b2(&self) -> u64 { todo!() }` (unstable, requires feature `trait2_b2`) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0046`.