Skip to content

Commit b1f169f

Browse files
committed
Recover from parse errors in struct literal fields
Attempt to recover from parse errors while parsing a struct's literal fields by skipping tokens until a comma or the closing brace is found. This allows errors in other fields to be reported.
1 parent 0c0c585 commit b1f169f

File tree

7 files changed

+132
-15
lines changed

7 files changed

+132
-15
lines changed

src/libsyntax/parse/parser.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pub enum PathStyle {
100100
enum SemiColonMode {
101101
Break,
102102
Ignore,
103+
Comma,
103104
}
104105

105106
#[derive(Clone, Copy, PartialEq, Debug)]
@@ -2656,18 +2657,37 @@ impl<'a> Parser<'a> {
26562657
break;
26572658
}
26582659

2660+
let mut recovery_field = None;
2661+
if let token::Ident(ident, _) = self.token {
2662+
if !self.token.is_reserved_ident() {
2663+
let mut ident = ident.clone();
2664+
ident.span = self.span;
2665+
recovery_field = Some(ast::Field {
2666+
ident,
2667+
span: self.span,
2668+
expr: self.mk_expr(self.span, ExprKind::Err, ThinVec::new()),
2669+
is_shorthand: true,
2670+
attrs: ThinVec::new(),
2671+
});
2672+
}
2673+
}
26592674
match self.parse_field() {
26602675
Ok(f) => fields.push(f),
26612676
Err(mut e) => {
26622677
e.span_label(struct_sp, "while parsing this struct");
26632678
e.emit();
2679+
if let Some(f) = recovery_field {
2680+
fields.push(f);
2681+
}
26642682

26652683
// If the next token is a comma, then try to parse
26662684
// what comes next as additional fields, rather than
26672685
// bailing out until next `}`.
26682686
if self.token != token::Comma {
2669-
self.recover_stmt();
2670-
break;
2687+
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
2688+
if self.token != token::Comma {
2689+
break;
2690+
}
26712691
}
26722692
}
26732693
}
@@ -2676,9 +2696,10 @@ impl<'a> Parser<'a> {
26762696
&[token::CloseDelim(token::Brace)]) {
26772697
Ok(()) => {}
26782698
Err(mut e) => {
2699+
e.span_label(struct_sp, "while parsing this struct");
26792700
e.emit();
2680-
self.recover_stmt();
2681-
break;
2701+
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
2702+
self.eat(&token::Comma);
26822703
}
26832704
}
26842705
}
@@ -4538,13 +4559,13 @@ impl<'a> Parser<'a> {
45384559
token::CloseDelim(token::DelimToken::Brace) => {
45394560
if brace_depth == 0 {
45404561
debug!("recover_stmt_ return - close delim {:?}", self.token);
4541-
return;
4562+
break;
45424563
}
45434564
brace_depth -= 1;
45444565
self.bump();
45454566
if in_block && bracket_depth == 0 && brace_depth == 0 {
45464567
debug!("recover_stmt_ return - block end {:?}", self.token);
4547-
return;
4568+
break;
45484569
}
45494570
}
45504571
token::CloseDelim(token::DelimToken::Bracket) => {
@@ -4556,15 +4577,25 @@ impl<'a> Parser<'a> {
45564577
}
45574578
token::Eof => {
45584579
debug!("recover_stmt_ return - Eof");
4559-
return;
4580+
break;
45604581
}
45614582
token::Semi => {
45624583
self.bump();
45634584
if break_on_semi == SemiColonMode::Break &&
45644585
brace_depth == 0 &&
45654586
bracket_depth == 0 {
45664587
debug!("recover_stmt_ return - Semi");
4567-
return;
4588+
break;
4589+
}
4590+
}
4591+
token::Comma => {
4592+
if break_on_semi == SemiColonMode::Comma &&
4593+
brace_depth == 0 &&
4594+
bracket_depth == 0 {
4595+
debug!("recover_stmt_ return - Semi");
4596+
break;
4597+
} else {
4598+
self.bump();
45684599
}
45694600
}
45704601
_ => {

src/test/ui/issues/issue-52496.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
struct Foo { bar: f64, baz: i64, bat: i64 }
2+
3+
fn main() {
4+
let _ = Foo { bar: .5, baz: 42 };
5+
//~^ ERROR expected expression
6+
//~| ERROR missing field `bat` in initializer of `Foo`
7+
let bar = 1.5f32;
8+
let _ = Foo { bar.into(), bat: -1, . };
9+
//~^ ERROR expected one of
10+
//~| ERROR mismatched types
11+
//~| ERROR missing field `baz` in initializer of `Foo`
12+
//~| ERROR expected identifier, found `.`
13+
}

src/test/ui/issues/issue-52496.stderr

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
error: expected expression, found `.`
2+
--> $DIR/issue-52496.rs:4:24
3+
|
4+
LL | let _ = Foo { bar: .5, baz: 42 };
5+
| --- ^ expected expression
6+
| |
7+
| while parsing this struct
8+
9+
error: expected one of `,` or `}`, found `.`
10+
--> $DIR/issue-52496.rs:8:22
11+
|
12+
LL | let _ = Foo { bar.into(), bat: -1, . };
13+
| --- ^ expected one of `,` or `}` here
14+
| |
15+
| while parsing this struct
16+
17+
error: expected identifier, found `.`
18+
--> $DIR/issue-52496.rs:8:40
19+
|
20+
LL | let _ = Foo { bar.into(), bat: -1, . };
21+
| --- ^ expected identifier
22+
| |
23+
| while parsing this struct
24+
25+
error[E0063]: missing field `bat` in initializer of `Foo`
26+
--> $DIR/issue-52496.rs:4:13
27+
|
28+
LL | let _ = Foo { bar: .5, baz: 42 };
29+
| ^^^ missing `bat`
30+
31+
error[E0308]: mismatched types
32+
--> $DIR/issue-52496.rs:8:19
33+
|
34+
LL | let _ = Foo { bar.into(), bat: -1, . };
35+
| ^^^ expected f64, found f32
36+
help: you can cast an `f32` to `f64` in a lossless way
37+
|
38+
LL | let _ = Foo { bar: bar.into().into(), bat: -1, . };
39+
| ^^^^^^^^^^^^^^^
40+
41+
error[E0063]: missing field `baz` in initializer of `Foo`
42+
--> $DIR/issue-52496.rs:8:13
43+
|
44+
LL | let _ = Foo { bar.into(), bat: -1, . };
45+
| ^^^ missing `baz`
46+
47+
error: aborting due to 6 previous errors
48+
49+
Some errors occurred: E0063, E0308.
50+
For more information about an error, try `rustc --explain E0063`.

src/test/ui/parser/removed-syntax-with-1.stderr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
22
--> $DIR/removed-syntax-with-1.rs:8:25
33
|
44
LL | let b = S { foo: () with a };
5-
| ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here
5+
| - ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here
6+
| |
7+
| while parsing this struct
68

79
error[E0063]: missing field `bar` in initializer of `main::S`
810
--> $DIR/removed-syntax-with-1.rs:8:13

src/test/ui/parser/removed-syntax-with-2.stderr

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ error: expected one of `,` or `}`, found `a`
22
--> $DIR/removed-syntax-with-2.rs:8:31
33
|
44
LL | let b = S { foo: (), with a };
5-
| ^ expected one of `,` or `}` here
5+
| - ^ expected one of `,` or `}` here
6+
| |
7+
| while parsing this struct
68

79
error[E0425]: cannot find value `with` in this scope
810
--> $DIR/removed-syntax-with-2.rs:8:26
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
struct Rgb(u8, u8, u8);
22

33
fn main() {
4-
let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0`
5-
//~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb`
4+
let _ = Rgb { 0, 1, 2 };
5+
//~^ ERROR expected identifier, found `0`
6+
//~| ERROR expected identifier, found `1`
7+
//~| ERROR expected identifier, found `2`
8+
//~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb`
69
}
Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
error: expected identifier, found `0`
22
--> $DIR/struct-field-numeric-shorthand.rs:4:19
33
|
4-
LL | let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0`
4+
LL | let _ = Rgb { 0, 1, 2 };
55
| --- ^ expected identifier
66
| |
77
| while parsing this struct
88

9+
error: expected identifier, found `1`
10+
--> $DIR/struct-field-numeric-shorthand.rs:4:22
11+
|
12+
LL | let _ = Rgb { 0, 1, 2 };
13+
| --- ^ expected identifier
14+
| |
15+
| while parsing this struct
16+
17+
error: expected identifier, found `2`
18+
--> $DIR/struct-field-numeric-shorthand.rs:4:25
19+
|
20+
LL | let _ = Rgb { 0, 1, 2 };
21+
| --- ^ expected identifier
22+
| |
23+
| while parsing this struct
24+
925
error[E0063]: missing fields `0`, `1`, `2` in initializer of `Rgb`
1026
--> $DIR/struct-field-numeric-shorthand.rs:4:13
1127
|
12-
LL | let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0`
28+
LL | let _ = Rgb { 0, 1, 2 };
1329
| ^^^ missing `0`, `1`, `2`
1430

15-
error: aborting due to 2 previous errors
31+
error: aborting due to 4 previous errors
1632

1733
For more information about this error, try `rustc --explain E0063`.

0 commit comments

Comments
 (0)