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