Skip to content

Commit abbab2d

Browse files
Rollup merge of #157917 - onehr:fix-for-loop-missing-in-suggestion-103561, r=folkertdev
Don't suggest adding `in` to a `for` loop that already has one Closes #103561. When a `for` loop is missing its `in`, the parser suggested inserting one based only on the token following the pattern. For a malformed binding such as `for i i in 0..10` it therefore suggested `for i in i in 0..10`, which is itself invalid. The suggestion is now only emitted when the loop header does not already contain an `in` before the body. The `for x EXPR` / `for x of EXPR` / `for x = EXPR` suggestions are unchanged.
2 parents a42e95d + 0ab14ca commit abbab2d

4 files changed

Lines changed: 52 additions & 6 deletions

File tree

compiler/rustc_parse/src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,7 @@ pub(crate) struct MissingInInForLoop {
689689
#[primary_span]
690690
pub span: Span,
691691
#[subdiagnostic]
692-
pub sub: MissingInInForLoopSub,
692+
pub sub: Option<MissingInInForLoopSub>,
693693
}
694694

695695
#[derive(Subdiagnostic)]

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3086,18 +3086,41 @@ impl<'a> Parser<'a> {
30863086
}
30873087

30883088
fn error_missing_in_for_loop(&mut self) {
3089-
let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) {
3089+
let (span, sub) = if self.token.is_ident_named(sym::of) {
30903090
// Possibly using JS syntax (#75311).
30913091
let span = self.token.span;
30923092
self.bump();
3093-
(span, errors::MissingInInForLoopSub::InNotOf)
3093+
(span, Some(errors::MissingInInForLoopSub::InNotOf(span)))
30943094
} else if self.eat(exp!(Eq)) {
3095-
(self.prev_token.span, errors::MissingInInForLoopSub::InNotEq)
3095+
let span = self.prev_token.span;
3096+
(span, Some(errors::MissingInInForLoopSub::InNotEq(span)))
30963097
} else {
3097-
(self.prev_token.span.between(self.token.span), errors::MissingInInForLoopSub::AddIn)
3098+
let span = self.prev_token.span.between(self.token.span);
3099+
let sub = (!self.for_loop_head_has_in())
3100+
.then_some(errors::MissingInInForLoopSub::AddIn(span));
3101+
(span, sub)
30983102
};
30993103

3100-
self.dcx().emit_err(errors::MissingInInForLoop { span, sub: sub(span) });
3104+
self.dcx().emit_err(errors::MissingInInForLoop { span, sub });
3105+
}
3106+
3107+
/// Whether the `for` loop header already contains an `in` before its body.
3108+
/// If it does, the binding is malformed (e.g. `for i i in 0..10`) rather
3109+
/// than missing `in`, so suggesting another `in` would just be invalid too.
3110+
fn for_loop_head_has_in(&self) -> bool {
3111+
let mut dist = 0;
3112+
loop {
3113+
let (is_in, is_end) = self.look_ahead(dist, |t| {
3114+
(t.is_keyword(kw::In), matches!(t.kind, token::OpenBrace | token::Eof))
3115+
});
3116+
if is_in {
3117+
return true;
3118+
}
3119+
if is_end {
3120+
return false;
3121+
}
3122+
dist += 1;
3123+
}
31013124
}
31023125

31033126
/// Parses a `while` or `while let` expression (`while` token already eaten).
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Regression test for https://github.com/rust-lang/rust/issues/103561: when the
2+
// `for` loop header already contains an `in`, a missing `in` is not the problem,
3+
// so we must not suggest inserting another one (which would not compile).
4+
5+
fn main() {
6+
for i i in 0..10 {}
7+
//~^ ERROR missing `in` in `for` loop
8+
//~| ERROR expected `{`, found keyword `in`
9+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: missing `in` in `for` loop
2+
--> $DIR/for-loop-no-spurious-in-suggestion.rs:6:10
3+
|
4+
LL | for i i in 0..10 {}
5+
| ^
6+
7+
error: expected `{`, found keyword `in`
8+
--> $DIR/for-loop-no-spurious-in-suggestion.rs:6:13
9+
|
10+
LL | for i i in 0..10 {}
11+
| ^^ expected `{`
12+
13+
error: aborting due to 2 previous errors
14+

0 commit comments

Comments
 (0)