Skip to content

Commit

Permalink
Account for fn() types in lifetime suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed Feb 5, 2020
1 parent ba3b44c commit 92505df
Show file tree
Hide file tree
Showing 20 changed files with 210 additions and 153 deletions.
24 changes: 18 additions & 6 deletions src/librustc_resolve/diagnostics.rs
Expand Up @@ -19,7 +19,7 @@ use syntax::ast::{self, Ident, Path};
use syntax::util::lev_distance::find_best_match_for_name;

use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
use crate::lifetimes::{ElisionFailureInfo, HRLTSpanType, MissingLifetimeSpot};
use crate::lifetimes::{ElisionFailureInfo, ForLifetimeSpanType, MissingLifetimeSpot};
use crate::path_names_to_string;
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot};
Expand Down Expand Up @@ -1495,23 +1495,35 @@ crate fn add_missing_lifetime_specifiers_label(
let should_break;
introduce_suggestion.push(match missing {
MissingLifetimeSpot::Generics(generics) => {
msg = "consider introducing a named lifetime parameter";
msg = "consider introducing a named lifetime parameter".to_string();
should_break = true;
match &generics.params {
[] => (generics.span, "<'a>".to_string()),
[param, ..] => (param.span.shrink_to_lo(), "'a, ".to_string()),
}
}
MissingLifetimeSpot::HRLT { span, span_type } => {
msg = "consider introducing a higher-ranked lifetime";
msg = format!(
"consider making the {} lifetime-generic with a new `'a` lifetime",
match span_type {
ForLifetimeSpanType::BoundEmpty
| ForLifetimeSpanType::BoundTail => "bound",
ForLifetimeSpanType::TypeEmpty | ForLifetimeSpanType::TypeTail =>
"type",
}
);
should_break = false;
err.note(
"for more information on higher-ranked lifetimes, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
);
let suggestion = match span_type {
HRLTSpanType::Empty => "for<'a> ",
HRLTSpanType::Tail => ", 'a",
ForLifetimeSpanType::BoundEmpty | ForLifetimeSpanType::TypeEmpty => {
"for<'a> "
}
ForLifetimeSpanType::BoundTail | ForLifetimeSpanType::TypeTail => {
", 'a"
}
};
(*span, suggestion.to_string())
}
Expand All @@ -1528,7 +1540,7 @@ crate fn add_missing_lifetime_specifiers_label(
}
}
introduce_suggestion.push((span, sugg.to_string()));
err.multipart_suggestion(msg, introduce_suggestion, Applicability::MaybeIncorrect);
err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
if should_break {
break;
}
Expand Down
64 changes: 42 additions & 22 deletions src/librustc_resolve/lifetimes.rs
Expand Up @@ -155,12 +155,14 @@ struct NamedRegionMap {

crate enum MissingLifetimeSpot<'tcx> {
Generics(&'tcx hir::Generics<'tcx>),
HRLT { span: Span, span_type: HRLTSpanType },
HRLT { span: Span, span_type: ForLifetimeSpanType },
}

crate enum HRLTSpanType {
Empty,
Tail,
crate enum ForLifetimeSpanType {
BoundEmpty,
BoundTail,
TypeEmpty,
TypeTail,
}

impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
Expand Down Expand Up @@ -509,6 +511,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
let next_early_index = self.next_early_index();
let was_in_fn_syntax = self.is_in_fn_syntax;
self.is_in_fn_syntax = true;
let lifetime_span: Option<Span> = c
.generic_params
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => Some(param.span),
_ => None,
})
.last();
let (span, span_type) = if let Some(span) = lifetime_span {
(span.shrink_to_hi(), ForLifetimeSpanType::TypeTail)
} else {
(ty.span.shrink_to_lo(), ForLifetimeSpanType::TypeEmpty)
};
self.missing_named_lifetime_spots
.push(MissingLifetimeSpot::HRLT { span, span_type });
let scope = Scope::Binder {
lifetimes: c
.generic_params
Expand All @@ -531,6 +548,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
this.check_lifetime_params(old_scope, &c.generic_params);
intravisit::walk_ty(this, ty);
});
self.missing_named_lifetime_spots.pop();
self.is_in_fn_syntax = was_in_fn_syntax;
}
hir::TyKind::TraitObject(bounds, ref lifetime) => {
Expand Down Expand Up @@ -1873,12 +1891,23 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
err.span_suggestion(
*span,
&format!(
"consider introducing a higher-ranked lifetime `{}` here",
"consider making the {} lifetime-generic with a new `{}` lifetime",
match span_type {
ForLifetimeSpanType::BoundEmpty
| ForLifetimeSpanType::BoundTail => "bound",
ForLifetimeSpanType::TypeEmpty
| ForLifetimeSpanType::TypeTail => "type",
},
lifetime_ref
),
match span_type {
HRLTSpanType::Empty => format!("for<{}> ", lifetime_ref),
HRLTSpanType::Tail => format!(", {}", lifetime_ref),
ForLifetimeSpanType::TypeEmpty
| ForLifetimeSpanType::BoundEmpty => {
format!("for<{}> ", lifetime_ref)
}
ForLifetimeSpanType::TypeTail | ForLifetimeSpanType::BoundTail => {
format!(", {}", lifetime_ref)
}
}
.to_string(),
Applicability::MaybeIncorrect,
Expand Down Expand Up @@ -2487,13 +2516,12 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
params.iter().cloned().filter(|info| info.lifetime_count > 0).collect();

let elided_len = elided_params.len();
let mut spans = vec![];

for (i, info) in elided_params.into_iter().enumerate() {
let ElisionFailureInfo { parent, index, lifetime_count: n, have_bound_regions, span } =
info;

spans.push(span);
db.span_label(span, "");
let help_name = if let Some(ident) =
parent.and_then(|body| self.tcx.hir().body(body).params[index].pat.simple_ident())
{
Expand Down Expand Up @@ -2524,37 +2552,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
}
}

let help = |msg| {
if spans.is_empty() {
db.help(msg);
} else {
db.span_help(spans, msg);
}
};

if len == 0 {
db.help(
"this function's return type contains a borrowed value, \
but there is no value for it to be borrowed from",
);
self.suggest_lifetime(db, span, "consider giving it a 'static lifetime")
} else if elided_len == 0 {
help(
db.help(
"this function's return type contains a borrowed value with \
an elided lifetime, but the lifetime cannot be derived from \
the arguments",
);
let msg = "consider giving it an explicit bounded or 'static lifetime";
self.suggest_lifetime(db, span, msg)
} else if elided_len == 1 {
help(&format!(
db.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say which {} it is borrowed from",
m
));
true
} else {
help(&format!(
db.help(&format!(
"this function's return type contains a borrowed value, \
but the signature does not say whether it is borrowed from {}",
m
Expand Down Expand Up @@ -2816,8 +2836,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
.contains(&Some(did))
{
let (span, span_type) = match &trait_ref.bound_generic_params {
[] => (trait_ref.span.shrink_to_lo(), HRLTSpanType::Empty),
[.., bound] => (bound.span.shrink_to_hi(), HRLTSpanType::Tail),
[] => (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty),
[.., bound] => (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail),
};
self.missing_named_lifetime_spots
.push(MissingLifetimeSpot::HRLT { span, span_type });
Expand Down
8 changes: 3 additions & 5 deletions src/test/ui/async-await/issues/issue-63388-2.stderr
@@ -1,14 +1,12 @@
error[E0106]: missing lifetime specifier
--> $DIR/issue-63388-2.rs:12:10
|
LL | foo: &dyn Foo, bar: &'a dyn Foo
| -------- -----------
LL | ) -> &dyn Foo
| ^ help: consider using the named lifetime: `&'a`
|
help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `foo` or `bar`
--> $DIR/issue-63388-2.rs:11:14
|
LL | foo: &dyn Foo, bar: &'a dyn Foo
| ^^^^^^^^ ^^^^^^^^^^^
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `foo` or `bar`

error: cannot infer an appropriate lifetime
--> $DIR/issue-63388-2.rs:11:9
Expand Down
12 changes: 12 additions & 0 deletions src/test/ui/generic/generic-extern-lifetime.stderr
Expand Up @@ -9,12 +9,24 @@ error[E0261]: use of undeclared lifetime name `'a`
|
LL | pub fn life4<'b>(x: for<'c> fn(&'a i32));
| ^^ undeclared lifetime
|
= note: for more information on higher-ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the type lifetime-generic with a new `'a` lifetime
|
LL | pub fn life4<'b>(x: for<'c, 'a> fn(&'a i32));
| ^^^^

error[E0261]: use of undeclared lifetime name `'a`
--> $DIR/generic-extern-lifetime.rs:11:38
|
LL | pub fn life7<'b>() -> for<'c> fn(&'a i32);
| ^^ undeclared lifetime
|
= note: for more information on higher-ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the type lifetime-generic with a new `'a` lifetime
|
LL | pub fn life7<'b>() -> for<'c, 'a> fn(&'a i32);
| ^^^^

error: aborting due to 3 previous errors

Expand Down
Expand Up @@ -9,10 +9,18 @@ LL | let y: &'test u32 = x;
error[E0261]: use of undeclared lifetime name `'test`
--> $DIR/no_introducing_in_band_in_locals.rs:10:16
|
LL | fn bar() {
| - help: consider introducing lifetime `'test` here: `<'test>`
LL | let y: fn(&'test u32) = foo2;
| ^^^^^ undeclared lifetime
|
= note: for more information on higher-ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider introducing lifetime `'test` here
|
LL | fn bar<'test>() {
| ^^^^^^^
help: consider making the type lifetime-generic with a new `'test` lifetime
|
LL | let y: for<'test> fn(&'test u32) = foo2;
| ^^^^^^^^^^

error: aborting due to 2 previous errors

Expand Down
21 changes: 9 additions & 12 deletions src/test/ui/issues/issue-19707.stderr
Expand Up @@ -2,13 +2,14 @@ error[E0106]: missing lifetime specifier
--> $DIR/issue-19707.rs:3:28
|
LL | type Foo = fn(&u8, &u8) -> &u8;
| ^ expected named lifetime parameter
| --- --- ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
--> $DIR/issue-19707.rs:3:15
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
= note: for more information on higher-ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the type lifetime-generic with a new `'a` lifetime
|
LL | type Foo = fn(&u8, &u8) -> &u8;
| ^^^ ^^^
LL | type Foo = for<'a> fn(&'a u8, &'a u8) -> &'a u8;
| ^^^^^^^ ^^^^^^ ^^^^^^ ^^^
help: consider introducing a named lifetime parameter
|
LL | type Foo<'a> = fn(&'a u8, &'a u8) -> &'a u8;
Expand All @@ -18,15 +19,11 @@ error[E0106]: missing lifetime specifier
--> $DIR/issue-19707.rs:5:27
|
LL | fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {}
| ^ expected named lifetime parameter
| --- --- ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
--> $DIR/issue-19707.rs:5:14
|
LL | fn bar<F: Fn(&u8, &u8) -> &u8>(f: &F) {}
| ^^^ ^^^
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from argument 1 or argument 2
= note: for more information on higher-ranked lifetimes, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider introducing a higher-ranked lifetime
help: consider making the bound lifetime-generic with a new `'a` lifetime
|
LL | fn bar<F: for<'a> Fn(&'a u8, &'a u8) -> &'a u8>(f: &F) {}
| ^^^^^^^ ^^^^^^ ^^^^^^ ^^^
Expand Down
8 changes: 2 additions & 6 deletions src/test/ui/issues/issue-26638.stderr
Expand Up @@ -2,13 +2,9 @@ error[E0106]: missing lifetime specifier
--> $DIR/issue-26638.rs:1:62
|
LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next() }
| ^ expected named lifetime parameter
| ------------------------------------ ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say which one of `iter`'s 2 lifetimes it is borrowed from
--> $DIR/issue-26638.rs:1:21
|
LL | fn parse_type(iter: Box<dyn Iterator<Item=&str>+'static>) -> &str { iter.next() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: this function's return type contains a borrowed value, but the signature does not say which one of `iter`'s 2 lifetimes it is borrowed from
help: consider introducing a named lifetime parameter
|
LL | fn parse_type<'a>(iter: Box<dyn Iterator<Item=&str>+'static>) -> &'a str { iter.next() }
Expand Down
24 changes: 6 additions & 18 deletions src/test/ui/issues/issue-30255.stderr
Expand Up @@ -2,13 +2,9 @@ error[E0106]: missing lifetime specifier
--> $DIR/issue-30255.rs:9:24
|
LL | fn f(a: &S, b: i32) -> &i32 {
| ^ expected named lifetime parameter
| -- ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say which one of `a`'s 2 lifetimes it is borrowed from
--> $DIR/issue-30255.rs:9:9
|
LL | fn f(a: &S, b: i32) -> &i32 {
| ^^
= help: this function's return type contains a borrowed value, but the signature does not say which one of `a`'s 2 lifetimes it is borrowed from
help: consider introducing a named lifetime parameter
|
LL | fn f<'a>(a: &'a S, b: i32) -> &'a i32 {
Expand All @@ -18,13 +14,9 @@ error[E0106]: missing lifetime specifier
--> $DIR/issue-30255.rs:14:34
|
LL | fn g(a: &S, b: bool, c: &i32) -> &i32 {
| ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `a`'s 2 lifetimes or `c`
--> $DIR/issue-30255.rs:14:9
| -- ---- ^ expected named lifetime parameter
|
LL | fn g(a: &S, b: bool, c: &i32) -> &i32 {
| ^^ ^^^^
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `a`'s 2 lifetimes or `c`
help: consider introducing a named lifetime parameter
|
LL | fn g<'a>(a: &'a S, b: bool, c: &'a i32) -> &'a i32 {
Expand All @@ -34,13 +26,9 @@ error[E0106]: missing lifetime specifier
--> $DIR/issue-30255.rs:19:44
|
LL | fn h(a: &bool, b: bool, c: &S, d: &i32) -> &i32 {
| ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a`, one of `c`'s 2 lifetimes, or `d`
--> $DIR/issue-30255.rs:19:9
| ----- -- ---- ^ expected named lifetime parameter
|
LL | fn h(a: &bool, b: bool, c: &S, d: &i32) -> &i32 {
| ^^^^^ ^^ ^^^^
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a`, one of `c`'s 2 lifetimes, or `d`
help: consider introducing a named lifetime parameter
|
LL | fn h<'a>(a: &'a bool, b: bool, c: &'a S, d: &'a i32) -> &'a i32 {
Expand Down
Expand Up @@ -10,13 +10,9 @@ error[E0106]: missing lifetime specifier
--> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:7:33
|
LL | fn g(_x: &isize, _y: &isize) -> &isize {
| ^ expected named lifetime parameter
| ------ ------ ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_x` or `_y`
--> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:7:10
|
LL | fn g(_x: &isize, _y: &isize) -> &isize {
| ^^^^^^ ^^^^^^
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_x` or `_y`
help: consider introducing a named lifetime parameter
|
LL | fn g<'a>(_x: &'a isize, _y: &'a isize) -> &'a isize {
Expand All @@ -26,13 +22,9 @@ error[E0106]: missing lifetime specifier
--> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:17:19
|
LL | fn h(_x: &Foo) -> &isize {
| ^ expected named lifetime parameter
| ---- ^ expected named lifetime parameter
|
help: this function's return type contains a borrowed value, but the signature does not say which one of `_x`'s 2 lifetimes it is borrowed from
--> $DIR/lifetime-elision-return-type-requires-explicit-lifetime.rs:17:10
|
LL | fn h(_x: &Foo) -> &isize {
| ^^^^
= help: this function's return type contains a borrowed value, but the signature does not say which one of `_x`'s 2 lifetimes it is borrowed from
help: consider introducing a named lifetime parameter
|
LL | fn h<'a>(_x: &'a Foo) -> &'a isize {
Expand Down

0 comments on commit 92505df

Please sign in to comment.