Skip to content

Commit

Permalink
Suggest appropriate place for lifetime when declared after type argum…
Browse files Browse the repository at this point in the history
…ents
  • Loading branch information
estebank committed Nov 25, 2018
1 parent e9bca7a commit 79ee8f3
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 8 deletions.
43 changes: 35 additions & 8 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5178,8 +5178,10 @@ 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;
loop {
let attrs = self.parse_outer_attributes()?;
if self.check_lifetime() {
Expand All @@ -5190,25 +5192,48 @@ 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)
};
let mut err = self.struct_span_err(
self.prev_span,
"lifetime parameters must be declared prior to type parameters",
);
if let Ok(snippet) = self.sess.source_map().span_to_snippet(param_span) {
err.multipart_suggestion(
"move the lifetime parameter prior to the first type parameter",
vec![
(remove_sp, String::new()),
(sp.shrink_to_lo(), format!("{}, ", snippet)),
],
);
}
err.emit();
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;
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 +5243,10 @@ impl<'a> Parser<'a> {
if !self.eat(&token::Comma) {
break
}
last_comma_span = Some(self.prev_span);
}
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

15 changes: 15 additions & 0 deletions src/test/ui/suggestions/suggest-move-lifetimes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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,
}

fn main() {}
32 changes: 32 additions & 0 deletions src/test/ui/suggestions/suggest-move-lifetimes.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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:15
|
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<T, 'a, U> {
| ^^^ --

error: aborting due to 3 previous errors

0 comments on commit 79ee8f3

Please sign in to comment.