diff options
| author | Jacob Pratt <jacob@jhpratt.dev> | 2025-08-22 22:00:52 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-22 22:00:52 -0400 |
| commit | 15eeddee1f5a6ae523ef9aa74f0a50169a137e08 (patch) | |
| tree | 7f005b0630331bd54c3ac558c329fedc070e2959 /compiler/rustc_trait_selection | |
| parent | 6991786d5ebba0f917a09fb5d5f1c4a166089163 (diff) | |
| parent | 049c32797b5f797d5823cdc0e5aaeb0fd68175da (diff) | |
| download | rust-15eeddee1f5a6ae523ef9aa74f0a50169a137e08.tar.gz rust-15eeddee1f5a6ae523ef9aa74f0a50169a137e08.zip | |
Rollup merge of #145641 - estebank:point-at-type-in-e0277, r=davidtwco
On E0277, point at type that doesn't implement bound When encountering an unmet trait bound, point at local type that doesn't implement the trait: ``` error[E0277]: the trait bound `Bar<T>: Foo` is not satisfied --> $DIR/issue-64855.rs:9:19 | LL | pub struct Bar<T>(<Self as Foo>::Type) where Self: ; | ^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound | help: the trait `Foo` is not implemented for `Bar<T>` --> $DIR/issue-64855.rs:9:1 | LL | pub struct Bar<T>(<Self as Foo>::Type) where Self: ; | ^^^^^^^^^^^^^^^^^ ```
Diffstat (limited to 'compiler/rustc_trait_selection')
| -rw-r--r-- | compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs | 47 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs | 96 |
2 files changed, 88 insertions, 55 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 214a6e86d39..d768e0bf63f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -275,8 +275,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { *err.long_ty_path() = long_ty_file; let mut suggested = false; + let mut noted_missing_impl = false; if is_try_conversion || is_question_mark { - suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err); + (suggested, noted_missing_impl) = self.try_conversion_context(&obligation, main_trait_predicate, &mut err); } if let Some(ret_span) = self.return_type_span(&obligation) { @@ -335,6 +336,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return err.emit(); } + let ty_span = match leaf_trait_predicate.self_ty().skip_binder().kind() { + ty::Adt(def, _) if def.did().is_local() + && !self.can_suggest_derive(&obligation, leaf_trait_predicate) => self.tcx.def_span(def.did()), + _ => DUMMY_SP, + }; if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! @@ -347,15 +353,28 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Don't say "the trait `FromResidual<Option<Infallible>>` is // not implemented for `Result<T, E>`". { - err.help(explanation); + // We do this just so that the JSON output's `help` position is the + // right one and not `file.rs:1:1`. The render is the same. + if ty_span == DUMMY_SP { + err.help(explanation); + } else { + err.span_help(ty_span, explanation); + } } } else if let Some(custom_explanation) = safe_transmute_explanation { err.span_label(span, custom_explanation); - } else if explanation.len() > self.tcx.sess.diagnostic_width() { + } else if (explanation.len() > self.tcx.sess.diagnostic_width() || ty_span != DUMMY_SP) && !noted_missing_impl { // Really long types don't look good as span labels, instead move it // to a `help`. err.span_label(span, "unsatisfied trait bound"); - err.help(explanation); + + // We do this just so that the JSON output's `help` position is the + // right one and not `file.rs:1:1`. The render is the same. + if ty_span == DUMMY_SP { + err.help(explanation); + } else { + err.span_help(ty_span, explanation); + } } else { err.span_label(span, explanation); } @@ -939,7 +958,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, trait_pred: ty::PolyTraitPredicate<'tcx>, err: &mut Diag<'_>, - ) -> bool { + ) -> (bool, bool) { let span = obligation.cause.span; /// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call. struct FindMethodSubexprOfTry { @@ -959,21 +978,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); - let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false }; + let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return (false, false) }; let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir_body(body_id)) else { - return false; + return (false, false); }; let Some(typeck) = &self.typeck_results else { - return false; + return (false, false); }; let ObligationCauseCode::QuestionMark = obligation.cause.code().peel_derives() else { - return false; + return (false, false); }; let self_ty = trait_pred.skip_binder().self_ty(); let found_ty = trait_pred.skip_binder().trait_ref.args.get(1).and_then(|a| a.as_type()); - self.note_missing_impl_for_question_mark(err, self_ty, found_ty, trait_pred); + let noted_missing_impl = + self.note_missing_impl_for_question_mark(err, self_ty, found_ty, trait_pred); let mut prev_ty = self.resolve_vars_if_possible( typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), @@ -1137,7 +1157,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } prev = Some(err_ty); } - suggested + (suggested, noted_missing_impl) } fn note_missing_impl_for_question_mark( @@ -1146,7 +1166,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self_ty: Ty<'_>, found_ty: Option<Ty<'_>>, trait_pred: ty::PolyTraitPredicate<'tcx>, - ) { + ) -> bool { match (self_ty.kind(), found_ty) { (ty::Adt(def, _), Some(ty)) if let ty::Adt(found, _) = ty.kind() @@ -1187,8 +1207,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("`{ty}` needs to implement `Into<{self_ty}>`"), ); } - _ => {} + _ => return false, } + true } fn report_const_param_not_wf( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 953449c6758..e31ff8b8729 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3853,59 +3853,71 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - pub fn suggest_derive( + pub fn can_suggest_derive( &self, obligation: &PredicateObligation<'tcx>, - err: &mut Diag<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, - ) { + ) -> bool { if trait_pred.polarity() == ty::PredicatePolarity::Negative { - return; + return false; } let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else { - return; + return false; }; let (adt, args) = match trait_pred.skip_binder().self_ty().kind() { ty::Adt(adt, args) if adt.did().is_local() => (adt, args), - _ => return, + _ => return false, }; - let can_derive = { - let is_derivable_trait = match diagnostic_name { - sym::Default => !adt.is_enum(), - sym::PartialEq | sym::PartialOrd => { - let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1); - trait_pred.skip_binder().self_ty() == rhs_ty - } - sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true, - _ => false, - }; - is_derivable_trait && - // Ensure all fields impl the trait. - adt.all_fields().all(|field| { - let field_ty = ty::GenericArg::from(field.ty(self.tcx, args)); - let trait_args = match diagnostic_name { - sym::PartialEq | sym::PartialOrd => { - Some(field_ty) - } - _ => None, - }; - let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { - trait_ref: ty::TraitRef::new(self.tcx, - trait_pred.def_id(), - [field_ty].into_iter().chain(trait_args), - ), - ..*tr - }); - let field_obl = Obligation::new( - self.tcx, - obligation.cause.clone(), - obligation.param_env, - trait_pred, - ); - self.predicate_must_hold_modulo_regions(&field_obl) - }) + let is_derivable_trait = match diagnostic_name { + sym::Default => !adt.is_enum(), + sym::PartialEq | sym::PartialOrd => { + let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1); + trait_pred.skip_binder().self_ty() == rhs_ty + } + sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true, + _ => false, + }; + is_derivable_trait && + // Ensure all fields impl the trait. + adt.all_fields().all(|field| { + let field_ty = ty::GenericArg::from(field.ty(self.tcx, args)); + let trait_args = match diagnostic_name { + sym::PartialEq | sym::PartialOrd => { + Some(field_ty) + } + _ => None, + }; + let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { + trait_ref: ty::TraitRef::new(self.tcx, + trait_pred.def_id(), + [field_ty].into_iter().chain(trait_args), + ), + ..*tr + }); + let field_obl = Obligation::new( + self.tcx, + obligation.cause.clone(), + obligation.param_env, + trait_pred, + ); + self.predicate_must_hold_modulo_regions(&field_obl) + }) + } + + pub fn suggest_derive( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diag<'_>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else { + return; + }; + let adt = match trait_pred.skip_binder().self_ty().kind() { + ty::Adt(adt, _) if adt.did().is_local() => adt, + _ => return, }; - if can_derive { + if self.can_suggest_derive(obligation, trait_pred) { err.span_suggestion_verbose( self.tcx.def_span(adt.did()).shrink_to_lo(), format!( |
