Skip to content

Commit c67660f

Browse files
Merge #11375
11375: feat: Support if- and while- let chains r=Veykril a=ChayimFriedman2 Closes #11320. Co-authored-by: Chayim Refael Friedman <chayimfr@gmail.com>
2 parents c149c36 + f70512c commit c67660f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1177
-1094
lines changed

Cargo.lock

+2-2
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

+11-62
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::{
2828
db::DefDatabase,
2929
expr::{
3030
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm,
31-
MatchGuard, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
31+
Pat, PatId, RecordFieldPat, RecordLitField, Statement,
3232
},
3333
intern::Interned,
3434
item_scope::BuiltinShadowMode,
@@ -155,9 +155,6 @@ impl ExprCollector<'_> {
155155
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
156156
self.make_expr(expr, Err(SyntheticSyntax))
157157
}
158-
fn unit(&mut self) -> ExprId {
159-
self.alloc_expr_desugared(Expr::Tuple { exprs: Box::default() })
160-
}
161158
fn missing_expr(&mut self) -> ExprId {
162159
self.alloc_expr_desugared(Expr::Missing)
163160
}
@@ -215,33 +212,15 @@ impl ExprCollector<'_> {
215212
}
216213
});
217214

218-
let condition = match e.condition() {
219-
None => self.missing_expr(),
220-
Some(condition) => match condition.pat() {
221-
None => self.collect_expr_opt(condition.expr()),
222-
// if let -- desugar to match
223-
Some(pat) => {
224-
let pat = self.collect_pat(pat);
225-
let match_expr = self.collect_expr_opt(condition.expr());
226-
let placeholder_pat = self.missing_pat();
227-
let arms = vec![
228-
MatchArm { pat, expr: then_branch, guard: None },
229-
MatchArm {
230-
pat: placeholder_pat,
231-
expr: else_branch.unwrap_or_else(|| self.unit()),
232-
guard: None,
233-
},
234-
]
235-
.into();
236-
return Some(
237-
self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr),
238-
);
239-
}
240-
},
241-
};
215+
let condition = self.collect_expr_opt(e.condition());
242216

243217
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
244218
}
219+
ast::Expr::LetExpr(e) => {
220+
let pat = self.collect_pat_opt(e.pat());
221+
let expr = self.collect_expr_opt(e.expr());
222+
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
223+
}
245224
ast::Expr::BlockExpr(e) => match e.modifier() {
246225
Some(ast::BlockModifier::Try(_)) => {
247226
let body = self.collect_block(e);
@@ -282,31 +261,7 @@ impl ExprCollector<'_> {
282261
let label = e.label().map(|label| self.collect_label(label));
283262
let body = self.collect_block_opt(e.loop_body());
284263

285-
let condition = match e.condition() {
286-
None => self.missing_expr(),
287-
Some(condition) => match condition.pat() {
288-
None => self.collect_expr_opt(condition.expr()),
289-
// if let -- desugar to match
290-
Some(pat) => {
291-
cov_mark::hit!(infer_resolve_while_let);
292-
let pat = self.collect_pat(pat);
293-
let match_expr = self.collect_expr_opt(condition.expr());
294-
let placeholder_pat = self.missing_pat();
295-
let break_ =
296-
self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
297-
let arms = vec![
298-
MatchArm { pat, expr: body, guard: None },
299-
MatchArm { pat: placeholder_pat, expr: break_, guard: None },
300-
]
301-
.into();
302-
let match_expr =
303-
self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
304-
return Some(
305-
self.alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr),
306-
);
307-
}
308-
},
309-
};
264+
let condition = self.collect_expr_opt(e.condition());
310265

311266
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
312267
}
@@ -352,15 +307,9 @@ impl ExprCollector<'_> {
352307
self.check_cfg(&arm).map(|()| MatchArm {
353308
pat: self.collect_pat_opt(arm.pat()),
354309
expr: self.collect_expr_opt(arm.expr()),
355-
guard: arm.guard().map(|guard| match guard.pat() {
356-
Some(pat) => MatchGuard::IfLet {
357-
pat: self.collect_pat(pat),
358-
expr: self.collect_expr_opt(guard.expr()),
359-
},
360-
None => {
361-
MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) }
362-
}
363-
}),
310+
guard: arm
311+
.guard()
312+
.map(|guard| self.collect_expr_opt(guard.condition())),
364313
})
365314
})
366315
.collect()

crates/hir_def/src/body/scope.rs

+87-37
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, MatchGuard, Pat, PatId, Statement},
11+
expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
1212
BlockId, DefWithBodyId,
1313
};
1414

@@ -53,9 +53,9 @@ impl ExprScopes {
5353
fn new(body: &Body) -> ExprScopes {
5454
let mut scopes =
5555
ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
56-
let root = scopes.root_scope();
56+
let mut root = scopes.root_scope();
5757
scopes.add_params_bindings(body, root, &body.params);
58-
compute_expr_scopes(body.body_expr, body, &mut scopes, root);
58+
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
5959
scopes
6060
}
6161

@@ -151,79 +151,82 @@ fn compute_block_scopes(
151151
match stmt {
152152
Statement::Let { pat, initializer, else_branch, .. } => {
153153
if let Some(expr) = initializer {
154-
compute_expr_scopes(*expr, body, scopes, scope);
154+
compute_expr_scopes(*expr, body, scopes, &mut scope);
155155
}
156156
if let Some(expr) = else_branch {
157-
compute_expr_scopes(*expr, body, scopes, scope);
157+
compute_expr_scopes(*expr, body, scopes, &mut scope);
158158
}
159159
scope = scopes.new_scope(scope);
160160
scopes.add_bindings(body, scope, *pat);
161161
}
162162
Statement::Expr { expr, .. } => {
163-
compute_expr_scopes(*expr, body, scopes, scope);
163+
compute_expr_scopes(*expr, body, scopes, &mut scope);
164164
}
165165
}
166166
}
167167
if let Some(expr) = tail {
168-
compute_expr_scopes(expr, body, scopes, scope);
168+
compute_expr_scopes(expr, body, scopes, &mut scope);
169169
}
170170
}
171171

172-
fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
172+
fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId) {
173173
let make_label =
174174
|label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone()));
175175

176-
scopes.set_scope(expr, scope);
176+
scopes.set_scope(expr, *scope);
177177
match &body[expr] {
178178
Expr::Block { statements, tail, id, label } => {
179-
let scope = scopes.new_block_scope(scope, *id, make_label(label));
179+
let scope = scopes.new_block_scope(*scope, *id, make_label(label));
180180
// Overwrite the old scope for the block expr, so that every block scope can be found
181181
// via the block itself (important for blocks that only contain items, no expressions).
182182
scopes.set_scope(expr, scope);
183183
compute_block_scopes(statements, *tail, body, scopes, scope);
184184
}
185185
Expr::For { iterable, pat, body: body_expr, label } => {
186186
compute_expr_scopes(*iterable, body, scopes, scope);
187-
let scope = scopes.new_labeled_scope(scope, make_label(label));
187+
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
188188
scopes.add_bindings(body, scope, *pat);
189-
compute_expr_scopes(*body_expr, body, scopes, scope);
189+
compute_expr_scopes(*body_expr, body, scopes, &mut scope);
190190
}
191191
Expr::While { condition, body: body_expr, label } => {
192-
let scope = scopes.new_labeled_scope(scope, make_label(label));
193-
compute_expr_scopes(*condition, body, scopes, scope);
194-
compute_expr_scopes(*body_expr, body, scopes, scope);
192+
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
193+
compute_expr_scopes(*condition, body, scopes, &mut scope);
194+
compute_expr_scopes(*body_expr, body, scopes, &mut scope);
195195
}
196196
Expr::Loop { body: body_expr, label } => {
197-
let scope = scopes.new_labeled_scope(scope, make_label(label));
198-
compute_expr_scopes(*body_expr, body, scopes, scope);
197+
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
198+
compute_expr_scopes(*body_expr, body, scopes, &mut scope);
199199
}
200200
Expr::Lambda { args, body: body_expr, .. } => {
201-
let scope = scopes.new_scope(scope);
201+
let mut scope = scopes.new_scope(*scope);
202202
scopes.add_params_bindings(body, scope, args);
203-
compute_expr_scopes(*body_expr, body, scopes, scope);
203+
compute_expr_scopes(*body_expr, body, scopes, &mut scope);
204204
}
205205
Expr::Match { expr, arms } => {
206206
compute_expr_scopes(*expr, body, scopes, scope);
207207
for arm in arms.iter() {
208-
let mut scope = scopes.new_scope(scope);
208+
let mut scope = scopes.new_scope(*scope);
209209
scopes.add_bindings(body, scope, arm.pat);
210-
match arm.guard {
211-
Some(MatchGuard::If { expr: guard }) => {
212-
scopes.set_scope(guard, scope);
213-
compute_expr_scopes(guard, body, scopes, scope);
214-
}
215-
Some(MatchGuard::IfLet { pat, expr: guard }) => {
216-
scopes.set_scope(guard, scope);
217-
compute_expr_scopes(guard, body, scopes, scope);
218-
scope = scopes.new_scope(scope);
219-
scopes.add_bindings(body, scope, pat);
220-
}
221-
_ => {}
222-
};
223-
scopes.set_scope(arm.expr, scope);
224-
compute_expr_scopes(arm.expr, body, scopes, scope);
210+
if let Some(guard) = arm.guard {
211+
scope = scopes.new_scope(scope);
212+
compute_expr_scopes(guard, body, scopes, &mut scope);
213+
}
214+
compute_expr_scopes(arm.expr, body, scopes, &mut scope);
215+
}
216+
}
217+
&Expr::If { condition, then_branch, else_branch } => {
218+
let mut then_branch_scope = scopes.new_scope(*scope);
219+
compute_expr_scopes(condition, body, scopes, &mut then_branch_scope);
220+
compute_expr_scopes(then_branch, body, scopes, &mut then_branch_scope);
221+
if let Some(else_branch) = else_branch {
222+
compute_expr_scopes(else_branch, body, scopes, scope);
225223
}
226224
}
225+
&Expr::Let { pat, expr } => {
226+
compute_expr_scopes(expr, body, scopes, scope);
227+
*scope = scopes.new_scope(*scope);
228+
scopes.add_bindings(body, *scope, pat);
229+
}
227230
e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
228231
};
229232
}
@@ -500,8 +503,7 @@ fn foo() {
500503
}
501504

502505
#[test]
503-
fn while_let_desugaring() {
504-
cov_mark::check!(infer_resolve_while_let);
506+
fn while_let_adds_binding() {
505507
do_check_local_name(
506508
r#"
507509
fn test() {
@@ -513,5 +515,53 @@ fn test() {
513515
"#,
514516
75,
515517
);
518+
do_check_local_name(
519+
r#"
520+
fn test() {
521+
let foo: Option<f32> = None;
522+
while (((let Option::Some(_) = foo))) && let Option::Some(spam) = foo {
523+
spam$0
524+
}
525+
}
526+
"#,
527+
107,
528+
);
529+
}
530+
531+
#[test]
532+
fn match_guard_if_let() {
533+
do_check_local_name(
534+
r#"
535+
fn test() {
536+
let foo: Option<f32> = None;
537+
match foo {
538+
_ if let Option::Some(spam) = foo => spam$0,
539+
}
540+
}
541+
"#,
542+
93,
543+
);
544+
}
545+
546+
#[test]
547+
fn let_chains_can_reference_previous_lets() {
548+
do_check_local_name(
549+
r#"
550+
fn test() {
551+
let foo: Option<i32> = None;
552+
if let Some(spam) = foo && spa$0m > 1 && let Some(spam) = foo && spam > 1 {}
553+
}
554+
"#,
555+
61,
556+
);
557+
do_check_local_name(
558+
r#"
559+
fn test() {
560+
let foo: Option<i32> = None;
561+
if let Some(spam) = foo && spam > 1 && let Some(spam) = foo && sp$0am > 1 {}
562+
}
563+
"#,
564+
100,
565+
);
516566
}
517567
}

crates/hir_def/src/expr.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ pub enum Expr {
5959
then_branch: ExprId,
6060
else_branch: Option<ExprId>,
6161
},
62+
Let {
63+
pat: PatId,
64+
expr: ExprId,
65+
},
6266
Block {
6367
id: BlockId,
6468
statements: Box<[Statement]>,
@@ -189,17 +193,10 @@ pub enum Array {
189193
#[derive(Debug, Clone, Eq, PartialEq)]
190194
pub struct MatchArm {
191195
pub pat: PatId,
192-
pub guard: Option<MatchGuard>,
196+
pub guard: Option<ExprId>,
193197
pub expr: ExprId,
194198
}
195199

196-
#[derive(Debug, Clone, Eq, PartialEq)]
197-
pub enum MatchGuard {
198-
If { expr: ExprId },
199-
200-
IfLet { pat: PatId, expr: ExprId },
201-
}
202-
203200
#[derive(Debug, Clone, Eq, PartialEq)]
204201
pub struct RecordLitField {
205202
pub name: Name,
@@ -232,6 +229,9 @@ impl Expr {
232229
f(else_branch);
233230
}
234231
}
232+
Expr::Let { expr, .. } => {
233+
f(*expr);
234+
}
235235
Expr::Block { statements, tail, .. } => {
236236
for stmt in statements.iter() {
237237
match stmt {

crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,18 @@ fn expansion_does_not_parse_as_expression() {
108108
check(
109109
r#"
110110
macro_rules! stmts {
111-
() => { let _ = 0; }
111+
() => { fn foo() {} }
112112
}
113113
114114
fn f() { let _ = stmts!/*+errors*/(); }
115115
"#,
116116
expect![[r#"
117117
macro_rules! stmts {
118-
() => { let _ = 0; }
118+
() => { fn foo() {} }
119119
}
120120
121121
fn f() { let _ = /* parse error: expected expression */
122-
let _ = 0;; }
122+
fn foo() {}; }
123123
"#]],
124124
)
125125
}

crates/hir_expand/src/lib.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -811,10 +811,10 @@ impl ExpandTo {
811811
MACRO_TYPE => ExpandTo::Type,
812812

813813
ARG_LIST | TRY_EXPR | TUPLE_EXPR | PAREN_EXPR | ARRAY_EXPR | FOR_EXPR | PATH_EXPR
814-
| CLOSURE_EXPR | CONDITION | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM
815-
| MATCH_GUARD | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR
816-
| FIELD_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR
817-
| BIN_EXPR => ExpandTo::Expr,
814+
| CLOSURE_EXPR | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM | MATCH_GUARD
815+
| RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR
816+
| AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR
817+
| LET_EXPR => ExpandTo::Expr,
818818
LET_STMT => {
819819
// FIXME: Handle LHS Pattern
820820
ExpandTo::Expr

0 commit comments

Comments
 (0)