From 66f3da078621a44bf504cbb472146c2b7513eae5 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 28 Jul 2026 10:25:53 -0700 Subject: [PATCH 1/3] Refactor `missing_items_err`: factor out function to find suggestion span `missing_items_err` has logic to figure out where in the impl to insert suggestions. Factor that logic out into a function to support reusing it. --- .../rustc_hir_analysis/src/check/check.rs | 3 +- compiler/rustc_hir_analysis/src/check/mod.rs | 34 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 626529cb6fc5b..2cad6a1f5a2d8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1436,8 +1436,7 @@ fn check_impl_items_against_trait<'tcx>( } if !missing_items.is_empty() { - let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id)); - missing_items_err(tcx, impl_id, &missing_items, full_impl_span); + missing_items_err(tcx, impl_id, &missing_items); } if let Some(missing_items) = must_implement_one_of { diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index a3bdd0bae7e77..e6b22bd22a883 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -203,22 +203,9 @@ pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDef } } -fn missing_items_err( - tcx: TyCtxt<'_>, - impl_def_id: LocalDefId, - missing_items: &[ty::AssocItem], - full_impl_span: Span, -) { - let missing_items = - missing_items.iter().filter(|trait_item| !trait_item.is_impl_trait_in_trait()); - - let missing_items_msg = missing_items - .clone() - .map(|trait_item| trait_item.name().to_string()) - .collect::>() - .join("`, `"); - - let sugg_sp = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span) +fn impl_suggestion_span(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) -> Span { + let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_def_id)); + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span) && snippet.ends_with("}") { // `Span` before impl block closing brace. @@ -228,7 +215,20 @@ fn missing_items_err( full_impl_span.with_lo(hi).with_hi(hi) } else { full_impl_span.shrink_to_hi() - }; + } +} + +fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ty::AssocItem]) { + let missing_items = + missing_items.iter().filter(|trait_item| !trait_item.is_impl_trait_in_trait()); + + let missing_items_msg = missing_items + .clone() + .map(|trait_item| trait_item.name().to_string()) + .collect::>() + .join("`, `"); + + let sugg_sp = impl_suggestion_span(tcx, impl_def_id); // Obtain the level of indentation ending in `sugg_sp`. let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new); From 6640449d6e28bd6866aa2274fa55e46855955bd6 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 28 Jul 2026 16:00:18 -0700 Subject: [PATCH 2/3] Refactor `missing_items_err`: factor out function to find suggestions `missing_items_err` has logic to compute a set of suggestions regarding the missing items. Factor that logic out into a function to support reusing it. --- compiler/rustc_hir_analysis/src/check/mod.rs | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index e6b22bd22a883..ac9af13ed5078 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -103,6 +103,9 @@ use tracing::debug; 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, +}; use crate::{check_c_variadic_abi, diagnostics}; /// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`] @@ -218,7 +221,16 @@ fn impl_suggestion_span(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) -> Span { } } -fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ty::AssocItem]) { +fn missing_items_suggestions( + tcx: TyCtxt<'_>, + impl_def_id: LocalDefId, + missing_items: &[ty::AssocItem], +) -> ( + String, + Vec, + Vec, + Vec, +) { let missing_items = missing_items.iter().filter(|trait_item| !trait_item.is_impl_trait_in_trait()); @@ -259,6 +271,13 @@ fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ } } + (missing_items_msg, missing_trait_item, missing_trait_item_none, 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); + tcx.dcx().emit_err(diagnostics::MissingTraitItem { span: tcx.span_of_impl(impl_def_id.to_def_id()).unwrap(), missing_items_msg, From bf8708efd809273b6e55d57a7510dd1b81fc02d8 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 28 Jul 2026 14:48:51 -0700 Subject: [PATCH 3/3] Add suggestions for `must_implement_one_of` As with the suggestions for mandatory trait methods, provide suggestions for `must_implement_one_of`, which include the signatures of the trait methods. This makes it easy to copy-paste the signatures into the impl block. Invoke the same logic from `check_drop_xor_pin_drop`, to provide suggestions with the signatures of `drop` and `pin_drop`. Without this change: ``` error[E0046]: not all trait items implemented, missing one of: `read`, `read_buf` --> src/main.rs:3:1 | 3 | impl std::io::Read for R { | ^^^^^^^^^^^^^^^^^^^^^^^^ missing one of `read`, `read_buf` in implementation For more information about this error, try `rustc --explain E0046`. ``` With this change: ``` error[E0046]: not all trait items implemented, missing one of: `read`, `read_buf` --> src/main.rs:3:1 | 3 | impl std::io::Read for R { | ^^^^^^^^^^^^^^^^^^^^^^^^ missing one of `read`, `read_buf` in implementation | = help: implement the missing item: `fn read(&mut self, _: &mut [u8]) -> Result { todo!() }` = help: implement the missing item: `fn read_buf(&mut self, _: BorrowedCursor<'_, u8>) -> Result<(), std::io::Error> { todo!() }` For more information about this error, try `rustc --explain E0046`. ``` --- .../src/check/always_applicable.rs | 12 +++++---- .../rustc_hir_analysis/src/check/check.rs | 9 ++----- compiler/rustc_hir_analysis/src/check/mod.rs | 25 +++++++++++++------ .../rustc_hir_analysis/src/diagnostics.rs | 6 +++++ .../pin-ergonomics/pinned-drop-check.stderr | 6 +++++ .../rustc_must_implement_one_of.stderr | 6 +++++ 6 files changed, 45 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/always_applicable.rs b/compiler/rustc_hir_analysis/src/check/always_applicable.rs index 9a8008214ed04..71d9b4da64218 100644 --- a/compiler/rustc_hir_analysis/src/check/always_applicable.rs +++ b/compiler/rustc_hir_analysis/src/check/always_applicable.rs @@ -16,6 +16,7 @@ use rustc_span::sym; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::{self, ObligationCtxt}; +use crate::check::missing_items_must_implement_one_of_err; use crate::diagnostics; use crate::hir::def_id::{DefId, LocalDefId}; @@ -394,11 +395,12 @@ fn check_drop_xor_pin_drop<'tcx>( match (drop_span, pin_drop_span) { (None, None) => { if tcx.features().pin_ergonomics() { - return Err(tcx.dcx().emit_err(crate::diagnostics::MissingOneOfTraitItem { - span: tcx.def_span(drop_impl_did), - note: None, - missing_items_msg: "drop`, `pin_drop".to_string(), - })); + return Err(missing_items_must_implement_one_of_err( + tcx, + drop_impl_did, + [sym::drop, sym::pin_drop].into_iter(), + None, + )); } else { return Err(tcx .dcx() diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 2cad6a1f5a2d8..f08541b2dc5d8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1441,13 +1441,8 @@ fn check_impl_items_against_trait<'tcx>( if let Some(missing_items) = must_implement_one_of { let attr_span = find_attr!(tcx, trait_ref.def_id, RustcMustImplementOneOf {attr_span, ..} => *attr_span); - - missing_items_must_implement_one_of_err( - tcx, - tcx.def_span(impl_id), - missing_items, - attr_span, - ); + let missing_items = missing_items.into_iter().map(|i| i.name); + missing_items_must_implement_one_of_err(tcx, impl_id, missing_items, attr_span); } } } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index ac9af13ed5078..2da1f8ed43b1c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -289,18 +289,29 @@ fn missing_items_err(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ fn missing_items_must_implement_one_of_err( tcx: TyCtxt<'_>, - impl_span: Span, - missing_items: &[Ident], + impl_def_id: LocalDefId, + missing_items: impl Iterator, annotation_span: Option, -) { - let missing_items_msg = - missing_items.iter().map(Ident::to_string).collect::>().join("`, `"); +) -> ErrorGuaranteed { + // Look up the associated items so we can use them to emit better errors. + let trait_def_id = tcx.impl_trait_id(impl_def_id); + let assoc_items = tcx.associated_items(trait_def_id); + let missing_items = missing_items + .flat_map(|s| assoc_items.filter_by_name_unhygienic_and_kind(s, ty::AssocTag::Fn)) + .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); tcx.dcx().emit_err(diagnostics::MissingOneOfTraitItem { - span: impl_span, + span: tcx.def_span(impl_def_id), note: annotation_span, missing_items_msg, - }); + missing_trait_item_label, + missing_trait_item, + missing_trait_item_none, + }) } fn default_body_is_unstable( diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 0ea353cf14cee..eb0dcb3a346b2 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -966,6 +966,12 @@ pub(crate) struct MissingOneOfTraitItem { pub span: Span, #[note("required because of this annotation")] pub note: Option, + #[subdiagnostic] + pub missing_trait_item_label: Vec, + #[subdiagnostic] + pub missing_trait_item: Vec, + #[subdiagnostic] + pub missing_trait_item_none: Vec, pub missing_items_msg: String, } diff --git a/tests/ui/pin-ergonomics/pinned-drop-check.stderr b/tests/ui/pin-ergonomics/pinned-drop-check.stderr index 3a848c66578b4..3dc6e18a9178e 100644 --- a/tests/ui/pin-ergonomics/pinned-drop-check.stderr +++ b/tests/ui/pin-ergonomics/pinned-drop-check.stderr @@ -71,12 +71,18 @@ error[E0046]: not all trait items implemented, missing one of: `drop`, `pin_drop | LL | impl Drop for Foo {} | ^^^^^^^^^^^^^^^^^ missing one of `drop`, `pin_drop` in implementation + | + = help: implement the missing item: `fn drop(&mut self) { todo!() }` + = help: implement the missing item: `fn pin_drop(self: Pin<&mut Self>) { todo!() }` error[E0046]: not all trait items implemented, missing one of: `drop`, `pin_drop` --> $DIR/pinned-drop-check.rs:60:5 | LL | impl Drop for Bar {} | ^^^^^^^^^^^^^^^^^ missing one of `drop`, `pin_drop` in implementation + | + = help: implement the missing item: `fn drop(&mut self) { todo!() }` + = help: implement the missing item: `fn pin_drop(self: Pin<&mut Self>) { todo!() }` error: `Bar` must implement `pin_drop` --> $DIR/pinned-drop-check.rs:87:9 diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of.stderr b/tests/ui/traits/default-method/rustc_must_implement_one_of.stderr index 7ad10cfce984f..9868cf8faa084 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of.stderr +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of.stderr @@ -1,6 +1,12 @@ error[E0046]: not all trait items implemented, missing one of: `eq`, `neq` --> $DIR/rustc_must_implement_one_of.rs:41:1 | +LL | fn eq(&self, other: &Self) -> bool { + | ---------------------------------- `eq` from trait +... +LL | fn neq(&self, other: &Self) -> bool { + | ----------------------------------- `neq` from trait +... LL | impl Equal for T3 {} | ^^^^^^^^^^^^^^^^^ missing one of `eq`, `neq` in implementation |