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:
commit
9b2f8ac29e
8 changed files with 102 additions and 15 deletions
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
29
src/test/ui/token/issue-15980.rs
Normal file
29
src/test/ui/token/issue-15980.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
26
src/test/ui/token/issue-15980.stderr
Normal file
26
src/test/ui/token/issue-15980.stderr
Normal 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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue