]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc/infer/error_reporting/util.rs
Rollup merge of #44562 - eddyb:ugh-rustdoc, r=nikomatsakis
[rust.git] / src / librustc / infer / error_reporting / util.rs
index c8f78367420e64e0c00a52c98cb80bdb6999bbb1..94faec464b24414f0508fc75e053ef0eac244239 100644 (file)
 //! anonymous regions.
 use hir;
 use infer::InferCtxt;
-use ty::{self, Region};
+use ty::{self, Region, Ty};
 use hir::def_id::DefId;
 use hir::map as hir_map;
 
+macro_rules! or_false {
+     ($v:expr) => {
+         match $v {
+             Some(v) => v,
+             None => {
+                 debug!("or_false failed: {}", stringify!($v));
+                 return false;
+             }
+         }
+     }
+}
+
+// The struct contains the information about the anonymous region
+// we are searching for.
+#[derive(Debug)]
+pub struct AnonymousArgInfo<'tcx> {
+    // the argument corresponding to the anonymous region
+    pub arg: &'tcx hir::Arg,
+    // the type corresponding to the anonymopus region argument
+    pub arg_ty: Ty<'tcx>,
+    // the ty::BoundRegion corresponding to the anonymous region
+    pub bound_region: ty::BoundRegion,
+    // corresponds to id the argument is the first parameter
+    // in the declaration
+    pub is_first: bool,
+}
+
+// This struct contains information regarding the
+// Refree((FreeRegion) corresponding to lifetime conflict
+#[derive(Debug)]
+pub struct FreeRegionInfo {
+    // def id corresponding to FreeRegion
+    pub def_id: DefId,
+    // the bound region corresponding to FreeRegion
+    pub boundregion: ty::BoundRegion,
+    // checks if bound region is in Impl Item
+    pub is_impl_item: bool,
+}
+
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     // This method walks the Type of the function body arguments using
     // `fold_regions()` function and returns the
@@ -28,44 +67,54 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     // i32, which is the type of y but with the anonymous region replaced
     // with 'a, the corresponding bound region and is_first which is true if
     // the hir::Arg is the first argument in the function declaration.
-    pub fn find_arg_with_anonymous_region
-        (&self,
-         anon_region: Region<'tcx>,
-         replace_region: Region<'tcx>)
-         -> Option<(&hir::Arg, ty::Ty<'tcx>, ty::BoundRegion, bool)> {
-
-        if let ty::ReFree(ref free_region) = *anon_region {
-
-            let id = free_region.scope;
-            let hir = &self.tcx.hir;
-            if let Some(node_id) = hir.as_local_node_id(id) {
-                if let Some(body_id) = hir.maybe_body_owned_by(node_id) {
-                    let body = hir.body(body_id);
-                    if let Some(tables) = self.in_progress_tables {
-                        body.arguments
-                            .iter()
-                            .enumerate()
-                            .filter_map(|(index, arg)| {
-                                let ty = tables.borrow().node_id_to_type(arg.hir_id);
-                                let mut found_anon_region = false;
-                                let new_arg_ty = self.tcx
-                                    .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
-                                        found_anon_region = true;
-                                        replace_region
-                                    } else {
-                                        r
-                                    });
-                                if found_anon_region {
-                                    let is_first = index == 0;
-                                    Some((arg, new_arg_ty, free_region.bound_region, is_first))
+    pub fn find_arg_with_region(&self,
+                                anon_region: Region<'tcx>,
+                                replace_region: Region<'tcx>)
+                                -> Option<AnonymousArgInfo> {
+
+        let (id, bound_region) = match *anon_region {
+            ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+            ty::ReEarlyBound(ref ebr) => {
+                (self.tcx.parent_def_id(ebr.def_id).unwrap(),
+                 ty::BoundRegion::BrNamed(ebr.def_id, ebr.name))
+            }
+            _ => return None, // not a free region
+        };
+
+        let hir = &self.tcx.hir;
+        if let Some(node_id) = hir.as_local_node_id(id) {
+            if let Some(body_id) = hir.maybe_body_owned_by(node_id) {
+                let body = hir.body(body_id);
+                if let Some(tables) = self.in_progress_tables {
+                    body.arguments
+                        .iter()
+                        .enumerate()
+                        .filter_map(|(index, arg)| {
+                            let ty = match tables.borrow().node_id_to_type_opt(arg.hir_id) {
+                                Some(v) => v,
+                                None => return None, // sometimes the tables are not yet populated
+                            };
+                            let mut found_anon_region = false;
+                            let new_arg_ty = self.tcx
+                                .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
+                                    found_anon_region = true;
+                                    replace_region
                                 } else {
-                                    None
-                                }
-                            })
-                            .next()
-                    } else {
-                        None
-                    }
+                                    r
+                                });
+                            if found_anon_region {
+                                let is_first = index == 0;
+                                Some(AnonymousArgInfo {
+                                         arg: arg,
+                                         arg_ty: new_arg_ty,
+                                         bound_region: bound_region,
+                                         is_first: is_first,
+                                     })
+                            } else {
+                                None
+                            }
+                        })
+                        .next()
                 } else {
                     None
                 }
@@ -77,44 +126,97 @@ pub fn find_arg_with_anonymous_region
         }
     }
 
-    // This method returns whether the given Region is Anonymous
-    // and returns the DefId and the BoundRegion corresponding to the given region.
-    pub fn is_suitable_anonymous_region(&self,
-                                        region: Region<'tcx>)
-                                        -> Option<(DefId, ty::BoundRegion)> {
-        if let ty::ReFree(ref free_region) = *region {
-            if let ty::BrAnon(..) = free_region.bound_region{
-                    let anonymous_region_binding_scope = free_region.scope;
-                    let node_id = self.tcx
-                        .hir
-                        .as_local_node_id(anonymous_region_binding_scope)
-                        .unwrap();
-                    match self.tcx.hir.find(node_id) {
-                        Some(hir_map::NodeItem(..)) |
-                        Some(hir_map::NodeTraitItem(..)) => {
-                            // Success -- proceed to return Some below
-                        }
-                        Some(hir_map::NodeImplItem(..)) => {
-                            let container_id = self.tcx
-                                .associated_item(anonymous_region_binding_scope)
-                                .container
-                                .id();
-                            if self.tcx.impl_trait_ref(container_id).is_some() {
-                                // For now, we do not try to target impls of traits. This is
-                                // because this message is going to suggest that the user
-                                // change the fn signature, but they may not be free to do so,
-                                // since the signature must match the trait.
-                                //
-                                // FIXME(#42706) -- in some cases, we could do better here.
-                                return None;
-                            }
-                        }
-                        _ => return None, // inapplicable
-                        // we target only top-level functions
-                    }
-                    return Some((anonymous_region_binding_scope, free_region.bound_region));
+    // This method returns the DefId and the BoundRegion corresponding to the given region.
+    pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option<FreeRegionInfo> {
+
+        let (suitable_region_binding_scope, bound_region) = match *region {
+            ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+            ty::ReEarlyBound(ref ebr) => {
+                (self.tcx.parent_def_id(ebr.def_id).unwrap(),
+                 ty::BoundRegion::BrNamed(ebr.def_id, ebr.name))
+            }
+            _ => return None, // not a free region
+        };
+
+        let node_id = self.tcx
+            .hir
+            .as_local_node_id(suitable_region_binding_scope)
+            .unwrap();
+        let is_impl_item = match self.tcx.hir.find(node_id) {
+
+            Some(hir_map::NodeItem(..)) |
+            Some(hir_map::NodeTraitItem(..)) => false,
+            Some(hir_map::NodeImplItem(..)) => {
+                self.is_bound_region_in_impl_item(suitable_region_binding_scope)
+            }
+            _ => return None,
+        };
+
+        return Some(FreeRegionInfo {
+                        def_id: suitable_region_binding_scope,
+                        boundregion: bound_region,
+                        is_impl_item: is_impl_item,
+                    });
+
+    }
+
+    // Here, we check for the case where the anonymous region
+    // is in the return type.
+    // FIXME(#42703) - Need to handle certain cases here.
+    pub fn is_return_type_anon(&self, scope_def_id: DefId, br: ty::BoundRegion) -> bool {
+        let ret_ty = self.tcx.type_of(scope_def_id);
+        match ret_ty.sty {
+            ty::TyFnDef(_, _) => {
+                let sig = ret_ty.fn_sig(self.tcx);
+                let late_bound_regions = self.tcx
+                    .collect_referenced_late_bound_regions(&sig.output());
+                if late_bound_regions.iter().any(|r| *r == br) {
+                    return true;
                 }
             }
-            None
+            _ => {}
         }
+        false
+    }
+    // Here we check for the case where anonymous region
+    // corresponds to self and if yes, we display E0312.
+    // FIXME(#42700) - Need to format self properly to
+    // enable E0621 for it.
+    pub fn is_self_anon(&self, is_first: bool, scope_def_id: DefId) -> bool {
+        is_first &&
+        self.tcx
+            .opt_associated_item(scope_def_id)
+            .map(|i| i.method_has_self_argument) == Some(true)
+    }
+
+    // Here we check if the bound region is in Impl Item.
+    pub fn is_bound_region_in_impl_item(&self, suitable_region_binding_scope: DefId) -> bool {
+        let container_id = self.tcx
+            .associated_item(suitable_region_binding_scope)
+            .container
+            .id();
+        if self.tcx.impl_trait_ref(container_id).is_some() {
+            // For now, we do not try to target impls of traits. This is
+            // because this message is going to suggest that the user
+            // change the fn signature, but they may not be free to do so,
+            // since the signature must match the trait.
+            //
+            // FIXME(#42706) -- in some cases, we could do better here.
+            return true;
+        }
+        false
+    }
+
+    // This method returns whether the given Region is Named
+    pub fn is_named_region(&self, region: Region<'tcx>) -> bool {
+        match *region {
+            ty::ReFree(ref free_region) => {
+                match free_region.bound_region {
+                    ty::BrNamed(..) => true,
+                    _ => false,
+                }
+            }
+            _ => false,
+        }
+    }
 }