1
Fork 0

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:
Mazdak Farrokhzad 2019-07-30 05:37:32 +02:00 committed by GitHub
commit 36029878e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 259 additions and 162 deletions

View file

@ -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 {

View file

@ -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",

View 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
}
}

View file

@ -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`.