]> git.lizzy.rs Git - rust.git/blob - src/librustc/infer/error_reporting/util.rs
Auto merge of #44046 - alexcrichton:capture-diagnostics, r=michaelwoerister
[rust.git] / src / librustc / infer / error_reporting / util.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Helper functions corresponding to lifetime errors due to
12 //! anonymous regions.
13 use hir;
14 use infer::InferCtxt;
15 use ty::{self, Region};
16 use hir::def_id::DefId;
17 use hir::map as hir_map;
18
19 macro_rules! or_false {
20      ($v:expr) => {
21           match $v {
22                Some(v) => v,
23                None => return false,
24           }
25      }
26 }
27
28 // The struct contains the information about the anonymous region
29 // we are searching for.
30 pub struct AnonymousArgInfo<'tcx> {
31     // the argument corresponding to the anonymous region
32     pub arg: &'tcx hir::Arg,
33     // the type corresponding to the anonymopus region argument
34     pub arg_ty: ty::Ty<'tcx>,
35     // the ty::BoundRegion corresponding to the anonymous region
36     pub bound_region: ty::BoundRegion,
37     // corresponds to id the argument is the first parameter
38     // in the declaration
39     pub is_first: bool,
40 }
41
42 // This struct contains information regarding the
43 // Refree((FreeRegion) corresponding to lifetime conflict
44 pub struct FreeRegionInfo {
45     // def id corresponding to FreeRegion
46     pub def_id: DefId,
47     // the bound region corresponding to FreeRegion
48     pub boundregion: ty::BoundRegion,
49     // checks if bound region is in Impl Item
50     pub is_impl_item: bool,
51 }
52
53 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
54     // This method walks the Type of the function body arguments using
55     // `fold_regions()` function and returns the
56     // &hir::Arg of the function argument corresponding to the anonymous
57     // region and the Ty corresponding to the named region.
58     // Currently only the case where the function declaration consists of
59     // one named region and one anonymous region is handled.
60     // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
61     // Here, we would return the hir::Arg for y, we return the type &'a
62     // i32, which is the type of y but with the anonymous region replaced
63     // with 'a, the corresponding bound region and is_first which is true if
64     // the hir::Arg is the first argument in the function declaration.
65     pub fn find_arg_with_anonymous_region(&self,
66                                           anon_region: Region<'tcx>,
67                                           replace_region: Region<'tcx>)
68                                           -> Option<AnonymousArgInfo> {
69
70         if let ty::ReFree(ref free_region) = *anon_region {
71             let id = free_region.scope;
72             let hir = &self.tcx.hir;
73             if let Some(node_id) = hir.as_local_node_id(id) {
74                 if let Some(body_id) = hir.maybe_body_owned_by(node_id) {
75                     let body = hir.body(body_id);
76                     if let Some(tables) = self.in_progress_tables {
77                         body.arguments
78                             .iter()
79                             .enumerate()
80                             .filter_map(|(index, arg)| {
81                                 let ty = tables.borrow().node_id_to_type(arg.hir_id);
82                                 let mut found_anon_region = false;
83                                 let new_arg_ty = self.tcx
84                                     .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
85                                         found_anon_region = true;
86                                         replace_region
87                                     } else {
88                                         r
89                                     });
90                                 if found_anon_region {
91                                     let is_first = index == 0;
92                                     Some(AnonymousArgInfo {
93                                              arg: arg,
94                                              arg_ty: new_arg_ty,
95                                              bound_region: free_region.bound_region,
96                                              is_first: is_first,
97                                          })
98                                 } else {
99                                     None
100                                 }
101                             })
102                             .next()
103                     } else {
104                         None
105                     }
106                 } else {
107                     None
108                 }
109             } else {
110                 None
111             }
112         } else {
113             None
114         }
115     }
116
117     // This method returns whether the given Region is Anonymous
118     // and returns the DefId and the BoundRegion corresponding to the given region.
119     pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
120         if let ty::ReFree(ref free_region) = *region {
121             if let ty::BrAnon(..) = free_region.bound_region {
122                 let anonymous_region_binding_scope = free_region.scope;
123                 let node_id = self.tcx
124                     .hir
125                     .as_local_node_id(anonymous_region_binding_scope)
126                     .unwrap();
127                 let mut is_impl_item = false;
128                 match self.tcx.hir.find(node_id) {
129
130                     Some(hir_map::NodeItem(..)) |
131                     Some(hir_map::NodeTraitItem(..)) => {
132                         // Success -- proceed to return Some below
133                     }
134                     Some(hir_map::NodeImplItem(..)) => {
135                         is_impl_item =
136                             self.is_bound_region_in_impl_item(anonymous_region_binding_scope);
137                     }
138                     _ => return None,
139                 }
140                 return Some(FreeRegionInfo {
141                                 def_id: anonymous_region_binding_scope,
142                                 boundregion: free_region.bound_region,
143                                 is_impl_item: is_impl_item,
144                             });
145             }
146         }
147         None
148     }
149
150     // Here, we check for the case where the anonymous region
151     // is in the return type.
152     // FIXME(#42703) - Need to handle certain cases here.
153     pub fn is_return_type_anon(&self, scope_def_id: DefId, br: ty::BoundRegion) -> bool {
154         let ret_ty = self.tcx.type_of(scope_def_id);
155         match ret_ty.sty {
156             ty::TyFnDef(_, _) => {
157                 let sig = ret_ty.fn_sig(self.tcx);
158                 let late_bound_regions = self.tcx
159                     .collect_referenced_late_bound_regions(&sig.output());
160                 if late_bound_regions.iter().any(|r| *r == br) {
161                     return true;
162                 }
163             }
164             _ => {}
165         }
166         false
167     }
168     // Here we check for the case where anonymous region
169     // corresponds to self and if yes, we display E0312.
170     // FIXME(#42700) - Need to format self properly to
171     // enable E0621 for it.
172     pub fn is_self_anon(&self, is_first: bool, scope_def_id: DefId) -> bool {
173         is_first &&
174         self.tcx
175             .opt_associated_item(scope_def_id)
176             .map(|i| i.method_has_self_argument) == Some(true)
177     }
178
179     // Here we check if the bound region is in Impl Item.
180     pub fn is_bound_region_in_impl_item(&self, anonymous_region_binding_scope: DefId) -> bool {
181         let container_id = self.tcx
182             .associated_item(anonymous_region_binding_scope)
183             .container
184             .id();
185         if self.tcx.impl_trait_ref(container_id).is_some() {
186             // For now, we do not try to target impls of traits. This is
187             // because this message is going to suggest that the user
188             // change the fn signature, but they may not be free to do so,
189             // since the signature must match the trait.
190             //
191             // FIXME(#42706) -- in some cases, we could do better here.
192             return true;
193         }
194         false
195     }
196 }