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