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]]
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"

View file

@ -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()

View file

@ -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);
}

View file

@ -229,10 +229,17 @@ 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,

View file

@ -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;

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 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(

View file

@ -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)
}

View file

@ -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)]

View file

@ -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"

View file

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