]> git.lizzy.rs Git - rust.git/commitdiff
Emit appropriate suggestion when there's already 'static bound on the return type.
authorDavid Wood <david@davidtw.co>
Tue, 4 Sep 2018 16:56:14 +0000 (18:56 +0200)
committerDavid Wood <david@davidtw.co>
Thu, 13 Sep 2018 08:01:18 +0000 (10:01 +0200)
src/librustc/ty/context.rs
src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs
src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs
src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr

index ae840f93c20e4df157c35a6969127d6c927697af..03c99180d5e503c6da6b06e42d979ce1d7243746 100644 (file)
@@ -1590,7 +1590,7 @@ pub fn is_suitable_region(&self, region: Region<'tcx>) -> Option<FreeRegionInfo>
     pub fn return_type_impl_trait(
         &self,
         scope_def_id: DefId,
-    ) -> Option<Ty> {
+    ) -> Option<Ty<'tcx>> {
         let ret_ty = self.type_of(scope_def_id);
         match ret_ty.sty {
             ty::FnDef(_, _) => {
index bf3b5e2a17c6e9ca61b250de39e18952b4b4d452..8690212804e219d5149ebe2fa30a8a14d9b703b3 100644 (file)
@@ -15,7 +15,7 @@
 use rustc::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc::infer::InferCtxt;
 use rustc::mir::{self, Location, Mir, Place, Rvalue, StatementKind, TerminatorKind};
-use rustc::ty::{TyCtxt, Ty, TyS, TyKind, Region, RegionKind, RegionVid};
+use rustc::ty::{self, TyCtxt, Region, RegionKind, RegionVid};
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_errors::{Diagnostic, DiagnosticBuilder};
 use std::collections::VecDeque;
@@ -491,26 +491,67 @@ fn add_static_impl_trait_suggestion(
         fr_region: Option<Region<'tcx>>,
         outlived_fr_region: Option<Region<'tcx>>,
     ) {
-        if let (Some(f), Some(RegionKind::ReStatic)) = (fr_region, outlived_fr_region) {
-            if let Some(TyS {
-                sty: TyKind::Anon(did, _),
+        if let (Some(f), Some(ty::RegionKind::ReStatic)) = (fr_region, outlived_fr_region) {
+            if let Some(ty::TyS {
+                sty: ty::TyKind::Anon(did, substs),
                 ..
-            }) = self.return_type_impl_trait(infcx, f) {
+            }) = infcx.tcx.is_suitable_region(f)
+                    .map(|r| r.def_id)
+                    .map(|id| infcx.tcx.return_type_impl_trait(id))
+                    .unwrap_or(None)
+            {
+                let has_static_predicate = {
+                    let predicates_of = infcx.tcx.predicates_of(*did);
+                    let bounds = predicates_of.instantiate(infcx.tcx, substs);
+
+                    let mut found = false;
+                    for predicate in bounds.predicates {
+                        if let ty::Predicate::TypeOutlives(binder) = predicate {
+                            if let ty::OutlivesPredicate(
+                                _,
+                                RegionKind::ReStatic
+                            ) = binder.skip_binder() {
+                                found = true;
+                                break;
+                            }
+                        }
+                    }
+
+                    found
+                };
+
+                debug!("add_static_impl_trait_suggestion: has_static_predicate={:?}",
+                       has_static_predicate);
                 let static_str = keywords::StaticLifetime.name();
-                let span = infcx.tcx.def_span(*did);
-                if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
-                    diag.span_suggestion(
-                        span,
-                        &format!(
-                            "you can add a constraint to the return type to make it last \
-                             less than `{}` and match `{}`",
-                            static_str, fr_name,
-                        ),
-                        match fr_name {
-                            RegionName::Named(name) => format!("{} + {}", snippet, name),
-                            RegionName::Synthesized(_) => format!("{} + '_", snippet),
-                        },
-                    );
+                if has_static_predicate {
+                    let span = self.get_span_of_named_region(infcx.tcx, f, &fr_name);
+                    if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
+                        diag.span_suggestion(
+                            span,
+                            &format!(
+                                "you can add a constraint to the definition of `{}` to require it \
+                                 outlive `{}`",
+                                fr_name, static_str,
+                            ),
+                            format!("{}: {}", snippet, static_str),
+                        );
+                    }
+                } else {
+                    let span = infcx.tcx.def_span(*did);
+                    if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
+                        diag.span_suggestion(
+                            span,
+                            &format!(
+                                "you can add a constraint to the return type to make it last \
+                                 less than `{}` and match `{}`",
+                                static_str, fr_name,
+                            ),
+                            match fr_name {
+                                RegionName::Named(name) => format!("{} + {}", snippet, name),
+                                RegionName::Synthesized(_) => format!("{} + '_", snippet),
+                            },
+                        );
+                    }
                 }
             }
         }
@@ -538,15 +579,4 @@ fn add_static_impl_trait_suggestion(
         let (_, span, _) = self.best_blame_constraint(mir, tcx, fr1, |r| r == fr2);
         span
     }
-
-    fn return_type_impl_trait<'cx>(
-        &self,
-        infcx: &'cx InferCtxt<'_, '_, 'tcx>,
-        outlived_fr_region: Region<'tcx>,
-    ) -> Option<Ty<'cx>> {
-        infcx.tcx.is_suitable_region(outlived_fr_region)
-            .map(|r| r.def_id)
-            .map(|id| infcx.tcx.return_type_impl_trait(id))
-            .unwrap_or(None)
-    }
 }
index 136f660fe8daad7c804cfd119b3bdf5bf157632f..857f1dae7ab5175c27e347630c3e743a5a8938f9 100644 (file)
@@ -22,6 +22,7 @@
 use rustc_errors::DiagnosticBuilder;
 use syntax::ast::{Name, DUMMY_NODE_ID};
 use syntax::symbol::keywords;
+use syntax_pos::Span;
 use syntax_pos::symbol::InternedString;
 
 /// Name of a region used in error reporting. Variants denote the source of the region name -
@@ -33,6 +34,14 @@ pub(crate) enum RegionName {
     Synthesized(InternedString),
 }
 
+impl RegionName {
+    fn as_interned_string(&self) -> &InternedString {
+        match self {
+            RegionName::Named(name) | RegionName::Synthesized(name) => name,
+        }
+    }
+}
+
 impl Display for RegionName {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
@@ -121,8 +130,9 @@ fn give_name_from_error_region(
         match error_region {
             ty::ReEarlyBound(ebr) => {
                 if ebr.has_name() {
-                    self.highlight_named_span(tcx, error_region, &ebr.name, diag);
-                    Some(RegionName::Named(ebr.name))
+                    let name = RegionName::Named(ebr.name);
+                    self.highlight_named_span(tcx, error_region, &name, diag);
+                    Some(name)
                 } else {
                     None
                 }
@@ -134,8 +144,9 @@ fn give_name_from_error_region(
 
             ty::ReFree(free_region) => match free_region.bound_region {
                 ty::BoundRegion::BrNamed(_, name) => {
+                    let name = RegionName::Named(name);
                     self.highlight_named_span(tcx, error_region, &name, diag);
-                    Some(RegionName::Named(name))
+                    Some(name)
                 },
 
                 ty::BoundRegion::BrEnv => {
@@ -198,6 +209,26 @@ fn give_name_from_error_region(
         }
     }
 
+    /// Get the span of a named region.
+    pub(super) fn get_span_of_named_region(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        error_region: &RegionKind,
+        name: &RegionName,
+    ) -> Span {
+        let scope = error_region.free_region_binding_scope(tcx);
+        let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
+
+        let span = tcx.sess.source_map().def_span(tcx.hir.span(node));
+        if let Some(param) = tcx.hir.get_generics(scope).and_then(|generics| {
+            generics.get_named(name.as_interned_string())
+        }) {
+            param.span
+        } else {
+            span
+        }
+    }
+
     /// Highlight a named span to provide context for error messages that
     /// mention that span, for example:
     ///
@@ -216,23 +247,15 @@ fn highlight_named_span(
         &self,
         tcx: TyCtxt<'_, '_, 'tcx>,
         error_region: &RegionKind,
-        name: &InternedString,
+        name: &RegionName,
         diag: &mut DiagnosticBuilder<'_>,
     ) {
-        let cm = tcx.sess.source_map();
-
-        let scope = error_region.free_region_binding_scope(tcx);
-        let node = tcx.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
-
-        let mut sp = cm.def_span(tcx.hir.span(node));
-        if let Some(param) = tcx.hir
-            .get_generics(scope)
-            .and_then(|generics| generics.get_named(name))
-        {
-            sp = param.span;
-        }
+        let span = self.get_span_of_named_region(tcx, error_region, name);
 
-        diag.span_label(sp, format!("lifetime `{}` defined here", name));
+        diag.span_label(
+            span,
+            format!("lifetime `{}` defined here", name),
+        );
     }
 
     /// Find an argument that contains `fr` and label it with a fully
index e95fc7663710e0fb8f680bdeb8b85b10c7cc616c..1e527140127a5445645eb377936777fe01a0a964 100644 (file)
@@ -21,10 +21,10 @@ error: unsatisfied lifetime constraints
    |
 LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
    |               -- lifetime `'a` defined here                         ^ returning this value requires that `'a` must outlive `'static`
-help: you can add a constraint to the return type to make it last less than `'static` and match `'a`
+help: you can add a constraint to the definition of `'a` to require it outlive `'static`
    |
-LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static + 'a { x }
-   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | fn with_bound<'a: 'static>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
+   |               ^^^^^^^^^^^
 
 error: unsatisfied lifetime constraints
   --> $DIR/must_outlive_least_region_or_bound.rs:29:5