diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index c679efd41ea46..ced480f656b60 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -30,6 +30,11 @@ use std::str; pub type PResult<'a, T> = Result>; +/// Like `PResult`, but the `Err` value is a tuple whose first value is a +/// "partial result" (of some useful work we did before hitting the error), and +/// whose second value is the error. +pub type PartialPResult<'a, T> = Result)>; + #[macro_use] pub mod parser; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 726db7334824e..520d235f29914 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -52,7 +52,7 @@ use parse::{new_sub_parser_from_file, ParseSess, Directory, DirectoryOwnership}; use util::parser::{AssocOp, Fixity}; use print::pprust; use ptr::P; -use parse::PResult; +use parse::{PResult, PartialPResult}; use tokenstream::{self, Delimited, ThinTokenStream, TokenTree, TokenStream}; use symbol::{Symbol, keywords}; use util::ThinVec; @@ -3426,20 +3426,23 @@ impl<'a> Parser<'a> { } /// Parse the fields of a struct-like pattern - fn parse_pat_fields(&mut self) -> PResult<'a, (Vec>, bool)> { + fn parse_pat_fields(&mut self) + -> PartialPResult<'a, (Vec>, bool)> { let mut fields = Vec::new(); let mut etc = false; let mut first = true; + while self.token != token::CloseDelim(token::Brace) { if first { first = false; } else { - self.expect(&token::Comma)?; + self.expect(&token::Comma).map_err(|err| ((fields.clone(), etc), err))?; // accept trailing commas if self.check(&token::CloseDelim(token::Brace)) { break } } - let attrs = self.parse_outer_attributes()?; + let attrs = self.parse_outer_attributes() + .map_err(|err| ((fields.clone(), etc), err))?; let lo = self.span; let hi; @@ -3447,19 +3450,28 @@ impl<'a> Parser<'a> { self.bump(); if self.token != token::CloseDelim(token::Brace) { let token_str = self.this_token_to_string(); - return Err(self.fatal(&format!("expected `{}`, found `{}`", "}", - token_str))) + let err = self.fatal(&format!("expected `{}`, found `{}`", "}", + token_str)); + return Err(((fields, etc), err)); } etc = true; break; + } else if self.token == token::DotDotDot { + let mut err = self.fatal("expected field pattern, found `...`"); + err.span_suggestion(self.span, + "to omit remaining fields, use one fewer `.`", + "..".to_owned()); + return Err(((fields, false), err)); } // Check if a colon exists one ahead. This means we're parsing a fieldname. let (subpat, fieldname, is_shorthand) = if self.look_ahead(1, |t| t == &token::Colon) { // Parsing a pattern of the form "fieldname: pat" - let fieldname = self.parse_field_name()?; + let fieldname = self.parse_field_name() + .map_err(|err| ((fields.clone(), etc), err))?; self.bump(); - let pat = self.parse_pat()?; + let pat = self.parse_pat() + .map_err(|err| ((fields.clone(), etc), err))?; hi = pat.span; (pat, fieldname, false) } else { @@ -3468,7 +3480,8 @@ impl<'a> Parser<'a> { let boxed_span = self.span; let is_ref = self.eat_keyword(keywords::Ref); let is_mut = self.eat_keyword(keywords::Mut); - let fieldname = self.parse_ident()?; + let fieldname = self.parse_ident() + .map_err(|err| ((fields.clone(), etc), err))?; hi = self.prev_span; let bind_type = match (is_ref, is_mut) { @@ -3646,11 +3659,12 @@ impl<'a> Parser<'a> { } // Parse struct pattern self.bump(); - let (fields, etc) = self.parse_pat_fields().unwrap_or_else(|mut e| { - e.emit(); - self.recover_stmt(); - (vec![], false) - }); + let (fields, etc) = self.parse_pat_fields() + .unwrap_or_else(|((fields, etc), mut err)| { + err.emit(); + self.recover_stmt(); + (fields, etc) + }); self.bump(); pat = PatKind::Struct(path, fields, etc); } diff --git a/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.rs b/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.rs new file mode 100644 index 0000000000000..7181ba0d24de6 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.rs @@ -0,0 +1,28 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused)] + +struct PersonalityInventory { + expressivity: f32, + instrumentality: f32 +} + +impl PersonalityInventory { + fn expressivity(&self) -> f32 { + match *self { + PersonalityInventory { expressivity: exp, ... } => exp + //~^ ERROR expected field pattern, found `...` + //~| ERROR pattern does not mention field `instrumentality` [E0027] + } + } +} + +fn main() {} diff --git a/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.stderr b/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.stderr new file mode 100644 index 0000000000000..b45410b1ecc01 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.stderr @@ -0,0 +1,14 @@ +error: expected field pattern, found `...` + --> $DIR/issue-46718-expected-dotdot-found-dotdotdot.rs:21:55 + | +21 | PersonalityInventory { expressivity: exp, ... } => exp + | ^^^ help: to omit remaining fields, use one fewer `.`: `..` + +error[E0027]: pattern does not mention field `instrumentality` + --> $DIR/issue-46718-expected-dotdot-found-dotdotdot.rs:21:13 + | +21 | PersonalityInventory { expressivity: exp, ... } => exp + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `instrumentality` + +error: aborting due to 2 previous errors +