Rollup merge of #62928 - Centril:recover-parens-around-for-head, r=estebank
Syntax: Recover on `for ( $pat in $expr ) $block` Fixes #62724 by adding some recovery: ``` error: unexpected closing `)` --> $DIR/recover-for-loop-parens-around-head.rs:10:23 | LL | for ( elem in vec ) { | --------------^ | | | opening `(` | help: remove parenthesis in `for` loop: `elem in vec` ``` The last 2 commits are drive-by cleanups. r? @estebank
This commit is contained in:
commit
36029878e7
4 changed files with 259 additions and 162 deletions
|
@ -14,7 +14,7 @@ use crate::ThinVec;
|
||||||
use crate::util::parser::AssocOp;
|
use crate::util::parser::AssocOp;
|
||||||
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
|
use syntax_pos::{Span, DUMMY_SP, MultiSpan, SpanSnippetError};
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -199,6 +199,10 @@ impl<'a> Parser<'a> {
|
||||||
&self.sess.span_diagnostic
|
&self.sess.span_diagnostic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
|
||||||
|
self.sess.source_map().span_to_snippet(span)
|
||||||
|
}
|
||||||
|
|
||||||
crate fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
|
crate fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
|
||||||
let mut err = self.struct_span_err(
|
let mut err = self.struct_span_err(
|
||||||
self.token.span,
|
self.token.span,
|
||||||
|
@ -549,8 +553,10 @@ impl<'a> Parser<'a> {
|
||||||
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
|
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
|
||||||
// respan to include both operators
|
// respan to include both operators
|
||||||
let op_span = op.span.to(self.token.span);
|
let op_span = op.span.to(self.token.span);
|
||||||
let mut err = self.diagnostic().struct_span_err(op_span,
|
let mut err = self.struct_span_err(
|
||||||
"chained comparison operators require parentheses");
|
op_span,
|
||||||
|
"chained comparison operators require parentheses",
|
||||||
|
);
|
||||||
if op.node == BinOpKind::Lt &&
|
if op.node == BinOpKind::Lt &&
|
||||||
*outer_op == AssocOp::Less || // Include `<` to provide this recommendation
|
*outer_op == AssocOp::Less || // Include `<` to provide this recommendation
|
||||||
*outer_op == AssocOp::Greater // even in a case like the following:
|
*outer_op == AssocOp::Greater // even in a case like the following:
|
||||||
|
@ -717,8 +723,6 @@ impl<'a> Parser<'a> {
|
||||||
path.span = ty_span.to(self.prev_span);
|
path.span = ty_span.to(self.prev_span);
|
||||||
|
|
||||||
let ty_str = self
|
let ty_str = self
|
||||||
.sess
|
|
||||||
.source_map()
|
|
||||||
.span_to_snippet(ty_span)
|
.span_to_snippet(ty_span)
|
||||||
.unwrap_or_else(|_| pprust::ty_to_string(&ty));
|
.unwrap_or_else(|_| pprust::ty_to_string(&ty));
|
||||||
self.diagnostic()
|
self.diagnostic()
|
||||||
|
@ -889,7 +893,7 @@ impl<'a> Parser<'a> {
|
||||||
err.span_label(await_sp, "while parsing this incorrect await expression");
|
err.span_label(await_sp, "while parsing this incorrect await expression");
|
||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
let expr_str = self.sess.source_map().span_to_snippet(expr.span)
|
let expr_str = self.span_to_snippet(expr.span)
|
||||||
.unwrap_or_else(|_| pprust::expr_to_string(&expr));
|
.unwrap_or_else(|_| pprust::expr_to_string(&expr));
|
||||||
let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
|
let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
|
||||||
let sp = lo.to(expr.span);
|
let sp = lo.to(expr.span);
|
||||||
|
@ -923,6 +927,48 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recover a situation like `for ( $pat in $expr )`
|
||||||
|
/// and suggest writing `for $pat in $expr` instead.
|
||||||
|
///
|
||||||
|
/// This should be called before parsing the `$block`.
|
||||||
|
crate fn recover_parens_around_for_head(
|
||||||
|
&mut self,
|
||||||
|
pat: P<Pat>,
|
||||||
|
expr: &Expr,
|
||||||
|
begin_paren: Option<Span>,
|
||||||
|
) -> P<Pat> {
|
||||||
|
match (&self.token.kind, begin_paren) {
|
||||||
|
(token::CloseDelim(token::Paren), Some(begin_par_sp)) => {
|
||||||
|
self.bump();
|
||||||
|
|
||||||
|
let pat_str = self
|
||||||
|
// Remove the `(` from the span of the pattern:
|
||||||
|
.span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap())
|
||||||
|
.unwrap_or_else(|_| pprust::pat_to_string(&pat));
|
||||||
|
|
||||||
|
self.struct_span_err(self.prev_span, "unexpected closing `)`")
|
||||||
|
.span_label(begin_par_sp, "opening `(`")
|
||||||
|
.span_suggestion(
|
||||||
|
begin_par_sp.to(self.prev_span),
|
||||||
|
"remove parenthesis in `for` loop",
|
||||||
|
format!("{} in {}", pat_str, pprust::expr_to_string(&expr)),
|
||||||
|
// With e.g. `for (x) in y)` this would replace `(x) in y)`
|
||||||
|
// with `x) in y)` which is syntactically invalid.
|
||||||
|
// However, this is prevented before we get here.
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
|
||||||
|
// Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
|
||||||
|
pat.and_then(|pat| match pat.node {
|
||||||
|
PatKind::Paren(pat) => pat,
|
||||||
|
_ => P(pat),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => pat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
|
crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
|
||||||
self.token.is_ident() &&
|
self.token.is_ident() &&
|
||||||
if let ast::ExprKind::Path(..) = node { true } else { false } &&
|
if let ast::ExprKind::Path(..) = node { true } else { false } &&
|
||||||
|
@ -1105,17 +1151,14 @@ impl<'a> Parser<'a> {
|
||||||
crate fn check_for_for_in_in_typo(&mut self, in_span: Span) {
|
crate fn check_for_for_in_in_typo(&mut self, in_span: Span) {
|
||||||
if self.eat_keyword(kw::In) {
|
if self.eat_keyword(kw::In) {
|
||||||
// a common typo: `for _ in in bar {}`
|
// a common typo: `for _ in in bar {}`
|
||||||
let mut err = self.sess.span_diagnostic.struct_span_err(
|
self.struct_span_err(self.prev_span, "expected iterable, found keyword `in`")
|
||||||
self.prev_span,
|
.span_suggestion_short(
|
||||||
"expected iterable, found keyword `in`",
|
in_span.until(self.prev_span),
|
||||||
);
|
"remove the duplicated `in`",
|
||||||
err.span_suggestion_short(
|
String::new(),
|
||||||
in_span.until(self.prev_span),
|
Applicability::MachineApplicable,
|
||||||
"remove the duplicated `in`",
|
)
|
||||||
String::new(),
|
.emit();
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
err.emit();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1128,12 +1171,12 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
crate fn eat_incorrect_doc_comment_for_arg_type(&mut self) {
|
crate fn eat_incorrect_doc_comment_for_arg_type(&mut self) {
|
||||||
if let token::DocComment(_) = self.token.kind {
|
if let token::DocComment(_) = self.token.kind {
|
||||||
let mut err = self.diagnostic().struct_span_err(
|
self.struct_span_err(
|
||||||
self.token.span,
|
self.token.span,
|
||||||
"documentation comments cannot be applied to a function parameter's type",
|
"documentation comments cannot be applied to a function parameter's type",
|
||||||
);
|
)
|
||||||
err.span_label(self.token.span, "doc comments are not allowed here");
|
.span_label(self.token.span, "doc comments are not allowed here")
|
||||||
err.emit();
|
.emit();
|
||||||
self.bump();
|
self.bump();
|
||||||
} else if self.token == token::Pound && self.look_ahead(1, |t| {
|
} else if self.token == token::Pound && self.look_ahead(1, |t| {
|
||||||
*t == token::OpenDelim(token::Bracket)
|
*t == token::OpenDelim(token::Bracket)
|
||||||
|
@ -1145,12 +1188,12 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
let sp = lo.to(self.token.span);
|
let sp = lo.to(self.token.span);
|
||||||
self.bump();
|
self.bump();
|
||||||
let mut err = self.diagnostic().struct_span_err(
|
self.struct_span_err(
|
||||||
sp,
|
sp,
|
||||||
"attributes cannot be applied to a function parameter's type",
|
"attributes cannot be applied to a function parameter's type",
|
||||||
);
|
)
|
||||||
err.span_label(sp, "attributes are not allowed here");
|
.span_label(sp, "attributes are not allowed here")
|
||||||
err.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,18 +1249,19 @@ impl<'a> Parser<'a> {
|
||||||
self.expect(&token::Colon)?;
|
self.expect(&token::Colon)?;
|
||||||
let ty = self.parse_ty()?;
|
let ty = self.parse_ty()?;
|
||||||
|
|
||||||
let mut err = self.diagnostic().struct_span_err_with_code(
|
self.diagnostic()
|
||||||
pat.span,
|
.struct_span_err_with_code(
|
||||||
"patterns aren't allowed in methods without bodies",
|
pat.span,
|
||||||
DiagnosticId::Error("E0642".into()),
|
"patterns aren't allowed in methods without bodies",
|
||||||
);
|
DiagnosticId::Error("E0642".into()),
|
||||||
err.span_suggestion_short(
|
)
|
||||||
pat.span,
|
.span_suggestion_short(
|
||||||
"give this argument a name or use an underscore to ignore it",
|
pat.span,
|
||||||
"_".to_owned(),
|
"give this argument a name or use an underscore to ignore it",
|
||||||
Applicability::MachineApplicable,
|
"_".to_owned(),
|
||||||
);
|
Applicability::MachineApplicable,
|
||||||
err.emit();
|
)
|
||||||
|
.emit();
|
||||||
|
|
||||||
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
|
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
|
||||||
let pat = P(Pat {
|
let pat = P(Pat {
|
||||||
|
|
|
@ -2329,19 +2329,19 @@ impl<'a> Parser<'a> {
|
||||||
// This is a struct literal, but we don't can't accept them here
|
// This is a struct literal, but we don't can't accept them here
|
||||||
let expr = self.parse_struct_expr(lo, path.clone(), attrs.clone());
|
let expr = self.parse_struct_expr(lo, path.clone(), attrs.clone());
|
||||||
if let (Ok(expr), false) = (&expr, struct_allowed) {
|
if let (Ok(expr), false) = (&expr, struct_allowed) {
|
||||||
let mut err = self.diagnostic().struct_span_err(
|
self.struct_span_err(
|
||||||
expr.span,
|
expr.span,
|
||||||
"struct literals are not allowed here",
|
"struct literals are not allowed here",
|
||||||
);
|
)
|
||||||
err.multipart_suggestion(
|
.multipart_suggestion(
|
||||||
"surround the struct literal with parentheses",
|
"surround the struct literal with parentheses",
|
||||||
vec![
|
vec![
|
||||||
(lo.shrink_to_lo(), "(".to_string()),
|
(lo.shrink_to_lo(), "(".to_string()),
|
||||||
(expr.span.shrink_to_hi(), ")".to_string()),
|
(expr.span.shrink_to_hi(), ")".to_string()),
|
||||||
],
|
],
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
)
|
||||||
err.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
return Some(expr);
|
return Some(expr);
|
||||||
}
|
}
|
||||||
|
@ -2370,18 +2370,18 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.token == token::Comma {
|
if self.token == token::Comma {
|
||||||
let mut err = self.sess.span_diagnostic.mut_span_err(
|
self.struct_span_err(
|
||||||
exp_span.to(self.prev_span),
|
exp_span.to(self.prev_span),
|
||||||
"cannot use a comma after the base struct",
|
"cannot use a comma after the base struct",
|
||||||
);
|
)
|
||||||
err.span_suggestion_short(
|
.span_suggestion_short(
|
||||||
self.token.span,
|
self.token.span,
|
||||||
"remove this comma",
|
"remove this comma",
|
||||||
String::new(),
|
String::new(),
|
||||||
Applicability::MachineApplicable
|
Applicability::MachineApplicable
|
||||||
);
|
)
|
||||||
err.note("the base struct must always be the last field");
|
.note("the base struct must always be the last field")
|
||||||
err.emit();
|
.emit();
|
||||||
self.recover_stmt();
|
self.recover_stmt();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2736,15 +2736,14 @@ impl<'a> Parser<'a> {
|
||||||
let e = self.parse_prefix_expr(None);
|
let e = self.parse_prefix_expr(None);
|
||||||
let (span, e) = self.interpolated_or_expr_span(e)?;
|
let (span, e) = self.interpolated_or_expr_span(e)?;
|
||||||
let span_of_tilde = lo;
|
let span_of_tilde = lo;
|
||||||
let mut err = self.diagnostic()
|
self.struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator")
|
||||||
.struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator");
|
.span_suggestion_short(
|
||||||
err.span_suggestion_short(
|
span_of_tilde,
|
||||||
span_of_tilde,
|
"use `!` to perform bitwise negation",
|
||||||
"use `!` to perform bitwise negation",
|
"!".to_owned(),
|
||||||
"!".to_owned(),
|
Applicability::MachineApplicable
|
||||||
Applicability::MachineApplicable
|
)
|
||||||
);
|
.emit();
|
||||||
err.emit();
|
|
||||||
(lo.to(span), self.mk_unary(UnOp::Not, e))
|
(lo.to(span), self.mk_unary(UnOp::Not, e))
|
||||||
}
|
}
|
||||||
token::BinOp(token::Minus) => {
|
token::BinOp(token::Minus) => {
|
||||||
|
@ -2792,21 +2791,20 @@ impl<'a> Parser<'a> {
|
||||||
if cannot_continue_expr {
|
if cannot_continue_expr {
|
||||||
self.bump();
|
self.bump();
|
||||||
// Emit the error ...
|
// Emit the error ...
|
||||||
let mut err = self.diagnostic()
|
self.struct_span_err(
|
||||||
.struct_span_err(self.token.span,
|
self.token.span,
|
||||||
&format!("unexpected {} after identifier",
|
&format!("unexpected {} after identifier",self.this_token_descr())
|
||||||
self.this_token_descr()));
|
)
|
||||||
// span the `not` plus trailing whitespace to avoid
|
.span_suggestion_short(
|
||||||
// trailing whitespace after the `!` in our suggestion
|
// Span the `not` plus trailing whitespace to avoid
|
||||||
let to_replace = self.sess.source_map()
|
// trailing whitespace after the `!` in our suggestion
|
||||||
.span_until_non_whitespace(lo.to(self.token.span));
|
self.sess.source_map()
|
||||||
err.span_suggestion_short(
|
.span_until_non_whitespace(lo.to(self.token.span)),
|
||||||
to_replace,
|
|
||||||
"use `!` to perform logical negation",
|
"use `!` to perform logical negation",
|
||||||
"!".to_owned(),
|
"!".to_owned(),
|
||||||
Applicability::MachineApplicable
|
Applicability::MachineApplicable
|
||||||
);
|
)
|
||||||
err.emit();
|
.emit();
|
||||||
// —and recover! (just as if we were in the block
|
// —and recover! (just as if we were in the block
|
||||||
// for the `token::Not` arm)
|
// for the `token::Not` arm)
|
||||||
let e = self.parse_prefix_expr(None);
|
let e = self.parse_prefix_expr(None);
|
||||||
|
@ -2884,7 +2882,7 @@ impl<'a> Parser<'a> {
|
||||||
// We've found an expression that would be parsed as a statement, but the next
|
// We've found an expression that would be parsed as a statement, but the next
|
||||||
// token implies this should be parsed as an expression.
|
// token implies this should be parsed as an expression.
|
||||||
// For example: `if let Some(x) = x { x } else { 0 } / 2`
|
// For example: `if let Some(x) = x { x } else { 0 } / 2`
|
||||||
let mut err = self.sess.span_diagnostic.struct_span_err(self.token.span, &format!(
|
let mut err = self.struct_span_err(self.token.span, &format!(
|
||||||
"expected expression, found `{}`",
|
"expected expression, found `{}`",
|
||||||
pprust::token_to_string(&self.token),
|
pprust::token_to_string(&self.token),
|
||||||
));
|
));
|
||||||
|
@ -3072,28 +3070,29 @@ impl<'a> Parser<'a> {
|
||||||
// in AST and continue parsing.
|
// in AST and continue parsing.
|
||||||
let msg = format!("`<` is interpreted as a start of generic \
|
let msg = format!("`<` is interpreted as a start of generic \
|
||||||
arguments for `{}`, not a {}", path, op_noun);
|
arguments for `{}`, not a {}", path, op_noun);
|
||||||
let mut err =
|
|
||||||
self.sess.span_diagnostic.struct_span_err(self.token.span, &msg);
|
|
||||||
let span_after_type = parser_snapshot_after_type.token.span;
|
let span_after_type = parser_snapshot_after_type.token.span;
|
||||||
err.span_label(self.look_ahead(1, |t| t.span).to(span_after_type),
|
|
||||||
"interpreted as generic arguments");
|
|
||||||
err.span_label(self.token.span, format!("not interpreted as {}", op_noun));
|
|
||||||
|
|
||||||
let expr = mk_expr(self, P(Ty {
|
let expr = mk_expr(self, P(Ty {
|
||||||
span: path.span,
|
span: path.span,
|
||||||
node: TyKind::Path(None, path),
|
node: TyKind::Path(None, path),
|
||||||
id: ast::DUMMY_NODE_ID
|
id: ast::DUMMY_NODE_ID
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let expr_str = self.sess.source_map().span_to_snippet(expr.span)
|
let expr_str = self.span_to_snippet(expr.span)
|
||||||
.unwrap_or_else(|_| pprust::expr_to_string(&expr));
|
.unwrap_or_else(|_| pprust::expr_to_string(&expr));
|
||||||
err.span_suggestion(
|
|
||||||
expr.span,
|
self.struct_span_err(self.token.span, &msg)
|
||||||
&format!("try {} the cast value", op_verb),
|
.span_label(
|
||||||
format!("({})", expr_str),
|
self.look_ahead(1, |t| t.span).to(span_after_type),
|
||||||
Applicability::MachineApplicable
|
"interpreted as generic arguments"
|
||||||
);
|
)
|
||||||
err.emit();
|
.span_label(self.token.span, format!("not interpreted as {}", op_noun))
|
||||||
|
.span_suggestion(
|
||||||
|
expr.span,
|
||||||
|
&format!("try {} the cast value", op_verb),
|
||||||
|
format!("({})", expr_str),
|
||||||
|
Applicability::MachineApplicable
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
@ -3276,26 +3275,40 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a 'for' .. 'in' expression ('for' token already eaten)
|
/// Parse a 'for' .. 'in' expression ('for' token already eaten)
|
||||||
fn parse_for_expr(&mut self, opt_label: Option<Label>,
|
fn parse_for_expr(
|
||||||
span_lo: Span,
|
&mut self,
|
||||||
mut attrs: ThinVec<Attribute>) -> PResult<'a, P<Expr>> {
|
opt_label: Option<Label>,
|
||||||
|
span_lo: Span,
|
||||||
|
mut attrs: ThinVec<Attribute>
|
||||||
|
) -> PResult<'a, P<Expr>> {
|
||||||
// Parse: `for <src_pat> in <src_expr> <src_loop_block>`
|
// Parse: `for <src_pat> in <src_expr> <src_loop_block>`
|
||||||
|
|
||||||
|
// Record whether we are about to parse `for (`.
|
||||||
|
// This is used below for recovery in case of `for ( $stuff ) $block`
|
||||||
|
// in which case we will suggest `for $stuff $block`.
|
||||||
|
let begin_paren = match self.token.kind {
|
||||||
|
token::OpenDelim(token::Paren) => Some(self.token.span),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
let pat = self.parse_top_level_pat()?;
|
let pat = self.parse_top_level_pat()?;
|
||||||
if !self.eat_keyword(kw::In) {
|
if !self.eat_keyword(kw::In) {
|
||||||
let in_span = self.prev_span.between(self.token.span);
|
let in_span = self.prev_span.between(self.token.span);
|
||||||
let mut err = self.sess.span_diagnostic
|
self.struct_span_err(in_span, "missing `in` in `for` loop")
|
||||||
.struct_span_err(in_span, "missing `in` in `for` loop");
|
.span_suggestion_short(
|
||||||
err.span_suggestion_short(
|
in_span,
|
||||||
in_span, "try adding `in` here", " in ".into(),
|
"try adding `in` here", " in ".into(),
|
||||||
// has been misleading, at least in the past (closed Issue #48492)
|
// has been misleading, at least in the past (closed Issue #48492)
|
||||||
Applicability::MaybeIncorrect
|
Applicability::MaybeIncorrect
|
||||||
);
|
)
|
||||||
err.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
let in_span = self.prev_span;
|
let in_span = self.prev_span;
|
||||||
self.check_for_for_in_in_typo(in_span);
|
self.check_for_for_in_in_typo(in_span);
|
||||||
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
let expr = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
|
||||||
|
|
||||||
|
let pat = self.recover_parens_around_for_head(pat, &expr, begin_paren);
|
||||||
|
|
||||||
let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
|
let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?;
|
||||||
attrs.extend(iattrs);
|
attrs.extend(iattrs);
|
||||||
|
|
||||||
|
@ -3522,15 +3535,14 @@ impl<'a> Parser<'a> {
|
||||||
pats.push(self.parse_top_level_pat()?);
|
pats.push(self.parse_top_level_pat()?);
|
||||||
|
|
||||||
if self.token == token::OrOr {
|
if self.token == token::OrOr {
|
||||||
let mut err = self.struct_span_err(self.token.span,
|
self.struct_span_err(self.token.span, "unexpected token `||` after pattern")
|
||||||
"unexpected token `||` after pattern");
|
.span_suggestion(
|
||||||
err.span_suggestion(
|
self.token.span,
|
||||||
self.token.span,
|
"use a single `|` to specify multiple patterns",
|
||||||
"use a single `|` to specify multiple patterns",
|
"|".to_owned(),
|
||||||
"|".to_owned(),
|
Applicability::MachineApplicable
|
||||||
Applicability::MachineApplicable
|
)
|
||||||
);
|
.emit();
|
||||||
err.emit();
|
|
||||||
self.bump();
|
self.bump();
|
||||||
} else if self.eat(&token::BinOp(token::Or)) {
|
} else if self.eat(&token::BinOp(token::Or)) {
|
||||||
// This is a No-op. Continue the loop to parse the next
|
// This is a No-op. Continue the loop to parse the next
|
||||||
|
@ -3627,15 +3639,14 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
if self.token == token::DotDotDot { // Issue #46718
|
if self.token == token::DotDotDot { // Issue #46718
|
||||||
// Accept `...` as if it were `..` to avoid further errors
|
// Accept `...` as if it were `..` to avoid further errors
|
||||||
let mut err = self.struct_span_err(self.token.span,
|
self.struct_span_err(self.token.span, "expected field pattern, found `...`")
|
||||||
"expected field pattern, found `...`");
|
.span_suggestion(
|
||||||
err.span_suggestion(
|
self.token.span,
|
||||||
self.token.span,
|
"to omit remaining fields, use one fewer `.`",
|
||||||
"to omit remaining fields, use one fewer `.`",
|
"..".to_owned(),
|
||||||
"..".to_owned(),
|
Applicability::MachineApplicable
|
||||||
Applicability::MachineApplicable
|
)
|
||||||
);
|
.emit();
|
||||||
err.emit();
|
|
||||||
}
|
}
|
||||||
self.bump(); // `..` || `...`
|
self.bump(); // `..` || `...`
|
||||||
|
|
||||||
|
@ -3788,7 +3799,7 @@ impl<'a> Parser<'a> {
|
||||||
let seq_span = pat.span.to(self.prev_span);
|
let seq_span = pat.span.to(self.prev_span);
|
||||||
let mut err = self.struct_span_err(comma_span,
|
let mut err = self.struct_span_err(comma_span,
|
||||||
"unexpected `,` in pattern");
|
"unexpected `,` in pattern");
|
||||||
if let Ok(seq_snippet) = self.sess.source_map().span_to_snippet(seq_span) {
|
if let Ok(seq_snippet) = self.span_to_snippet(seq_span) {
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
seq_span,
|
seq_span,
|
||||||
"try adding parentheses to match on a tuple..",
|
"try adding parentheses to match on a tuple..",
|
||||||
|
@ -4137,7 +4148,7 @@ impl<'a> Parser<'a> {
|
||||||
let parser_snapshot_after_type = self.clone();
|
let parser_snapshot_after_type = self.clone();
|
||||||
mem::replace(self, parser_snapshot_before_type);
|
mem::replace(self, parser_snapshot_before_type);
|
||||||
|
|
||||||
let snippet = self.sess.source_map().span_to_snippet(pat.span).unwrap();
|
let snippet = self.span_to_snippet(pat.span).unwrap();
|
||||||
err.span_label(pat.span, format!("while parsing the type for `{}`", snippet));
|
err.span_label(pat.span, format!("while parsing the type for `{}`", snippet));
|
||||||
(Some((parser_snapshot_after_type, colon_sp, err)), None)
|
(Some((parser_snapshot_after_type, colon_sp, err)), None)
|
||||||
}
|
}
|
||||||
|
@ -4557,7 +4568,7 @@ impl<'a> Parser<'a> {
|
||||||
if self.eat(&token::Semi) {
|
if self.eat(&token::Semi) {
|
||||||
stmt_span = stmt_span.with_hi(self.prev_span.hi());
|
stmt_span = stmt_span.with_hi(self.prev_span.hi());
|
||||||
}
|
}
|
||||||
if let Ok(snippet) = self.sess.source_map().span_to_snippet(stmt_span) {
|
if let Ok(snippet) = self.span_to_snippet(stmt_span) {
|
||||||
e.span_suggestion(
|
e.span_suggestion(
|
||||||
stmt_span,
|
stmt_span,
|
||||||
"try placing this code inside a block",
|
"try placing this code inside a block",
|
||||||
|
@ -4730,7 +4741,7 @@ impl<'a> Parser<'a> {
|
||||||
lo.to(self.prev_span),
|
lo.to(self.prev_span),
|
||||||
"parenthesized lifetime bounds are not supported"
|
"parenthesized lifetime bounds are not supported"
|
||||||
);
|
);
|
||||||
if let Ok(snippet) = self.sess.source_map().span_to_snippet(inner_span) {
|
if let Ok(snippet) = self.span_to_snippet(inner_span) {
|
||||||
err.span_suggestion_short(
|
err.span_suggestion_short(
|
||||||
lo.to(self.prev_span),
|
lo.to(self.prev_span),
|
||||||
"remove the parentheses",
|
"remove the parentheses",
|
||||||
|
@ -4788,7 +4799,7 @@ impl<'a> Parser<'a> {
|
||||||
let mut new_bound_list = String::new();
|
let mut new_bound_list = String::new();
|
||||||
if !bounds.is_empty() {
|
if !bounds.is_empty() {
|
||||||
let mut snippets = bounds.iter().map(|bound| bound.span())
|
let mut snippets = bounds.iter().map(|bound| bound.span())
|
||||||
.map(|span| self.sess.source_map().span_to_snippet(span));
|
.map(|span| self.span_to_snippet(span));
|
||||||
while let Some(Ok(snippet)) = snippets.next() {
|
while let Some(Ok(snippet)) = snippets.next() {
|
||||||
new_bound_list.push_str(" + ");
|
new_bound_list.push_str(" + ");
|
||||||
new_bound_list.push_str(&snippet);
|
new_bound_list.push_str(&snippet);
|
||||||
|
@ -5853,15 +5864,16 @@ impl<'a> Parser<'a> {
|
||||||
if let token::DocComment(_) = self.token.kind {
|
if let token::DocComment(_) = self.token.kind {
|
||||||
if self.look_ahead(1,
|
if self.look_ahead(1,
|
||||||
|tok| tok == &token::CloseDelim(token::Brace)) {
|
|tok| tok == &token::CloseDelim(token::Brace)) {
|
||||||
let mut err = self.diagnostic().struct_span_err_with_code(
|
self.diagnostic().struct_span_err_with_code(
|
||||||
self.token.span,
|
self.token.span,
|
||||||
"found a documentation comment that doesn't document anything",
|
"found a documentation comment that doesn't document anything",
|
||||||
DiagnosticId::Error("E0584".into()),
|
DiagnosticId::Error("E0584".into()),
|
||||||
);
|
)
|
||||||
err.help("doc comments must come before what they document, maybe a \
|
.help(
|
||||||
|
"doc comments must come before what they document, maybe a \
|
||||||
comment was intended with `//`?",
|
comment was intended with `//`?",
|
||||||
);
|
)
|
||||||
err.emit();
|
.emit();
|
||||||
self.bump();
|
self.bump();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -6305,12 +6317,15 @@ impl<'a> Parser<'a> {
|
||||||
let sp = path.span;
|
let sp = path.span;
|
||||||
let help_msg = format!("make this visible only to module `{}` with `in`", path);
|
let help_msg = format!("make this visible only to module `{}` with `in`", path);
|
||||||
self.expect(&token::CloseDelim(token::Paren))?; // `)`
|
self.expect(&token::CloseDelim(token::Paren))?; // `)`
|
||||||
let mut err = struct_span_err!(self.sess.span_diagnostic, sp, E0704, "{}", msg);
|
struct_span_err!(self.sess.span_diagnostic, sp, E0704, "{}", msg)
|
||||||
err.help(suggestion);
|
.help(suggestion)
|
||||||
err.span_suggestion(
|
.span_suggestion(
|
||||||
sp, &help_msg, format!("in {}", path), Applicability::MachineApplicable
|
sp,
|
||||||
);
|
&help_msg,
|
||||||
err.emit(); // emit diagnostic, but continue with public visibility
|
format!("in {}", path),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
.emit(); // emit diagnostic, but continue with public visibility
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6744,14 +6759,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
ident = Ident::from_str(&fixed_name).with_span_pos(fixed_name_sp);
|
ident = Ident::from_str(&fixed_name).with_span_pos(fixed_name_sp);
|
||||||
|
|
||||||
let mut err = self.struct_span_err(fixed_name_sp, error_msg);
|
self.struct_span_err(fixed_name_sp, error_msg)
|
||||||
err.span_label(fixed_name_sp, "dash-separated idents are not valid");
|
.span_label(fixed_name_sp, "dash-separated idents are not valid")
|
||||||
err.multipart_suggestion(
|
.multipart_suggestion(suggestion_msg, replacement, Applicability::MachineApplicable)
|
||||||
suggestion_msg,
|
.emit();
|
||||||
replacement,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
err.emit();
|
|
||||||
}
|
}
|
||||||
Ok(ident)
|
Ok(ident)
|
||||||
}
|
}
|
||||||
|
@ -6906,14 +6917,14 @@ impl<'a> Parser<'a> {
|
||||||
if !self.eat(&token::Comma) {
|
if !self.eat(&token::Comma) {
|
||||||
if self.token.is_ident() && !self.token.is_reserved_ident() {
|
if self.token.is_ident() && !self.token.is_reserved_ident() {
|
||||||
let sp = self.sess.source_map().next_point(self.prev_span);
|
let sp = self.sess.source_map().next_point(self.prev_span);
|
||||||
let mut err = self.struct_span_err(sp, "missing comma");
|
self.struct_span_err(sp, "missing comma")
|
||||||
err.span_suggestion_short(
|
.span_suggestion_short(
|
||||||
sp,
|
sp,
|
||||||
"missing comma",
|
"missing comma",
|
||||||
",".to_owned(),
|
",".to_owned(),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
)
|
||||||
err.emit();
|
.emit();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -6952,15 +6963,16 @@ impl<'a> Parser<'a> {
|
||||||
Some(abi) => Ok(Some(abi)),
|
Some(abi) => Ok(Some(abi)),
|
||||||
None => {
|
None => {
|
||||||
let prev_span = self.prev_span;
|
let prev_span = self.prev_span;
|
||||||
let mut err = struct_span_err!(
|
struct_span_err!(
|
||||||
self.sess.span_diagnostic,
|
self.sess.span_diagnostic,
|
||||||
prev_span,
|
prev_span,
|
||||||
E0703,
|
E0703,
|
||||||
"invalid ABI: found `{}`",
|
"invalid ABI: found `{}`",
|
||||||
symbol);
|
symbol
|
||||||
err.span_label(prev_span, "invalid ABI");
|
)
|
||||||
err.help(&format!("valid ABIs: {}", abi::all_names().join(", ")));
|
.span_label(prev_span, "invalid ABI")
|
||||||
err.emit();
|
.help(&format!("valid ABIs: {}", abi::all_names().join(", ")))
|
||||||
|
.emit();
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7130,16 +7142,15 @@ impl<'a> Parser<'a> {
|
||||||
// CONST ITEM
|
// CONST ITEM
|
||||||
if self.eat_keyword(kw::Mut) {
|
if self.eat_keyword(kw::Mut) {
|
||||||
let prev_span = self.prev_span;
|
let prev_span = self.prev_span;
|
||||||
let mut err = self.diagnostic()
|
self.struct_span_err(prev_span, "const globals cannot be mutable")
|
||||||
.struct_span_err(prev_span, "const globals cannot be mutable");
|
.span_label(prev_span, "cannot be mutable")
|
||||||
err.span_label(prev_span, "cannot be mutable");
|
.span_suggestion(
|
||||||
err.span_suggestion(
|
const_span,
|
||||||
const_span,
|
"you might want to declare a static instead",
|
||||||
"you might want to declare a static instead",
|
"static".to_owned(),
|
||||||
"static".to_owned(),
|
Applicability::MaybeIncorrect,
|
||||||
Applicability::MaybeIncorrect,
|
)
|
||||||
);
|
.emit();
|
||||||
err.emit();
|
|
||||||
}
|
}
|
||||||
let (ident, item_, extra_attrs) = self.parse_item_const(None)?;
|
let (ident, item_, extra_attrs) = self.parse_item_const(None)?;
|
||||||
let prev_span = self.prev_span;
|
let prev_span = self.prev_span;
|
||||||
|
@ -7407,7 +7418,7 @@ impl<'a> Parser<'a> {
|
||||||
sp, &suggestion, format!(" {} ", kw), Applicability::MachineApplicable
|
sp, &suggestion, format!(" {} ", kw), Applicability::MachineApplicable
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if let Ok(snippet) = self.sess.source_map().span_to_snippet(ident_sp) {
|
if let Ok(snippet) = self.span_to_snippet(ident_sp) {
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
full_sp,
|
full_sp,
|
||||||
"if you meant to call a macro, try",
|
"if you meant to call a macro, try",
|
||||||
|
|
15
src/test/ui/parser/recover-for-loop-parens-around-head.rs
Normal file
15
src/test/ui/parser/recover-for-loop-parens-around-head.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Here we test that the parser is able to recover in a situation like
|
||||||
|
// `for ( $pat in $expr )` since that is familiar syntax in other languages.
|
||||||
|
// Instead we suggest that the user writes `for $pat in $expr`.
|
||||||
|
|
||||||
|
#![deny(unused)] // Make sure we don't trigger `unused_parens`.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let vec = vec![1, 2, 3];
|
||||||
|
|
||||||
|
for ( elem in vec ) {
|
||||||
|
//~^ ERROR expected one of `)`, `,`, or `@`, found `in`
|
||||||
|
//~| ERROR unexpected closing `)`
|
||||||
|
const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
error: expected one of `)`, `,`, or `@`, found `in`
|
||||||
|
--> $DIR/recover-for-loop-parens-around-head.rs:10:16
|
||||||
|
|
|
||||||
|
LL | for ( elem in vec ) {
|
||||||
|
| ^^ expected one of `)`, `,`, or `@` here
|
||||||
|
|
||||||
|
error: unexpected closing `)`
|
||||||
|
--> $DIR/recover-for-loop-parens-around-head.rs:10:23
|
||||||
|
|
|
||||||
|
LL | for ( elem in vec ) {
|
||||||
|
| --------------^
|
||||||
|
| |
|
||||||
|
| opening `(`
|
||||||
|
| help: remove parenthesis in `for` loop: `elem in vec`
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/recover-for-loop-parens-around-head.rs:13:38
|
||||||
|
|
|
||||||
|
LL | const RECOVERY_WITNESS: () = 0;
|
||||||
|
| ^ expected (), found integer
|
||||||
|
|
|
||||||
|
= note: expected type `()`
|
||||||
|
found type `{integer}`
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
Loading…
Add table
Add a link
Reference in a new issue