From 8ec8e564a06d81623c05004aee43ab1e4f8c8aa1 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Wed, 13 Dec 2017 18:25:29 -0800 Subject: [PATCH 1/2] suggest `..` when `...` is erroneously used in struct-like field pattern This resolves #46718. --- src/libsyntax/parse/parser.rs | 6 ++++ ...e-46718-expected-dotdot-found-dotdotdot.rs | 30 +++++++++++++++++++ ...718-expected-dotdot-found-dotdotdot.stderr | 26 ++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.rs create mode 100644 src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.stderr diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 726db7334824e..371165787c695 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3452,6 +3452,12 @@ impl<'a> Parser<'a> { } 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(err); } // Check if a colon exists one ahead. This means we're parsing a fieldname. 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..65e74cd031977 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.rs @@ -0,0 +1,30 @@ +// 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 cannot find value `exp` in this scope [E0425] + //~| ERROR pattern does not mention field `expressivity` [E0027] + //~| 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..4448eccb934b7 --- /dev/null +++ b/src/test/ui/did_you_mean/issue-46718-expected-dotdot-found-dotdotdot.stderr @@ -0,0 +1,26 @@ +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[E0425]: cannot find value `exp` in this scope + --> $DIR/issue-46718-expected-dotdot-found-dotdotdot.rs:21:64 + | +21 | PersonalityInventory { expressivity: exp, ... } => exp + | ^^^ not found in this scope + +error[E0027]: pattern does not mention field `expressivity` + --> $DIR/issue-46718-expected-dotdot-found-dotdotdot.rs:21:13 + | +21 | PersonalityInventory { expressivity: exp, ... } => exp + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `expressivity` + +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 4 previous errors + From d1e4ac02baf98b8940c031459321f1bfaa966e08 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Wed, 13 Dec 2017 20:24:46 -0800 Subject: [PATCH 2/2] don't throw away already-parsed field patterns on subsequent error Previously, if an error occured while parsing field patterns, we would just return the error, discarding any field patterns that did successfully parse, which sometimes resulted in spurious diagnostics. At the cost of a little boilerplate (the repetitious `.map_err(|err| ((fields.clone(), etc), err))` is annoying and the present author is sorry to say she could not do better), we can pack the successfully-parsed field patterns into the error and use them. This was inspired by #46718. --- src/libsyntax/parse/mod.rs | 5 +++ src/libsyntax/parse/parser.rs | 38 +++++++++++-------- ...e-46718-expected-dotdot-found-dotdotdot.rs | 2 - ...718-expected-dotdot-found-dotdotdot.stderr | 14 +------ 4 files changed, 29 insertions(+), 30 deletions(-) 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 371165787c695..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,8 +3450,9 @@ 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; @@ -3457,15 +3461,17 @@ impl<'a> Parser<'a> { err.span_suggestion(self.span, "to omit remaining fields, use one fewer `.`", "..".to_owned()); - return Err(err); + 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 { @@ -3474,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) { @@ -3652,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 index 65e74cd031977..7181ba0d24de6 100644 --- 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 @@ -20,8 +20,6 @@ impl PersonalityInventory { match *self { PersonalityInventory { expressivity: exp, ... } => exp //~^ ERROR expected field pattern, found `...` - //~| ERROR cannot find value `exp` in this scope [E0425] - //~| ERROR pattern does not mention field `expressivity` [E0027] //~| ERROR pattern does not mention field `instrumentality` [E0027] } } 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 index 4448eccb934b7..b45410b1ecc01 100644 --- 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 @@ -4,23 +4,11 @@ error: expected field pattern, found `...` 21 | PersonalityInventory { expressivity: exp, ... } => exp | ^^^ help: to omit remaining fields, use one fewer `.`: `..` -error[E0425]: cannot find value `exp` in this scope - --> $DIR/issue-46718-expected-dotdot-found-dotdotdot.rs:21:64 - | -21 | PersonalityInventory { expressivity: exp, ... } => exp - | ^^^ not found in this scope - -error[E0027]: pattern does not mention field `expressivity` - --> $DIR/issue-46718-expected-dotdot-found-dotdotdot.rs:21:13 - | -21 | PersonalityInventory { expressivity: exp, ... } => exp - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `expressivity` - 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 4 previous errors +error: aborting due to 2 previous errors