Skip to content

Commit 2dd63a2

Browse files
authoredJan 23, 2019
Rollup merge of #57779 - estebank:recover-struct-fields, r=davidtwco
Recover from parse errors in literal struct fields and incorrect float literals Fix #52496.
2 parents b0ec43f + 4745b86 commit 2dd63a2

11 files changed

+225
-44
lines changed
 

‎src/libsyntax/parse/parser.rs

+84-10
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)]
@@ -1988,6 +1989,44 @@ impl<'a> Parser<'a> {
19881989

19891990
result.unwrap()
19901991
}
1992+
token::Dot if self.look_ahead(1, |t| match t {
1993+
token::Literal(parse::token::Lit::Integer(_) , _) => true,
1994+
_ => false,
1995+
}) => { // recover from `let x = .4;`
1996+
let lo = self.span;
1997+
self.bump();
1998+
if let token::Literal(
1999+
parse::token::Lit::Integer(val),
2000+
suffix,
2001+
) = self.token {
2002+
let suffix = suffix.and_then(|s| {
2003+
let s = s.as_str().get();
2004+
if ["f32", "f64"].contains(&s) {
2005+
Some(s)
2006+
} else {
2007+
None
2008+
}
2009+
}).unwrap_or("");
2010+
self.bump();
2011+
let sp = lo.to(self.prev_span);
2012+
let mut err = self.diagnostic()
2013+
.struct_span_err(sp, "float literals must have an integer part");
2014+
err.span_suggestion_with_applicability(
2015+
sp,
2016+
"must have an integer part",
2017+
format!("0.{}{}", val, suffix),
2018+
Applicability::MachineApplicable,
2019+
);
2020+
err.emit();
2021+
return Ok(match suffix {
2022+
"f32" => ast::LitKind::Float(val, ast::FloatTy::F32),
2023+
"f64" => ast::LitKind::Float(val, ast::FloatTy::F64),
2024+
_ => ast::LitKind::FloatUnsuffixed(val),
2025+
});
2026+
} else {
2027+
unreachable!();
2028+
};
2029+
}
19912030
_ => { return self.unexpected_last(&self.token); }
19922031
};
19932032

@@ -2656,8 +2695,24 @@ impl<'a> Parser<'a> {
26562695
break;
26572696
}
26582697

2698+
let mut recovery_field = None;
2699+
if let token::Ident(ident, _) = self.token {
2700+
if !self.token.is_reserved_ident() && self.look_ahead(1, |t| *t == token::Colon) {
2701+
// Use in case of error after field-looking code: `S { foo: () with a }`
2702+
let mut ident = ident.clone();
2703+
ident.span = self.span;
2704+
recovery_field = Some(ast::Field {
2705+
ident,
2706+
span: self.span,
2707+
expr: self.mk_expr(self.span, ExprKind::Err, ThinVec::new()),
2708+
is_shorthand: false,
2709+
attrs: ThinVec::new(),
2710+
});
2711+
}
2712+
}
2713+
let mut parsed_field = None;
26592714
match self.parse_field() {
2660-
Ok(f) => fields.push(f),
2715+
Ok(f) => parsed_field = Some(f),
26612716
Err(mut e) => {
26622717
e.span_label(struct_sp, "while parsing this struct");
26632718
e.emit();
@@ -2666,19 +2721,28 @@ impl<'a> Parser<'a> {
26662721
// what comes next as additional fields, rather than
26672722
// bailing out until next `}`.
26682723
if self.token != token::Comma {
2669-
self.recover_stmt();
2670-
break;
2724+
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
2725+
if self.token != token::Comma {
2726+
break;
2727+
}
26712728
}
26722729
}
26732730
}
26742731

26752732
match self.expect_one_of(&[token::Comma],
26762733
&[token::CloseDelim(token::Brace)]) {
2677-
Ok(()) => {}
2734+
Ok(()) => if let Some(f) = parsed_field.or(recovery_field) {
2735+
// only include the field if there's no parse error for the field name
2736+
fields.push(f);
2737+
}
26782738
Err(mut e) => {
2739+
if let Some(f) = recovery_field {
2740+
fields.push(f);
2741+
}
2742+
e.span_label(struct_sp, "while parsing this struct");
26792743
e.emit();
2680-
self.recover_stmt();
2681-
break;
2744+
self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
2745+
self.eat(&token::Comma);
26822746
}
26832747
}
26842748
}
@@ -4542,13 +4606,13 @@ impl<'a> Parser<'a> {
45424606
token::CloseDelim(token::DelimToken::Brace) => {
45434607
if brace_depth == 0 {
45444608
debug!("recover_stmt_ return - close delim {:?}", self.token);
4545-
return;
4609+
break;
45464610
}
45474611
brace_depth -= 1;
45484612
self.bump();
45494613
if in_block && bracket_depth == 0 && brace_depth == 0 {
45504614
debug!("recover_stmt_ return - block end {:?}", self.token);
4551-
return;
4615+
break;
45524616
}
45534617
}
45544618
token::CloseDelim(token::DelimToken::Bracket) => {
@@ -4560,15 +4624,25 @@ impl<'a> Parser<'a> {
45604624
}
45614625
token::Eof => {
45624626
debug!("recover_stmt_ return - Eof");
4563-
return;
4627+
break;
45644628
}
45654629
token::Semi => {
45664630
self.bump();
45674631
if break_on_semi == SemiColonMode::Break &&
45684632
brace_depth == 0 &&
45694633
bracket_depth == 0 {
45704634
debug!("recover_stmt_ return - Semi");
4571-
return;
4635+
break;
4636+
}
4637+
}
4638+
token::Comma => {
4639+
if break_on_semi == SemiColonMode::Comma &&
4640+
brace_depth == 0 &&
4641+
bracket_depth == 0 {
4642+
debug!("recover_stmt_ return - Semi");
4643+
break;
4644+
} else {
4645+
self.bump();
45724646
}
45734647
}
45744648
_ => {

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

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
struct Foo { bar: f64, baz: i64, bat: i64 }
2+
3+
fn main() {
4+
let _ = Foo { bar: .5, baz: 42 };
5+
//~^ ERROR float literals must have an integer part
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 missing fields `bar`, `baz` in initializer of `Foo`
11+
//~| ERROR expected identifier, found `.`
12+
}

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

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error: float literals must have an integer part
2+
--> $DIR/issue-52496.rs:4:24
3+
|
4+
LL | let _ = Foo { bar: .5, baz: 42 };
5+
| ^^ help: must have an integer part: `0.5`
6+
7+
error: expected one of `,` or `}`, found `.`
8+
--> $DIR/issue-52496.rs:8:22
9+
|
10+
LL | let _ = Foo { bar.into(), bat: -1, . };
11+
| --- ^ expected one of `,` or `}` here
12+
| |
13+
| while parsing this struct
14+
15+
error: expected identifier, found `.`
16+
--> $DIR/issue-52496.rs:8:40
17+
|
18+
LL | let _ = Foo { bar.into(), bat: -1, . };
19+
| --- ^ expected identifier
20+
| |
21+
| while parsing this struct
22+
23+
error[E0063]: missing field `bat` in initializer of `Foo`
24+
--> $DIR/issue-52496.rs:4:13
25+
|
26+
LL | let _ = Foo { bar: .5, baz: 42 };
27+
| ^^^ missing `bat`
28+
29+
error[E0063]: missing fields `bar`, `baz` in initializer of `Foo`
30+
--> $DIR/issue-52496.rs:8:13
31+
|
32+
LL | let _ = Foo { bar.into(), bat: -1, . };
33+
| ^^^ missing `bar`, `baz`
34+
35+
error: aborting due to 5 previous errors
36+
37+
For more information about this error, try `rustc --explain E0063`.

‎src/test/ui/parser/removed-syntax-with-1.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ fn main() {
55
}
66

77
let a = S { foo: (), bar: () };
8-
let b = S { foo: () with a };
8+
let b = S { foo: () with a, bar: () };
99
//~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
10-
//~| ERROR missing field `bar` in initializer of `main::S`
1110
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
22
--> $DIR/removed-syntax-with-1.rs:8:25
33
|
4-
LL | let b = S { foo: () with a };
5-
| ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here
4+
LL | let b = S { foo: () with a, bar: () };
5+
| - ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here
6+
| |
7+
| while parsing this struct
68

7-
error[E0063]: missing field `bar` in initializer of `main::S`
8-
--> $DIR/removed-syntax-with-1.rs:8:13
9-
|
10-
LL | let b = S { foo: () with a };
11-
| ^ missing `bar`
12-
13-
error: aborting due to 2 previous errors
9+
error: aborting due to previous error
1410

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

‎src/test/ui/parser/removed-syntax-with-2.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ fn main() {
77
let a = S { foo: (), bar: () };
88
let b = S { foo: (), with a };
99
//~^ ERROR expected one of `,` or `}`, found `a`
10-
//~| ERROR cannot find value `with` in this scope
11-
//~| ERROR struct `main::S` has no field named `with`
10+
//~| ERROR missing field `bar` in initializer of `main::S`
1211
}

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

+8-15
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,16 @@ 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

7-
error[E0425]: cannot find value `with` in this scope
8-
--> $DIR/removed-syntax-with-2.rs:8:26
9+
error[E0063]: missing field `bar` in initializer of `main::S`
10+
--> $DIR/removed-syntax-with-2.rs:8:13
911
|
1012
LL | let b = S { foo: (), with a };
11-
| ^^^^ not found in this scope
13+
| ^ missing `bar`
1214

13-
error[E0560]: struct `main::S` has no field named `with`
14-
--> $DIR/removed-syntax-with-2.rs:8:26
15-
|
16-
LL | let b = S { foo: (), with a };
17-
| ^^^^ `main::S` does not have this field
18-
|
19-
= note: available fields are: `foo`, `bar`
20-
21-
error: aborting due to 3 previous errors
15+
error: aborting due to 2 previous errors
2216

23-
Some errors occurred: E0425, E0560.
24-
For more information about an error, try `rustc --explain E0425`.
17+
For more information about this error, try `rustc --explain E0063`.
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
}
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`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fn main() {
2+
let _: usize = .3;
3+
//~^ ERROR float literals must have an integer part
4+
//~| ERROR mismatched types
5+
let _: usize = .42f32;
6+
//~^ ERROR float literals must have an integer part
7+
//~| ERROR mismatched types
8+
let _: usize = .5f64;
9+
//~^ ERROR float literals must have an integer part
10+
//~| ERROR mismatched types
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error: float literals must have an integer part
2+
--> $DIR/recover-invalid-float.rs:2:20
3+
|
4+
LL | let _: usize = .3;
5+
| ^^ help: must have an integer part: `0.3`
6+
7+
error: float literals must have an integer part
8+
--> $DIR/recover-invalid-float.rs:5:20
9+
|
10+
LL | let _: usize = .42f32;
11+
| ^^^^^^ help: must have an integer part: `0.42f32`
12+
13+
error: float literals must have an integer part
14+
--> $DIR/recover-invalid-float.rs:8:20
15+
|
16+
LL | let _: usize = .5f64;
17+
| ^^^^^ help: must have an integer part: `0.5f64`
18+
19+
error[E0308]: mismatched types
20+
--> $DIR/recover-invalid-float.rs:2:20
21+
|
22+
LL | let _: usize = .3;
23+
| ^^ expected usize, found floating-point number
24+
|
25+
= note: expected type `usize`
26+
found type `{float}`
27+
28+
error[E0308]: mismatched types
29+
--> $DIR/recover-invalid-float.rs:5:20
30+
|
31+
LL | let _: usize = .42f32;
32+
| ^^^^^^ expected usize, found f32
33+
34+
error[E0308]: mismatched types
35+
--> $DIR/recover-invalid-float.rs:8:20
36+
|
37+
LL | let _: usize = .5f64;
38+
| ^^^^^ expected usize, found f64
39+
40+
error: aborting due to 6 previous errors
41+
42+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)
Please sign in to comment.