From 795d307f10ec0fd3851b18a3365755d76b7403e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Tue, 12 Mar 2019 14:57:13 -0700
Subject: [PATCH 1/3] Suggest return lifetime when there's only one named
 lifetime

---
 src/librustc/middle/resolve_lifetime.rs       | 37 +++++++++++++++++--
 .../ui/suggestions/return-without-lifetime.rs |  8 ++++
 .../return-without-lifetime.stderr            | 19 ++++++++++
 3 files changed, 60 insertions(+), 4 deletions(-)
 create mode 100644 src/test/ui/suggestions/return-without-lifetime.rs
 create mode 100644 src/test/ui/suggestions/return-without-lifetime.stderr

diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index f862b690f8806..35bca04d1e623 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -2298,6 +2298,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         let span = lifetime_refs[0].span;
         let mut late_depth = 0;
         let mut scope = self.scope;
+        let mut lifetime_names = FxHashSet::default();
         let error = loop {
             match *scope {
                 // Do not assign any resolution, it will be inferred.
@@ -2310,7 +2311,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                     scope = s;
                 }
 
-                Scope::Elision { ref elide, .. } => {
+                Scope::Elision { ref elide, ref s, .. } => {
                     let lifetime = match *elide {
                         Elide::FreshLateAnon(ref counter) => {
                             for lifetime_ref in lifetime_refs {
@@ -2320,7 +2321,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                             return;
                         }
                         Elide::Exact(l) => l.shifted(late_depth),
-                        Elide::Error(ref e) => break Some(e),
+                        Elide::Error(ref e) => {
+                            if let Scope::Binder { ref lifetimes, .. } = s {
+                                for name in lifetimes.keys() {
+                                    if let hir::ParamName::Plain(name) = name {
+                                        lifetime_names.insert(*name);
+                                    }
+                                }
+                            }
+                            break Some(e);
+                        }
                     };
                     for lifetime_ref in lifetime_refs {
                         self.insert_lifetime(lifetime_ref, lifetime);
@@ -2343,7 +2353,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             }
         }
         if add_label {
-            add_missing_lifetime_specifiers_label(&mut err, span, lifetime_refs.len());
+            add_missing_lifetime_specifiers_label(
+                &mut err,
+                span,
+                lifetime_refs.len(),
+                &lifetime_names,
+                self.tcx.sess.source_map().span_to_snippet(span).ok().as_ref().map(|s| s.as_str()),
+            );
         }
 
         err.emit();
@@ -2884,10 +2900,23 @@ fn add_missing_lifetime_specifiers_label(
     err: &mut DiagnosticBuilder<'_>,
     span: Span,
     count: usize,
+    lifetime_names: &FxHashSet<ast::Ident>,
+    snippet: Option<&str>,
 ) {
     if count > 1 {
         err.span_label(span, format!("expected {} lifetime parameters", count));
+    } else if let (1, Some(name), Some("&")) = (
+        lifetime_names.len(),
+        lifetime_names.iter().next(),
+        snippet,
+    ) {
+        err.span_suggestion(
+            span,
+            &format!("consider using the named lifetime `{}`", name),
+            format!("&{} ", name),
+            Applicability::MaybeIncorrect,
+        );
     } else {
         err.span_label(span, "expected lifetime parameter");
-    };
+    }
 }
diff --git a/src/test/ui/suggestions/return-without-lifetime.rs b/src/test/ui/suggestions/return-without-lifetime.rs
new file mode 100644
index 0000000000000..5f19e93013acf
--- /dev/null
+++ b/src/test/ui/suggestions/return-without-lifetime.rs
@@ -0,0 +1,8 @@
+struct Thing<'a>(&'a ());
+
+fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
+//~^ ERROR missing lifetime specifier
+fn func2<'a>(_arg: &Thing<'a>) -> &() { unimplemented!() }
+//~^ ERROR missing lifetime specifier
+
+fn main() {}
diff --git a/src/test/ui/suggestions/return-without-lifetime.stderr b/src/test/ui/suggestions/return-without-lifetime.stderr
new file mode 100644
index 0000000000000..72f1c142d028f
--- /dev/null
+++ b/src/test/ui/suggestions/return-without-lifetime.stderr
@@ -0,0 +1,19 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-without-lifetime.rs:3:34
+   |
+LL | fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
+   |                                  ^ help: consider using the named lifetime `'a`: `&'a`
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-without-lifetime.rs:5:35
+   |
+LL | fn func2<'a>(_arg: &Thing<'a>) -> &() { unimplemented!() }
+   |                                   ^ help: consider using the named lifetime `'a`: `&'a`
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0106`.

From f9234767e4ff9b682437464efc9a6ff59db4cff9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Tue, 12 Mar 2019 15:34:16 -0700
Subject: [PATCH 2/3] review comments

---
 src/librustc/middle/resolve_lifetime.rs                | 2 +-
 src/test/ui/suggestions/return-without-lifetime.stderr | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index 35bca04d1e623..d03268df5e148 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -2912,7 +2912,7 @@ fn add_missing_lifetime_specifiers_label(
     ) {
         err.span_suggestion(
             span,
-            &format!("consider using the named lifetime `{}`", name),
+            "consider using the named lifetime",
             format!("&{} ", name),
             Applicability::MaybeIncorrect,
         );
diff --git a/src/test/ui/suggestions/return-without-lifetime.stderr b/src/test/ui/suggestions/return-without-lifetime.stderr
index 72f1c142d028f..1ffe91bce05a5 100644
--- a/src/test/ui/suggestions/return-without-lifetime.stderr
+++ b/src/test/ui/suggestions/return-without-lifetime.stderr
@@ -2,7 +2,7 @@ error[E0106]: missing lifetime specifier
   --> $DIR/return-without-lifetime.rs:3:34
    |
 LL | fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
-   |                                  ^ help: consider using the named lifetime `'a`: `&'a`
+   |                                  ^ help: consider using the named lifetime: `&'a`
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
 
@@ -10,7 +10,7 @@ error[E0106]: missing lifetime specifier
   --> $DIR/return-without-lifetime.rs:5:35
    |
 LL | fn func2<'a>(_arg: &Thing<'a>) -> &() { unimplemented!() }
-   |                                   ^ help: consider using the named lifetime `'a`: `&'a`
+   |                                   ^ help: consider using the named lifetime: `&'a`
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
 

From 0ea9b58029bc7c3da3f213eb9e39acdefcf12647 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Tue, 12 Mar 2019 18:18:24 -0700
Subject: [PATCH 3/3] Suggest adding lifetime to struct field

---
 src/librustc/middle/resolve_lifetime.rs              |  9 ++++++++-
 src/test/ui/suggestions/return-without-lifetime.rs   |  2 ++
 .../ui/suggestions/return-without-lifetime.stderr    | 12 +++++++++---
 3 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs
index d03268df5e148..c9ff84ab2f08a 100644
--- a/src/librustc/middle/resolve_lifetime.rs
+++ b/src/librustc/middle/resolve_lifetime.rs
@@ -2306,7 +2306,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
 
                 Scope::Root => break None,
 
-                Scope::Binder { s, .. } => {
+                Scope::Binder { s, ref lifetimes, .. } => {
+                    // collect named lifetimes for suggestions
+                    for name in lifetimes.keys() {
+                        if let hir::ParamName::Plain(name) = name {
+                            lifetime_names.insert(*name);
+                        }
+                    }
                     late_depth += 1;
                     scope = s;
                 }
@@ -2323,6 +2329,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                         Elide::Exact(l) => l.shifted(late_depth),
                         Elide::Error(ref e) => {
                             if let Scope::Binder { ref lifetimes, .. } = s {
+                                // collect named lifetimes for suggestions
                                 for name in lifetimes.keys() {
                                     if let hir::ParamName::Plain(name) = name {
                                         lifetime_names.insert(*name);
diff --git a/src/test/ui/suggestions/return-without-lifetime.rs b/src/test/ui/suggestions/return-without-lifetime.rs
index 5f19e93013acf..9bfce11be9ea3 100644
--- a/src/test/ui/suggestions/return-without-lifetime.rs
+++ b/src/test/ui/suggestions/return-without-lifetime.rs
@@ -1,4 +1,6 @@
 struct Thing<'a>(&'a ());
+struct Foo<'a>(&usize);
+//~^ ERROR missing lifetime specifier
 
 fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
 //~^ ERROR missing lifetime specifier
diff --git a/src/test/ui/suggestions/return-without-lifetime.stderr b/src/test/ui/suggestions/return-without-lifetime.stderr
index 1ffe91bce05a5..7f5ff95938e30 100644
--- a/src/test/ui/suggestions/return-without-lifetime.stderr
+++ b/src/test/ui/suggestions/return-without-lifetime.stderr
@@ -1,5 +1,11 @@
 error[E0106]: missing lifetime specifier
-  --> $DIR/return-without-lifetime.rs:3:34
+  --> $DIR/return-without-lifetime.rs:2:16
+   |
+LL | struct Foo<'a>(&usize);
+   |                ^ help: consider using the named lifetime: `&'a`
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/return-without-lifetime.rs:5:34
    |
 LL | fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
    |                                  ^ help: consider using the named lifetime: `&'a`
@@ -7,13 +13,13 @@ LL | fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
 
 error[E0106]: missing lifetime specifier
-  --> $DIR/return-without-lifetime.rs:5:35
+  --> $DIR/return-without-lifetime.rs:7:35
    |
 LL | fn func2<'a>(_arg: &Thing<'a>) -> &() { unimplemented!() }
    |                                   ^ help: consider using the named lifetime: `&'a`
    |
    = help: this function's return type contains a borrowed value, but the signature does not say which one of `_arg`'s 2 lifetimes it is borrowed from
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0106`.