about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--crates/hir_def/src/body/lower.rs17
-rw-r--r--crates/hir_def/src/body/scope.rs21
-rw-r--r--crates/hir_def/src/expr.rs9
-rw-r--r--crates/hir_ty/src/infer/expr.rs5
-rw-r--r--crates/ide_assists/src/handlers/move_guard.rs4
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs5
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs3
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0118_match_guard.rast48
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0118_match_guard.rs1
10 files changed, 90 insertions, 27 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 61180fcb9a8..a5f46ff4014 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1777,9 +1777,9 @@ dependencies = [
 
 [[package]]
 name = "ungrammar"
-version = "1.14.2"
+version = "1.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5334230a6ae9ca52bc811c968c0ae12f1750c0698ed52ea68dabab7ce5a80972"
+checksum = "0ef2f5093f958b9aecad9d80a01c49aece7a7bd15ad407e1986258fa36229910"
 
 [[package]]
 name = "unicase"
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index f6e6cdbe2e3..4f912136739 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -28,8 +28,8 @@ use crate::{
     db::DefDatabase,
     expr::{
         dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
-        LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
-        Statement,
+        LabelId, Literal, LogicOp, MatchArm, MatchGuard, Ordering, Pat, PatId, RecordFieldPat,
+        RecordLitField, Statement,
     },
     intern::Interned,
     item_scope::BuiltinShadowMode,
@@ -361,10 +361,15 @@ impl ExprCollector<'_> {
                             self.check_cfg(&arm).map(|()| MatchArm {
                                 pat: self.collect_pat_opt(arm.pat()),
                                 expr: self.collect_expr_opt(arm.expr()),
-                                guard: arm
-                                    .guard()
-                                    .and_then(|guard| guard.expr())
-                                    .map(|e| self.collect_expr(e)),
+                                guard: arm.guard().map(|guard| match guard.pat() {
+                                    Some(pat) => MatchGuard::IfLet {
+                                        pat: self.collect_pat(pat),
+                                        expr: self.collect_expr_opt(guard.expr()),
+                                    },
+                                    None => {
+                                        MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) }
+                                    }
+                                }),
                             })
                         })
                         .collect()
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 58a1fc81cba..5d9dd73a120 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
 use crate::{
     body::Body,
     db::DefDatabase,
-    expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
+    expr::{Expr, ExprId, LabelId, MatchGuard, Pat, PatId, Statement},
     BlockId, DefWithBodyId,
 };
 
@@ -204,12 +204,21 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
         Expr::Match { expr, arms } => {
             compute_expr_scopes(*expr, body, scopes, scope);
             for arm in arms {
-                let scope = scopes.new_scope(scope);
+                let mut scope = scopes.new_scope(scope);
                 scopes.add_bindings(body, scope, arm.pat);
-                if let Some(guard) = arm.guard {
-                    scopes.set_scope(guard, scope);
-                    compute_expr_scopes(guard, body, scopes, scope);
-                }
+                match arm.guard {
+                    Some(MatchGuard::If { expr: guard }) => {
+                        scopes.set_scope(guard, scope);
+                        compute_expr_scopes(guard, body, scopes, scope);
+                    }
+                    Some(MatchGuard::IfLet { pat, expr: guard }) => {
+                        scopes.set_scope(guard, scope);
+                        compute_expr_scopes(guard, body, scopes, scope);
+                        scope = scopes.new_scope(scope);
+                        scopes.add_bindings(body, scope, pat);
+                    }
+                    _ => {}
+                };
                 scopes.set_scope(arm.expr, scope);
                 compute_expr_scopes(arm.expr, body, scopes, scope);
             }
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 2ba619d2341..e8e73f79fe2 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -229,11 +229,18 @@ pub enum Array {
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct MatchArm {
     pub pat: PatId,
-    pub guard: Option<ExprId>,
+    pub guard: Option<MatchGuard>,
     pub expr: ExprId,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
+pub enum MatchGuard {
+    If { expr: ExprId },
+
+    IfLet { pat: PatId, expr: ExprId },
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
 pub struct RecordLitField {
     pub name: Name,
     pub expr: ExprId,
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index d667613428f..b773630e83d 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -8,7 +8,7 @@ use std::{
 
 use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
 use hir_def::{
-    expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
+    expr::{Array, BinaryOp, Expr, ExprId, Literal, MatchGuard, Statement, UnaryOp},
     path::{GenericArg, GenericArgs},
     resolver::resolver_for_expr,
     AssocContainerId, FieldId, Lookup,
@@ -366,12 +366,13 @@ impl<'a> InferenceContext<'a> {
                 for arm in arms {
                     self.diverges = Diverges::Maybe;
                     let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
-                    if let Some(guard_expr) = arm.guard {
+                    if let Some(MatchGuard::If { expr: guard_expr }) = arm.guard {
                         self.infer_expr(
                             guard_expr,
                             &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(&Interner)),
                         );
                     }
+                    // FIXME: infer `if let` guard
 
                     let arm_ty = self.infer_expr_inner(arm.expr, &expected);
                     all_arms_diverge &= self.diverges;
diff --git a/crates/ide_assists/src/handlers/move_guard.rs b/crates/ide_assists/src/handlers/move_guard.rs
index e38e9e4524d..6841f506aa4 100644
--- a/crates/ide_assists/src/handlers/move_guard.rs
+++ b/crates/ide_assists/src/handlers/move_guard.rs
@@ -37,6 +37,10 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
     let guard = match_arm.guard()?;
     let space_before_guard = guard.syntax().prev_sibling_or_token();
 
+    // FIXME: support `if let` guards too
+    if guard.let_token().is_some() {
+        return None;
+    }
     let guard_condition = guard.expr()?;
     let arm_expr = match_arm.expr()?;
     let if_expr = make::expr_if(
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 70037c02d46..f0048aa3014 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -458,12 +458,17 @@ fn match_arm(p: &mut Parser) {
 // fn foo() {
 //     match () {
 //         _ if foo => (),
+//         _ if let foo = bar => (),
 //     }
 // }
 fn match_guard(p: &mut Parser) -> CompletedMarker {
     assert!(p.at(T![if]));
     let m = p.start();
     p.bump(T![if]);
+    if p.eat(T![let]) {
+        patterns::pattern_top(p);
+        p.expect(T![=]);
+    }
     expr(p);
     m.complete(p, MATCH_GUARD)
 }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 287160a19fb..205d01e974e 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1021,6 +1021,9 @@ pub struct MatchGuard {
 }
 impl MatchGuard {
     pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
+    pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
+    pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
+    pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
     pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
 }
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rast b/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rast
index 12d498af62c..646e2d1fd99 100644
--- a/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rast
@@ -1,5 +1,5 @@
-SOURCE_FILE@0..58
-  FN@0..57
+SOURCE_FILE@0..92
+  FN@0..91
     FN_KW@0..2 "fn"
     WHITESPACE@2..3 " "
     NAME@3..6
@@ -8,17 +8,17 @@ SOURCE_FILE@0..58
       L_PAREN@6..7 "("
       R_PAREN@7..8 ")"
     WHITESPACE@8..9 " "
-    BLOCK_EXPR@9..57
+    BLOCK_EXPR@9..91
       L_CURLY@9..10 "{"
       WHITESPACE@10..15 "\n    "
-      MATCH_EXPR@15..55
+      MATCH_EXPR@15..89
         MATCH_KW@15..20 "match"
         WHITESPACE@20..21 " "
         TUPLE_EXPR@21..23
           L_PAREN@21..22 "("
           R_PAREN@22..23 ")"
         WHITESPACE@23..24 " "
-        MATCH_ARM_LIST@24..55
+        MATCH_ARM_LIST@24..89
           L_CURLY@24..25 "{"
           WHITESPACE@25..34 "\n        "
           MATCH_ARM@34..49
@@ -40,8 +40,36 @@ SOURCE_FILE@0..58
               L_PAREN@46..47 "("
               R_PAREN@47..48 ")"
             COMMA@48..49 ","
-          WHITESPACE@49..54 "\n    "
-          R_CURLY@54..55 "}"
-      WHITESPACE@55..56 "\n"
-      R_CURLY@56..57 "}"
-  WHITESPACE@57..58 "\n"
+          WHITESPACE@49..58 "\n        "
+          MATCH_ARM@58..83
+            WILDCARD_PAT@58..59
+              UNDERSCORE@58..59 "_"
+            WHITESPACE@59..60 " "
+            MATCH_GUARD@60..76
+              IF_KW@60..62 "if"
+              WHITESPACE@62..63 " "
+              LET_KW@63..66 "let"
+              WHITESPACE@66..67 " "
+              IDENT_PAT@67..70
+                NAME@67..70
+                  IDENT@67..70 "foo"
+              WHITESPACE@70..71 " "
+              EQ@71..72 "="
+              WHITESPACE@72..73 " "
+              PATH_EXPR@73..76
+                PATH@73..76
+                  PATH_SEGMENT@73..76
+                    NAME_REF@73..76
+                      IDENT@73..76 "bar"
+            WHITESPACE@76..77 " "
+            FAT_ARROW@77..79 "=>"
+            WHITESPACE@79..80 " "
+            TUPLE_EXPR@80..82
+              L_PAREN@80..81 "("
+              R_PAREN@81..82 ")"
+            COMMA@82..83 ","
+          WHITESPACE@83..88 "\n    "
+          R_CURLY@88..89 "}"
+      WHITESPACE@89..90 "\n"
+      R_CURLY@90..91 "}"
+  WHITESPACE@91..92 "\n"
diff --git a/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rs b/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rs
index f1bd72fc4cb..cfe05ce4e18 100644
--- a/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rs
+++ b/crates/syntax/test_data/parser/inline/ok/0118_match_guard.rs
@@ -1,5 +1,6 @@
 fn foo() {
     match () {
         _ if foo => (),
+        _ if let foo = bar => (),
     }
 }