about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser/expr.rs
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2023-03-01 23:40:19 +0530
committerGitHub <noreply@github.com>2023-03-01 23:40:19 +0530
commit27fa7b5e6d2eaeba5daf6354df9de0d03cdf795c (patch)
tree5ed9ee91dfd808cf79037ba5f994114eda7af339 /compiler/rustc_parse/src/parser/expr.rs
parent38461f8b8aa3979f5008005c049cd84c4e216ace (diff)
parent0758c05c9792700dbc482eda8cd464c39d5ebed5 (diff)
downloadrust-27fa7b5e6d2eaeba5daf6354df9de0d03cdf795c.tar.gz
rust-27fa7b5e6d2eaeba5daf6354df9de0d03cdf795c.zip
Rollup merge of #108427 - y21:for-else-diagnostic, r=compiler-errors
Recover from for-else and while-else

This recovers from attempts at writing for-else or while-else loops, which might help users coming from e.g. Python.
```rs
for _ in 0..0 {
  // ...
} else {
  // ...
}
```
Combined with trying to store it in a let binding, the current diagnostic can be a bit confusing. It mentions let-else and suggests wrapping the loop in parentheses, which the user probably doesn't want. let-else doesn't make sense for `for` and `while` loops, as they are of type `()` (which already is an irrefutable pattern and doesn't need let-else).
<details>
<summary>Current diagnostic</summary>

```rs
error: right curly brace `}` before `else` in a `let...else` statement not allowed
 --> src/main.rs:4:5
  |
4 |     } else {
  |     ^
  |
help: wrap the expression in parentheses
  |
2 ~     let _x = (for _ in 0..0 {
3 |
4 ~     }) else {
  |
```
</details>

Some questions:
- Can the wording for the error message be improved? Would "for...else loops are not allowed" fit better?
- Should we be more "conservative" in case we want to support this in the future (i.e. say "for...else loops are **currently** not allowed/supported")?
- Is there a better way than storing a `&'static str` for the loop type? It is used for substituting the placeholder in the locale file (since it can emit either `for...else` or `while...else`). Maybe there is an enum I could use that I couldn't find
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs22
1 files changed, 22 insertions, 0 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 24d4c17f5d8..b2951e7a184 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2503,9 +2503,27 @@ impl<'a> Parser<'a> {
         let (attrs, loop_block) = self.parse_inner_attrs_and_block()?;
 
         let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label);
+
+        self.recover_loop_else("for", lo)?;
+
         Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
     }
 
+    /// Recovers from an `else` clause after a loop (`for...else`, `while...else`)
+    fn recover_loop_else(&mut self, loop_kind: &'static str, loop_kw: Span) -> PResult<'a, ()> {
+        if self.token.is_keyword(kw::Else) && self.may_recover() {
+            let else_span = self.token.span;
+            self.bump();
+            let else_clause = self.parse_expr_else()?;
+            self.sess.emit_err(errors::LoopElseNotSupported {
+                span: else_span.to(else_clause.span),
+                loop_kind,
+                loop_kw,
+            });
+        }
+        Ok(())
+    }
+
     fn error_missing_in_for_loop(&mut self) {
         let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) {
             // Possibly using JS syntax (#75311).
@@ -2530,6 +2548,9 @@ impl<'a> Parser<'a> {
             err.span_label(cond.span, "this `while` condition successfully parsed");
             err
         })?;
+
+        self.recover_loop_else("while", lo)?;
+
         Ok(self.mk_expr_with_attrs(
             lo.to(self.prev_token.span),
             ExprKind::While(cond, body, opt_label),
@@ -2541,6 +2562,7 @@ impl<'a> Parser<'a> {
     fn parse_expr_loop(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
         let loop_span = self.prev_token.span;
         let (attrs, body) = self.parse_inner_attrs_and_block()?;
+        self.recover_loop_else("loop", lo)?;
         Ok(self.mk_expr_with_attrs(
             lo.to(self.prev_token.span),
             ExprKind::Loop(body, opt_label, loop_span),