Skip to content

Commit 28ea03e

Browse files
committed
Suggest correct location for lifetime parameters in use
1 parent db74031 commit 28ea03e

7 files changed

+82
-47
lines changed

src/libsyntax/parse/parser.rs

+43-9
Original file line numberDiff line numberDiff line change
@@ -5330,37 +5330,46 @@ impl<'a> Parser<'a> {
53305330

53315331
/// Parses (possibly empty) list of lifetime and type arguments and associated type bindings,
53325332
/// possibly including trailing comma.
5333-
fn parse_generic_args(&mut self)
5334-
-> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
5333+
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
53355334
let mut args = Vec::new();
53365335
let mut bindings = Vec::new();
53375336
let mut seen_type = false;
53385337
let mut seen_binding = false;
5338+
let mut first_type_or_binding_span: Option<Span> = None;
5339+
let mut bad_lifetime_pos = vec![];
5340+
let mut last_comma_span = None;
5341+
let mut suggestions = vec![];
53395342
loop {
53405343
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
53415344
// Parse lifetime argument.
53425345
args.push(GenericArg::Lifetime(self.expect_lifetime()));
53435346
if seen_type || seen_binding {
5344-
self.struct_span_err(
5345-
self.prev_span,
5346-
"lifetime parameters must be declared prior to type parameters"
5347-
)
5348-
.span_label(self.prev_span, "must be declared prior to type parameters")
5349-
.emit();
5347+
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
5348+
bad_lifetime_pos.push(self.prev_span);
5349+
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
5350+
suggestions.push((remove_sp, String::new()));
5351+
suggestions.push((
5352+
first_type_or_binding_span.unwrap().shrink_to_lo(),
5353+
format!("{}, ", snippet)));
5354+
}
53505355
}
53515356
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
53525357
// Parse associated type binding.
53535358
let lo = self.span;
53545359
let ident = self.parse_ident()?;
53555360
self.bump();
53565361
let ty = self.parse_ty()?;
5362+
let span = lo.to(self.prev_span);
53575363
bindings.push(TypeBinding {
53585364
id: ast::DUMMY_NODE_ID,
53595365
ident,
53605366
ty,
5361-
span: lo.to(self.prev_span),
5367+
span,
53625368
});
53635369
seen_binding = true;
5370+
if first_type_or_binding_span.is_none() {
5371+
first_type_or_binding_span = Some(span);
5372+
}
53645373
} else if self.check_type() {
53655374
// Parse type argument.
53665375
let ty_param = self.parse_ty()?;
@@ -5375,6 +5384,9 @@ impl<'a> Parser<'a> {
53755384
)
53765385
.emit();
53775386
}
5387+
if first_type_or_binding_span.is_none() {
5388+
first_type_or_binding_span = Some(ty_param.span);
5389+
}
53785390
args.push(GenericArg::Type(ty_param));
53795391
seen_type = true;
53805392
} else {
@@ -5383,8 +5395,30 @@ impl<'a> Parser<'a> {
53835395

53845396
if !self.eat(&token::Comma) {
53855397
break
5398+
} else {
5399+
last_comma_span = Some(self.prev_span);
53865400
}
53875401
}
5402+
if !bad_lifetime_pos.is_empty() {
5403+
let mut err = self.struct_span_err(
5404+
bad_lifetime_pos.clone(),
5405+
"lifetime parameters must be declared prior to type parameters"
5406+
);
5407+
for sp in &bad_lifetime_pos {
5408+
err.span_label(*sp, "must be declared prior to type parameters");
5409+
}
5410+
if !suggestions.is_empty() {
5411+
err.multipart_suggestion_with_applicability(
5412+
&format!(
5413+
"move the lifetime parameter{} prior to the first type parameter",
5414+
if bad_lifetime_pos.len() > 1 { "s" } else { "" },
5415+
),
5416+
suggestions,
5417+
Applicability::MachineApplicable,
5418+
);
5419+
}
5420+
err.emit();
5421+
}
53885422
Ok((args, bindings))
53895423
}
53905424

+16-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
fn main() {
2-
(0..4)
3-
.map(|x| x * 2)
4-
.collect::<Vec<'a, usize, 'b>>()
5-
//~^ ERROR lifetime parameters must be declared prior to type parameters
6-
//~| ERROR use of undeclared lifetime name
7-
//~| ERROR use of undeclared lifetime name
1+
// can't run rustfix because it doesn't handle multipart suggestions correctly
2+
// compile-flags: -Zborrowck=mir
3+
// we need the above to avoid ast borrowck failure in recovered code
4+
5+
struct S<'a, T> {
6+
a: &'a T,
7+
b: &'a T,
88
}
9+
10+
fn foo<'a, 'b>(start: &'a usize, end: &'a usize) {
11+
let _x = (*start..*end)
12+
.map(|x| S { a: start, b: end })
13+
.collect::<Vec<S<_, 'a>>>();
14+
//~^ ERROR lifetime parameters must be declared prior to type parameters
15+
}
16+
17+
fn main() {}
+7-16
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
error: lifetime parameters must be declared prior to type parameters
2-
--> $DIR/issue-14303-fncall.rs:4:31
2+
--> $DIR/issue-14303-fncall.rs:13:29
33
|
4-
LL | .collect::<Vec<'a, usize, 'b>>()
5-
| ^^ must be declared prior to type parameters
6-
7-
error[E0261]: use of undeclared lifetime name `'a`
8-
--> $DIR/issue-14303-fncall.rs:4:20
9-
|
10-
LL | .collect::<Vec<'a, usize, 'b>>()
11-
| ^^ undeclared lifetime
12-
13-
error[E0261]: use of undeclared lifetime name `'b`
14-
--> $DIR/issue-14303-fncall.rs:4:31
4+
LL | .collect::<Vec<S<_, 'a>>>();
5+
| ^^ must be declared prior to type parameters
6+
help: move the lifetime parameter prior to the first type parameter
157
|
16-
LL | .collect::<Vec<'a, usize, 'b>>()
17-
| ^^ undeclared lifetime
8+
LL | .collect::<Vec<S<'a, _>>>();
9+
| ^^^ --
1810

19-
error: aborting due to 3 previous errors
11+
error: aborting due to previous error
2012

21-
For more information about this error, try `rustc --explain E0261`.

src/test/ui/parser/issue-14303-path.rs

-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ mod foo {
99

1010
fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {}
1111
//~^ ERROR lifetime parameters must be declared prior to type parameters
12-
//~| ERROR lifetime parameters must be declared prior to type parameters
1312

1413
fn main() {}

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

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ error: lifetime parameters must be declared prior to type parameters
22
--> $DIR/issue-14303-path.rs:10:40
33
|
44
LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {}
5-
| ^^ must be declared prior to type parameters
6-
7-
error: lifetime parameters must be declared prior to type parameters
8-
--> $DIR/issue-14303-path.rs:10:44
5+
| ^^ ^^ must be declared prior to type parameters
6+
| |
7+
| must be declared prior to type parameters
8+
help: move the lifetime parameters prior to the first type parameter
99
|
10-
LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, T, 'b, 'c>) {}
11-
| ^^ must be declared prior to type parameters
10+
LL | fn bar<'a, 'b, 'c, T>(x: foo::X<'a, 'b, 'c, T>) {}
11+
| ^^^ ^^^ --
1212

13-
error: aborting due to 2 previous errors
13+
error: aborting due to previous error
1414

src/test/ui/traits/trait-object-vs-lifetime.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// A few contrived examples where lifetime should (or should not) be parsed as an object type.
22
// Lifetimes parsed as types are still rejected later by semantic checks.
33

4-
// compile-flags: -Z continue-parse-after-error
5-
64
struct S<'a, T>(&'a u8, T);
75

86
fn main() {

src/test/ui/traits/trait-object-vs-lifetime.stderr

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
11
error: lifetime parameters must be declared prior to type parameters
2-
--> $DIR/trait-object-vs-lifetime.rs:16:25
2+
--> $DIR/trait-object-vs-lifetime.rs:14:25
33
|
44
LL | let _: S<'static +, 'static>;
55
| ^^^^^^^ must be declared prior to type parameters
6+
help: move the lifetime parameter prior to the first type parameter
7+
|
8+
LL | let _: S<'static, 'static +>;
9+
| ^^^^^^^^ --
610

711
error[E0224]: at least one non-builtin trait is required for an object type
8-
--> $DIR/trait-object-vs-lifetime.rs:11:23
12+
--> $DIR/trait-object-vs-lifetime.rs:9:23
913
|
1014
LL | let _: S<'static, 'static +>;
1115
| ^^^^^^^^^
1216

1317
error[E0107]: wrong number of lifetime arguments: expected 1, found 2
14-
--> $DIR/trait-object-vs-lifetime.rs:13:23
18+
--> $DIR/trait-object-vs-lifetime.rs:11:23
1519
|
1620
LL | let _: S<'static, 'static>;
1721
| ^^^^^^^ unexpected lifetime argument
1822

1923
error[E0107]: wrong number of type arguments: expected 1, found 0
20-
--> $DIR/trait-object-vs-lifetime.rs:13:12
24+
--> $DIR/trait-object-vs-lifetime.rs:11:12
2125
|
2226
LL | let _: S<'static, 'static>;
2327
| ^^^^^^^^^^^^^^^^^^^ expected 1 type argument
2428

2529
error[E0224]: at least one non-builtin trait is required for an object type
26-
--> $DIR/trait-object-vs-lifetime.rs:16:14
30+
--> $DIR/trait-object-vs-lifetime.rs:14:14
2731
|
2832
LL | let _: S<'static +, 'static>;
2933
| ^^^^^^^^^

0 commit comments

Comments
 (0)