about summary refs log tree commit diff
path: root/compiler/rustc_parse/src/parser/expr.rs
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2025-09-24 21:06:54 +0000
committerEsteban Küber <esteban@kuber.com.ar>2025-09-24 21:31:23 +0000
commit43057698c12e8ceab1f49eff8a3e5a38cc05f7db (patch)
tree0570532f2d602bd1d5e7c5d7221ecff555f75c47 /compiler/rustc_parse/src/parser/expr.rs
parent15283f6fe95e5b604273d13a428bab5fc0788f5a (diff)
downloadrust-43057698c12e8ceab1f49eff8a3e5a38cc05f7db.tar.gz
rust-43057698c12e8ceab1f49eff8a3e5a38cc05f7db.zip
Tweak handling of "struct like start" where a struct isn't supported
This improves the case where someone tries to write a `match` expr where the patterns have type ascription syntax. Makes them less verbose, by giving up on the first encounter in the block, and makes them more accurate by only treating them as a struct literal if successfuly parsed as such.
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs61
1 files changed, 40 insertions, 21 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 81a5d48d94e..2f4158450d8 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3612,36 +3612,55 @@ impl<'a> Parser<'a> {
         self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1)
     }
 
-    fn is_certainly_not_a_block(&self) -> bool {
-        // `{ ident, ` and `{ ident: ` cannot start a block.
-        self.look_ahead(1, |t| t.is_ident())
-            && self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon)
-    }
-
     fn maybe_parse_struct_expr(
         &mut self,
         qself: &Option<Box<ast::QSelf>>,
         path: &ast::Path,
     ) -> Option<PResult<'a, Box<Expr>>> {
         let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
-        if struct_allowed || self.is_certainly_not_a_block() {
-            if let Err(err) = self.expect(exp!(OpenBrace)) {
-                return Some(Err(err));
+        let is_ident = self.look_ahead(1, |t| t.is_ident());
+        let is_comma = self.look_ahead(2, |t| t == &token::Comma);
+        let is_colon = self.look_ahead(2, |t| t == &token::Colon);
+        match (struct_allowed, is_ident, is_comma, is_colon) {
+            (false, true, true, _) | (false, true, _, true) => {
+                // We have something like `match foo { bar,` or `match foo { bar:`, which means the
+                // user might have meant to write a struct literal as part of the `match`
+                // discriminant.
+                let snapshot = self.create_snapshot_for_diagnostic();
+                if let Err(err) = self.expect(exp!(OpenBrace)) {
+                    return Some(Err(err));
+                }
+                match self.parse_expr_struct(qself.clone(), path.clone(), false) {
+                    Ok(expr) => {
+                        // This is a struct literal, but we don't accept them here.
+                        self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
+                            span: expr.span,
+                            sub: errors::StructLiteralNotAllowedHereSugg {
+                                left: path.span.shrink_to_lo(),
+                                right: expr.span.shrink_to_hi(),
+                            },
+                        });
+                        Some(Ok(expr))
+                    }
+                    Err(err) => {
+                        // We couldn't parse a valid struct, rollback and let the parser emit an
+                        // error elsewhere.
+                        err.cancel();
+                        self.restore_snapshot(snapshot);
+                        None
+                    }
+                }
             }
-            let expr = self.parse_expr_struct(qself.clone(), path.clone(), true);
-            if let (Ok(expr), false) = (&expr, struct_allowed) {
-                // This is a struct literal, but we don't can't accept them here.
-                self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
-                    span: expr.span,
-                    sub: errors::StructLiteralNotAllowedHereSugg {
-                        left: path.span.shrink_to_lo(),
-                        right: expr.span.shrink_to_hi(),
-                    },
-                });
+            (true, _, _, _) => {
+                // A struct is accepted here, try to parse it and rely on `parse_expr_struct` for
+                // any kind of recovery.
+                if let Err(err) = self.expect(exp!(OpenBrace)) {
+                    return Some(Err(err));
+                }
+                Some(self.parse_expr_struct(qself.clone(), path.clone(), true))
             }
-            return Some(expr);
+            (false, _, _, _) => None,
         }
-        None
     }
 
     pub(super) fn parse_struct_fields(