Tweak diagnostics

* Recover from invalid `'label: ` before block.
* Make suggestion to enclose statements in a block multipart.
* Point at `match`, `while`, `loop` and `unsafe` keywords when failing
  to parse their expression.
* Do not suggest `{ ; }`.
* Do not suggest `|` when very unlikely to be what was wanted (in `let`
  statements).
This commit is contained in:
Esteban Kuber 2022-01-12 20:43:24 +00:00
parent 97cde9fe08
commit f42b4f595e
26 changed files with 316 additions and 161 deletions

View File

@ -20,7 +20,7 @@ use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, PResult}; use rustc_errors::{Applicability, PResult};
use rustc_feature::Features; use rustc_feature::Features;
use rustc_parse::parser::{ use rustc_parse::parser::{
AttemptLocalParseRecovery, ForceCollect, Parser, RecoverColon, RecoverComma, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
}; };
use rustc_parse::validate_attr; use rustc_parse::validate_attr;
use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
@ -911,6 +911,7 @@ pub fn parse_ast_fragment<'a>(
None, None,
RecoverComma::No, RecoverComma::No,
RecoverColon::Yes, RecoverColon::Yes,
CommaRecoveryMode::LikelyTuple,
)?), )?),
AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?), AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?),
AstFragmentKind::Arms AstFragmentKind::Arms

View File

@ -1,8 +1,8 @@
use super::pat::Expected; use super::pat::Expected;
use super::ty::{AllowPlus, IsAsCast}; use super::ty::{AllowPlus, IsAsCast};
use super::{ use super::{
BlockMode, Parser, PathStyle, RecoverColon, RecoverComma, Restrictions, SemiColonMode, SeqSep, BlockMode, CommaRecoveryMode, Parser, PathStyle, RecoverColon, RecoverComma, Restrictions,
TokenExpectType, TokenType, SemiColonMode, SeqSep, TokenExpectType, TokenType,
}; };
use rustc_ast as ast; use rustc_ast as ast;
@ -2245,12 +2245,32 @@ impl<'a> Parser<'a> {
first_pat first_pat
} }
crate fn maybe_recover_unexpected_block_label(&mut self) -> bool {
let Some(label) = self.eat_label().filter(|_| {
self.eat(&token::Colon) && self.token.kind == token::OpenDelim(token::Brace)
}) else {
return false;
};
let span = label.ident.span.to(self.prev_token.span);
let mut err = self.struct_span_err(span, "block label not supported here");
err.span_label(span, "not supported here");
err.tool_only_span_suggestion(
label.ident.span.until(self.token.span),
"remove this block label",
String::new(),
Applicability::MachineApplicable,
);
err.emit();
true
}
/// Some special error handling for the "top-level" patterns in a match arm, /// Some special error handling for the "top-level" patterns in a match arm,
/// `for` loop, `let`, &c. (in contrast to subpatterns within such). /// `for` loop, `let`, &c. (in contrast to subpatterns within such).
crate fn maybe_recover_unexpected_comma( crate fn maybe_recover_unexpected_comma(
&mut self, &mut self,
lo: Span, lo: Span,
rc: RecoverComma, rc: RecoverComma,
rt: CommaRecoveryMode,
) -> PResult<'a, ()> { ) -> PResult<'a, ()> {
if rc == RecoverComma::No || self.token != token::Comma { if rc == RecoverComma::No || self.token != token::Comma {
return Ok(()); return Ok(());
@ -2270,20 +2290,25 @@ impl<'a> Parser<'a> {
let seq_span = lo.to(self.prev_token.span); let seq_span = lo.to(self.prev_token.span);
let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern"); let mut err = self.struct_span_err(comma_span, "unexpected `,` in pattern");
if let Ok(seq_snippet) = self.span_to_snippet(seq_span) { if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
const MSG: &str = "try adding parentheses to match on a tuple..."; err.multipart_suggestion(
&format!(
err.span_suggestion( "try adding parentheses to match on a tuple{}",
seq_span, if let CommaRecoveryMode::LikelyTuple = rt { "" } else { "..." },
MSG, ),
format!("({})", seq_snippet), vec![
Applicability::MachineApplicable, (seq_span.shrink_to_lo(), "(".to_string()),
); (seq_span.shrink_to_hi(), ")".to_string()),
err.span_suggestion( ],
seq_span,
"...or a vertical bar to match on multiple alternatives",
seq_snippet.replace(',', " |"),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
if let CommaRecoveryMode::EitherTupleOrPipe = rt {
err.span_suggestion(
seq_span,
"...or a vertical bar to match on multiple alternatives",
seq_snippet.replace(',', " |"),
Applicability::MachineApplicable,
);
}
} }
Err(err) Err(err)
} }

View File

@ -1,4 +1,4 @@
use super::pat::{RecoverColon, RecoverComma, PARAM_EXPECTED}; use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{ use super::{
AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, TokenType, AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, TokenType,
@ -1286,18 +1286,27 @@ impl<'a> Parser<'a> {
} else if let Some(label) = self.eat_label() { } else if let Some(label) = self.eat_label() {
self.parse_labeled_expr(label, attrs, true) self.parse_labeled_expr(label, attrs, true)
} else if self.eat_keyword(kw::Loop) { } else if self.eat_keyword(kw::Loop) {
self.parse_loop_expr(None, self.prev_token.span, attrs) let sp = self.prev_token.span;
self.parse_loop_expr(None, self.prev_token.span, attrs).map_err(|mut err| {
err.span_label(sp, "while parsing this `loop` expression");
err
})
} else if self.eat_keyword(kw::Continue) { } else if self.eat_keyword(kw::Continue) {
let kind = ExprKind::Continue(self.eat_label()); let kind = ExprKind::Continue(self.eat_label());
Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs)) Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs))
} else if self.eat_keyword(kw::Match) { } else if self.eat_keyword(kw::Match) {
let match_sp = self.prev_token.span; let match_sp = self.prev_token.span;
self.parse_match_expr(attrs).map_err(|mut err| { self.parse_match_expr(attrs).map_err(|mut err| {
err.span_label(match_sp, "while parsing this match expression"); err.span_label(match_sp, "while parsing this `match` expression");
err err
}) })
} else if self.eat_keyword(kw::Unsafe) { } else if self.eat_keyword(kw::Unsafe) {
let sp = self.prev_token.span;
self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs)
.map_err(|mut err| {
err.span_label(sp, "while parsing this `unsafe` expression");
err
})
} else if self.check_inline_const(0) { } else if self.check_inline_const(0) {
self.parse_const_block(lo.to(self.token.span), false) self.parse_const_block(lo.to(self.token.span), false)
} else if self.is_do_catch_block() { } else if self.is_do_catch_block() {
@ -2160,7 +2169,12 @@ impl<'a> Parser<'a> {
/// The `let` token has already been eaten. /// The `let` token has already been eaten.
fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span; let lo = self.prev_token.span;
let pat = self.parse_pat_allow_top_alt(None, RecoverComma::Yes, RecoverColon::Yes)?; let pat = self.parse_pat_allow_top_alt(
None,
RecoverComma::Yes,
RecoverColon::Yes,
CommaRecoveryMode::LikelyTuple,
)?;
self.expect(&token::Eq)?; self.expect(&token::Eq)?;
let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
@ -2223,7 +2237,12 @@ impl<'a> Parser<'a> {
_ => None, _ => None,
}; };
let pat = self.parse_pat_allow_top_alt(None, RecoverComma::Yes, RecoverColon::Yes)?; let pat = self.parse_pat_allow_top_alt(
None,
RecoverComma::Yes,
RecoverColon::Yes,
CommaRecoveryMode::LikelyTuple,
)?;
if !self.eat_keyword(kw::In) { if !self.eat_keyword(kw::In) {
self.error_missing_in_for_loop(); self.error_missing_in_for_loop();
} }
@ -2266,8 +2285,15 @@ impl<'a> Parser<'a> {
lo: Span, lo: Span,
mut attrs: AttrVec, mut attrs: AttrVec,
) -> PResult<'a, P<Expr>> { ) -> PResult<'a, P<Expr>> {
let cond = self.parse_cond_expr()?; let cond = self.parse_cond_expr().map_err(|mut err| {
let (iattrs, body) = self.parse_inner_attrs_and_block()?; err.span_label(lo, "while parsing the condition of this `while` expression");
err
})?;
let (iattrs, body) = self.parse_inner_attrs_and_block().map_err(|mut err| {
err.span_label(lo, "while parsing the body of this `while` expression");
err.span_label(cond.span, "this `while` condition successfully parsed");
err
})?;
attrs.extend(iattrs); attrs.extend(iattrs);
Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::While(cond, body, opt_label), attrs)) Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::While(cond, body, opt_label), attrs))
} }
@ -2284,7 +2310,7 @@ impl<'a> Parser<'a> {
Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::Loop(body, opt_label), attrs)) Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::Loop(body, opt_label), attrs))
} }
fn eat_label(&mut self) -> Option<Label> { crate fn eat_label(&mut self) -> Option<Label> {
self.token.lifetime().map(|ident| { self.token.lifetime().map(|ident| {
self.bump(); self.bump();
Label { ident } Label { ident }
@ -2305,7 +2331,12 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect, // speculative Applicability::MaybeIncorrect, // speculative
); );
} }
return Err(e); if self.maybe_recover_unexpected_block_label() {
e.cancel();
self.bump();
} else {
return Err(e);
}
} }
attrs.extend(self.parse_inner_attributes()?); attrs.extend(self.parse_inner_attributes()?);
@ -2441,7 +2472,12 @@ impl<'a> Parser<'a> {
let attrs = self.parse_outer_attributes()?; let attrs = self.parse_outer_attributes()?;
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let lo = this.token.span; let lo = this.token.span;
let pat = this.parse_pat_allow_top_alt(None, RecoverComma::Yes, RecoverColon::Yes)?; let pat = this.parse_pat_allow_top_alt(
None,
RecoverComma::Yes,
RecoverColon::Yes,
CommaRecoveryMode::EitherTupleOrPipe,
)?;
let guard = if this.eat_keyword(kw::If) { let guard = if this.eat_keyword(kw::If) {
let if_span = this.prev_token.span; let if_span = this.prev_token.span;
let cond = this.parse_expr()?; let cond = this.parse_expr()?;

View File

@ -15,7 +15,7 @@ pub use attr_wrapper::AttrWrapper;
pub use diagnostics::AttemptLocalParseRecovery; pub use diagnostics::AttemptLocalParseRecovery;
use diagnostics::Error; use diagnostics::Error;
pub(crate) use item::FnParseMode; pub(crate) use item::FnParseMode;
pub use pat::{RecoverColon, RecoverComma}; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
pub use path::PathStyle; pub use path::PathStyle;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;

View File

@ -5,7 +5,7 @@ use rustc_ast_pretty::pprust;
use rustc_errors::PResult; use rustc_errors::PResult;
use rustc_span::symbol::{kw, Ident}; use rustc_span::symbol::{kw, Ident};
use crate::parser::pat::{RecoverColon, RecoverComma}; use crate::parser::pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle}; use crate::parser::{FollowedByType, ForceCollect, Parser, PathStyle};
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
@ -125,7 +125,7 @@ impl<'a> Parser<'a> {
token::NtPat(self.collect_tokens_no_attrs(|this| match kind { token::NtPat(self.collect_tokens_no_attrs(|this| match kind {
NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None), NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None),
NonterminalKind::PatWithOr { .. } => { NonterminalKind::PatWithOr { .. } => {
this.parse_pat_allow_top_alt(None, RecoverComma::No, RecoverColon::No) this.parse_pat_allow_top_alt(None, RecoverComma::No, RecoverColon::No, CommaRecoveryMode::EitherTupleOrPipe)
} }
_ => unreachable!(), _ => unreachable!(),
})?) })?)

View File

@ -33,6 +33,13 @@ pub enum RecoverColon {
No, No,
} }
/// Whether or not to recover a `a, b` when parsing patterns as `(a, b)` or that *and* `a | b`.
#[derive(PartialEq, Copy, Clone)]
pub enum CommaRecoveryMode {
LikelyTuple,
EitherTupleOrPipe,
}
/// The result of `eat_or_separator`. We want to distinguish which case we are in to avoid /// The result of `eat_or_separator`. We want to distinguish which case we are in to avoid
/// emitting duplicate diagnostics. /// emitting duplicate diagnostics.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -68,8 +75,9 @@ impl<'a> Parser<'a> {
expected: Expected, expected: Expected,
rc: RecoverComma, rc: RecoverComma,
ra: RecoverColon, ra: RecoverColon,
rt: CommaRecoveryMode,
) -> PResult<'a, P<Pat>> { ) -> PResult<'a, P<Pat>> {
self.parse_pat_allow_top_alt_inner(expected, rc, ra).map(|(pat, _)| pat) self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt).map(|(pat, _)| pat)
} }
/// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true =
@ -79,6 +87,7 @@ impl<'a> Parser<'a> {
expected: Expected, expected: Expected,
rc: RecoverComma, rc: RecoverComma,
ra: RecoverColon, ra: RecoverColon,
rt: CommaRecoveryMode,
) -> PResult<'a, (P<Pat>, bool)> { ) -> PResult<'a, (P<Pat>, bool)> {
// Keep track of whether we recovered from a trailing vert so that we can avoid duplicated // Keep track of whether we recovered from a trailing vert so that we can avoid duplicated
// suggestions (which bothers rustfix). // suggestions (which bothers rustfix).
@ -92,7 +101,7 @@ impl<'a> Parser<'a> {
// Parse the first pattern (`p_0`). // Parse the first pattern (`p_0`).
let first_pat = self.parse_pat_no_top_alt(expected)?; let first_pat = self.parse_pat_no_top_alt(expected)?;
self.maybe_recover_unexpected_comma(first_pat.span, rc)?; self.maybe_recover_unexpected_comma(first_pat.span, rc, rt)?;
// If the next token is not a `|`, // If the next token is not a `|`,
// this is not an or-pattern and we should exit here. // this is not an or-pattern and we should exit here.
@ -130,7 +139,7 @@ impl<'a> Parser<'a> {
err.span_label(lo, WHILE_PARSING_OR_MSG); err.span_label(lo, WHILE_PARSING_OR_MSG);
err err
})?; })?;
self.maybe_recover_unexpected_comma(pat.span, rc)?; self.maybe_recover_unexpected_comma(pat.span, rc, rt)?;
pats.push(pat); pats.push(pat);
} }
let or_pattern_span = lo.to(self.prev_token.span); let or_pattern_span = lo.to(self.prev_token.span);
@ -155,8 +164,12 @@ impl<'a> Parser<'a> {
// We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level
// or-patterns so that we can detect when a user tries to use it. This allows us to print a // or-patterns so that we can detect when a user tries to use it. This allows us to print a
// better error message. // better error message.
let (pat, trailing_vert) = let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner(
self.parse_pat_allow_top_alt_inner(expected, rc, RecoverColon::No)?; expected,
rc,
RecoverColon::No,
CommaRecoveryMode::LikelyTuple,
)?;
let colon = self.eat(&token::Colon); let colon = self.eat(&token::Colon);
if let PatKind::Or(pats) = &pat.kind { if let PatKind::Or(pats) = &pat.kind {
@ -315,7 +328,12 @@ impl<'a> Parser<'a> {
} else if self.check(&token::OpenDelim(token::Bracket)) { } else if self.check(&token::OpenDelim(token::Bracket)) {
// Parse `[pat, pat,...]` as a slice pattern. // Parse `[pat, pat,...]` as a slice pattern.
let (pats, _) = self.parse_delim_comma_seq(token::Bracket, |p| { let (pats, _) = self.parse_delim_comma_seq(token::Bracket, |p| {
p.parse_pat_allow_top_alt(None, RecoverComma::No, RecoverColon::No) p.parse_pat_allow_top_alt(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)
})?; })?;
PatKind::Slice(pats) PatKind::Slice(pats)
} else if self.check(&token::DotDot) && !self.is_pat_range_end_start(1) { } else if self.check(&token::DotDot) && !self.is_pat_range_end_start(1) {
@ -529,7 +547,12 @@ impl<'a> Parser<'a> {
/// Parse a tuple or parenthesis pattern. /// Parse a tuple or parenthesis pattern.
fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> { fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
p.parse_pat_allow_top_alt(None, RecoverComma::No, RecoverColon::No) p.parse_pat_allow_top_alt(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::LikelyTuple,
)
})?; })?;
// Here, `(pat,)` is a tuple pattern. // Here, `(pat,)` is a tuple pattern.
@ -873,7 +896,12 @@ impl<'a> Parser<'a> {
/// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`). /// Parse tuple struct or tuple variant pattern (e.g. `Foo(...)` or `Foo::Bar(...)`).
fn parse_pat_tuple_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> { fn parse_pat_tuple_struct(&mut self, qself: Option<QSelf>, path: Path) -> PResult<'a, PatKind> {
let (fields, _) = self.parse_paren_comma_seq(|p| { let (fields, _) = self.parse_paren_comma_seq(|p| {
p.parse_pat_allow_top_alt(None, RecoverComma::No, RecoverColon::No) p.parse_pat_allow_top_alt(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)
})?; })?;
if qself.is_some() { if qself.is_some() {
self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); self.sess.gated_spans.gate(sym::more_qualified_paths, path.span);
@ -1033,7 +1061,12 @@ impl<'a> Parser<'a> {
// Parsing a pattern of the form `fieldname: pat`. // Parsing a pattern of the form `fieldname: pat`.
let fieldname = self.parse_field_name()?; let fieldname = self.parse_field_name()?;
self.bump(); self.bump();
let pat = self.parse_pat_allow_top_alt(None, RecoverComma::No, RecoverColon::No)?; let pat = self.parse_pat_allow_top_alt(
None,
RecoverComma::No,
RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe,
)?;
hi = pat.span; hi = pat.span;
(pat, fieldname, false) (pat, fieldname, false)
} else { } else {

View File

@ -434,6 +434,8 @@ impl<'a> Parser<'a> {
Ok(Some(_)) Ok(Some(_))
if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
|| do_not_suggest_help => {} || do_not_suggest_help => {}
// Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
Ok(Some(stmt)) => { Ok(Some(stmt)) => {
let stmt_own_line = self.sess.source_map().is_line_before_span_empty(sp); let stmt_own_line = self.sess.source_map().is_line_before_span_empty(sp);
let stmt_span = if stmt_own_line && self.eat(&token::Semi) { let stmt_span = if stmt_own_line && self.eat(&token::Semi) {
@ -442,15 +444,15 @@ impl<'a> Parser<'a> {
} else { } else {
stmt.span stmt.span
}; };
if let Ok(snippet) = self.span_to_snippet(stmt_span) { e.multipart_suggestion(
e.span_suggestion( "try placing this code inside a block",
stmt_span, vec![
"try placing this code inside a block", (stmt_span.shrink_to_lo(), "{ ".to_string()),
format!("{{ {} }}", snippet), (stmt_span.shrink_to_hi(), " }".to_string()),
// Speculative; has been misleading in the past (#46836). ],
Applicability::MaybeIncorrect, // Speculative; has been misleading in the past (#46836).
); Applicability::MaybeIncorrect,
} );
} }
Err(e) => { Err(e) => {
self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
@ -483,15 +485,15 @@ impl<'a> Parser<'a> {
) -> PResult<'a, (Vec<Attribute>, P<Block>)> { ) -> PResult<'a, (Vec<Attribute>, P<Block>)> {
maybe_whole!(self, NtBlock, |x| (Vec::new(), x)); maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
self.maybe_recover_unexpected_block_label();
if !self.eat(&token::OpenDelim(token::Brace)) { if !self.eat(&token::OpenDelim(token::Brace)) {
return self.error_block_no_opening_brace(); return self.error_block_no_opening_brace();
} }
let attrs = self.parse_inner_attributes()?; let attrs = self.parse_inner_attributes()?;
let tail = if let Some(tail) = self.maybe_suggest_struct_literal(lo, blk_mode) { let tail = match self.maybe_suggest_struct_literal(lo, blk_mode) {
tail? Some(tail) => tail?,
} else { None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?
}; };
Ok((attrs, tail)) Ok((attrs, tail))
} }
@ -587,11 +589,11 @@ impl<'a> Parser<'a> {
// We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover. // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
match &mut local.kind { match &mut local.kind {
LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => { LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?; self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
// We found `foo<bar, baz>`, have we fully recovered? // We found `foo<bar, baz>`, have we fully recovered?
self.expect_semi()?; self.expect_semi()?;
} }
LocalKind::Decl => return Err(e), LocalKind::Decl => return Err(e),
} }
eat_semi = false; eat_semi = false;
} }

View File

@ -132,7 +132,7 @@ error: expected one of `.`, `?`, `{`, or an operator, found `}`
LL | match await { await => () } LL | match await { await => () }
| ----- - expected one of `.`, `?`, `{`, or an operator | ----- - expected one of `.`, `?`, `{`, or an operator
| | | |
| while parsing this match expression | while parsing this `match` expression
... ...
LL | } LL | }
| ^ unexpected token | ^ unexpected token

View File

@ -28,10 +28,7 @@ error: expected `{`, found `;`
LL | if not // lack of braces is [sic] LL | if not // lack of braces is [sic]
| -- this `if` expression has a condition, but no block | -- this `if` expression has a condition, but no block
LL | println!("Then when?"); LL | println!("Then when?");
| ^ | ^ expected `{`
| |
| expected `{`
| help: try placing this code inside a block: `{ ; }`
error: unexpected `2` after identifier error: unexpected `2` after identifier
--> $DIR/issue-46836-identifier-not-instead-of-negation.rs:26:24 --> $DIR/issue-46836-identifier-not-instead-of-negation.rs:26:24

View File

@ -2,16 +2,14 @@ error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:38:17 --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:38:17
| |
LL | while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") { LL | while let b1, b2, b3 = reading_frame.next().expect("there should be a start codon") {
| ^ | ----- ^
| |
| while parsing the condition of this `while` expression
| |
help: try adding parentheses to match on a tuple... help: try adding parentheses to match on a tuple
| |
LL | while let (b1, b2, b3) = reading_frame.next().expect("there should be a start codon") { LL | while let (b1, b2, b3) = reading_frame.next().expect("there should be a start codon") {
| ~~~~~~~~~~~~ | + +
help: ...or a vertical bar to match on multiple alternatives
|
LL | while let b1 | b2 | b3 = reading_frame.next().expect("there should be a start codon") {
| ~~~~~~~~~~~~
error: unexpected `,` in pattern error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:49:14 --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:49:14
@ -19,14 +17,10 @@ error: unexpected `,` in pattern
LL | if let b1, b2, b3 = reading_frame.next().unwrap() { LL | if let b1, b2, b3 = reading_frame.next().unwrap() {
| ^ | ^
| |
help: try adding parentheses to match on a tuple... help: try adding parentheses to match on a tuple
| |
LL | if let (b1, b2, b3) = reading_frame.next().unwrap() { LL | if let (b1, b2, b3) = reading_frame.next().unwrap() {
| ~~~~~~~~~~~~ | + +
help: ...or a vertical bar to match on multiple alternatives
|
LL | if let b1 | b2 | b3 = reading_frame.next().unwrap() {
| ~~~~~~~~~~~~
error: unexpected `,` in pattern error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:59:28 --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:59:28
@ -37,7 +31,7 @@ LL | Nucleotide::Adenine, Nucleotide::Cytosine, _ => true
help: try adding parentheses to match on a tuple... help: try adding parentheses to match on a tuple...
| |
LL | (Nucleotide::Adenine, Nucleotide::Cytosine, _) => true LL | (Nucleotide::Adenine, Nucleotide::Cytosine, _) => true
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | + +
help: ...or a vertical bar to match on multiple alternatives help: ...or a vertical bar to match on multiple alternatives
| |
LL | Nucleotide::Adenine | Nucleotide::Cytosine | _ => true LL | Nucleotide::Adenine | Nucleotide::Cytosine | _ => true
@ -49,14 +43,10 @@ error: unexpected `,` in pattern
LL | for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) { LL | for x, _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
| ^ | ^
| |
help: try adding parentheses to match on a tuple... help: try adding parentheses to match on a tuple
| |
LL | for (x, _barr_body) in women.iter().map(|woman| woman.allosomes.clone()) { LL | for (x, _barr_body) in women.iter().map(|woman| woman.allosomes.clone()) {
| ~~~~~~~~~~~~~~~ | + +
help: ...or a vertical bar to match on multiple alternatives
|
LL | for x | _barr_body in women.iter().map(|woman| woman.allosomes.clone()) {
| ~~~~~~~~~~~~~~
error: unexpected `,` in pattern error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:75:10 --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:75:10
@ -64,14 +54,10 @@ error: unexpected `,` in pattern
LL | for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) { LL | for x, y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
| ^ | ^
| |
help: try adding parentheses to match on a tuple... help: try adding parentheses to match on a tuple
| |
LL | for (x, y @ Allosome::Y(_)) in men.iter().map(|man| man.allosomes.clone()) { LL | for (x, y @ Allosome::Y(_)) in men.iter().map(|man| man.allosomes.clone()) {
| ~~~~~~~~~~~~~~~~~~~~~~~ | + +
help: ...or a vertical bar to match on multiple alternatives
|
LL | for x | y @ Allosome::Y(_) in men.iter().map(|man| man.allosomes.clone()) {
| ~~~~~~~~~~~~~~~~~~~~~~
error: unexpected `,` in pattern error: unexpected `,` in pattern
--> $DIR/issue-48492-tuple-destructure-missing-parens.rs:84:14 --> $DIR/issue-48492-tuple-destructure-missing-parens.rs:84:14
@ -79,14 +65,10 @@ error: unexpected `,` in pattern
LL | let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() LL | let women, men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
| ^ | ^
| |
help: try adding parentheses to match on a tuple... help: try adding parentheses to match on a tuple
| |
LL | let (women, men): (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned() LL | let (women, men): (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
| ~~~~~~~~~~~~ | + +
help: ...or a vertical bar to match on multiple alternatives
|
LL | let women | men: (Vec<Genome>, Vec<Genome>) = genomes.iter().cloned()
| ~~~~~~~~~~~
error: aborting due to 6 previous errors error: aborting due to 6 previous errors

View File

@ -2,16 +2,18 @@ error: expected `{`, found `foo`
--> $DIR/issue-39848.rs:3:21 --> $DIR/issue-39848.rs:3:21
| |
LL | if $tgt.has_$field() {} LL | if $tgt.has_$field() {}
| -- ^^^^^^-- | -- ^^^^^^ expected `{`
| | | | |
| | expected `{`
| | help: try placing this code inside a block: `{ $field() }`
| this `if` expression has a condition, but no block | this `if` expression has a condition, but no block
... ...
LL | get_opt!(bar, foo); LL | get_opt!(bar, foo);
| ------------------ in this macro invocation | ------------------ in this macro invocation
| |
= note: this error originates in the macro `get_opt` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `get_opt` (in Nightly builds, run with -Z macro-backtrace for more info)
help: try placing this code inside a block
|
LL | if $tgt.has_{ $field() } {}
| + +
error: aborting due to previous error error: aborting due to previous error

View File

@ -0,0 +1,30 @@
// run-rustfix
#![feature(label_break_value)]
// These are forbidden occurrences of label-break-value
#[allow(unused_unsafe)]
fn labeled_unsafe() {
unsafe {} //~ ERROR block label not supported here
}
fn labeled_if() {
if true {} //~ ERROR block label not supported here
}
fn labeled_else() {
if true {} else {} //~ ERROR block label not supported here
}
fn labeled_match() {
match false { //~ ERROR block label not supported here
_ => {}
}
}
fn main() {
labeled_unsafe();
labeled_if();
labeled_else();
labeled_match();
}

View File

@ -1,21 +1,30 @@
// run-rustfix
#![feature(label_break_value)] #![feature(label_break_value)]
// These are forbidden occurrences of label-break-value // These are forbidden occurrences of label-break-value
#[allow(unused_unsafe)]
fn labeled_unsafe() { fn labeled_unsafe() {
unsafe 'b: {} //~ ERROR expected `{`, found `'b` unsafe 'b: {} //~ ERROR block label not supported here
} }
fn labeled_if() { fn labeled_if() {
if true 'b: {} //~ ERROR expected `{`, found `'b` if true 'b: {} //~ ERROR block label not supported here
} }
fn labeled_else() { fn labeled_else() {
if true {} else 'b: {} //~ ERROR expected `{`, found `'b` if true {} else 'b: {} //~ ERROR block label not supported here
} }
fn labeled_match() { fn labeled_match() {
match false 'b: {} //~ ERROR expected one of `.`, `?`, `{`, or an operator match false 'b: { //~ ERROR block label not supported here
_ => {}
}
} }
pub fn main() {} fn main() {
labeled_unsafe();
labeled_if();
labeled_else();
labeled_match();
}

View File

@ -1,38 +1,26 @@
error: expected `{`, found `'b` error: block label not supported here
--> $DIR/label_break_value_illegal_uses.rs:6:12 --> $DIR/label_break_value_illegal_uses.rs:8:12
| |
LL | unsafe 'b: {} LL | unsafe 'b: {}
| ^^---- | ^^^ not supported here
| |
| expected `{`
| help: try placing this code inside a block: `{ 'b: {} }`
error: expected `{`, found `'b` error: block label not supported here
--> $DIR/label_break_value_illegal_uses.rs:10:13 --> $DIR/label_break_value_illegal_uses.rs:12:13
| |
LL | if true 'b: {} LL | if true 'b: {}
| -- ^^---- | ^^^ not supported here
| | |
| | expected `{`
| | help: try placing this code inside a block: `{ 'b: {} }`
| this `if` expression has a condition, but no block
error: expected `{`, found `'b` error: block label not supported here
--> $DIR/label_break_value_illegal_uses.rs:14:21 --> $DIR/label_break_value_illegal_uses.rs:16:21
| |
LL | if true {} else 'b: {} LL | if true {} else 'b: {}
| ^^---- | ^^^ not supported here
| |
| expected `{`
| help: try placing this code inside a block: `{ 'b: {} }`
error: expected one of `.`, `?`, `{`, or an operator, found `'b` error: block label not supported here
--> $DIR/label_break_value_illegal_uses.rs:18:17 --> $DIR/label_break_value_illegal_uses.rs:20:17
| |
LL | match false 'b: {} LL | match false 'b: {
| ----- ^^ expected one of `.`, `?`, `{`, or an operator | ^^^ not supported here
| |
| while parsing this match expression
error: aborting due to 4 previous errors error: aborting due to 4 previous errors

View File

@ -7,10 +7,10 @@ LL | let Some(_) = Some(()) else if true {
help: try placing this code inside a block help: try placing this code inside a block
| |
LL ~ let Some(_) = Some(()) else { if true { LL ~ let Some(_) = Some(()) else { if true {
LL + LL |
LL + return; LL | return;
LL + } else { LL | } else {
LL + return; LL | return;
LL ~ } }; LL ~ } };
| |

View File

@ -12,10 +12,12 @@ error: expected `{`, found `bar`
LL | if (foo) LL | if (foo)
| -- this `if` expression has a condition, but no block | -- this `if` expression has a condition, but no block
LL | bar; LL | bar;
| ^^^- | ^^^ expected `{`
| | |
| expected `{` help: try placing this code inside a block
| help: try placing this code inside a block: `{ bar; }` |
LL | { bar; }
| + +
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View File

@ -1,29 +1,41 @@
error: expected `{`, found keyword `let` error: expected `{`, found keyword `let`
--> $DIR/block-no-opening-brace.rs:9:9 --> $DIR/block-no-opening-brace.rs:9:9
| |
LL | loop
| ---- while parsing this `loop` expression
LL | let x = 0; LL | let x = 0;
| ^^^------- | ^^^ expected `{`
| | |
| expected `{` help: try placing this code inside a block
| help: try placing this code inside a block: `{ let x = 0; }` |
LL | { let x = 0; }
| + +
error: expected `{`, found keyword `let` error: expected `{`, found keyword `let`
--> $DIR/block-no-opening-brace.rs:15:9 --> $DIR/block-no-opening-brace.rs:15:9
| |
LL | while true
| ----- ---- this `while` condition successfully parsed
| |
| while parsing the body of this `while` expression
LL | let x = 0; LL | let x = 0;
| ^^^------- | ^^^ expected `{`
| | |
| expected `{` help: try placing this code inside a block
| help: try placing this code inside a block: `{ let x = 0; }` |
LL | { let x = 0; }
| + +
error: expected `{`, found keyword `let` error: expected `{`, found keyword `let`
--> $DIR/block-no-opening-brace.rs:20:9 --> $DIR/block-no-opening-brace.rs:20:9
| |
LL | let x = 0; LL | let x = 0;
| ^^^------- | ^^^ expected `{`
| | |
| expected `{` help: try placing this code inside a block
| help: try placing this code inside a block: `{ let x = 0; }` |
LL | { let x = 0; }
| + +
error: expected expression, found reserved keyword `try` error: expected expression, found reserved keyword `try`
--> $DIR/block-no-opening-brace.rs:24:5 --> $DIR/block-no-opening-brace.rs:24:5

View File

@ -2,10 +2,12 @@ error: expected `{`, found `22`
--> $DIR/closure-return-syntax.rs:5:23 --> $DIR/closure-return-syntax.rs:5:23
| |
LL | let x = || -> i32 22; LL | let x = || -> i32 22;
| ^^ | ^^ expected `{`
| | |
| expected `{` help: try placing this code inside a block
| help: try placing this code inside a block: `{ 22 }` |
LL | let x = || -> i32 { 22 };
| + +
error: aborting due to previous error error: aborting due to previous error

View File

@ -63,9 +63,8 @@ LL | fn foo(u: u8) { if u8 macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s
| |
help: try placing this code inside a block help: try placing this code inside a block
| |
LL ~ fn foo(u: u8) { if u8 { macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { LL | fn foo(u: u8) { if u8 { macro_rules! u8 { (u6) => { fn uuuuuuuuuuu() { use s loo mod u8 { }
LL + } | + +
|
error: aborting due to 6 previous errors error: aborting due to 6 previous errors

View File

@ -50,7 +50,7 @@ error: expected one of `.`, `?`, `{`, or an operator, found `}`
--> $DIR/issue-62973.rs:8:2 --> $DIR/issue-62973.rs:8:2
| |
LL | fn p() { match s { v, E { [) {) } LL | fn p() { match s { v, E { [) {) }
| ----- while parsing this match expression | ----- while parsing this `match` expression
LL | LL |
LL | LL |
| ^ expected one of `.`, `?`, `{`, or an operator | ^ expected one of `.`, `?`, `{`, or an operator

View File

@ -2,7 +2,7 @@
fn main() { fn main() {
let foo = let foo =
//~ NOTE while parsing this match expression //~ NOTE while parsing this `match` expression
Some(4).unwrap_or(5) Some(4).unwrap_or(5)
//~^ NOTE expected one of `.`, `?`, `{`, or an operator //~^ NOTE expected one of `.`, `?`, `{`, or an operator
; //~ NOTE unexpected token ; //~ NOTE unexpected token

View File

@ -2,7 +2,7 @@
fn main() { fn main() {
let foo = let foo =
match //~ NOTE while parsing this match expression match //~ NOTE while parsing this `match` expression
Some(4).unwrap_or(5) Some(4).unwrap_or(5)
//~^ NOTE expected one of `.`, `?`, `{`, or an operator //~^ NOTE expected one of `.`, `?`, `{`, or an operator
; //~ NOTE unexpected token ; //~ NOTE unexpected token

View File

@ -4,7 +4,7 @@ error: expected one of `.`, `?`, `{`, or an operator, found `;`
LL | match LL | match
| ----- | -----
| | | |
| while parsing this match expression | while parsing this `match` expression
| help: try removing this `match` | help: try removing this `match`
LL | Some(4).unwrap_or(5) LL | Some(4).unwrap_or(5)
| - expected one of `.`, `?`, `{`, or an operator | - expected one of `.`, `?`, `{`, or an operator

View File

@ -0,0 +1,13 @@
fn main() {
let container = vec![Some(1), Some(2), None];
let mut i = 0;
while if let Some(thing) = container.get(i) {
//~^ NOTE while parsing the body of this `while` expression
//~| NOTE this `while` condition successfully parsed
println!("{:?}", thing);
i += 1;
}
}
//~^ ERROR expected `{`, found `}`
//~| NOTE expected `{`

View File

@ -0,0 +1,18 @@
error: expected `{`, found `}`
--> $DIR/while-if-let-without-body.rs:11:1
|
LL | while if let Some(thing) = container.get(i) {
| _____-----_-
| | |
| | while parsing the body of this `while` expression
LL | |
LL | |
LL | | println!("{:?}", thing);
LL | | i += 1;
LL | | }
| |_____- this `while` condition successfully parsed
LL | }
| ^ expected `{`
error: aborting due to previous error

View File

@ -1,11 +1,15 @@
error: expected `{`, found `std` error: expected `{`, found `std`
--> $DIR/unsafe-block-without-braces.rs:3:9 --> $DIR/unsafe-block-without-braces.rs:3:9
| |
LL | unsafe //{
| ------ while parsing this `unsafe` expression
LL | std::mem::transmute::<f32, u32>(1.0); LL | std::mem::transmute::<f32, u32>(1.0);
| ^^^---------------------------------- | ^^^ expected `{`
| | |
| expected `{` help: try placing this code inside a block
| help: try placing this code inside a block: `{ std::mem::transmute::<f32, u32>(1.0); }` |
LL | { std::mem::transmute::<f32, u32>(1.0); }
| + +
error: aborting due to previous error error: aborting due to previous error