]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
0ca0245a203d1ef0f90c9edc2ce2da20aadaff0b
[rust.git] / compiler / rustc_trait_selection / src / traits / error_reporting / on_unimplemented.rs
1 use super::{
2     ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation,
3 };
4 use crate::infer::InferCtxt;
5 use rustc_hir as hir;
6 use rustc_hir::def_id::DefId;
7 use rustc_middle::ty::subst::Subst;
8 use rustc_middle::ty::{self, GenericParamDefKind};
9 use rustc_span::symbol::sym;
10 use std::iter;
11
12 use super::InferCtxtPrivExt;
13
14 crate trait InferCtxtExt<'tcx> {
15     /*private*/
16     fn impl_similar_to(
17         &self,
18         trait_ref: ty::PolyTraitRef<'tcx>,
19         obligation: &PredicateObligation<'tcx>,
20     ) -> Option<DefId>;
21
22     /*private*/
23     fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str>;
24
25     fn on_unimplemented_note(
26         &self,
27         trait_ref: ty::PolyTraitRef<'tcx>,
28         obligation: &PredicateObligation<'tcx>,
29     ) -> OnUnimplementedNote;
30 }
31
32 impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
33     fn impl_similar_to(
34         &self,
35         trait_ref: ty::PolyTraitRef<'tcx>,
36         obligation: &PredicateObligation<'tcx>,
37     ) -> Option<DefId> {
38         let tcx = self.tcx;
39         let param_env = obligation.param_env;
40         let trait_ref = tcx.erase_late_bound_regions(trait_ref);
41         let trait_self_ty = trait_ref.self_ty();
42
43         let mut self_match_impls = vec![];
44         let mut fuzzy_match_impls = vec![];
45
46         self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
47             let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
48             let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
49
50             let impl_self_ty = impl_trait_ref.self_ty();
51
52             if let Ok(..) = self.can_eq(param_env, trait_self_ty, impl_self_ty) {
53                 self_match_impls.push(def_id);
54
55                 if iter::zip(
56                     trait_ref.substs.types().skip(1),
57                     impl_trait_ref.substs.types().skip(1),
58                 )
59                 .all(|(u, v)| self.fuzzy_match_tys(u, v))
60                 {
61                     fuzzy_match_impls.push(def_id);
62                 }
63             }
64         });
65
66         let impl_def_id = if self_match_impls.len() == 1 {
67             self_match_impls[0]
68         } else if fuzzy_match_impls.len() == 1 {
69             fuzzy_match_impls[0]
70         } else {
71             return None;
72         };
73
74         tcx.has_attr(impl_def_id, sym::rustc_on_unimplemented).then_some(impl_def_id)
75     }
76
77     /// Used to set on_unimplemented's `ItemContext`
78     /// to be the enclosing (async) block/function/closure
79     fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
80         let hir = &self.tcx.hir();
81         let node = hir.find(hir_id)?;
82         match &node {
83             hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => {
84                 self.describe_generator(*body_id).or_else(|| {
85                     Some(match sig.header {
86                         hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async function",
87                         _ => "a function",
88                     })
89                 })
90             }
91             hir::Node::TraitItem(hir::TraitItem {
92                 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body_id)),
93                 ..
94             }) => self.describe_generator(*body_id).or_else(|| Some("a trait method")),
95             hir::Node::ImplItem(hir::ImplItem {
96                 kind: hir::ImplItemKind::Fn(sig, body_id),
97                 ..
98             }) => self.describe_generator(*body_id).or_else(|| {
99                 Some(match sig.header {
100                     hir::FnHeader { asyncness: hir::IsAsync::Async, .. } => "an async method",
101                     _ => "a method",
102                 })
103             }),
104             hir::Node::Expr(hir::Expr {
105                 kind: hir::ExprKind::Closure(_is_move, _, body_id, _, gen_movability),
106                 ..
107             }) => self.describe_generator(*body_id).or_else(|| {
108                 Some(if gen_movability.is_some() { "an async closure" } else { "a closure" })
109             }),
110             hir::Node::Expr(hir::Expr { .. }) => {
111                 let parent_hid = hir.get_parent_node(hir_id);
112                 if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None }
113             }
114             _ => None,
115         }
116     }
117
118     fn on_unimplemented_note(
119         &self,
120         trait_ref: ty::PolyTraitRef<'tcx>,
121         obligation: &PredicateObligation<'tcx>,
122     ) -> OnUnimplementedNote {
123         let def_id =
124             self.impl_similar_to(trait_ref, obligation).unwrap_or_else(|| trait_ref.def_id());
125         let trait_ref = trait_ref.skip_binder();
126
127         let mut flags = vec![];
128         flags.push((
129             sym::ItemContext,
130             self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
131         ));
132
133         match obligation.cause.code {
134             ObligationCauseCode::BuiltinDerivedObligation(..)
135             | ObligationCauseCode::ImplDerivedObligation(..)
136             | ObligationCauseCode::DerivedObligation(..) => {}
137             _ => {
138                 // this is a "direct", user-specified, rather than derived,
139                 // obligation.
140                 flags.push((sym::direct, None));
141             }
142         }
143
144         if let ObligationCauseCode::ItemObligation(item)
145         | ObligationCauseCode::BindingObligation(item, _) = obligation.cause.code
146         {
147             // FIXME: maybe also have some way of handling methods
148             // from other traits? That would require name resolution,
149             // which we might want to be some sort of hygienic.
150             //
151             // Currently I'm leaving it for what I need for `try`.
152             if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
153                 let method = self.tcx.item_name(item);
154                 flags.push((sym::from_method, None));
155                 flags.push((sym::from_method, Some(method.to_string())));
156             }
157         }
158         if let Some((t, _)) = self.get_parent_trait_ref(&obligation.cause.code) {
159             flags.push((sym::parent_trait, Some(t)));
160         }
161
162         if let Some(k) = obligation.cause.span.desugaring_kind() {
163             flags.push((sym::from_desugaring, None));
164             flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
165         }
166
167         // Add all types without trimmed paths.
168         ty::print::with_no_trimmed_paths(|| {
169             let generics = self.tcx.generics_of(def_id);
170             let self_ty = trait_ref.self_ty();
171             // This is also included through the generics list as `Self`,
172             // but the parser won't allow you to use it
173             flags.push((sym::_Self, Some(self_ty.to_string())));
174             if let Some(def) = self_ty.ty_adt_def() {
175                 // We also want to be able to select self's original
176                 // signature with no type arguments resolved
177                 flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string())));
178             }
179
180             for param in generics.params.iter() {
181                 let value = match param.kind {
182                     GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
183                         trait_ref.substs[param.index as usize].to_string()
184                     }
185                     GenericParamDefKind::Lifetime => continue,
186                 };
187                 let name = param.name;
188                 flags.push((name, Some(value)));
189
190                 if let GenericParamDefKind::Type { .. } = param.kind {
191                     let param_ty = trait_ref.substs[param.index as usize].expect_ty();
192                     if let Some(def) = param_ty.ty_adt_def() {
193                         // We also want to be able to select the parameter's
194                         // original signature with no type arguments resolved
195                         flags.push((name, Some(self.tcx.type_of(def.did).to_string())));
196                     }
197                 }
198             }
199
200             if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) {
201                 flags.push((sym::crate_local, None));
202             }
203
204             // Allow targeting all integers using `{integral}`, even if the exact type was resolved
205             if self_ty.is_integral() {
206                 flags.push((sym::_Self, Some("{integral}".to_owned())));
207             }
208
209             if let ty::Array(aty, len) = self_ty.kind() {
210                 flags.push((sym::_Self, Some("[]".to_owned())));
211                 flags.push((sym::_Self, Some(format!("[{}]", aty))));
212                 if let Some(def) = aty.ty_adt_def() {
213                     // We also want to be able to select the array's type's original
214                     // signature with no type arguments resolved
215                     let type_string = self.tcx.type_of(def.did).to_string();
216                     flags.push((sym::_Self, Some(format!("[{}]", type_string))));
217
218                     let len = len.val.try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
219                     let string = match len {
220                         Some(n) => format!("[{}; {}]", type_string, n),
221                         None => format!("[{}; _]", type_string),
222                     };
223                     flags.push((sym::_Self, Some(string)));
224                 }
225             }
226             if let ty::Dynamic(traits, _) = self_ty.kind() {
227                 for t in traits.iter() {
228                     if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
229                         flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
230                     }
231                 }
232             }
233         });
234
235         if let Ok(Some(command)) =
236             OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)
237         {
238             command.evaluate(self.tcx, trait_ref, &flags[..])
239         } else {
240             OnUnimplementedNote::default()
241         }
242     }
243 }