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 626529cb6fc5b..f08541b2dc5d8 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1436,19 +1436,13 @@ 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 { 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 a3bdd0bae7e77..2da1f8ed43b1c 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`] @@ -203,11 +206,30 @@ pub(super) fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDef } } -fn missing_items_err( +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. + let hi = full_impl_span.hi() - BytePos(1); + // Point at the place right before the closing brace of the relevant `impl` to suggest + // adding the associated item at the end of its body. + full_impl_span.with_lo(hi).with_hi(hi) + } else { + full_impl_span.shrink_to_hi() + } +} + +fn missing_items_suggestions( tcx: TyCtxt<'_>, impl_def_id: LocalDefId, missing_items: &[ty::AssocItem], - full_impl_span: Span, +) -> ( + String, + Vec, + Vec, + Vec, ) { let missing_items = missing_items.iter().filter(|trait_item| !trait_item.is_impl_trait_in_trait()); @@ -218,17 +240,7 @@ fn missing_items_err( .collect::>() .join("`, `"); - let sugg_sp = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(full_impl_span) - && snippet.ends_with("}") - { - // `Span` before impl block closing brace. - let hi = full_impl_span.hi() - BytePos(1); - // Point at the place right before the closing brace of the relevant `impl` to suggest - // adding the associated item at the end of its body. - full_impl_span.with_lo(hi).with_hi(hi) - } else { - full_impl_span.shrink_to_hi() - }; + 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); @@ -259,6 +271,13 @@ fn missing_items_err( } } + (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, @@ -270,18 +289,29 @@ fn missing_items_err( 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 |