Skip to content

Commit 79ee8f3

Browse files
committed
Suggest appropriate place for lifetime when declared after type arguments
1 parent e9bca7a commit 79ee8f3

File tree

8 files changed

+102
-8
lines changed

8 files changed

+102
-8
lines changed

src/libsyntax/parse/parser.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5178,8 +5178,10 @@ impl<'a> Parser<'a> {
51785178
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
51795179
/// trailing comma and erroneous trailing attributes.
51805180
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
5181+
let mut lifetimes = Vec::new();
51815182
let mut params = Vec::new();
5182-
let mut seen_ty_param = false;
5183+
let mut seen_ty_param: Option<Span> = None;
5184+
let mut last_comma_span = None;
51835185
loop {
51845186
let attrs = self.parse_outer_attributes()?;
51855187
if self.check_lifetime() {
@@ -5190,25 +5192,48 @@ impl<'a> Parser<'a> {
51905192
} else {
51915193
Vec::new()
51925194
};
5193-
params.push(ast::GenericParam {
5195+
lifetimes.push(ast::GenericParam {
51945196
ident: lifetime.ident,
51955197
id: lifetime.id,
51965198
attrs: attrs.into(),
51975199
bounds,
51985200
kind: ast::GenericParamKind::Lifetime,
51995201
});
5200-
if seen_ty_param {
5201-
self.span_err(self.prev_span,
5202-
"lifetime parameters must be declared prior to type parameters");
5202+
if let Some(sp) = seen_ty_param {
5203+
let param_span = self.prev_span;
5204+
let ate_comma = self.eat(&token::Comma);
5205+
let remove_sp = if ate_comma {
5206+
param_span.until(self.span)
5207+
} else {
5208+
last_comma_span.unwrap_or(param_span).to(param_span)
5209+
};
5210+
let mut err = self.struct_span_err(
5211+
self.prev_span,
5212+
"lifetime parameters must be declared prior to type parameters",
5213+
);
5214+
if let Ok(snippet) = self.sess.source_map().span_to_snippet(param_span) {
5215+
err.multipart_suggestion(
5216+
"move the lifetime parameter prior to the first type parameter",
5217+
vec![
5218+
(remove_sp, String::new()),
5219+
(sp.shrink_to_lo(), format!("{}, ", snippet)),
5220+
],
5221+
);
5222+
}
5223+
err.emit();
5224+
if ate_comma {
5225+
last_comma_span = Some(self.prev_span);
5226+
continue
5227+
}
52035228
}
52045229
} else if self.check_ident() {
52055230
// Parse type parameter.
52065231
params.push(self.parse_ty_param(attrs)?);
5207-
seen_ty_param = true;
5232+
seen_ty_param = Some(self.prev_span);
52085233
} else {
52095234
// Check for trailing attributes and stop parsing.
52105235
if !attrs.is_empty() {
5211-
let param_kind = if seen_ty_param { "type" } else { "lifetime" };
5236+
let param_kind = if seen_ty_param.is_some() { "type" } else { "lifetime" };
52125237
self.span_err(attrs[0].span,
52135238
&format!("trailing attribute after {} parameters", param_kind));
52145239
}
@@ -5218,8 +5243,10 @@ impl<'a> Parser<'a> {
52185243
if !self.eat(&token::Comma) {
52195244
break
52205245
}
5246+
last_comma_span = Some(self.prev_span);
52215247
}
5222-
Ok(params)
5248+
lifetimes.extend(params); // ensure the correct order of lifetimes and type params
5249+
Ok(lifetimes)
52235250
}
52245251

52255252
/// Parse a set of optional generic type parameter declarations. Where

src/test/ui/parser/issue-14303-enum.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
33
|
44
LL | enum X<'a, T, 'b> {
55
| ^^
6+
help: move the lifetime parameter prior to the first type parameter
7+
|
8+
LL | enum X<'a, 'b, T> {
9+
| ^^^ --
610

711
error: aborting due to previous error
812

src/test/ui/parser/issue-14303-fn-def.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
33
|
44
LL | fn foo<'a, T, 'b>(x: &'a T) {}
55
| ^^
6+
help: move the lifetime parameter prior to the first type parameter
7+
|
8+
LL | fn foo<'a, 'b, T>(x: &'a T) {}
9+
| ^^^ --
610

711
error: aborting due to previous error
812

src/test/ui/parser/issue-14303-impl.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
33
|
44
LL | impl<'a, T, 'b> X {}
55
| ^^
6+
help: move the lifetime parameter prior to the first type parameter
7+
|
8+
LL | impl<'a, 'b, T> X {}
9+
| ^^^ --
610

711
error: aborting due to previous error
812

src/test/ui/parser/issue-14303-struct.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
33
|
44
LL | struct X<'a, T, 'b> {
55
| ^^
6+
help: move the lifetime parameter prior to the first type parameter
7+
|
8+
LL | struct X<'a, 'b, T> {
9+
| ^^^ --
610

711
error: aborting due to previous error
812

src/test/ui/parser/issue-14303-trait.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ error: lifetime parameters must be declared prior to type parameters
33
|
44
LL | trait Foo<'a, T, 'b> {}
55
| ^^
6+
help: move the lifetime parameter prior to the first type parameter
7+
|
8+
LL | trait Foo<'a, 'b, T> {}
9+
| ^^^ --
610

711
error: aborting due to previous error
812

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
struct A<T, 'a> {
2+
t: &'a T,
3+
}
4+
5+
struct B<T, 'a, U> {
6+
t: &'a T,
7+
u: U,
8+
}
9+
10+
struct C<T, U, 'a> {
11+
t: &'a T,
12+
u: U,
13+
}
14+
15+
fn main() {}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
error: lifetime parameters must be declared prior to type parameters
2+
--> $DIR/suggest-move-lifetimes.rs:1:13
3+
|
4+
LL | struct A<T, 'a> {
5+
| ^^
6+
help: move the lifetime parameter prior to the first type parameter
7+
|
8+
LL | struct A<'a, T> {
9+
| ^^^ --
10+
11+
error: lifetime parameters must be declared prior to type parameters
12+
--> $DIR/suggest-move-lifetimes.rs:5:15
13+
|
14+
LL | struct B<T, 'a, U> {
15+
| ^
16+
help: move the lifetime parameter prior to the first type parameter
17+
|
18+
LL | struct B<'a, T, U> {
19+
| ^^^ --
20+
21+
error: lifetime parameters must be declared prior to type parameters
22+
--> $DIR/suggest-move-lifetimes.rs:10:16
23+
|
24+
LL | struct C<T, U, 'a> {
25+
| ^^
26+
help: move the lifetime parameter prior to the first type parameter
27+
|
28+
LL | struct C<T, 'a, U> {
29+
| ^^^ --
30+
31+
error: aborting due to 3 previous errors
32+

0 commit comments

Comments
 (0)