diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs new file mode 100644 index 000000000..da465a764 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -0,0 +1,176 @@ +//! Error Reporting for `impl` items that do not match the obligations from their `trait`. + +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::Subtype; +use crate::traits::ObligationCauseCode::CompareImplItemObligation; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::print::RegionHighlightMode; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_span::Span; + +use std::ops::ControlFlow; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. + pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> { + let error = self.error.as_ref()?; + debug!("try_report_impl_not_conforming_to_trait {:?}", error); + if let RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + _sub, + sup_origin, + _sup, + _, + ) = error.clone() + && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin) + && let sub_expected_found @ Some((sub_expected, sub_found)) = sub_trace.values.ty() + && let sup_expected_found @ Some(_) = sup_trace.values.ty() + && let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code() + && sup_expected_found == sub_expected_found + { + let guar = + self.emit_err(var_origin.span(), sub_expected, sub_found, *trait_item_def_id); + return Some(guar); + } + None + } + + fn emit_err( + &self, + sp: Span, + expected: Ty<'tcx>, + found: Ty<'tcx>, + trait_def_id: DefId, + ) -> ErrorGuaranteed { + let trait_sp = self.tcx().def_span(trait_def_id); + let mut err = self + .tcx() + .sess + .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); + + // Mark all unnamed regions in the type with a number. + // This diagnostic is called in response to lifetime errors, so be informative. + struct HighlightBuilder<'tcx> { + highlight: RegionHighlightMode<'tcx>, + counter: usize, + } + + impl<'tcx> HighlightBuilder<'tcx> { + fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> { + let mut builder = + HighlightBuilder { highlight: RegionHighlightMode::new(tcx), counter: 1 }; + builder.visit_ty(ty); + builder.highlight + } + } + + impl<'tcx> ty::visit::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if !r.has_name() && self.counter <= 3 { + self.highlight.highlighting_region(r, self.counter); + self.counter += 1; + } + r.super_visit_with(self) + } + } + + let expected_highlight = HighlightBuilder::build(self.tcx(), expected); + let expected = self + .infcx + .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) + .name; + let found_highlight = HighlightBuilder::build(self.tcx(), found); + let found = + self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; + + err.span_label(sp, &format!("found `{}`", found)); + err.span_label(trait_sp, &format!("expected `{}`", expected)); + + // Get the span of all the used type parameters in the method. + let assoc_item = self.tcx().associated_item(trait_def_id); + let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; + match assoc_item.kind { + ty::AssocKind::Fn => { + let hir = self.tcx().hir(); + if let Some(hir_id) = + assoc_item.def_id.as_local().map(|id| hir.local_def_id_to_hir_id(id)) + { + if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) { + visitor.visit_fn_decl(decl); + } + } + } + _ => {} + } + let mut type_param_span: MultiSpan = visitor.types.to_vec().into(); + for &span in &visitor.types { + type_param_span + .push_span_label(span, "consider borrowing this type parameter in the trait"); + } + + err.note(&format!("expected `{}`\n found `{}`", expected, found)); + + err.span_help( + type_param_span, + "the lifetime requirements from the `impl` do not correspond to the requirements in \ + the `trait`", + ); + if visitor.types.is_empty() { + err.help( + "verify the lifetime relationships in the `trait` and `impl` between the `self` \ + argument, the other inputs and its output", + ); + } + err.emit() + } +} + +struct TypeParamSpanVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + types: Vec<Span>, +} + +impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + match arg.kind { + hir::TyKind::Rptr(_, ref mut_ty) => { + // We don't want to suggest looking into borrowing `&T` or `&Self`. + hir::intravisit::walk_ty(self, mut_ty.ty); + return; + } + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { + [segment] + if segment + .res + .map(|res| { + matches!( + res, + Res::SelfTy { trait_: _, alias_to: _ } + | Res::Def(hir::def::DefKind::TyParam, _) + ) + }) + .unwrap_or(false) => + { + self.types.push(path.span); + } + _ => {} + }, + _ => {} + } + hir::intravisit::walk_ty(self, arg); + } +} |