diff --git a/Cargo.lock b/Cargo.lock index 61180fcb9a..a5f46ff401 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 f6e6cdbe2e..4f91213673 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 58a1fc81cb..5d9dd73a12 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 2ba619d234..e8e73f79fe 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs @@ -229,10 +229,17 @@ pub enum Array { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MatchArm { pub pat: PatId, - pub guard: Option, + pub guard: Option, 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, diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index d667613428..b773630e83 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 e38e9e4524..6841f506aa 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 70037c02d4..f0048aa301 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 287160a19f..205d01e974 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 { support::token(&self.syntax, T![if]) } + pub fn let_token(&self) -> Option { support::token(&self.syntax, T![let]) } + pub fn pat(&self) -> Option { support::child(&self.syntax) } + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } pub fn expr(&self) -> Option { 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 12d498af62..646e2d1fd9 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 f1bd72fc4c..cfe05ce4e1 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 => (), } }