Skip to content

Commit c00ccde

Browse files
Merge #9879
9879: feat: Support `if let` match guards r=jonas-schievink a=jonas-schievink Adds support for parsing and computing bindings for `if let` match guards (rust-lang/rust#51114). Type inference support is still missing, but shouldn't be hard to add in a follow-up PR. Closes #9876 bors r+ Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
2 parents 4466e07 + d568e76 commit c00ccde

File tree

10 files changed

+90
-27
lines changed

10 files changed

+90
-27
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/hir_def/src/body/lower.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ use crate::{
2828
db::DefDatabase,
2929
expr::{
3030
dummy_expr_id, ArithOp, Array, BinaryOp, BindingAnnotation, CmpOp, Expr, ExprId, Label,
31-
LabelId, Literal, LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField,
32-
Statement,
31+
LabelId, Literal, LogicOp, MatchArm, MatchGuard, Ordering, Pat, PatId, RecordFieldPat,
32+
RecordLitField, Statement,
3333
},
3434
intern::Interned,
3535
item_scope::BuiltinShadowMode,
@@ -361,10 +361,15 @@ impl ExprCollector<'_> {
361361
self.check_cfg(&arm).map(|()| MatchArm {
362362
pat: self.collect_pat_opt(arm.pat()),
363363
expr: self.collect_expr_opt(arm.expr()),
364-
guard: arm
365-
.guard()
366-
.and_then(|guard| guard.expr())
367-
.map(|e| self.collect_expr(e)),
364+
guard: arm.guard().map(|guard| match guard.pat() {
365+
Some(pat) => MatchGuard::IfLet {
366+
pat: self.collect_pat(pat),
367+
expr: self.collect_expr_opt(guard.expr()),
368+
},
369+
None => {
370+
MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) }
371+
}
372+
}),
368373
})
369374
})
370375
.collect()

crates/hir_def/src/body/scope.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
88
use crate::{
99
body::Body,
1010
db::DefDatabase,
11-
expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
11+
expr::{Expr, ExprId, LabelId, MatchGuard, Pat, PatId, Statement},
1212
BlockId, DefWithBodyId,
1313
};
1414

@@ -204,12 +204,21 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
204204
Expr::Match { expr, arms } => {
205205
compute_expr_scopes(*expr, body, scopes, scope);
206206
for arm in arms {
207-
let scope = scopes.new_scope(scope);
207+
let mut scope = scopes.new_scope(scope);
208208
scopes.add_bindings(body, scope, arm.pat);
209-
if let Some(guard) = arm.guard {
210-
scopes.set_scope(guard, scope);
211-
compute_expr_scopes(guard, body, scopes, scope);
212-
}
209+
match arm.guard {
210+
Some(MatchGuard::If { expr: guard }) => {
211+
scopes.set_scope(guard, scope);
212+
compute_expr_scopes(guard, body, scopes, scope);
213+
}
214+
Some(MatchGuard::IfLet { pat, expr: guard }) => {
215+
scopes.set_scope(guard, scope);
216+
compute_expr_scopes(guard, body, scopes, scope);
217+
scope = scopes.new_scope(scope);
218+
scopes.add_bindings(body, scope, pat);
219+
}
220+
_ => {}
221+
};
213222
scopes.set_scope(arm.expr, scope);
214223
compute_expr_scopes(arm.expr, body, scopes, scope);
215224
}

crates/hir_def/src/expr.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,17 @@ pub enum Array {
229229
#[derive(Debug, Clone, Eq, PartialEq)]
230230
pub struct MatchArm {
231231
pub pat: PatId,
232-
pub guard: Option<ExprId>,
232+
pub guard: Option<MatchGuard>,
233233
pub expr: ExprId,
234234
}
235235

236+
#[derive(Debug, Clone, Eq, PartialEq)]
237+
pub enum MatchGuard {
238+
If { expr: ExprId },
239+
240+
IfLet { pat: PatId, expr: ExprId },
241+
}
242+
236243
#[derive(Debug, Clone, Eq, PartialEq)]
237244
pub struct RecordLitField {
238245
pub name: Name,

crates/hir_ty/src/infer/expr.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88

99
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
1010
use hir_def::{
11-
expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
11+
expr::{Array, BinaryOp, Expr, ExprId, Literal, MatchGuard, Statement, UnaryOp},
1212
path::{GenericArg, GenericArgs},
1313
resolver::resolver_for_expr,
1414
AssocContainerId, FieldId, Lookup,
@@ -366,12 +366,13 @@ impl<'a> InferenceContext<'a> {
366366
for arm in arms {
367367
self.diverges = Diverges::Maybe;
368368
let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
369-
if let Some(guard_expr) = arm.guard {
369+
if let Some(MatchGuard::If { expr: guard_expr }) = arm.guard {
370370
self.infer_expr(
371371
guard_expr,
372372
&Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(&Interner)),
373373
);
374374
}
375+
// FIXME: infer `if let` guard
375376

376377
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
377378
all_arms_diverge &= self.diverges;

crates/ide_assists/src/handlers/move_guard.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
3737
let guard = match_arm.guard()?;
3838
let space_before_guard = guard.syntax().prev_sibling_or_token();
3939

40+
// FIXME: support `if let` guards too
41+
if guard.let_token().is_some() {
42+
return None;
43+
}
4044
let guard_condition = guard.expr()?;
4145
let arm_expr = match_arm.expr()?;
4246
let if_expr = make::expr_if(

crates/parser/src/grammar/expressions/atom.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,12 +458,17 @@ fn match_arm(p: &mut Parser) {
458458
// fn foo() {
459459
// match () {
460460
// _ if foo => (),
461+
// _ if let foo = bar => (),
461462
// }
462463
// }
463464
fn match_guard(p: &mut Parser) -> CompletedMarker {
464465
assert!(p.at(T![if]));
465466
let m = p.start();
466467
p.bump(T![if]);
468+
if p.eat(T![let]) {
469+
patterns::pattern_top(p);
470+
p.expect(T![=]);
471+
}
467472
expr(p);
468473
m.complete(p, MATCH_GUARD)
469474
}

crates/syntax/src/ast/generated/nodes.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,9 @@ pub struct MatchGuard {
10211021
}
10221022
impl MatchGuard {
10231023
pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
1024+
pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
1025+
pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
1026+
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
10241027
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
10251028
}
10261029
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

crates/syntax/test_data/parser/inline/ok/0118_match_guard.rast

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
SOURCE_FILE@0..58
2-
FN@0..57
1+
SOURCE_FILE@0..92
2+
FN@0..91
33
FN_KW@0..2 "fn"
44
WHITESPACE@2..3 " "
55
NAME@3..6
@@ -8,17 +8,17 @@ SOURCE_FILE@0..58
88
L_PAREN@6..7 "("
99
R_PAREN@7..8 ")"
1010
WHITESPACE@8..9 " "
11-
BLOCK_EXPR@9..57
11+
BLOCK_EXPR@9..91
1212
L_CURLY@9..10 "{"
1313
WHITESPACE@10..15 "\n "
14-
MATCH_EXPR@15..55
14+
MATCH_EXPR@15..89
1515
MATCH_KW@15..20 "match"
1616
WHITESPACE@20..21 " "
1717
TUPLE_EXPR@21..23
1818
L_PAREN@21..22 "("
1919
R_PAREN@22..23 ")"
2020
WHITESPACE@23..24 " "
21-
MATCH_ARM_LIST@24..55
21+
MATCH_ARM_LIST@24..89
2222
L_CURLY@24..25 "{"
2323
WHITESPACE@25..34 "\n "
2424
MATCH_ARM@34..49
@@ -40,8 +40,36 @@ SOURCE_FILE@0..58
4040
L_PAREN@46..47 "("
4141
R_PAREN@47..48 ")"
4242
COMMA@48..49 ","
43-
WHITESPACE@49..54 "\n "
44-
R_CURLY@54..55 "}"
45-
WHITESPACE@55..56 "\n"
46-
R_CURLY@56..57 "}"
47-
WHITESPACE@57..58 "\n"
43+
WHITESPACE@49..58 "\n "
44+
MATCH_ARM@58..83
45+
WILDCARD_PAT@58..59
46+
UNDERSCORE@58..59 "_"
47+
WHITESPACE@59..60 " "
48+
MATCH_GUARD@60..76
49+
IF_KW@60..62 "if"
50+
WHITESPACE@62..63 " "
51+
LET_KW@63..66 "let"
52+
WHITESPACE@66..67 " "
53+
IDENT_PAT@67..70
54+
NAME@67..70
55+
IDENT@67..70 "foo"
56+
WHITESPACE@70..71 " "
57+
EQ@71..72 "="
58+
WHITESPACE@72..73 " "
59+
PATH_EXPR@73..76
60+
PATH@73..76
61+
PATH_SEGMENT@73..76
62+
NAME_REF@73..76
63+
IDENT@73..76 "bar"
64+
WHITESPACE@76..77 " "
65+
FAT_ARROW@77..79 "=>"
66+
WHITESPACE@79..80 " "
67+
TUPLE_EXPR@80..82
68+
L_PAREN@80..81 "("
69+
R_PAREN@81..82 ")"
70+
COMMA@82..83 ","
71+
WHITESPACE@83..88 "\n "
72+
R_CURLY@88..89 "}"
73+
WHITESPACE@89..90 "\n"
74+
R_CURLY@90..91 "}"
75+
WHITESPACE@91..92 "\n"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
fn foo() {
22
match () {
33
_ if foo => (),
4+
_ if let foo = bar => (),
45
}
56
}

0 commit comments

Comments
 (0)