Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest appropriate place for lifetime when declared after type arguments #56220

Merged
merged 4 commits into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 45 additions & 8 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5178,8 +5178,12 @@ impl<'a> Parser<'a> {
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
/// trailing comma and erroneous trailing attributes.
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
let mut lifetimes = Vec::new();
let mut params = Vec::new();
let mut seen_ty_param = false;
let mut seen_ty_param: Option<Span> = None;
let mut last_comma_span = None;
let mut bad_lifetime_pos = vec![];
let mut suggestions = vec![];
loop {
let attrs = self.parse_outer_attributes()?;
if self.check_lifetime() {
Expand All @@ -5190,25 +5194,42 @@ impl<'a> Parser<'a> {
} else {
Vec::new()
};
params.push(ast::GenericParam {
lifetimes.push(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
attrs: attrs.into(),
bounds,
kind: ast::GenericParamKind::Lifetime,
});
if seen_ty_param {
self.span_err(self.prev_span,
"lifetime parameters must be declared prior to type parameters");
if let Some(sp) = seen_ty_param {
let param_span = self.prev_span;
let ate_comma = self.eat(&token::Comma);
let remove_sp = if ate_comma {
param_span.until(self.span)
} else {
last_comma_span.unwrap_or(param_span).to(param_span)
};
bad_lifetime_pos.push(param_span);

if let Ok(snippet) = self.sess.source_map().span_to_snippet(param_span) {
suggestions.push((remove_sp, String::new()));
suggestions.push((sp.shrink_to_lo(), format!("{}, ", snippet)));
}
if ate_comma {
last_comma_span = Some(self.prev_span);
continue
}
}
} else if self.check_ident() {
// Parse type parameter.
params.push(self.parse_ty_param(attrs)?);
seen_ty_param = true;
if seen_ty_param.is_none() {
seen_ty_param = Some(self.prev_span);
}
} else {
// Check for trailing attributes and stop parsing.
if !attrs.is_empty() {
let param_kind = if seen_ty_param { "type" } else { "lifetime" };
let param_kind = if seen_ty_param.is_some() { "type" } else { "lifetime" };
self.span_err(attrs[0].span,
&format!("trailing attribute after {} parameters", param_kind));
}
Expand All @@ -5218,8 +5239,24 @@ impl<'a> Parser<'a> {
if !self.eat(&token::Comma) {
break
}
last_comma_span = Some(self.prev_span);
}
if !bad_lifetime_pos.is_empty() {
let mut err = self.struct_span_err(
bad_lifetime_pos,
"lifetime parameters must be declared prior to type parameters",
);
if !suggestions.is_empty() {
err.multipart_suggestion_with_applicability(
"move the lifetime parameter prior to the first type parameter",
suggestions,
Applicability::MachineApplicable,
);
}
err.emit();
}
Ok(params)
lifetimes.extend(params); // ensure the correct order of lifetimes and type params
Ok(lifetimes)
}

/// Parse a set of optional generic type parameter declarations. Where
Expand Down
4 changes: 4 additions & 0 deletions src/test/ui/parser/issue-14303-enum.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
|
LL | enum X<'a, T, 'b> {
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | enum X<'a, 'b, T> {
| ^^^ --

error: aborting due to previous error

4 changes: 4 additions & 0 deletions src/test/ui/parser/issue-14303-fn-def.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
|
LL | fn foo<'a, T, 'b>(x: &'a T) {}
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | fn foo<'a, 'b, T>(x: &'a T) {}
| ^^^ --

error: aborting due to previous error

4 changes: 4 additions & 0 deletions src/test/ui/parser/issue-14303-impl.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
|
LL | impl<'a, T, 'b> X {}
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | impl<'a, 'b, T> X {}
| ^^^ --

error: aborting due to previous error

4 changes: 4 additions & 0 deletions src/test/ui/parser/issue-14303-struct.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
|
LL | struct X<'a, T, 'b> {
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | struct X<'a, 'b, T> {
| ^^^ --

error: aborting due to previous error

4 changes: 4 additions & 0 deletions src/test/ui/parser/issue-14303-trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
|
LL | trait Foo<'a, T, 'b> {}
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | trait Foo<'a, 'b, T> {}
| ^^^ --

error: aborting due to previous error

21 changes: 21 additions & 0 deletions src/test/ui/suggestions/suggest-move-lifetimes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
struct A<T, 'a> {
t: &'a T,
}

struct B<T, 'a, U> {
t: &'a T,
u: U,
}

struct C<T, U, 'a> {
t: &'a T,
u: U,
}

struct D<T, U, 'a, 'b, V, 'c> {
t: &'a T,
u: &'b U,
v: &'c V,
}

fn main() {}
42 changes: 42 additions & 0 deletions src/test/ui/suggestions/suggest-move-lifetimes.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
error: lifetime parameters must be declared prior to type parameters
--> $DIR/suggest-move-lifetimes.rs:1:13
|
LL | struct A<T, 'a> {
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | struct A<'a, T> {
| ^^^ --

error: lifetime parameters must be declared prior to type parameters
--> $DIR/suggest-move-lifetimes.rs:5:13
|
LL | struct B<T, 'a, U> {
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | struct B<'a, T, U> {
| ^^^ --

error: lifetime parameters must be declared prior to type parameters
--> $DIR/suggest-move-lifetimes.rs:10:16
|
LL | struct C<T, U, 'a> {
| ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | struct C<'a, T, U> {
| ^^^ --

error: lifetime parameters must be declared prior to type parameters
--> $DIR/suggest-move-lifetimes.rs:15:16
|
LL | struct D<T, U, 'a, 'b, V, 'c> {
| ^^ ^^ ^^
help: move the lifetime parameter prior to the first type parameter
|
LL | struct D<'a, 'b, 'c, T, U, V> {
| ^^^ ^^^ ^^^ ---

error: aborting due to 4 previous errors