Skip to content

Commit ddd59bc

Browse files
authoredMar 22, 2020
Rollup merge of rust-lang#70209 - Centril:recover-quant-closure, r=petrochenkov
parser: recover on `for<'a> |...| body` closures When encountering `for` and `<` is 1 token ahead, interpret this as an explicitly quantified generic closure and recover, rather than attempting to parse a `for` loop. This provides both improved diagnostics as well as an insurance policy for the ability to use this as the syntax for generic closures in the future. As requested by r? @eddyb
2 parents 34ef6d0 + 4d30b92 commit ddd59bc

File tree

5 files changed

+66
-14
lines changed

5 files changed

+66
-14
lines changed
 

‎src/librustc_parse/parser/expr.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -925,8 +925,17 @@ impl<'a> Parser<'a> {
925925
self.parse_closure_expr(attrs)
926926
} else if self.eat_keyword(kw::If) {
927927
self.parse_if_expr(attrs)
928-
} else if self.eat_keyword(kw::For) {
929-
self.parse_for_expr(None, self.prev_token.span, attrs)
928+
} else if self.check_keyword(kw::For) {
929+
if self.choose_generics_over_qpath(1) {
930+
// NOTE(Centril, eddyb): DO NOT REMOVE! Beyond providing parser recovery,
931+
// this is an insurance policy in case we allow qpaths in (tuple-)struct patterns.
932+
// When `for <Foo as Bar>::Proj in $expr $block` is wanted,
933+
// you can disambiguate in favor of a pattern with `(...)`.
934+
self.recover_quantified_closure_expr(attrs)
935+
} else {
936+
assert!(self.eat_keyword(kw::For));
937+
self.parse_for_expr(None, self.prev_token.span, attrs)
938+
}
930939
} else if self.eat_keyword(kw::While) {
931940
self.parse_while_expr(None, self.prev_token.span, attrs)
932941
} else if let Some(label) = self.eat_label() {
@@ -1417,6 +1426,26 @@ impl<'a> Parser<'a> {
14171426
Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs))
14181427
}
14191428

1429+
/// Recover on an explicitly quantified closure expression, e.g., `for<'a> |x: &'a u8| *x + 1`.
1430+
fn recover_quantified_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
1431+
let lo = self.token.span;
1432+
let _ = self.parse_late_bound_lifetime_defs()?;
1433+
let span_for = lo.to(self.prev_token.span);
1434+
let closure = self.parse_closure_expr(attrs)?;
1435+
1436+
self.struct_span_err(span_for, "cannot introduce explicit parameters for a closure")
1437+
.span_label(closure.span, "the parameters are attached to this closure")
1438+
.span_suggestion(
1439+
span_for,
1440+
"remove the parameters",
1441+
String::new(),
1442+
Applicability::MachineApplicable,
1443+
)
1444+
.emit();
1445+
1446+
Ok(self.mk_expr_err(lo.to(closure.span)))
1447+
}
1448+
14201449
/// Parses a closure expression (e.g., `move |args| expr`).
14211450
fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
14221451
let lo = self.token.span;

‎src/librustc_parse/parser/generics.rs

+8-11
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ impl<'a> Parser<'a> {
181181
// We are considering adding generics to the `where` keyword as an alternative higher-rank
182182
// parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
183183
// change we parse those generics now, but report an error.
184-
if self.choose_generics_over_qpath() {
184+
if self.choose_generics_over_qpath(0) {
185185
let generics = self.parse_generics()?;
186186
self.struct_span_err(
187187
generics.span,
@@ -257,7 +257,7 @@ impl<'a> Parser<'a> {
257257
}
258258
}
259259

260-
pub(super) fn choose_generics_over_qpath(&self) -> bool {
260+
pub(super) fn choose_generics_over_qpath(&self, start: usize) -> bool {
261261
// There's an ambiguity between generic parameters and qualified paths in impls.
262262
// If we see `<` it may start both, so we have to inspect some following tokens.
263263
// The following combinations can only start generics,
@@ -274,15 +274,12 @@ impl<'a> Parser<'a> {
274274
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
275275
// because this is what almost always expected in practice, qualified paths in impls
276276
// (`impl <Type>::AssocTy { ... }`) aren't even allowed by type checker at the moment.
277-
self.token == token::Lt
278-
&& (self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt)
279-
|| self.look_ahead(1, |t| t.is_lifetime() || t.is_ident())
280-
&& self.look_ahead(2, |t| {
281-
t == &token::Gt
282-
|| t == &token::Comma
283-
|| t == &token::Colon
284-
|| t == &token::Eq
277+
self.look_ahead(start, |t| t == &token::Lt)
278+
&& (self.look_ahead(start + 1, |t| t == &token::Pound || t == &token::Gt)
279+
|| self.look_ahead(start + 1, |t| t.is_lifetime() || t.is_ident())
280+
&& self.look_ahead(start + 2, |t| {
281+
matches!(t.kind, token::Gt | token::Comma | token::Colon | token::Eq)
285282
})
286-
|| self.is_keyword_ahead(1, &[kw::Const]))
283+
|| self.is_keyword_ahead(start + 1, &[kw::Const]))
287284
}
288285
}

‎src/librustc_parse/parser/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ impl<'a> Parser<'a> {
458458
self.expect_keyword(kw::Impl)?;
459459

460460
// First, parse generic parameters if necessary.
461-
let mut generics = if self.choose_generics_over_qpath() {
461+
let mut generics = if self.choose_generics_over_qpath(0) {
462462
self.parse_generics()?
463463
} else {
464464
let mut generics = Generics::default();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
fn main() {
2+
for<'a> |x: &'a u8| *x + 1;
3+
//~^ ERROR cannot introduce explicit parameters for a closure
4+
}
5+
6+
enum Foo { Bar }
7+
fn foo(x: impl Iterator<Item = Foo>) {
8+
for <Foo>::Bar in x {}
9+
//~^ ERROR expected one of `move`, `static`, `|`
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: cannot introduce explicit parameters for a closure
2+
--> $DIR/recover-quantified-closure.rs:2:5
3+
|
4+
LL | for<'a> |x: &'a u8| *x + 1;
5+
| ^^^^^^^ ------------------ the parameters are attached to this closure
6+
| |
7+
| help: remove the parameters
8+
9+
error: expected one of `move`, `static`, `|`, or `||`, found `::`
10+
--> $DIR/recover-quantified-closure.rs:8:14
11+
|
12+
LL | for <Foo>::Bar in x {}
13+
| ^^ expected one of `move`, `static`, `|`, or `||`
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)