about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-01-24 00:19:53 +0100
committerGitHub <noreply@github.com>2019-01-24 00:19:53 +0100
commit2dd63a2e10aa130f6d7b10ddaad8f8120d1b8c58 (patch)
treecc97523c0cfde2cfcc0429ffcd87633bb4016ab4
parentb0ec43f5692754c06701a534755f346aecc1d122 (diff)
parent4745b86202f0e96b4c0d0de05220a5ac4b5308ef (diff)
downloadrust-2dd63a2e10aa130f6d7b10ddaad8f8120d1b8c58.tar.gz
rust-2dd63a2e10aa130f6d7b10ddaad8f8120d1b8c58.zip
Rollup merge of #57779 - estebank:recover-struct-fields, r=davidtwco
Recover from parse errors in literal struct fields and incorrect float literals

Fix #52496.
-rw-r--r--src/libsyntax/parse/parser.rs94
-rw-r--r--src/test/ui/issues/issue-52496.rs12
-rw-r--r--src/test/ui/issues/issue-52496.stderr37
-rw-r--r--src/test/ui/parser/removed-syntax-with-1.rs3
-rw-r--r--src/test/ui/parser/removed-syntax-with-1.stderr15
-rw-r--r--src/test/ui/parser/removed-syntax-with-2.rs3
-rw-r--r--src/test/ui/parser/removed-syntax-with-2.stderr23
-rw-r--r--src/test/ui/parser/struct-field-numeric-shorthand.rs7
-rw-r--r--src/test/ui/parser/struct-field-numeric-shorthand.stderr22
-rw-r--r--src/test/ui/suggestions/recover-invalid-float.rs11
-rw-r--r--src/test/ui/suggestions/recover-invalid-float.stderr42
11 files changed, 225 insertions, 44 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 09ea0995253..325bc2fb91a 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -100,6 +100,7 @@ pub enum PathStyle {
 enum SemiColonMode {
     Break,
     Ignore,
+    Comma,
 }
 
 #[derive(Clone, Copy, PartialEq, Debug)]
@@ -1988,6 +1989,44 @@ impl<'a> Parser<'a> {
 
                 result.unwrap()
             }
+            token::Dot if self.look_ahead(1, |t| match t {
+                token::Literal(parse::token::Lit::Integer(_) , _) => true,
+                _ => false,
+            }) => { // recover from `let x = .4;`
+                let lo = self.span;
+                self.bump();
+                if let token::Literal(
+                    parse::token::Lit::Integer(val),
+                    suffix,
+                ) = self.token {
+                    let suffix = suffix.and_then(|s| {
+                        let s = s.as_str().get();
+                        if ["f32", "f64"].contains(&s) {
+                            Some(s)
+                        } else {
+                            None
+                        }
+                    }).unwrap_or("");
+                    self.bump();
+                    let sp = lo.to(self.prev_span);
+                    let mut err = self.diagnostic()
+                        .struct_span_err(sp, "float literals must have an integer part");
+                    err.span_suggestion_with_applicability(
+                        sp,
+                        "must have an integer part",
+                        format!("0.{}{}", val, suffix),
+                        Applicability::MachineApplicable,
+                    );
+                    err.emit();
+                    return Ok(match suffix {
+                        "f32" => ast::LitKind::Float(val, ast::FloatTy::F32),
+                        "f64" => ast::LitKind::Float(val, ast::FloatTy::F64),
+                        _ => ast::LitKind::FloatUnsuffixed(val),
+                    });
+                } else {
+                    unreachable!();
+                };
+            }
             _ => { return self.unexpected_last(&self.token); }
         };
 
@@ -2656,8 +2695,24 @@ impl<'a> Parser<'a> {
                 break;
             }
 
+            let mut recovery_field = None;
+            if let token::Ident(ident, _) = self.token {
+                if !self.token.is_reserved_ident() && self.look_ahead(1, |t| *t == token::Colon) {
+                    // Use in case of error after field-looking code: `S { foo: () with a }`
+                    let mut ident = ident.clone();
+                    ident.span = self.span;
+                    recovery_field = Some(ast::Field {
+                        ident,
+                        span: self.span,
+                        expr: self.mk_expr(self.span, ExprKind::Err, ThinVec::new()),
+                        is_shorthand: false,
+                        attrs: ThinVec::new(),
+                    });
+                }
+            }
+            let mut parsed_field = None;
             match self.parse_field() {
-                Ok(f) => fields.push(f),
+                Ok(f) => parsed_field = Some(f),
                 Err(mut e) => {
                     e.span_label(struct_sp, "while parsing this struct");
                     e.emit();
@@ -2666,19 +2721,28 @@ impl<'a> Parser<'a> {
                     // what comes next as additional fields, rather than
                     // bailing out until next `}`.
                     if self.token != token::Comma {
-                        self.recover_stmt();
-                        break;
+                        self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
+                        if self.token != token::Comma {
+                            break;
+                        }
                     }
                 }
             }
 
             match self.expect_one_of(&[token::Comma],
                                      &[token::CloseDelim(token::Brace)]) {
-                Ok(()) => {}
+                Ok(()) => if let Some(f) = parsed_field.or(recovery_field) {
+                    // only include the field if there's no parse error for the field name
+                    fields.push(f);
+                }
                 Err(mut e) => {
+                    if let Some(f) = recovery_field {
+                        fields.push(f);
+                    }
+                    e.span_label(struct_sp, "while parsing this struct");
                     e.emit();
-                    self.recover_stmt();
-                    break;
+                    self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
+                    self.eat(&token::Comma);
                 }
             }
         }
@@ -4542,13 +4606,13 @@ impl<'a> Parser<'a> {
                 token::CloseDelim(token::DelimToken::Brace) => {
                     if brace_depth == 0 {
                         debug!("recover_stmt_ return - close delim {:?}", self.token);
-                        return;
+                        break;
                     }
                     brace_depth -= 1;
                     self.bump();
                     if in_block && bracket_depth == 0 && brace_depth == 0 {
                         debug!("recover_stmt_ return - block end {:?}", self.token);
-                        return;
+                        break;
                     }
                 }
                 token::CloseDelim(token::DelimToken::Bracket) => {
@@ -4560,7 +4624,7 @@ impl<'a> Parser<'a> {
                 }
                 token::Eof => {
                     debug!("recover_stmt_ return - Eof");
-                    return;
+                    break;
                 }
                 token::Semi => {
                     self.bump();
@@ -4568,7 +4632,17 @@ impl<'a> Parser<'a> {
                        brace_depth == 0 &&
                        bracket_depth == 0 {
                         debug!("recover_stmt_ return - Semi");
-                        return;
+                        break;
+                    }
+                }
+                token::Comma => {
+                    if break_on_semi == SemiColonMode::Comma &&
+                       brace_depth == 0 &&
+                       bracket_depth == 0 {
+                        debug!("recover_stmt_ return - Semi");
+                        break;
+                    } else {
+                        self.bump();
                     }
                 }
                 _ => {
diff --git a/src/test/ui/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs
new file mode 100644
index 00000000000..4e945365373
--- /dev/null
+++ b/src/test/ui/issues/issue-52496.rs
@@ -0,0 +1,12 @@
+struct Foo { bar: f64, baz: i64, bat: i64 }
+
+fn main() {
+    let _ = Foo { bar: .5, baz: 42 };
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR missing field `bat` in initializer of `Foo`
+    let bar = 1.5f32;
+    let _ = Foo { bar.into(), bat: -1, . };
+    //~^ ERROR expected one of
+    //~| ERROR missing fields `bar`, `baz` in initializer of `Foo`
+    //~| ERROR expected identifier, found `.`
+}
diff --git a/src/test/ui/issues/issue-52496.stderr b/src/test/ui/issues/issue-52496.stderr
new file mode 100644
index 00000000000..43009a15bd4
--- /dev/null
+++ b/src/test/ui/issues/issue-52496.stderr
@@ -0,0 +1,37 @@
+error: float literals must have an integer part
+  --> $DIR/issue-52496.rs:4:24
+   |
+LL |     let _ = Foo { bar: .5, baz: 42 };
+   |                        ^^ help: must have an integer part: `0.5`
+
+error: expected one of `,` or `}`, found `.`
+  --> $DIR/issue-52496.rs:8:22
+   |
+LL |     let _ = Foo { bar.into(), bat: -1, . };
+   |             ---      ^ expected one of `,` or `}` here
+   |             |
+   |             while parsing this struct
+
+error: expected identifier, found `.`
+  --> $DIR/issue-52496.rs:8:40
+   |
+LL |     let _ = Foo { bar.into(), bat: -1, . };
+   |             ---                        ^ expected identifier
+   |             |
+   |             while parsing this struct
+
+error[E0063]: missing field `bat` in initializer of `Foo`
+  --> $DIR/issue-52496.rs:4:13
+   |
+LL |     let _ = Foo { bar: .5, baz: 42 };
+   |             ^^^ missing `bat`
+
+error[E0063]: missing fields `bar`, `baz` in initializer of `Foo`
+  --> $DIR/issue-52496.rs:8:13
+   |
+LL |     let _ = Foo { bar.into(), bat: -1, . };
+   |             ^^^ missing `bar`, `baz`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0063`.
diff --git a/src/test/ui/parser/removed-syntax-with-1.rs b/src/test/ui/parser/removed-syntax-with-1.rs
index 57cbe8d5be6..2c1e152dcee 100644
--- a/src/test/ui/parser/removed-syntax-with-1.rs
+++ b/src/test/ui/parser/removed-syntax-with-1.rs
@@ -5,7 +5,6 @@ fn main() {
     }
 
     let a = S { foo: (), bar: () };
-    let b = S { foo: () with a };
+    let b = S { foo: () with a, bar: () };
     //~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
-    //~| ERROR missing field `bar` in initializer of `main::S`
 }
diff --git a/src/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr
index 77ed4fcea51..a157873916a 100644
--- a/src/test/ui/parser/removed-syntax-with-1.stderr
+++ b/src/test/ui/parser/removed-syntax-with-1.stderr
@@ -1,15 +1,10 @@
 error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
   --> $DIR/removed-syntax-with-1.rs:8:25
    |
-LL |     let b = S { foo: () with a };
-   |                         ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here
+LL |     let b = S { foo: () with a, bar: () };
+   |             -           ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here
+   |             |
+   |             while parsing this struct
 
-error[E0063]: missing field `bar` in initializer of `main::S`
-  --> $DIR/removed-syntax-with-1.rs:8:13
-   |
-LL |     let b = S { foo: () with a };
-   |             ^ missing `bar`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0063`.
diff --git a/src/test/ui/parser/removed-syntax-with-2.rs b/src/test/ui/parser/removed-syntax-with-2.rs
index 11db391c548..f666da49696 100644
--- a/src/test/ui/parser/removed-syntax-with-2.rs
+++ b/src/test/ui/parser/removed-syntax-with-2.rs
@@ -7,6 +7,5 @@ fn main() {
     let a = S { foo: (), bar: () };
     let b = S { foo: (), with a };
     //~^ ERROR expected one of `,` or `}`, found `a`
-    //~| ERROR cannot find value `with` in this scope
-    //~| ERROR struct `main::S` has no field named `with`
+    //~| ERROR missing field `bar` in initializer of `main::S`
 }
diff --git a/src/test/ui/parser/removed-syntax-with-2.stderr b/src/test/ui/parser/removed-syntax-with-2.stderr
index 5642d2f45ff..7717b49d3a2 100644
--- a/src/test/ui/parser/removed-syntax-with-2.stderr
+++ b/src/test/ui/parser/removed-syntax-with-2.stderr
@@ -2,23 +2,16 @@ error: expected one of `,` or `}`, found `a`
   --> $DIR/removed-syntax-with-2.rs:8:31
    |
 LL |     let b = S { foo: (), with a };
-   |                               ^ expected one of `,` or `}` here
+   |             -                 ^ expected one of `,` or `}` here
+   |             |
+   |             while parsing this struct
 
-error[E0425]: cannot find value `with` in this scope
-  --> $DIR/removed-syntax-with-2.rs:8:26
+error[E0063]: missing field `bar` in initializer of `main::S`
+  --> $DIR/removed-syntax-with-2.rs:8:13
    |
 LL |     let b = S { foo: (), with a };
-   |                          ^^^^ not found in this scope
+   |             ^ missing `bar`
 
-error[E0560]: struct `main::S` has no field named `with`
-  --> $DIR/removed-syntax-with-2.rs:8:26
-   |
-LL |     let b = S { foo: (), with a };
-   |                          ^^^^ `main::S` does not have this field
-   |
-   = note: available fields are: `foo`, `bar`
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
 
-Some errors occurred: E0425, E0560.
-For more information about an error, try `rustc --explain E0425`.
+For more information about this error, try `rustc --explain E0063`.
diff --git a/src/test/ui/parser/struct-field-numeric-shorthand.rs b/src/test/ui/parser/struct-field-numeric-shorthand.rs
index 914588f51e1..58c40b3d96a 100644
--- a/src/test/ui/parser/struct-field-numeric-shorthand.rs
+++ b/src/test/ui/parser/struct-field-numeric-shorthand.rs
@@ -1,6 +1,9 @@
 struct Rgb(u8, u8, u8);
 
 fn main() {
-    let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0`
-                             //~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb`
+    let _ = Rgb { 0, 1, 2 };
+    //~^ ERROR expected identifier, found `0`
+    //~| ERROR expected identifier, found `1`
+    //~| ERROR expected identifier, found `2`
+    //~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb`
 }
diff --git a/src/test/ui/parser/struct-field-numeric-shorthand.stderr b/src/test/ui/parser/struct-field-numeric-shorthand.stderr
index f5dc226934e..cfb1f820147 100644
--- a/src/test/ui/parser/struct-field-numeric-shorthand.stderr
+++ b/src/test/ui/parser/struct-field-numeric-shorthand.stderr
@@ -1,17 +1,33 @@
 error: expected identifier, found `0`
   --> $DIR/struct-field-numeric-shorthand.rs:4:19
    |
-LL |     let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0`
+LL |     let _ = Rgb { 0, 1, 2 };
    |             ---   ^ expected identifier
    |             |
    |             while parsing this struct
 
+error: expected identifier, found `1`
+  --> $DIR/struct-field-numeric-shorthand.rs:4:22
+   |
+LL |     let _ = Rgb { 0, 1, 2 };
+   |             ---      ^ expected identifier
+   |             |
+   |             while parsing this struct
+
+error: expected identifier, found `2`
+  --> $DIR/struct-field-numeric-shorthand.rs:4:25
+   |
+LL |     let _ = Rgb { 0, 1, 2 };
+   |             ---         ^ expected identifier
+   |             |
+   |             while parsing this struct
+
 error[E0063]: missing fields `0`, `1`, `2` in initializer of `Rgb`
   --> $DIR/struct-field-numeric-shorthand.rs:4:13
    |
-LL |     let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0`
+LL |     let _ = Rgb { 0, 1, 2 };
    |             ^^^ missing `0`, `1`, `2`
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0063`.
diff --git a/src/test/ui/suggestions/recover-invalid-float.rs b/src/test/ui/suggestions/recover-invalid-float.rs
new file mode 100644
index 00000000000..506ef8900b8
--- /dev/null
+++ b/src/test/ui/suggestions/recover-invalid-float.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let _: usize = .3;
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR mismatched types
+    let _: usize = .42f32;
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR mismatched types
+    let _: usize = .5f64;
+    //~^ ERROR float literals must have an integer part
+    //~| ERROR mismatched types
+}
diff --git a/src/test/ui/suggestions/recover-invalid-float.stderr b/src/test/ui/suggestions/recover-invalid-float.stderr
new file mode 100644
index 00000000000..c464676b444
--- /dev/null
+++ b/src/test/ui/suggestions/recover-invalid-float.stderr
@@ -0,0 +1,42 @@
+error: float literals must have an integer part
+  --> $DIR/recover-invalid-float.rs:2:20
+   |
+LL |     let _: usize = .3;
+   |                    ^^ help: must have an integer part: `0.3`
+
+error: float literals must have an integer part
+  --> $DIR/recover-invalid-float.rs:5:20
+   |
+LL |     let _: usize = .42f32;
+   |                    ^^^^^^ help: must have an integer part: `0.42f32`
+
+error: float literals must have an integer part
+  --> $DIR/recover-invalid-float.rs:8:20
+   |
+LL |     let _: usize = .5f64;
+   |                    ^^^^^ help: must have an integer part: `0.5f64`
+
+error[E0308]: mismatched types
+  --> $DIR/recover-invalid-float.rs:2:20
+   |
+LL |     let _: usize = .3;
+   |                    ^^ expected usize, found floating-point number
+   |
+   = note: expected type `usize`
+              found type `{float}`
+
+error[E0308]: mismatched types
+  --> $DIR/recover-invalid-float.rs:5:20
+   |
+LL |     let _: usize = .42f32;
+   |                    ^^^^^^ expected usize, found f32
+
+error[E0308]: mismatched types
+  --> $DIR/recover-invalid-float.rs:8:20
+   |
+LL |     let _: usize = .5f64;
+   |                    ^^^^^ expected usize, found f64
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0308`.