]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
Rollup merge of #99332 - jyn514:stabilize-label-break-value, r=petrochenkov
[rust.git] / compiler / rustc_infer / src / infer / error_reporting / nice_region_error / trait_impl_difference.rs
1 //! Error Reporting for `impl` items that do not match the obligations from their `trait`.
2
3 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
4 use crate::infer::lexical_region_resolve::RegionResolutionError;
5 use crate::infer::Subtype;
6 use crate::traits::ObligationCauseCode::CompareImplItemObligation;
7 use rustc_errors::{ErrorGuaranteed, MultiSpan};
8 use rustc_hir as hir;
9 use rustc_hir::def::Res;
10 use rustc_hir::def_id::DefId;
11 use rustc_hir::intravisit::Visitor;
12 use rustc_middle::hir::nested_filter;
13 use rustc_middle::ty::print::RegionHighlightMode;
14 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
15 use rustc_span::Span;
16
17 use std::ops::ControlFlow;
18
19 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
20     /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
21     pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> {
22         let error = self.error.as_ref()?;
23         debug!("try_report_impl_not_conforming_to_trait {:?}", error);
24         if let RegionResolutionError::SubSupConflict(
25                 _,
26                 var_origin,
27                 sub_origin,
28                 _sub,
29                 sup_origin,
30                 _sup,
31                 _,
32             ) = error.clone()
33             && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
34             && let sub_expected_found @ Some((sub_expected, sub_found)) = sub_trace.values.ty()
35             && let sup_expected_found @ Some(_) = sup_trace.values.ty()
36             && let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code()
37             && sup_expected_found == sub_expected_found
38         {
39             let guar =
40                 self.emit_err(var_origin.span(), sub_expected, sub_found, *trait_item_def_id);
41             return Some(guar);
42         }
43         None
44     }
45
46     fn emit_err(
47         &self,
48         sp: Span,
49         expected: Ty<'tcx>,
50         found: Ty<'tcx>,
51         trait_def_id: DefId,
52     ) -> ErrorGuaranteed {
53         let trait_sp = self.tcx().def_span(trait_def_id);
54         let mut err = self
55             .tcx()
56             .sess
57             .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature");
58
59         // Mark all unnamed regions in the type with a number.
60         // This diagnostic is called in response to lifetime errors, so be informative.
61         struct HighlightBuilder<'tcx> {
62             highlight: RegionHighlightMode<'tcx>,
63             counter: usize,
64         }
65
66         impl<'tcx> HighlightBuilder<'tcx> {
67             fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
68                 let mut builder =
69                     HighlightBuilder { highlight: RegionHighlightMode::new(tcx), counter: 1 };
70                 builder.visit_ty(ty);
71                 builder.highlight
72             }
73         }
74
75         impl<'tcx> ty::visit::TypeVisitor<'tcx> for HighlightBuilder<'tcx> {
76             fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
77                 if !r.has_name() && self.counter <= 3 {
78                     self.highlight.highlighting_region(r, self.counter);
79                     self.counter += 1;
80                 }
81                 r.super_visit_with(self)
82             }
83         }
84
85         let expected_highlight = HighlightBuilder::build(self.tcx(), expected);
86         let expected = self
87             .infcx
88             .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
89             .name;
90         let found_highlight = HighlightBuilder::build(self.tcx(), found);
91         let found =
92             self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
93
94         err.span_label(sp, &format!("found `{}`", found));
95         err.span_label(trait_sp, &format!("expected `{}`", expected));
96
97         // Get the span of all the used type parameters in the method.
98         let assoc_item = self.tcx().associated_item(trait_def_id);
99         let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
100         match assoc_item.kind {
101             ty::AssocKind::Fn => {
102                 let hir = self.tcx().hir();
103                 if let Some(hir_id) =
104                     assoc_item.def_id.as_local().map(|id| hir.local_def_id_to_hir_id(id))
105                 {
106                     if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
107                         visitor.visit_fn_decl(decl);
108                     }
109                 }
110             }
111             _ => {}
112         }
113         let mut type_param_span: MultiSpan = visitor.types.to_vec().into();
114         for &span in &visitor.types {
115             type_param_span
116                 .push_span_label(span, "consider borrowing this type parameter in the trait");
117         }
118
119         err.note(&format!("expected `{}`\n   found `{}`", expected, found));
120
121         err.span_help(
122             type_param_span,
123             "the lifetime requirements from the `impl` do not correspond to the requirements in \
124              the `trait`",
125         );
126         if visitor.types.is_empty() {
127             err.help(
128                 "verify the lifetime relationships in the `trait` and `impl` between the `self` \
129                  argument, the other inputs and its output",
130             );
131         }
132         err.emit()
133     }
134 }
135
136 struct TypeParamSpanVisitor<'tcx> {
137     tcx: TyCtxt<'tcx>,
138     types: Vec<Span>,
139 }
140
141 impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
142     type NestedFilter = nested_filter::OnlyBodies;
143
144     fn nested_visit_map(&mut self) -> Self::Map {
145         self.tcx.hir()
146     }
147
148     fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
149         match arg.kind {
150             hir::TyKind::Rptr(_, ref mut_ty) => {
151                 // We don't want to suggest looking into borrowing `&T` or `&Self`.
152                 hir::intravisit::walk_ty(self, mut_ty.ty);
153                 return;
154             }
155             hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
156                 [segment]
157                     if segment
158                         .res
159                         .map(|res| {
160                             matches!(
161                                 res,
162                                 Res::SelfTy { trait_: _, alias_to: _ }
163                                     | Res::Def(hir::def::DefKind::TyParam, _)
164                             )
165                         })
166                         .unwrap_or(false) =>
167                 {
168                     self.types.push(path.span);
169                 }
170                 _ => {}
171             },
172             _ => {}
173         }
174         hir::intravisit::walk_ty(self, arg);
175     }
176 }