]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
6c8a08c09e7778acd9549e4ebf59bc81d5636e3f
[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, false).is_some())
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             sym::ItemContext,
129             self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
130         )];
131
132         match obligation.cause.code() {
133             ObligationCauseCode::BuiltinDerivedObligation(..)
134             | ObligationCauseCode::ImplDerivedObligation(..)
135             | ObligationCauseCode::DerivedObligation(..) => {}
136             _ => {
137                 // this is a "direct", user-specified, rather than derived,
138                 // obligation.
139                 flags.push((sym::direct, None));
140             }
141         }
142
143         if let ObligationCauseCode::ItemObligation(item)
144         | ObligationCauseCode::BindingObligation(item, _) = *obligation.cause.code()
145         {
146             // FIXME: maybe also have some way of handling methods
147             // from other traits? That would require name resolution,
148             // which we might want to be some sort of hygienic.
149             //
150             // Currently I'm leaving it for what I need for `try`.
151             if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
152                 let method = self.tcx.item_name(item);
153                 flags.push((sym::from_method, None));
154                 flags.push((sym::from_method, Some(method.to_string())));
155             }
156         }
157
158         if let Some(k) = obligation.cause.span.desugaring_kind() {
159             flags.push((sym::from_desugaring, None));
160             flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
161         }
162
163         // Add all types without trimmed paths.
164         ty::print::with_no_trimmed_paths(|| {
165             let generics = self.tcx.generics_of(def_id);
166             let self_ty = trait_ref.self_ty();
167             // This is also included through the generics list as `Self`,
168             // but the parser won't allow you to use it
169             flags.push((sym::_Self, Some(self_ty.to_string())));
170             if let Some(def) = self_ty.ty_adt_def() {
171                 // We also want to be able to select self's original
172                 // signature with no type arguments resolved
173                 flags.push((sym::_Self, Some(self.tcx.type_of(def.did).to_string())));
174             }
175
176             for param in generics.params.iter() {
177                 let value = match param.kind {
178                     GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
179                         trait_ref.substs[param.index as usize].to_string()
180                     }
181                     GenericParamDefKind::Lifetime => continue,
182                 };
183                 let name = param.name;
184                 flags.push((name, Some(value)));
185
186                 if let GenericParamDefKind::Type { .. } = param.kind {
187                     let param_ty = trait_ref.substs[param.index as usize].expect_ty();
188                     if let Some(def) = param_ty.ty_adt_def() {
189                         // We also want to be able to select the parameter's
190                         // original signature with no type arguments resolved
191                         flags.push((name, Some(self.tcx.type_of(def.did).to_string())));
192                     }
193                 }
194             }
195
196             if let Some(true) = self_ty.ty_adt_def().map(|def| def.did.is_local()) {
197                 flags.push((sym::crate_local, None));
198             }
199
200             // Allow targeting all integers using `{integral}`, even if the exact type was resolved
201             if self_ty.is_integral() {
202                 flags.push((sym::_Self, Some("{integral}".to_owned())));
203             }
204
205             if let ty::Array(aty, len) = self_ty.kind() {
206                 flags.push((sym::_Self, Some("[]".to_owned())));
207                 flags.push((sym::_Self, Some(format!("[{}]", aty))));
208                 if let Some(def) = aty.ty_adt_def() {
209                     // We also want to be able to select the array's type's original
210                     // signature with no type arguments resolved
211                     let type_string = self.tcx.type_of(def.did).to_string();
212                     flags.push((sym::_Self, Some(format!("[{}]", type_string))));
213
214                     let len =
215                         len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
216                     let string = match len {
217                         Some(n) => format!("[{}; {}]", type_string, n),
218                         None => format!("[{}; _]", type_string),
219                     };
220                     flags.push((sym::_Self, Some(string)));
221                 }
222             }
223             if let ty::Dynamic(traits, _) = self_ty.kind() {
224                 for t in traits.iter() {
225                     if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
226                         flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
227                     }
228                 }
229             }
230         });
231
232         if let Ok(Some(command)) =
233             OnUnimplementedDirective::of_item(self.tcx, trait_ref.def_id, def_id)
234         {
235             command.evaluate(self.tcx, trait_ref, &flags)
236         } else {
237             OnUnimplementedNote::default()
238         }
239     }
240 }