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