]> git.lizzy.rs Git - rust.git/commitdiff
Recover from parse errors in struct literal fields
authorEsteban Küber <esteban@kuber.com.ar>
Sun, 20 Jan 2019 08:37:06 +0000 (00:37 -0800)
committerEsteban Küber <esteban@kuber.com.ar>
Sun, 20 Jan 2019 08:37:06 +0000 (00:37 -0800)
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.

src/libsyntax/parse/parser.rs
src/test/ui/issues/issue-52496.rs [new file with mode: 0644]
src/test/ui/issues/issue-52496.stderr [new file with mode: 0644]
src/test/ui/parser/removed-syntax-with-1.stderr
src/test/ui/parser/removed-syntax-with-2.stderr
src/test/ui/parser/struct-field-numeric-shorthand.rs
src/test/ui/parser/struct-field-numeric-shorthand.stderr

index 7e15b23127655ee5883451685b3754e5a5aeeb3c..9b20937cf933b395c8e9dc793ab2004ad19c40c6 100644 (file)
@@ -100,6 +100,7 @@ pub enum PathStyle {
 enum SemiColonMode {
     Break,
     Ignore,
+    Comma,
 }
 
 #[derive(Clone, Copy, PartialEq, Debug)]
@@ -2656,18 +2657,37 @@ fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Att
                 break;
             }
 
+            let mut recovery_field = None;
+            if let token::Ident(ident, _) = self.token {
+                if !self.token.is_reserved_ident() {
+                    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: true,
+                        attrs: ThinVec::new(),
+                    });
+                }
+            }
             match self.parse_field() {
                 Ok(f) => fields.push(f),
                 Err(mut e) => {
                     e.span_label(struct_sp, "while parsing this struct");
                     e.emit();
+                    if let Some(f) = recovery_field {
+                        fields.push(f);
+                    }
 
                     // If the next token is a comma, then try to parse
                     // 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;
+                        }
                     }
                 }
             }
@@ -2676,9 +2696,10 @@ fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Att
                                      &[token::CloseDelim(token::Brace)]) {
                 Ok(()) => {}
                 Err(mut e) => {
+                    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);
                 }
             }
         }
@@ -4538,13 +4559,13 @@ fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockM
                 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) => {
@@ -4556,7 +4577,7 @@ fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockM
                 }
                 token::Eof => {
                     debug!("recover_stmt_ return - Eof");
-                    return;
+                    break;
                 }
                 token::Semi => {
                     self.bump();
@@ -4564,7 +4585,17 @@ fn recover_stmt_(&mut self, break_on_semi: SemiColonMode, break_on_block: BlockM
                        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 (file)
index 0000000..d2636b7
--- /dev/null
@@ -0,0 +1,13 @@
+struct Foo { bar: f64, baz: i64, bat: i64 }
+
+fn main() {
+    let _ = Foo { bar: .5, baz: 42 };
+    //~^ ERROR expected expression
+    //~| ERROR missing field `bat` in initializer of `Foo`
+    let bar = 1.5f32;
+    let _ = Foo { bar.into(), bat: -1, . };
+    //~^ ERROR expected one of
+    //~| ERROR mismatched types
+    //~| ERROR missing field `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 (file)
index 0000000..c98de6f
--- /dev/null
@@ -0,0 +1,50 @@
+error: expected expression, found `.`
+  --> $DIR/issue-52496.rs:4:24
+   |
+LL |     let _ = Foo { bar: .5, baz: 42 };
+   |             ---        ^ expected expression
+   |             |
+   |             while parsing this struct
+
+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[E0308]: mismatched types
+  --> $DIR/issue-52496.rs:8:19
+   |
+LL |     let _ = Foo { bar.into(), bat: -1, . };
+   |                   ^^^ expected f64, found f32
+help: you can cast an `f32` to `f64` in a lossless way
+   |
+LL |     let _ = Foo { bar: bar.into().into(), bat: -1, . };
+   |                   ^^^^^^^^^^^^^^^
+
+error[E0063]: missing field `baz` in initializer of `Foo`
+  --> $DIR/issue-52496.rs:8:13
+   |
+LL |     let _ = Foo { bar.into(), bat: -1, . };
+   |             ^^^ missing `baz`
+
+error: aborting due to 6 previous errors
+
+Some errors occurred: E0063, E0308.
+For more information about an error, try `rustc --explain E0063`.
index 77ed4fcea517cca12e5611905c5d5fdae09392e3..b5956ad339db8fdc6b3a7a9d3c14c2a38b5de118 100644 (file)
@@ -2,7 +2,9 @@ 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
+   |             -           ^^^^ 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
index 5642d2f45ffce7a6ba73b7330f625147cb852163..ee7560017a675b660eabd5eec3b4ec0b05098db7 100644 (file)
@@ -2,7 +2,9 @@ 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
index 914588f51e1e384bbd35f62a89ba546d6b78db14..58c40b3d96a4973af4ccf42bccd710f69345900a 100644 (file)
@@ -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`
 }
index f5dc226934ec68f1a748aab792cf1a3199aba28d..cfb1f8201475469697bf279cf7b945673ec5cfb1 100644 (file)
@@ -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`.