Skip to content

Commit 796892e

Browse files
Rollup merge of rust-lang#56220 - estebank:suggest-lifetime-move, r=nikomatsakis
Suggest appropriate place for lifetime when declared after type arguments
2 parents 40ec109 + 6f028fe commit 796892e

File tree

8 files changed

+128
-8
lines changed

8 files changed

+128
-8
lines changed

src/libsyntax/parse/parser.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5172,8 +5172,12 @@ impl<'a> Parser<'a> {
51725172
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
51735173
/// trailing comma and erroneous trailing attributes.
51745174
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
5175+
let mut lifetimes = Vec::new();
51755176
let mut params = Vec::new();
5176-
let mut seen_ty_param = false;
5177+
let mut seen_ty_param: Option<Span> = None;
5178+
let mut last_comma_span = None;
5179+
let mut bad_lifetime_pos = vec![];
5180+
let mut suggestions = vec![];
51775181
loop {
51785182
let attrs = self.parse_outer_attributes()?;
51795183
if self.check_lifetime() {
@@ -5184,25 +5188,42 @@ impl<'a> Parser<'a> {
51845188
} else {
51855189
Vec::new()
51865190
};
5187-
params.push(ast::GenericParam {
5191+
lifetimes.push(ast::GenericParam {
51885192
ident: lifetime.ident,
51895193
id: lifetime.id,
51905194
attrs: attrs.into(),
51915195
bounds,
51925196
kind: ast::GenericParamKind::Lifetime,
51935197
});
5194-
if seen_ty_param {
5195-
self.span_err(self.prev_span,
5196-
"lifetime parameters must be declared prior to type parameters");
5198+
if let Some(sp) = seen_ty_param {
5199+
let param_span = self.prev_span;
5200+
let ate_comma = self.eat(&token::Comma);
5201+
let remove_sp = if ate_comma {
5202+
param_span.until(self.span)
5203+
} else {
5204+
last_comma_span.unwrap_or(param_span).to(param_span)
5205+
};
5206+
bad_lifetime_pos.push(param_span);
5207+
5208+
if let Ok(snippet) = self.sess.source_map().span_to_snippet(param_span) {
5209+
suggestions.push((remove_sp, String::new()));
5210+
suggestions.push((sp.shrink_to_lo(), format!("{}, ", snippet)));
5211+
}
5212+
if ate_comma {
5213+
last_comma_span = Some(self.prev_span);
5214+
continue
5215+
}
51975216
}
51985217
} else if self.check_ident() {
51995218
// Parse type parameter.
52005219
params.push(self.parse_ty_param(attrs)?);
5201-
seen_ty_param = true;
5220+
if seen_ty_param.is_none() {
5221+
seen_ty_param = Some(self.prev_span);
5222+
}
52025223
} else {
52035224
// Check for trailing attributes and stop parsing.
52045225
if !attrs.is_empty() {
5205-
let param_kind = if seen_ty_param { "type" } else { "lifetime" };
5226+
let param_kind = if seen_ty_param.is_some() { "type" } else { "lifetime" };
52065227
self.span_err(attrs[0].span,
52075228
&format!("trailing attribute after {} parameters", param_kind));
52085229
}
@@ -5212,8 +5233,24 @@ impl<'a> Parser<'a> {
52125233
if !self.eat(&token::Comma) {
52135234
break
52145235
}
5236+
last_comma_span = Some(self.prev_span);
5237+
}
5238+
if !bad_lifetime_pos.is_empty() {
5239+
let mut err = self.struct_span_err(
5240+
bad_lifetime_pos,
5241+
"lifetime parameters must be declared prior to type parameters",
5242+
);
5243+
if !suggestions.is_empty() {
5244+
err.multipart_suggestion_with_applicability(
5245+
"move the lifetime parameter prior to the first type parameter",
5246+
suggestions,
5247+
Applicability::MachineApplicable,
5248+
);
5249+
}
5250+
err.emit();
52155251
}
5216-
Ok(params)
5252+
lifetimes.extend(params); // ensure the correct order of lifetimes and type params
5253+
Ok(lifetimes)
52175254
}
52185255

52195256
/// 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: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
struct D<T, U, 'a, 'b, V, 'c> {
16+
t: &'a T,
17+
u: &'b U,
18+
v: &'c V,
19+
}
20+
21+
fn main() {}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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:13
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<'a, T, U> {
29+
| ^^^ --
30+
31+
error: lifetime parameters must be declared prior to type parameters
32+
--> $DIR/suggest-move-lifetimes.rs:15:16
33+
|
34+
LL | struct D<T, U, 'a, 'b, V, 'c> {
35+
| ^^ ^^ ^^
36+
help: move the lifetime parameter prior to the first type parameter
37+
|
38+
LL | struct D<'a, 'b, 'c, T, U, V> {
39+
| ^^^ ^^^ ^^^ ---
40+
41+
error: aborting due to 4 previous errors
42+

0 commit comments

Comments
 (0)