]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
Rollup merge of #89922 - JohnTitor:update-e0637, r=jackh726
[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, ValuePairs};
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::ty::error::ExpectedFound;
13 use rustc_middle::ty::{self, Ty, TyCtxt};
14 use rustc_span::{MultiSpan, Span, Symbol};
15
16 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
17     /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
18     pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorReported> {
19         let error = self.error.as_ref()?;
20         debug!("try_report_impl_not_conforming_to_trait {:?}", error);
21         if let RegionResolutionError::SubSupConflict(
22             _,
23             var_origin,
24             sub_origin,
25             _sub,
26             sup_origin,
27             _sup,
28         ) = error.clone()
29         {
30             if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) {
31                 if let (
32                     ValuePairs::Types(sub_expected_found),
33                     ValuePairs::Types(sup_expected_found),
34                     CompareImplMethodObligation { trait_item_def_id, .. },
35                 ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code)
36                 {
37                     if sup_expected_found == sub_expected_found {
38                         self.emit_err(
39                             var_origin.span(),
40                             sub_expected_found.expected,
41                             sub_expected_found.found,
42                             *trait_item_def_id,
43                         );
44                         return Some(ErrorReported);
45                     }
46                 }
47             }
48         }
49         if let RegionResolutionError::ConcreteFailure(origin, _, _)
50         | RegionResolutionError::GenericBoundFailure(origin, _, _) = error.clone()
51         {
52             if let SubregionOrigin::CompareImplTypeObligation {
53                 span,
54                 item_name,
55                 impl_item_def_id,
56                 trait_item_def_id,
57             } = origin
58             {
59                 self.emit_associated_type_err(span, item_name, impl_item_def_id, trait_item_def_id);
60                 return Some(ErrorReported);
61             }
62         }
63         None
64     }
65
66     fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, trait_def_id: DefId) {
67         let trait_sp = self.tcx().def_span(trait_def_id);
68         let mut err = self
69             .tcx()
70             .sess
71             .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature");
72         err.span_label(sp, &format!("found `{}`", found));
73         err.span_label(trait_sp, &format!("expected `{}`", expected));
74
75         // Get the span of all the used type parameters in the method.
76         let assoc_item = self.tcx().associated_item(trait_def_id);
77         let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
78         match assoc_item.kind {
79             ty::AssocKind::Fn => {
80                 let hir = self.tcx().hir();
81                 if let Some(hir_id) =
82                     assoc_item.def_id.as_local().map(|id| hir.local_def_id_to_hir_id(id))
83                 {
84                     if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
85                         visitor.visit_fn_decl(decl);
86                     }
87                 }
88             }
89             _ => {}
90         }
91         let mut type_param_span: MultiSpan = visitor.types.to_vec().into();
92         for &span in &visitor.types {
93             type_param_span.push_span_label(
94                 span,
95                 "consider borrowing this type parameter in the trait".to_string(),
96             );
97         }
98
99         if let Some((expected, found)) =
100             self.infcx.expected_found_str_ty(ExpectedFound { expected, found })
101         {
102             // Highlighted the differences when showing the "expected/found" note.
103             err.note_expected_found(&"", expected, &"", found);
104         } else {
105             // This fallback shouldn't be necessary, but let's keep it in just in case.
106             err.note(&format!("expected `{}`\n   found `{}`", expected, found));
107         }
108         err.span_help(
109             type_param_span,
110             "the lifetime requirements from the `impl` do not correspond to the requirements in \
111              the `trait`",
112         );
113         if visitor.types.is_empty() {
114             err.help(
115                 "verify the lifetime relationships in the `trait` and `impl` between the `self` \
116                  argument, the other inputs and its output",
117             );
118         }
119         err.emit();
120     }
121
122     fn emit_associated_type_err(
123         &self,
124         span: Span,
125         item_name: Symbol,
126         impl_item_def_id: DefId,
127         trait_item_def_id: DefId,
128     ) {
129         let impl_sp = self.tcx().def_span(impl_item_def_id);
130         let trait_sp = self.tcx().def_span(trait_item_def_id);
131         let mut err = self
132             .tcx()
133             .sess
134             .struct_span_err(span, &format!("`impl` associated type signature for `{}` doesn't match `trait` associated type signature", item_name));
135         err.span_label(impl_sp, "found");
136         err.span_label(trait_sp, "expected");
137
138         err.emit();
139     }
140 }
141
142 struct TypeParamSpanVisitor<'tcx> {
143     tcx: TyCtxt<'tcx>,
144     types: Vec<Span>,
145 }
146
147 impl Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
148     type Map = rustc_middle::hir::map::Map<'tcx>;
149
150     fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
151         hir::intravisit::NestedVisitorMap::OnlyBodies(self.tcx.hir())
152     }
153
154     fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
155         match arg.kind {
156             hir::TyKind::Rptr(_, ref mut_ty) => {
157                 // We don't want to suggest looking into borrowing `&T` or `&Self`.
158                 hir::intravisit::walk_ty(self, mut_ty.ty);
159                 return;
160             }
161             hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
162                 [segment]
163                     if segment
164                         .res
165                         .map(|res| {
166                             matches!(
167                                 res,
168                                 Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _)
169                             )
170                         })
171                         .unwrap_or(false) =>
172                 {
173                     self.types.push(path.span);
174                 }
175                 _ => {}
176             },
177             _ => {}
178         }
179         hir::intravisit::walk_ty(self, arg);
180     }
181 }