about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-09-27 22:00:11 +0000
committerbors <bors@rust-lang.org>2017-09-27 22:00:11 +0000
commit44d5090a6dbfbcd698ec53ef38981d9747112e0a (patch)
tree98ed0601b49109869a317e515d42629d3c5d1c9c /src
parent0e6f4cf51cd3b799fb057956f8e733d16605d09b (diff)
parentddee9fbc998e345e9a36f2066d51d389aa31a632 (diff)
downloadrust-44d5090a6dbfbcd698ec53ef38981d9747112e0a.tar.gz
rust-44d5090a6dbfbcd698ec53ef38981d9747112e0a.zip
Auto merge of #44782 - estebank:issue-36700, r=GuillaumeGomez
Point at parameter type on E0301

On "the parameter type `T` may not live long enough" error, point to the
parameter type suggesting lifetime bindings:

```
error[E0310]: the parameter type `T` may not live long enough
  --> $DIR/lifetime-doesnt-live-long-enough.rs:28:5
   |
27 | struct Foo<T> {
   |            - help: consider adding an explicit lifetime bound `T: 'static`...
28 |     foo: &'static T
   |     ^^^^^^^^^^^^^^^
   |
note: ...so that the reference type `&'static T` does not outlive the data it points at
  --> $DIR/lifetime-doesnt-live-long-enough.rs:28:5
   |
28 |     foo: &'static T
   |     ^^^^^^^^^^^^^^^
```

Fix #36700.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/infer/error_reporting/mod.rs70
-rw-r--r--src/librustc/middle/free_region.rs2
-rw-r--r--src/librustc/middle/region.rs2
-rw-r--r--src/librustc/ty/context.rs2
-rw-r--r--src/librustc_data_structures/bitvec.rs2
-rw-r--r--src/librustc_data_structures/transitive_relation.rs6
-rw-r--r--src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs (renamed from src/test/compile-fail/issue-16747.rs)8
-rw-r--r--src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr30
8 files changed, 102 insertions, 20 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index ead20b5eb5a..3f22950fc77 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -785,10 +785,44 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                     bound_kind: GenericKind<'tcx>,
                                     sub: Region<'tcx>)
     {
-        // FIXME: it would be better to report the first error message
-        // with the span of the parameter itself, rather than the span
-        // where the error was detected. But that span is not readily
-        // accessible.
+        // Attempt to obtain the span of the parameter so we can
+        // suggest adding an explicit lifetime bound to it.
+        let type_param_span = match (self.in_progress_tables, bound_kind) {
+            (Some(ref table), GenericKind::Param(ref param)) => {
+                let table = table.borrow();
+                table.local_id_root.and_then(|did| {
+                    let generics = self.tcx.generics_of(did);
+                    // Account for the case where `did` corresponds to `Self`, which doesn't have
+                    // the expected type argument.
+                    if generics.types.len() > 0 {
+                        let type_param = generics.type_param(param);
+                        let hir = &self.tcx.hir;
+                        hir.as_local_node_id(type_param.def_id).map(|id| {
+                            // Get the `hir::TyParam` to verify wether it already has any bounds.
+                            // We do this to avoid suggesting code that ends up as `T: 'a'b`,
+                            // instead we suggest `T: 'a + 'b` in that case.
+                            let has_lifetimes = if let hir_map::NodeTyParam(ref p) = hir.get(id) {
+                                p.bounds.len() > 0
+                            } else {
+                                false
+                            };
+                            let sp = hir.span(id);
+                            // `sp` only covers `T`, change it so that it covers
+                            // `T:` when appropriate
+                            let sp = if has_lifetimes {
+                                sp.to(sp.next_point().next_point())
+                            } else {
+                                sp
+                            };
+                            (sp, has_lifetimes)
+                        })
+                    } else {
+                        None
+                    }
+                })
+            }
+            _ => None,
+        };
 
         let labeled_user_string = match bound_kind {
             GenericKind::Param(ref p) =>
@@ -810,6 +844,26 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             return;
         }
 
+        fn binding_suggestion<'tcx, S: fmt::Display>(err: &mut DiagnosticBuilder<'tcx>,
+                                                     type_param_span: Option<(Span, bool)>,
+                                                     bound_kind: GenericKind<'tcx>,
+                                                     sub: S) {
+            let consider = &format!("consider adding an explicit lifetime bound `{}: {}`...",
+                                    bound_kind,
+                                    sub);
+            if let Some((sp, has_lifetimes)) = type_param_span {
+                let tail = if has_lifetimes {
+                    " + "
+                } else {
+                    ""
+                };
+                let suggestion = format!("{}: {}{}", bound_kind, sub, tail);
+                err.span_suggestion_short(sp, consider, suggestion);
+            } else {
+                err.help(consider);
+            }
+        }
+
         let mut err = match *sub {
             ty::ReEarlyBound(_) |
             ty::ReFree(ty::FreeRegion {bound_region: ty::BrNamed(..), ..}) => {
@@ -819,9 +873,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                                E0309,
                                                "{} may not live long enough",
                                                labeled_user_string);
-                err.help(&format!("consider adding an explicit lifetime bound `{}: {}`...",
-                         bound_kind,
-                         sub));
+                binding_suggestion(&mut err, type_param_span, bound_kind, sub);
                 err
             }
 
@@ -832,9 +884,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                                E0310,
                                                "{} may not live long enough",
                                                labeled_user_string);
-                err.help(&format!("consider adding an explicit lifetime \
-                                   bound `{}: 'static`...",
-                                  bound_kind));
+                binding_suggestion(&mut err, type_param_span, bound_kind, "'static");
                 err
             }
 
diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs
index 4de86b66916..49a241b86e0 100644
--- a/src/librustc/middle/free_region.rs
+++ b/src/librustc/middle/free_region.rs
@@ -117,7 +117,7 @@ impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
     }
 }
 
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct FreeRegionMap<'tcx> {
     // Stores the relation `a < b`, where `a` and `b` are regions.
     //
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index cede0c2b9a2..59c9e8b4c43 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -293,7 +293,7 @@ impl Scope {
 }
 
 /// The region scope tree encodes information about region relationships.
-#[derive(Default)]
+#[derive(Default, Debug)]
 pub struct ScopeTree {
     /// If not empty, this body is the root of this region hierarchy.
     root_body: Option<hir::HirId>,
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 054c5e122df..315ba622ccf 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -315,7 +315,7 @@ impl<'a, V> LocalTableInContextMut<'a, V> {
     }
 }
 
-#[derive(RustcEncodable, RustcDecodable)]
+#[derive(RustcEncodable, RustcDecodable, Debug)]
 pub struct TypeckTables<'tcx> {
     /// The HirId::owner all ItemLocalIds in this table are relative to.
     pub local_id_root: Option<DefId>,
diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs
index 7331016c2d2..e8f9a672087 100644
--- a/src/librustc_data_structures/bitvec.rs
+++ b/src/librustc_data_structures/bitvec.rs
@@ -138,7 +138,7 @@ impl FromIterator<bool> for BitVector {
 /// A "bit matrix" is basically a matrix of booleans represented as
 /// one gigantic bitvector. In other words, it is as if you have
 /// `rows` bitvectors, each of length `columns`.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct BitMatrix {
     columns: usize,
     vector: Vec<u64>,
diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs
index 46463944043..7cb386b0197 100644
--- a/src/librustc_data_structures/transitive_relation.rs
+++ b/src/librustc_data_structures/transitive_relation.rs
@@ -18,7 +18,7 @@ use std::hash::Hash;
 use std::mem;
 
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct TransitiveRelation<T: Clone + Debug + Eq + Hash + Clone> {
     // List of elements. This is used to map from a T to a usize.
     elements: Vec<T>,
@@ -42,10 +42,10 @@ pub struct TransitiveRelation<T: Clone + Debug + Eq + Hash + Clone> {
     closure: RefCell<Option<BitMatrix>>,
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, Debug)]
 struct Index(usize);
 
-#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Debug)]
 struct Edge {
     source: Index,
     target: Index,
diff --git a/src/test/compile-fail/issue-16747.rs b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs
index dd7e8a869ec..465b4271035 100644
--- a/src/test/compile-fail/issue-16747.rs
+++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.rs
@@ -16,14 +16,16 @@ trait Collection { fn len(&self) -> usize; }
 
 struct List<'a, T: ListItem<'a>> {
     slice: &'a [T]
-//~^ ERROR the parameter type `T` may not live long enough
-//~| HELP consider adding an explicit lifetime bound
-//~| NOTE ...so that the reference type `&'a [T]` does not outlive the data it points at
 }
+
 impl<'a, T: ListItem<'a>> Collection for List<'a, T> {
     fn len(&self) -> usize {
         0
     }
 }
 
+struct Foo<T> {
+    foo: &'static T
+}
+
 fn main() {}
diff --git a/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr
new file mode 100644
index 00000000000..e17a660c591
--- /dev/null
+++ b/src/test/ui/lifetimes/lifetime-doesnt-live-long-enough.stderr
@@ -0,0 +1,30 @@
+error[E0309]: the parameter type `T` may not live long enough
+  --> $DIR/lifetime-doesnt-live-long-enough.rs:18:5
+   |
+17 | struct List<'a, T: ListItem<'a>> {
+   |                 -- help: consider adding an explicit lifetime bound `T: 'a`...
+18 |     slice: &'a [T]
+   |     ^^^^^^^^^^^^^^
+   |
+note: ...so that the reference type `&'a [T]` does not outlive the data it points at
+  --> $DIR/lifetime-doesnt-live-long-enough.rs:18:5
+   |
+18 |     slice: &'a [T]
+   |     ^^^^^^^^^^^^^^
+
+error[E0310]: the parameter type `T` may not live long enough
+  --> $DIR/lifetime-doesnt-live-long-enough.rs:28:5
+   |
+27 | struct Foo<T> {
+   |            - help: consider adding an explicit lifetime bound `T: 'static`...
+28 |     foo: &'static T
+   |     ^^^^^^^^^^^^^^^
+   |
+note: ...so that the reference type `&'static T` does not outlive the data it points at
+  --> $DIR/lifetime-doesnt-live-long-enough.rs:28:5
+   |
+28 |     foo: &'static T
+   |     ^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+