Support if let match guards

This commit is contained in:
Jonas Schievink 2021-08-13 00:21:42 +02:00
parent 4466e07fd7
commit d568e7686a
10 changed files with 90 additions and 27 deletions

4
Cargo.lock generated
View file

@ -1777,9 +1777,9 @@ dependencies = [
[[package]] [[package]]
name = "ungrammar" name = "ungrammar"
version = "1.14.2" version = "1.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5334230a6ae9ca52bc811c968c0ae12f1750c0698ed52ea68dabab7ce5a80972" checksum = "0ef2f5093f958b9aecad9d80a01c49aece7a7bd15ad407e1986258fa36229910"
[[package]] [[package]]
name = "unicase" name = "unicase"

View file

@ -28,8 +28,8 @@ use crate::{
db::DefDatabase, db::DefDatabase,
expr::{ expr::{
dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label, dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, LabelId, Literal, LogicOp, MatchArm, MatchGuard, Ordering, Pat, PatId, RecordFieldPat,
Statement, RecordLitField, Statement,
}, },
intern::Interned, intern::Interned,
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
@ -361,10 +361,15 @@ impl ExprCollector<'_> {
self.check_cfg(&arm).map(|()| MatchArm { self.check_cfg(&arm).map(|()| MatchArm {
pat: self.collect_pat_opt(arm.pat()), pat: self.collect_pat_opt(arm.pat()),
expr: self.collect_expr_opt(arm.expr()), expr: self.collect_expr_opt(arm.expr()),
guard: arm guard: arm.guard().map(|guard| match guard.pat() {
.guard() Some(pat) => MatchGuard::IfLet {
.and_then(|guard| guard.expr()) pat: self.collect_pat(pat),
.map(|e| self.collect_expr(e)), expr: self.collect_expr_opt(guard.expr()),
},
None => {
MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) }
}
}),
}) })
}) })
.collect() .collect()

View file

@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
use crate::{ use crate::{
body::Body, body::Body,
db::DefDatabase, db::DefDatabase,
expr::{Expr, ExprId, LabelId, Pat, PatId, Statement}, expr::{Expr, ExprId, LabelId, MatchGuard, Pat, PatId, Statement},
BlockId, DefWithBodyId, BlockId, DefWithBodyId,
}; };
@ -204,12 +204,21 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
Expr::Match { expr, arms } => { Expr::Match { expr, arms } => {
compute_expr_scopes(*expr, body, scopes, scope); compute_expr_scopes(*expr, body, scopes, scope);
for arm in arms { for arm in arms {
let scope = scopes.new_scope(scope); let mut scope = scopes.new_scope(scope);
scopes.add_bindings(body, scope, arm.pat); scopes.add_bindings(body, scope, arm.pat);
if let Some(guard) = arm.guard { match arm.guard {
scopes.set_scope(guard, scope); Some(MatchGuard::If { expr: guard }) => {
compute_expr_scopes(guard, body, scopes, scope); 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); scopes.set_scope(arm.expr, scope);
compute_expr_scopes(arm.expr, body, scopes, scope); compute_expr_scopes(arm.expr, body, scopes, scope);
} }

View file

@ -229,10 +229,17 @@ pub enum Array {
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct MatchArm { pub struct MatchArm {
pub pat: PatId, pub pat: PatId,
pub guard: Option<ExprId>, pub guard: Option<MatchGuard>,
pub expr: ExprId, pub expr: ExprId,
} }
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum MatchGuard {
If { expr: ExprId },
IfLet { pat: PatId, expr: ExprId },
}
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct RecordLitField { pub struct RecordLitField {
pub name: Name, pub name: Name,

View file

@ -8,7 +8,7 @@ use std::{
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
use hir_def::{ use hir_def::{
expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, expr::{Array, BinaryOp, Expr, ExprId, Literal, MatchGuard, Statement, UnaryOp},
path::{GenericArg, GenericArgs}, path::{GenericArg, GenericArgs},
resolver::resolver_for_expr, resolver::resolver_for_expr,
AssocContainerId, FieldId, Lookup, AssocContainerId, FieldId, Lookup,
@ -366,12 +366,13 @@ impl<'a> InferenceContext<'a> {
for arm in arms { for arm in arms {
self.diverges = Diverges::Maybe; self.diverges = Diverges::Maybe;
let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); 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( self.infer_expr(
guard_expr, guard_expr,
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(&Interner)), &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(&Interner)),
); );
} }
// FIXME: infer `if let` guard
let arm_ty = self.infer_expr_inner(arm.expr, &expected); let arm_ty = self.infer_expr_inner(arm.expr, &expected);
all_arms_diverge &= self.diverges; all_arms_diverge &= self.diverges;

View file

@ -37,6 +37,10 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
let guard = match_arm.guard()?; let guard = match_arm.guard()?;
let space_before_guard = guard.syntax().prev_sibling_or_token(); 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 guard_condition = guard.expr()?;
let arm_expr = match_arm.expr()?; let arm_expr = match_arm.expr()?;
let if_expr = make::expr_if( let if_expr = make::expr_if(

View file

@ -458,12 +458,17 @@ fn match_arm(p: &mut Parser) {
// fn foo() { // fn foo() {
// match () { // match () {
// _ if foo => (), // _ if foo => (),
// _ if let foo = bar => (),
// } // }
// } // }
fn match_guard(p: &mut Parser) -> CompletedMarker { fn match_guard(p: &mut Parser) -> CompletedMarker {
assert!(p.at(T![if])); assert!(p.at(T![if]));
let m = p.start(); let m = p.start();
p.bump(T![if]); p.bump(T![if]);
if p.eat(T![let]) {
patterns::pattern_top(p);
p.expect(T![=]);
}
expr(p); expr(p);
m.complete(p, MATCH_GUARD) m.complete(p, MATCH_GUARD)
} }

View file

@ -1021,6 +1021,9 @@ pub struct MatchGuard {
} }
impl MatchGuard { impl MatchGuard {
pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) } 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) } pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -1,5 +1,5 @@
SOURCE_FILE@0..58 SOURCE_FILE@0..92
FN@0..57 FN@0..91
FN_KW@0..2 "fn" FN_KW@0..2 "fn"
WHITESPACE@2..3 " " WHITESPACE@2..3 " "
NAME@3..6 NAME@3..6
@ -8,17 +8,17 @@ SOURCE_FILE@0..58
L_PAREN@6..7 "(" L_PAREN@6..7 "("
R_PAREN@7..8 ")" R_PAREN@7..8 ")"
WHITESPACE@8..9 " " WHITESPACE@8..9 " "
BLOCK_EXPR@9..57 BLOCK_EXPR@9..91
L_CURLY@9..10 "{" L_CURLY@9..10 "{"
WHITESPACE@10..15 "\n " WHITESPACE@10..15 "\n "
MATCH_EXPR@15..55 MATCH_EXPR@15..89
MATCH_KW@15..20 "match" MATCH_KW@15..20 "match"
WHITESPACE@20..21 " " WHITESPACE@20..21 " "
TUPLE_EXPR@21..23 TUPLE_EXPR@21..23
L_PAREN@21..22 "(" L_PAREN@21..22 "("
R_PAREN@22..23 ")" R_PAREN@22..23 ")"
WHITESPACE@23..24 " " WHITESPACE@23..24 " "
MATCH_ARM_LIST@24..55 MATCH_ARM_LIST@24..89
L_CURLY@24..25 "{" L_CURLY@24..25 "{"
WHITESPACE@25..34 "\n " WHITESPACE@25..34 "\n "
MATCH_ARM@34..49 MATCH_ARM@34..49
@ -40,8 +40,36 @@ SOURCE_FILE@0..58
L_PAREN@46..47 "(" L_PAREN@46..47 "("
R_PAREN@47..48 ")" R_PAREN@47..48 ")"
COMMA@48..49 "," COMMA@48..49 ","
WHITESPACE@49..54 "\n " WHITESPACE@49..58 "\n "
R_CURLY@54..55 "}" MATCH_ARM@58..83
WHITESPACE@55..56 "\n" WILDCARD_PAT@58..59
R_CURLY@56..57 "}" UNDERSCORE@58..59 "_"
WHITESPACE@57..58 "\n" 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"

View file

@ -1,5 +1,6 @@
fn foo() { fn foo() {
match () { match () {
_ if foo => (), _ if foo => (),
_ if let foo = bar => (),
} }
} }