Auto merge of #47242 - estebank:issue-15980, r=petrochenkov

`struct` pattern parsing and diagnostic tweaks

 - Recover from struct parse error on match and point out missing match
   body.
 - Point at struct when finding non-identifier while parsing its fields.
 - Add label to "expected identifier, found {}" error.

Fix #15980.
This commit is contained in:
bors 2018-01-13 12:42:33 +00:00
commit 9b2f8ac29e
8 changed files with 102 additions and 15 deletions

View file

@ -609,14 +609,21 @@ impl<'a> Parser<'a> {
Parser::token_to_string(&self.token) Parser::token_to_string(&self.token)
} }
pub fn token_descr(&self) -> Option<&'static str> {
Some(match &self.token {
t if t.is_special_ident() => "reserved identifier",
t if t.is_used_keyword() => "keyword",
t if t.is_unused_keyword() => "reserved keyword",
_ => return None,
})
}
pub fn this_token_descr(&self) -> String { pub fn this_token_descr(&self) -> String {
let prefix = match &self.token { if let Some(prefix) = self.token_descr() {
t if t.is_special_ident() => "reserved identifier ", format!("{} `{}`", prefix, self.this_token_to_string())
t if t.is_used_keyword() => "keyword ", } else {
t if t.is_unused_keyword() => "reserved keyword ", format!("`{}`", self.this_token_to_string())
_ => "", }
};
format!("{}`{}`", prefix, self.this_token_to_string())
} }
pub fn unexpected_last<T>(&self, t: &token::Token) -> PResult<'a, T> { pub fn unexpected_last<T>(&self, t: &token::Token) -> PResult<'a, T> {
@ -752,11 +759,27 @@ impl<'a> Parser<'a> {
} }
pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> { pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> {
self.parse_ident_common(true)
}
fn parse_ident_common(&mut self, recover: bool) -> PResult<'a, ast::Ident> {
match self.token { match self.token {
token::Ident(i) => { token::Ident(i) => {
if self.token.is_reserved_ident() { if self.token.is_reserved_ident() {
self.span_err(self.span, &format!("expected identifier, found {}", let mut err = self.struct_span_err(self.span,
self.this_token_descr())); &format!("expected identifier, found {}",
self.this_token_descr()));
if let Some(token_descr) = self.token_descr() {
err.span_label(self.span, format!("expected identifier, found {}",
token_descr));
} else {
err.span_label(self.span, "expected identifier");
}
if recover {
err.emit();
} else {
return Err(err);
}
} }
self.bump(); self.bump();
Ok(i) Ok(i)
@ -767,6 +790,12 @@ impl<'a> Parser<'a> {
} else { } else {
let mut err = self.fatal(&format!("expected identifier, found `{}`", let mut err = self.fatal(&format!("expected identifier, found `{}`",
self.this_token_to_string())); self.this_token_to_string()));
if let Some(token_descr) = self.token_descr() {
err.span_label(self.span, format!("expected identifier, found {}",
token_descr));
} else {
err.span_label(self.span, "expected identifier");
}
if self.token == token::Underscore { if self.token == token::Underscore {
err.note("`_` is a wildcard pattern, not an identifier"); err.note("`_` is a wildcard pattern, not an identifier");
} }
@ -2058,7 +2087,7 @@ impl<'a> Parser<'a> {
self.bump(); self.bump();
Ok(Ident::with_empty_ctxt(name)) Ok(Ident::with_empty_ctxt(name))
} else { } else {
self.parse_ident() self.parse_ident_common(false)
} }
} }
@ -2075,7 +2104,7 @@ impl<'a> Parser<'a> {
hi = self.prev_span; hi = self.prev_span;
(fieldname, self.parse_expr()?, false) (fieldname, self.parse_expr()?, false)
} else { } else {
let fieldname = self.parse_ident()?; let fieldname = self.parse_ident_common(false)?;
hi = self.prev_span; hi = self.prev_span;
// Mimic `x: x` for the `x` field shorthand. // Mimic `x: x` for the `x` field shorthand.
@ -2426,6 +2455,7 @@ impl<'a> Parser<'a> {
fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Attribute>) fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Attribute>)
-> PResult<'a, P<Expr>> { -> PResult<'a, P<Expr>> {
let struct_sp = lo.to(self.prev_span);
self.bump(); self.bump();
let mut fields = Vec::new(); let mut fields = Vec::new();
let mut base = None; let mut base = None;
@ -2460,6 +2490,7 @@ impl<'a> Parser<'a> {
match self.parse_field() { match self.parse_field() {
Ok(f) => fields.push(f), Ok(f) => fields.push(f),
Err(mut e) => { Err(mut e) => {
e.span_label(struct_sp, "while parsing this struct");
e.emit(); e.emit();
self.recover_stmt(); self.recover_stmt();
break; break;

View file

@ -13,7 +13,7 @@
enum bird { enum bird {
pub duck, pub duck,
//~^ ERROR: expected identifier, found keyword `pub` //~^ ERROR: expected identifier, found keyword `pub`
//~^^ ERROR: expected //~| ERROR: expected
goose goose
} }

View file

@ -18,4 +18,5 @@ fn main() {
let mut _b = 0; let mut _b = 0;
let mut _ = 0; //~ ERROR expected identifier, found `_` let mut _ = 0; //~ ERROR expected identifier, found `_`
//~^ NOTE `_` is a wildcard pattern, not an identifier //~^ NOTE `_` is a wildcard pattern, not an identifier
//~| NOTE expected identifier
} }

View file

@ -2,7 +2,7 @@ error: expected identifier, found keyword `true`
--> $DIR/issue-44406.rs:18:10 --> $DIR/issue-44406.rs:18:10
| |
18 | foo!(true); //~ ERROR expected type, found keyword 18 | foo!(true); //~ ERROR expected type, found keyword
| ^^^^ | ^^^^ expected identifier, found keyword
error: expected type, found keyword `true` error: expected type, found keyword `true`
--> $DIR/issue-44406.rs:18:10 --> $DIR/issue-44406.rs:18:10

View file

@ -2,7 +2,7 @@ error: expected identifier, found `(`
--> $DIR/pub-restricted-error.rs:16:16 --> $DIR/pub-restricted-error.rs:16:16
| |
16 | pub(crate) () foo: usize, //~ ERROR expected identifier 16 | pub(crate) () foo: usize, //~ ERROR expected identifier
| ^ | ^ expected identifier
error: aborting due to previous error error: aborting due to previous error

View file

@ -2,7 +2,7 @@ error: expected identifier, found `.`
--> $DIR/pub-restricted-non-path.rs:13:6 --> $DIR/pub-restricted-non-path.rs:13:6
| |
13 | pub (.) fn afn() {} //~ ERROR expected identifier 13 | pub (.) fn afn() {} //~ ERROR expected identifier
| ^ | ^ expected identifier
error: aborting due to previous error error: aborting due to previous error

View file

@ -0,0 +1,29 @@
// Copyright 2018 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::io;
fn main(){
let x: io::IoResult<()> = Ok(());
//~^ ERROR cannot find type `IoResult` in module `io`
//~| NOTE did you mean `Result`?
match x {
Err(ref e) if e.kind == io::EndOfFile {
//~^ NOTE while parsing this struct
return
//~^ ERROR expected identifier, found keyword `return`
//~| NOTE expected identifier, found keyword
}
//~^ NOTE expected one of `.`, `=>`, `?`, or an operator here
_ => {}
//~^ ERROR expected one of `.`, `=>`, `?`, or an operator, found `_`
//~| NOTE unexpected token
}
}

View file

@ -0,0 +1,26 @@
error: expected identifier, found keyword `return`
--> $DIR/issue-15980.rs:20:13
|
18 | Err(ref e) if e.kind == io::EndOfFile {
| ------------- while parsing this struct
19 | //~^ NOTE while parsing this struct
20 | return
| ^^^^^^ expected identifier, found keyword
error: expected one of `.`, `=>`, `?`, or an operator, found `_`
--> $DIR/issue-15980.rs:25:9
|
23 | }
| - expected one of `.`, `=>`, `?`, or an operator here
24 | //~^ NOTE expected one of `.`, `=>`, `?`, or an operator here
25 | _ => {}
| ^ unexpected token
error[E0412]: cannot find type `IoResult` in module `io`
--> $DIR/issue-15980.rs:14:16
|
14 | let x: io::IoResult<()> = Ok(());
| ^^^^^^^^ did you mean `Result`?
error: aborting due to 3 previous errors