Skip to content

Commit 04ec35b

Browse files
authored
Rollup merge of rust-lang#62928 - Centril:recover-parens-around-for-head, r=estebank
Syntax: Recover on `for ( $pat in $expr ) $block` Fixes rust-lang#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
2 parents 600c8f6 + 56b39fb commit 04ec35b

File tree

4 files changed

+259
-162
lines changed

4 files changed

+259
-162
lines changed

src/libsyntax/parse/diagnostics.rs

+81-37
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::ThinVec;
1414
use crate::util::parser::AssocOp;
1515
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
1616
use rustc_data_structures::fx::FxHashSet;
17-
use syntax_pos::{Span, DUMMY_SP, MultiSpan};
17+
use syntax_pos::{Span, DUMMY_SP, MultiSpan, SpanSnippetError};
1818
use log::{debug, trace};
1919
use std::mem;
2020

@@ -199,6 +199,10 @@ impl<'a> Parser<'a> {
199199
&self.sess.span_diagnostic
200200
}
201201

202+
crate fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
203+
self.sess.source_map().span_to_snippet(span)
204+
}
205+
202206
crate fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
203207
let mut err = self.struct_span_err(
204208
self.token.span,
@@ -549,8 +553,10 @@ impl<'a> Parser<'a> {
549553
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
550554
// respan to include both operators
551555
let op_span = op.span.to(self.token.span);
552-
let mut err = self.diagnostic().struct_span_err(op_span,
553-
"chained comparison operators require parentheses");
556+
let mut err = self.struct_span_err(
557+
op_span,
558+
"chained comparison operators require parentheses",
559+
);
554560
if op.node == BinOpKind::Lt &&
555561
*outer_op == AssocOp::Less || // Include `<` to provide this recommendation
556562
*outer_op == AssocOp::Greater // even in a case like the following:
@@ -717,8 +723,6 @@ impl<'a> Parser<'a> {
717723
path.span = ty_span.to(self.prev_span);
718724

719725
let ty_str = self
720-
.sess
721-
.source_map()
722726
.span_to_snippet(ty_span)
723727
.unwrap_or_else(|_| pprust::ty_to_string(&ty));
724728
self.diagnostic()
@@ -889,7 +893,7 @@ impl<'a> Parser<'a> {
889893
err.span_label(await_sp, "while parsing this incorrect await expression");
890894
err
891895
})?;
892-
let expr_str = self.sess.source_map().span_to_snippet(expr.span)
896+
let expr_str = self.span_to_snippet(expr.span)
893897
.unwrap_or_else(|_| pprust::expr_to_string(&expr));
894898
let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" });
895899
let sp = lo.to(expr.span);
@@ -923,6 +927,48 @@ impl<'a> Parser<'a> {
923927
}
924928
}
925929

930+
/// Recover a situation like `for ( $pat in $expr )`
931+
/// and suggest writing `for $pat in $expr` instead.
932+
///
933+
/// This should be called before parsing the `$block`.
934+
crate fn recover_parens_around_for_head(
935+
&mut self,
936+
pat: P<Pat>,
937+
expr: &Expr,
938+
begin_paren: Option<Span>,
939+
) -> P<Pat> {
940+
match (&self.token.kind, begin_paren) {
941+
(token::CloseDelim(token::Paren), Some(begin_par_sp)) => {
942+
self.bump();
943+
944+
let pat_str = self
945+
// Remove the `(` from the span of the pattern:
946+
.span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap())
947+
.unwrap_or_else(|_| pprust::pat_to_string(&pat));
948+
949+
self.struct_span_err(self.prev_span, "unexpected closing `)`")
950+
.span_label(begin_par_sp, "opening `(`")
951+
.span_suggestion(
952+
begin_par_sp.to(self.prev_span),
953+
"remove parenthesis in `for` loop",
954+
format!("{} in {}", pat_str, pprust::expr_to_string(&expr)),
955+
// With e.g. `for (x) in y)` this would replace `(x) in y)`
956+
// with `x) in y)` which is syntactically invalid.
957+
// However, this is prevented before we get here.
958+
Applicability::MachineApplicable,
959+
)
960+
.emit();
961+
962+
// Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint.
963+
pat.and_then(|pat| match pat.node {
964+
PatKind::Paren(pat) => pat,
965+
_ => P(pat),
966+
})
967+
}
968+
_ => pat,
969+
}
970+
}
971+
926972
crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool {
927973
self.token.is_ident() &&
928974
if let ast::ExprKind::Path(..) = node { true } else { false } &&
@@ -1105,17 +1151,14 @@ impl<'a> Parser<'a> {
11051151
crate fn check_for_for_in_in_typo(&mut self, in_span: Span) {
11061152
if self.eat_keyword(kw::In) {
11071153
// a common typo: `for _ in in bar {}`
1108-
let mut err = self.sess.span_diagnostic.struct_span_err(
1109-
self.prev_span,
1110-
"expected iterable, found keyword `in`",
1111-
);
1112-
err.span_suggestion_short(
1113-
in_span.until(self.prev_span),
1114-
"remove the duplicated `in`",
1115-
String::new(),
1116-
Applicability::MachineApplicable,
1117-
);
1118-
err.emit();
1154+
self.struct_span_err(self.prev_span, "expected iterable, found keyword `in`")
1155+
.span_suggestion_short(
1156+
in_span.until(self.prev_span),
1157+
"remove the duplicated `in`",
1158+
String::new(),
1159+
Applicability::MachineApplicable,
1160+
)
1161+
.emit();
11191162
}
11201163
}
11211164

@@ -1128,12 +1171,12 @@ impl<'a> Parser<'a> {
11281171

11291172
crate fn eat_incorrect_doc_comment_for_arg_type(&mut self) {
11301173
if let token::DocComment(_) = self.token.kind {
1131-
let mut err = self.diagnostic().struct_span_err(
1174+
self.struct_span_err(
11321175
self.token.span,
11331176
"documentation comments cannot be applied to a function parameter's type",
1134-
);
1135-
err.span_label(self.token.span, "doc comments are not allowed here");
1136-
err.emit();
1177+
)
1178+
.span_label(self.token.span, "doc comments are not allowed here")
1179+
.emit();
11371180
self.bump();
11381181
} else if self.token == token::Pound && self.look_ahead(1, |t| {
11391182
*t == token::OpenDelim(token::Bracket)
@@ -1145,12 +1188,12 @@ impl<'a> Parser<'a> {
11451188
}
11461189
let sp = lo.to(self.token.span);
11471190
self.bump();
1148-
let mut err = self.diagnostic().struct_span_err(
1191+
self.struct_span_err(
11491192
sp,
11501193
"attributes cannot be applied to a function parameter's type",
1151-
);
1152-
err.span_label(sp, "attributes are not allowed here");
1153-
err.emit();
1194+
)
1195+
.span_label(sp, "attributes are not allowed here")
1196+
.emit();
11541197
}
11551198
}
11561199

@@ -1206,18 +1249,19 @@ impl<'a> Parser<'a> {
12061249
self.expect(&token::Colon)?;
12071250
let ty = self.parse_ty()?;
12081251

1209-
let mut err = self.diagnostic().struct_span_err_with_code(
1210-
pat.span,
1211-
"patterns aren't allowed in methods without bodies",
1212-
DiagnosticId::Error("E0642".into()),
1213-
);
1214-
err.span_suggestion_short(
1215-
pat.span,
1216-
"give this argument a name or use an underscore to ignore it",
1217-
"_".to_owned(),
1218-
Applicability::MachineApplicable,
1219-
);
1220-
err.emit();
1252+
self.diagnostic()
1253+
.struct_span_err_with_code(
1254+
pat.span,
1255+
"patterns aren't allowed in methods without bodies",
1256+
DiagnosticId::Error("E0642".into()),
1257+
)
1258+
.span_suggestion_short(
1259+
pat.span,
1260+
"give this argument a name or use an underscore to ignore it",
1261+
"_".to_owned(),
1262+
Applicability::MachineApplicable,
1263+
)
1264+
.emit();
12211265

12221266
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
12231267
let pat = P(Pat {

0 commit comments

Comments
 (0)