]> git.lizzy.rs Git - rust.git/blob - src/librustc_infer/infer/error_reporting/nice_region_error/find_anon_type.rs
Account for multiple impl/dyn Trait in return type when suggesting `'_`
[rust.git] / src / librustc_infer / infer / error_reporting / nice_region_error / find_anon_type.rs
1 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
2 use rustc_hir as hir;
3 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
4 use rustc_hir::Node;
5 use rustc_middle::hir::map::Map;
6 use rustc_middle::middle::resolve_lifetime as rl;
7 use rustc_middle::ty::{self, Region, TyCtxt};
8
9 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
10     /// This function calls the `visit_ty` method for the parameters
11     /// corresponding to the anonymous regions. The `nested_visitor.found_type`
12     /// contains the anonymous type.
13     ///
14     /// # Arguments
15     /// region - the anonymous region corresponding to the anon_anon conflict
16     /// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
17     ///
18     /// # Example
19     /// ```
20     /// fn foo(x: &mut Vec<&u8>, y: &u8)
21     ///    { x.push(y); }
22     /// ```
23     /// The function returns the nested type corresponding to the anonymous region
24     /// for e.g., `&u8` and Vec<`&u8`.
25     pub(super) fn find_anon_type(
26         &self,
27         region: Region<'tcx>,
28         br: &ty::BoundRegion,
29     ) -> Option<(&hir::Ty<'tcx>, &hir::FnDecl<'tcx>)> {
30         if let Some(anon_reg) = self.tcx().is_suitable_region(region) {
31             let def_id = anon_reg.def_id;
32             if let Some(def_id) = def_id.as_local() {
33                 let hir_id = self.tcx().hir().as_local_hir_id(def_id);
34                 let fndecl = match self.tcx().hir().get(hir_id) {
35                     Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref m, ..), .. })
36                     | Node::TraitItem(&hir::TraitItem {
37                         kind: hir::TraitItemKind::Fn(ref m, ..),
38                         ..
39                     })
40                     | Node::ImplItem(&hir::ImplItem {
41                         kind: hir::ImplItemKind::Fn(ref m, ..),
42                         ..
43                     }) => &m.decl,
44                     _ => return None,
45                 };
46
47                 return fndecl
48                     .inputs
49                     .iter()
50                     .find_map(|arg| self.find_component_for_bound_region(arg, br))
51                     .map(|ty| (ty, &**fndecl));
52             }
53         }
54         None
55     }
56
57     // This method creates a FindNestedTypeVisitor which returns the type corresponding
58     // to the anonymous region.
59     fn find_component_for_bound_region(
60         &self,
61         arg: &'tcx hir::Ty<'tcx>,
62         br: &ty::BoundRegion,
63     ) -> Option<&'tcx hir::Ty<'tcx>> {
64         let mut nested_visitor = FindNestedTypeVisitor {
65             tcx: self.tcx(),
66             bound_region: *br,
67             found_type: None,
68             current_index: ty::INNERMOST,
69         };
70         nested_visitor.visit_ty(arg);
71         nested_visitor.found_type
72     }
73 }
74
75 // The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
76 // anonymous region. The example above would lead to a conflict between
77 // the two anonymous lifetimes for &u8 in x and y respectively. This visitor
78 // would be invoked twice, once for each lifetime, and would
79 // walk the types like &mut Vec<&u8> and &u8 looking for the HIR
80 // where that lifetime appears. This allows us to highlight the
81 // specific part of the type in the error message.
82 struct FindNestedTypeVisitor<'tcx> {
83     tcx: TyCtxt<'tcx>,
84     // The bound_region corresponding to the Refree(freeregion)
85     // associated with the anonymous region we are looking for.
86     bound_region: ty::BoundRegion,
87     // The type where the anonymous lifetime appears
88     // for e.g., Vec<`&u8`> and <`&u8`>
89     found_type: Option<&'tcx hir::Ty<'tcx>>,
90     current_index: ty::DebruijnIndex,
91 }
92
93 impl Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
94     type Map = Map<'tcx>;
95
96     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
97         NestedVisitorMap::OnlyBodies(self.tcx.hir())
98     }
99
100     fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
101         match arg.kind {
102             hir::TyKind::BareFn(_) => {
103                 self.current_index.shift_in(1);
104                 intravisit::walk_ty(self, arg);
105                 self.current_index.shift_out(1);
106                 return;
107             }
108
109             hir::TyKind::TraitObject(bounds, _) => {
110                 for bound in bounds {
111                     self.current_index.shift_in(1);
112                     self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
113                     self.current_index.shift_out(1);
114                 }
115             }
116
117             hir::TyKind::Rptr(ref lifetime, _) => {
118                 // the lifetime of the TyRptr
119                 let hir_id = lifetime.hir_id;
120                 match (self.tcx.named_region(hir_id), self.bound_region) {
121                     // Find the index of the anonymous region that was part of the
122                     // error. We will then search the function parameters for a bound
123                     // region at the right depth with the same index
124                     (
125                         Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)),
126                         ty::BrAnon(br_index),
127                     ) => {
128                         debug!(
129                             "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}",
130                             debruijn_index, anon_index, br_index
131                         );
132                         if debruijn_index == self.current_index && anon_index == br_index {
133                             self.found_type = Some(arg);
134                             return; // we can stop visiting now
135                         }
136                     }
137
138                     // Find the index of the named region that was part of the
139                     // error. We will then search the function parameters for a bound
140                     // region at the right depth with the same index
141                     (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => {
142                         debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
143                         if id == def_id {
144                             self.found_type = Some(arg);
145                             return; // we can stop visiting now
146                         }
147                     }
148
149                     // Find the index of the named region that was part of the
150                     // error. We will then search the function parameters for a bound
151                     // region at the right depth with the same index
152                     (
153                         Some(rl::Region::LateBound(debruijn_index, id, _)),
154                         ty::BrNamed(def_id, _),
155                     ) => {
156                         debug!(
157                             "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
158                             debruijn_index
159                         );
160                         debug!("LateBound id={:?} def_id={:?}", id, def_id);
161                         if debruijn_index == self.current_index && id == def_id {
162                             self.found_type = Some(arg);
163                             return; // we can stop visiting now
164                         }
165                     }
166
167                     (
168                         Some(
169                             rl::Region::Static
170                             | rl::Region::Free(_, _)
171                             | rl::Region::EarlyBound(_, _, _)
172                             | rl::Region::LateBound(_, _, _)
173                             | rl::Region::LateBoundAnon(_, _),
174                         )
175                         | None,
176                         _,
177                     ) => {
178                         debug!("no arg found");
179                     }
180                 }
181             }
182             // Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
183             hir::TyKind::Path(_) => {
184                 let subvisitor = &mut TyPathVisitor {
185                     tcx: self.tcx,
186                     found_it: false,
187                     bound_region: self.bound_region,
188                     current_index: self.current_index,
189                 };
190                 intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
191                 // this will visit only outermost type
192                 if subvisitor.found_it {
193                     self.found_type = Some(arg);
194                 }
195             }
196             _ => {}
197         }
198         // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
199         // go on to visit `&Foo`
200         intravisit::walk_ty(self, arg);
201     }
202 }
203
204 // The visitor captures the corresponding `hir::Ty` of the anonymous region
205 // in the case of structs ie. `hir::TyKind::Path`.
206 // This visitor would be invoked for each lifetime corresponding to a struct,
207 // and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
208 // where that lifetime appears. This allows us to highlight the
209 // specific part of the type in the error message.
210 struct TyPathVisitor<'tcx> {
211     tcx: TyCtxt<'tcx>,
212     found_it: bool,
213     bound_region: ty::BoundRegion,
214     current_index: ty::DebruijnIndex,
215 }
216
217 impl Visitor<'tcx> for TyPathVisitor<'tcx> {
218     type Map = Map<'tcx>;
219
220     fn nested_visit_map(&mut self) -> NestedVisitorMap<Map<'tcx>> {
221         NestedVisitorMap::OnlyBodies(self.tcx.hir())
222     }
223
224     fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
225         match (self.tcx.named_region(lifetime.hir_id), self.bound_region) {
226             // the lifetime of the TyPath!
227             (Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)), ty::BrAnon(br_index)) => {
228                 if debruijn_index == self.current_index && anon_index == br_index {
229                     self.found_it = true;
230                     return;
231                 }
232             }
233
234             (Some(rl::Region::EarlyBound(_, id, _)), ty::BrNamed(def_id, _)) => {
235                 debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
236                 if id == def_id {
237                     self.found_it = true;
238                     return; // we can stop visiting now
239                 }
240             }
241
242             (Some(rl::Region::LateBound(debruijn_index, id, _)), ty::BrNamed(def_id, _)) => {
243                 debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,);
244                 debug!("id={:?}", id);
245                 debug!("def_id={:?}", def_id);
246                 if debruijn_index == self.current_index && id == def_id {
247                     self.found_it = true;
248                     return; // we can stop visiting now
249                 }
250             }
251
252             (
253                 Some(
254                     rl::Region::Static
255                     | rl::Region::EarlyBound(_, _, _)
256                     | rl::Region::LateBound(_, _, _)
257                     | rl::Region::LateBoundAnon(_, _)
258                     | rl::Region::Free(_, _),
259                 )
260                 | None,
261                 _,
262             ) => {
263                 debug!("no arg found");
264             }
265         }
266     }
267
268     fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
269         // ignore nested types
270         //
271         // If you have a type like `Foo<'a, &Ty>` we
272         // are only interested in the immediate lifetimes ('a).
273         //
274         // Making `visit_ty` empty will ignore the `&Ty` embedded
275         // inside, it will get reached by the outer visitor.
276         debug!("`Ty` corresponding to a struct is {:?}", arg);
277     }
278 }