Skip to content

Commit

Permalink
Rollup merge of rust-lang#59146 - estebank:suggest-return-lt, r=varkor
Browse files Browse the repository at this point in the history
Suggest return lifetime when there's only one named lifetime

Fix rust-lang#29094.
  • Loading branch information
kennytm committed Mar 15, 2019
2 parents 7f70db6 + 0ea9b58 commit d93e460
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 5 deletions.
46 changes: 41 additions & 5 deletions src/librustc/middle/resolve_lifetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2299,19 +2299,26 @@ 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.
Scope::Body { .. } => return,

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;
}

Scope::Elision { ref elide, .. } => {
Scope::Elision { ref elide, ref s, .. } => {
let lifetime = match *elide {
Elide::FreshLateAnon(ref counter) => {
for lifetime_ref in lifetime_refs {
Expand All @@ -2321,7 +2328,17 @@ 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 {
// collect named lifetimes for suggestions
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);
Expand All @@ -2344,7 +2361,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();
Expand Down Expand Up @@ -2885,10 +2908,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,
"consider using the named lifetime",
format!("&{} ", name),
Applicability::MaybeIncorrect,
);
} else {
err.span_label(span, "expected lifetime parameter");
};
}
}
10 changes: 10 additions & 0 deletions src/test/ui/suggestions/return-without-lifetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
struct Thing<'a>(&'a ());
struct Foo<'a>(&usize);
//~^ ERROR missing lifetime specifier

fn func1<'a>(_arg: &'a Thing) -> &() { unimplemented!() }
//~^ ERROR missing lifetime specifier
fn func2<'a>(_arg: &Thing<'a>) -> &() { unimplemented!() }
//~^ ERROR missing lifetime specifier

fn main() {}
25 changes: 25 additions & 0 deletions src/test/ui/suggestions/return-without-lifetime.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0106]: missing lifetime specifier
--> $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`
|
= 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: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 3 previous errors

For more information about this error, try `rustc --explain E0106`.

0 comments on commit d93e460

Please sign in to comment.