11375: feat: Support if- and while- let chains r=Veykril a=ChayimFriedman2

Closes #11320.

Co-authored-by: Chayim Refael Friedman <chayimfr@gmail.com>
This commit is contained in:
bors[bot] 2022-02-21 09:31:38 +00:00 committed by GitHub
commit c67660fc22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 1177 additions and 1094 deletions

4
Cargo.lock generated
View file

@ -1832,9 +1832,9 @@ checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae"
[[package]] [[package]]
name = "ungrammar" name = "ungrammar"
version = "1.14.9" version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66be59c2fd880e3d76d1a6cf6d34114008f1d8af2748d4ad9d39ea712f14fda9" checksum = "ed01567101450f7d600508e7680df6005ae4fe97119d79b0364cc5910ff39732"
[[package]] [[package]]
name = "unicase" name = "unicase"

View file

@ -28,7 +28,7 @@ use crate::{
db::DefDatabase, db::DefDatabase,
expr::{ expr::{
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm, dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm,
MatchGuard, Pat, PatId, RecordFieldPat, RecordLitField, Statement, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
}, },
intern::Interned, intern::Interned,
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
@ -155,9 +155,6 @@ impl ExprCollector<'_> {
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
self.make_expr(expr, Err(SyntheticSyntax)) self.make_expr(expr, Err(SyntheticSyntax))
} }
fn unit(&mut self) -> ExprId {
self.alloc_expr_desugared(Expr::Tuple { exprs: Box::default() })
}
fn missing_expr(&mut self) -> ExprId { fn missing_expr(&mut self) -> ExprId {
self.alloc_expr_desugared(Expr::Missing) self.alloc_expr_desugared(Expr::Missing)
} }
@ -215,33 +212,15 @@ impl ExprCollector<'_> {
} }
}); });
let condition = match e.condition() { let condition = self.collect_expr_opt(e.condition());
None => self.missing_expr(),
Some(condition) => match condition.pat() {
None => self.collect_expr_opt(condition.expr()),
// if let -- desugar to match
Some(pat) => {
let pat = self.collect_pat(pat);
let match_expr = self.collect_expr_opt(condition.expr());
let placeholder_pat = self.missing_pat();
let arms = vec![
MatchArm { pat, expr: then_branch, guard: None },
MatchArm {
pat: placeholder_pat,
expr: else_branch.unwrap_or_else(|| self.unit()),
guard: None,
},
]
.into();
return Some(
self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr),
);
}
},
};
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr) self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
} }
ast::Expr::LetExpr(e) => {
let pat = self.collect_pat_opt(e.pat());
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() { ast::Expr::BlockExpr(e) => match e.modifier() {
Some(ast::BlockModifier::Try(_)) => { Some(ast::BlockModifier::Try(_)) => {
let body = self.collect_block(e); let body = self.collect_block(e);
@ -282,31 +261,7 @@ impl ExprCollector<'_> {
let label = e.label().map(|label| self.collect_label(label)); let label = e.label().map(|label| self.collect_label(label));
let body = self.collect_block_opt(e.loop_body()); let body = self.collect_block_opt(e.loop_body());
let condition = match e.condition() { let condition = self.collect_expr_opt(e.condition());
None => self.missing_expr(),
Some(condition) => match condition.pat() {
None => self.collect_expr_opt(condition.expr()),
// if let -- desugar to match
Some(pat) => {
cov_mark::hit!(infer_resolve_while_let);
let pat = self.collect_pat(pat);
let match_expr = self.collect_expr_opt(condition.expr());
let placeholder_pat = self.missing_pat();
let break_ =
self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
let arms = vec![
MatchArm { pat, expr: body, guard: None },
MatchArm { pat: placeholder_pat, expr: break_, guard: None },
]
.into();
let match_expr =
self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
return Some(
self.alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr),
);
}
},
};
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
} }
@ -352,15 +307,9 @@ 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().map(|guard| match guard.pat() { guard: arm
Some(pat) => MatchGuard::IfLet { .guard()
pat: self.collect_pat(pat), .map(|guard| self.collect_expr_opt(guard.condition())),
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, MatchGuard, Pat, PatId, Statement}, expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
BlockId, DefWithBodyId, BlockId, DefWithBodyId,
}; };
@ -53,9 +53,9 @@ impl ExprScopes {
fn new(body: &Body) -> ExprScopes { fn new(body: &Body) -> ExprScopes {
let mut scopes = let mut scopes =
ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() }; ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
let root = scopes.root_scope(); let mut root = scopes.root_scope();
scopes.add_params_bindings(body, root, &body.params); scopes.add_params_bindings(body, root, &body.params);
compute_expr_scopes(body.body_expr, body, &mut scopes, root); compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
scopes scopes
} }
@ -151,32 +151,32 @@ fn compute_block_scopes(
match stmt { match stmt {
Statement::Let { pat, initializer, else_branch, .. } => { Statement::Let { pat, initializer, else_branch, .. } => {
if let Some(expr) = initializer { if let Some(expr) = initializer {
compute_expr_scopes(*expr, body, scopes, scope); compute_expr_scopes(*expr, body, scopes, &mut scope);
} }
if let Some(expr) = else_branch { if let Some(expr) = else_branch {
compute_expr_scopes(*expr, body, scopes, scope); compute_expr_scopes(*expr, body, scopes, &mut scope);
} }
scope = scopes.new_scope(scope); scope = scopes.new_scope(scope);
scopes.add_bindings(body, scope, *pat); scopes.add_bindings(body, scope, *pat);
} }
Statement::Expr { expr, .. } => { Statement::Expr { expr, .. } => {
compute_expr_scopes(*expr, body, scopes, scope); compute_expr_scopes(*expr, body, scopes, &mut scope);
} }
} }
} }
if let Some(expr) = tail { if let Some(expr) = tail {
compute_expr_scopes(expr, body, scopes, scope); compute_expr_scopes(expr, body, scopes, &mut scope);
} }
} }
fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) { fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId) {
let make_label = let make_label =
|label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone())); |label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone()));
scopes.set_scope(expr, scope); scopes.set_scope(expr, *scope);
match &body[expr] { match &body[expr] {
Expr::Block { statements, tail, id, label } => { Expr::Block { statements, tail, id, label } => {
let scope = scopes.new_block_scope(scope, *id, make_label(label)); let scope = scopes.new_block_scope(*scope, *id, make_label(label));
// Overwrite the old scope for the block expr, so that every block scope can be found // Overwrite the old scope for the block expr, so that every block scope can be found
// via the block itself (important for blocks that only contain items, no expressions). // via the block itself (important for blocks that only contain items, no expressions).
scopes.set_scope(expr, scope); scopes.set_scope(expr, scope);
@ -184,46 +184,49 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
} }
Expr::For { iterable, pat, body: body_expr, label } => { Expr::For { iterable, pat, body: body_expr, label } => {
compute_expr_scopes(*iterable, body, scopes, scope); compute_expr_scopes(*iterable, body, scopes, scope);
let scope = scopes.new_labeled_scope(scope, make_label(label)); let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
scopes.add_bindings(body, scope, *pat); scopes.add_bindings(body, scope, *pat);
compute_expr_scopes(*body_expr, body, scopes, scope); compute_expr_scopes(*body_expr, body, scopes, &mut scope);
} }
Expr::While { condition, body: body_expr, label } => { Expr::While { condition, body: body_expr, label } => {
let scope = scopes.new_labeled_scope(scope, make_label(label)); let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
compute_expr_scopes(*condition, body, scopes, scope); compute_expr_scopes(*condition, body, scopes, &mut scope);
compute_expr_scopes(*body_expr, body, scopes, scope); compute_expr_scopes(*body_expr, body, scopes, &mut scope);
} }
Expr::Loop { body: body_expr, label } => { Expr::Loop { body: body_expr, label } => {
let scope = scopes.new_labeled_scope(scope, make_label(label)); let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
compute_expr_scopes(*body_expr, body, scopes, scope); compute_expr_scopes(*body_expr, body, scopes, &mut scope);
} }
Expr::Lambda { args, body: body_expr, .. } => { Expr::Lambda { args, body: body_expr, .. } => {
let scope = scopes.new_scope(scope); let mut scope = scopes.new_scope(*scope);
scopes.add_params_bindings(body, scope, args); scopes.add_params_bindings(body, scope, args);
compute_expr_scopes(*body_expr, body, scopes, scope); compute_expr_scopes(*body_expr, body, scopes, &mut 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.iter() { for arm in arms.iter() {
let mut 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);
match arm.guard { if let Some(guard) = arm.guard {
Some(MatchGuard::If { expr: guard }) => { scope = scopes.new_scope(scope);
scopes.set_scope(guard, scope); compute_expr_scopes(guard, body, scopes, &mut scope);
compute_expr_scopes(guard, body, scopes, scope); }
} compute_expr_scopes(arm.expr, body, scopes, &mut 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);
} }
} }
&Expr::If { condition, then_branch, else_branch } => {
let mut then_branch_scope = scopes.new_scope(*scope);
compute_expr_scopes(condition, body, scopes, &mut then_branch_scope);
compute_expr_scopes(then_branch, body, scopes, &mut then_branch_scope);
if let Some(else_branch) = else_branch {
compute_expr_scopes(else_branch, body, scopes, scope);
}
}
&Expr::Let { pat, expr } => {
compute_expr_scopes(expr, body, scopes, scope);
*scope = scopes.new_scope(*scope);
scopes.add_bindings(body, *scope, pat);
}
e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)), e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
}; };
} }
@ -500,8 +503,7 @@ fn foo() {
} }
#[test] #[test]
fn while_let_desugaring() { fn while_let_adds_binding() {
cov_mark::check!(infer_resolve_while_let);
do_check_local_name( do_check_local_name(
r#" r#"
fn test() { fn test() {
@ -513,5 +515,53 @@ fn test() {
"#, "#,
75, 75,
); );
do_check_local_name(
r#"
fn test() {
let foo: Option<f32> = None;
while (((let Option::Some(_) = foo))) && let Option::Some(spam) = foo {
spam$0
}
}
"#,
107,
);
}
#[test]
fn match_guard_if_let() {
do_check_local_name(
r#"
fn test() {
let foo: Option<f32> = None;
match foo {
_ if let Option::Some(spam) = foo => spam$0,
}
}
"#,
93,
);
}
#[test]
fn let_chains_can_reference_previous_lets() {
do_check_local_name(
r#"
fn test() {
let foo: Option<i32> = None;
if let Some(spam) = foo && spa$0m > 1 && let Some(spam) = foo && spam > 1 {}
}
"#,
61,
);
do_check_local_name(
r#"
fn test() {
let foo: Option<i32> = None;
if let Some(spam) = foo && spam > 1 && let Some(spam) = foo && sp$0am > 1 {}
}
"#,
100,
);
} }
} }

View file

@ -59,6 +59,10 @@ pub enum Expr {
then_branch: ExprId, then_branch: ExprId,
else_branch: Option<ExprId>, else_branch: Option<ExprId>,
}, },
Let {
pat: PatId,
expr: ExprId,
},
Block { Block {
id: BlockId, id: BlockId,
statements: Box<[Statement]>, statements: Box<[Statement]>,
@ -189,17 +193,10 @@ 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<MatchGuard>, pub guard: Option<ExprId>,
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,
@ -232,6 +229,9 @@ impl Expr {
f(else_branch); f(else_branch);
} }
} }
Expr::Let { expr, .. } => {
f(*expr);
}
Expr::Block { statements, tail, .. } => { Expr::Block { statements, tail, .. } => {
for stmt in statements.iter() { for stmt in statements.iter() {
match stmt { match stmt {

View file

@ -108,18 +108,18 @@ fn expansion_does_not_parse_as_expression() {
check( check(
r#" r#"
macro_rules! stmts { macro_rules! stmts {
() => { let _ = 0; } () => { fn foo() {} }
} }
fn f() { let _ = stmts!/*+errors*/(); } fn f() { let _ = stmts!/*+errors*/(); }
"#, "#,
expect![[r#" expect![[r#"
macro_rules! stmts { macro_rules! stmts {
() => { let _ = 0; } () => { fn foo() {} }
} }
fn f() { let _ = /* parse error: expected expression */ fn f() { let _ = /* parse error: expected expression */
let _ = 0;; } fn foo() {}; }
"#]], "#]],
) )
} }

View file

@ -811,10 +811,10 @@ impl ExpandTo {
MACRO_TYPE => ExpandTo::Type, MACRO_TYPE => ExpandTo::Type,
ARG_LIST | TRY_EXPR | TUPLE_EXPR | PAREN_EXPR | ARRAY_EXPR | FOR_EXPR | PATH_EXPR ARG_LIST | TRY_EXPR | TUPLE_EXPR | PAREN_EXPR | ARRAY_EXPR | FOR_EXPR | PATH_EXPR
| CLOSURE_EXPR | CONDITION | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM | CLOSURE_EXPR | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM | MATCH_GUARD
| MATCH_GUARD | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR
| FIELD_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR
| BIN_EXPR => ExpandTo::Expr, | LET_EXPR => ExpandTo::Expr,
LET_STMT => { LET_STMT => {
// FIXME: Handle LHS Pattern // FIXME: Handle LHS Pattern
ExpandTo::Expr ExpandTo::Expr

View file

@ -8,10 +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::{ expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp},
ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, MatchGuard, Ordering, Statement,
UnaryOp,
},
path::{GenericArg, GenericArgs}, path::{GenericArg, GenericArgs},
resolver::resolver_for_expr, resolver::resolver_for_expr,
FieldId, FunctionId, ItemContainerId, Lookup, FieldId, FunctionId, ItemContainerId, Lookup,
@ -158,6 +155,11 @@ impl<'a> InferenceContext<'a> {
coerce.complete() coerce.complete()
} }
&Expr::Let { pat, expr } => {
let input_ty = self.infer_expr(expr, &Expectation::none());
self.infer_pat(pat, &input_ty, BindingMode::default());
TyKind::Scalar(Scalar::Bool).intern(Interner)
}
Expr::Block { statements, tail, label, id: _ } => { Expr::Block { statements, tail, label, id: _ } => {
let old_resolver = mem::replace( let old_resolver = mem::replace(
&mut self.resolver, &mut self.resolver,
@ -378,20 +380,11 @@ impl<'a> InferenceContext<'a> {
for arm in arms.iter() { for arm in arms.iter() {
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());
match arm.guard { if let Some(guard_expr) = arm.guard {
Some(MatchGuard::If { expr: guard_expr }) => { 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),
),
);
}
Some(MatchGuard::IfLet { expr, pat }) => {
let input_ty = self.infer_expr(expr, &Expectation::none());
let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::default());
}
_ => {}
} }
let arm_ty = self.infer_expr_inner(arm.expr, &expected); let arm_ty = self.infer_expr_inner(arm.expr, &expected);

View file

@ -190,7 +190,6 @@ fn expr_macro_def_expanded_in_various_places() {
!0..6 '1isize': isize !0..6 '1isize': isize
!0..6 '1isize': isize !0..6 '1isize': isize
!0..6 '1isize': isize !0..6 '1isize': isize
!0..6 '1isize': isize
39..442 '{ ...!(); }': () 39..442 '{ ...!(); }': ()
73..94 'spam!(...am!())': {unknown} 73..94 'spam!(...am!())': {unknown}
100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': ()
@ -198,6 +197,7 @@ fn expr_macro_def_expanded_in_various_places() {
117..119 '{}': () 117..119 '{}': ()
124..134 '|| spam!()': || -> isize 124..134 '|| spam!()': || -> isize
140..156 'while ...!() {}': () 140..156 'while ...!() {}': ()
146..153 'spam!()': bool
154..156 '{}': () 154..156 '{}': ()
161..174 'break spam!()': ! 161..174 'break spam!()': !
180..194 'return spam!()': ! 180..194 'return spam!()': !
@ -271,7 +271,6 @@ fn expr_macro_rules_expanded_in_various_places() {
!0..6 '1isize': isize !0..6 '1isize': isize
!0..6 '1isize': isize !0..6 '1isize': isize
!0..6 '1isize': isize !0..6 '1isize': isize
!0..6 '1isize': isize
53..456 '{ ...!(); }': () 53..456 '{ ...!(); }': ()
87..108 'spam!(...am!())': {unknown} 87..108 'spam!(...am!())': {unknown}
114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': ()
@ -279,6 +278,7 @@ fn expr_macro_rules_expanded_in_various_places() {
131..133 '{}': () 131..133 '{}': ()
138..148 '|| spam!()': || -> isize 138..148 '|| spam!()': || -> isize
154..170 'while ...!() {}': () 154..170 'while ...!() {}': ()
160..167 'spam!()': bool
168..170 '{}': () 168..170 '{}': ()
175..188 'break spam!()': ! 175..188 'break spam!()': !
194..208 'return spam!()': ! 194..208 'return spam!()': !

View file

@ -55,6 +55,7 @@ fn infer_pattern() {
139..140 'g': {unknown} 139..140 'g': {unknown}
143..144 'e': {unknown} 143..144 'e': {unknown}
157..204 'if let... }': () 157..204 'if let... }': ()
160..175 'let [val] = opt': bool
164..169 '[val]': [{unknown}] 164..169 '[val]': [{unknown}]
165..168 'val': {unknown} 165..168 'val': {unknown}
172..175 'opt': [{unknown}] 172..175 'opt': [{unknown}]
@ -62,6 +63,7 @@ fn infer_pattern() {
190..191 'h': {unknown} 190..191 'h': {unknown}
194..197 'val': {unknown} 194..197 'val': {unknown}
210..236 'if let...rue {}': () 210..236 'if let...rue {}': ()
213..233 'let x ... &true': bool
217..225 'x @ true': &bool 217..225 'x @ true': &bool
221..225 'true': bool 221..225 'true': bool
221..225 'true': bool 221..225 'true': bool
@ -111,36 +113,42 @@ fn infer_literal_pattern() {
37..38 'x': &i32 37..38 'x': &i32
46..208 '{ ...) {} }': () 46..208 '{ ...) {} }': ()
52..75 'if let...y() {}': () 52..75 'if let...y() {}': ()
55..72 'let "f... any()': bool
59..64 '"foo"': &str 59..64 '"foo"': &str
59..64 '"foo"': &str 59..64 '"foo"': &str
67..70 'any': fn any<&str>() -> &str 67..70 'any': fn any<&str>() -> &str
67..72 'any()': &str 67..72 'any()': &str
73..75 '{}': () 73..75 '{}': ()
80..99 'if let...y() {}': () 80..99 'if let...y() {}': ()
83..96 'let 1 = any()': bool
87..88 '1': i32 87..88 '1': i32
87..88 '1': i32 87..88 '1': i32
91..94 'any': fn any<i32>() -> i32 91..94 'any': fn any<i32>() -> i32
91..96 'any()': i32 91..96 'any()': i32
97..99 '{}': () 97..99 '{}': ()
104..126 'if let...y() {}': () 104..126 'if let...y() {}': ()
107..123 'let 1u... any()': bool
111..115 '1u32': u32 111..115 '1u32': u32
111..115 '1u32': u32 111..115 '1u32': u32
118..121 'any': fn any<u32>() -> u32 118..121 'any': fn any<u32>() -> u32
118..123 'any()': u32 118..123 'any()': u32
124..126 '{}': () 124..126 '{}': ()
131..153 'if let...y() {}': () 131..153 'if let...y() {}': ()
134..150 'let 1f... any()': bool
138..142 '1f32': f32 138..142 '1f32': f32
138..142 '1f32': f32 138..142 '1f32': f32
145..148 'any': fn any<f32>() -> f32 145..148 'any': fn any<f32>() -> f32
145..150 'any()': f32 145..150 'any()': f32
151..153 '{}': () 151..153 '{}': ()
158..179 'if let...y() {}': () 158..179 'if let...y() {}': ()
161..176 'let 1.0 = any()': bool
165..168 '1.0': f64 165..168 '1.0': f64
165..168 '1.0': f64 165..168 '1.0': f64
171..174 'any': fn any<f64>() -> f64 171..174 'any': fn any<f64>() -> f64
171..176 'any()': f64 171..176 'any()': f64
177..179 '{}': () 177..179 '{}': ()
184..206 'if let...y() {}': () 184..206 'if let...y() {}': ()
187..203 'let tr... any()': bool
191..195 'true': bool 191..195 'true': bool
191..195 'true': bool 191..195 'true': bool
198..201 'any': fn any<bool>() -> bool 198..201 'any': fn any<bool>() -> bool
@ -163,10 +171,12 @@ fn infer_range_pattern() {
8..9 'x': &i32 8..9 'x': &i32
17..75 '{ ...2 {} }': () 17..75 '{ ...2 {} }': ()
23..45 'if let...u32 {}': () 23..45 'if let...u32 {}': ()
26..42 'let 1....= 2u32': bool
30..35 '1..76': u32 30..35 '1..76': u32
38..42 '2u32': u32 38..42 '2u32': u32
43..45 '{}': () 43..45 '{}': ()
50..73 'if let...u32 {}': () 50..73 'if let...u32 {}': ()
53..70 'let 1....= 2u32': bool
57..63 '1..=76': u32 57..63 '1..=76': u32
66..70 '2u32': u32 66..70 '2u32': u32
71..73 '{}': () 71..73 '{}': ()

View file

@ -2248,6 +2248,7 @@ fn generic_default_in_struct_literal() {
176..193 'Thing ...1i32 }': Thing<i32> 176..193 'Thing ...1i32 }': Thing<i32>
187..191 '1i32': i32 187..191 '1i32': i32
199..240 'if let... }': () 199..240 'if let... }': ()
202..221 'let Th... } = z': bool
206..217 'Thing { t }': Thing<i32> 206..217 'Thing { t }': Thing<i32>
214..215 't': i32 214..215 't': i32
220..221 'z': Thing<i32> 220..221 'z': Thing<i32>

View file

@ -18,7 +18,7 @@ use syntax::{
algo, ast, algo, ast,
display::{fn_as_proc_macro_label, macro_label}, display::{fn_as_proc_macro_label, macro_label},
match_ast, AstNode, Direction, match_ast, AstNode, Direction,
SyntaxKind::{CONDITION, LET_STMT}, SyntaxKind::{LET_EXPR, LET_STMT},
SyntaxToken, T, SyntaxToken, T,
}; };
@ -484,7 +484,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
let let_kw = if ident let let_kw = if ident
.syntax() .syntax()
.parent() .parent()
.map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION) .map_or(false, |p| p.kind() == LET_STMT || p.kind() == LET_EXPR)
{ {
"let " "let "
} else { } else {

View file

@ -243,7 +243,7 @@ fn is_named_constructor(
let expr = match_ast! { let expr = match_ast! {
match let_node { match let_node {
ast::LetStmt(it) => it.initializer(), ast::LetStmt(it) => it.initializer(),
ast::Condition(it) => it.expr(), ast::LetExpr(it) => it.expr(),
_ => None, _ => None,
} }
}?; }?;
@ -372,15 +372,10 @@ fn should_not_display_type_hint(
match node { match node {
ast::LetStmt(it) => return it.ty().is_some(), ast::LetStmt(it) => return it.ty().is_some(),
ast::Param(it) => return it.ty().is_some(), ast::Param(it) => return it.ty().is_some(),
ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty), ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
ast::IfExpr(it) => { ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
return it.condition().and_then(|condition| condition.pat()).is_some() ast::IfExpr(_) => return false,
&& pat_is_enum_variant(db, bind_pat, pat_ty); ast::WhileExpr(_) => return false,
},
ast::WhileExpr(it) => {
return it.condition().and_then(|condition| condition.pat()).is_some()
&& pat_is_enum_variant(db, bind_pat, pat_ty);
},
ast::ForExpr(it) => { ast::ForExpr(it) => {
// We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit). // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
// Type of expr should be iterable. // Type of expr should be iterable.

View file

@ -2,7 +2,7 @@ use hir::{known, AsAssocItem, Semantics};
use ide_db::{ use ide_db::{
helpers::{ helpers::{
for_each_tail_expr, for_each_tail_expr,
node_ext::{block_as_lone_tail, preorder_expr}, node_ext::{block_as_lone_tail, is_pattern_cond, preorder_expr},
FamousDefs, FamousDefs,
}, },
RootDatabase, RootDatabase,
@ -45,8 +45,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) ->
return None; return None;
} }
let cond = expr.condition().filter(|cond| !cond.is_pattern_cond())?; let cond = expr.condition().filter(|cond| !is_pattern_cond(cond.clone()))?;
let cond = cond.expr()?;
let then = expr.then_branch()?; let then = expr.then_branch()?;
let else_ = match expr.else_branch()? { let else_ = match expr.else_branch()? {
ast::ElseBranch::Block(b) => b, ast::ElseBranch::Block(b) => b,
@ -209,7 +208,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext) ->
_ => receiver, _ => receiver,
}; };
let if_expr = make::expr_if( let if_expr = make::expr_if(
make::condition(cond, None), cond,
closure_body.reset_indent(), closure_body.reset_indent(),
Some(ast::ElseBranch::Block(make::block_expr(None, Some(none_path)))), Some(ast::ElseBranch::Block(make::block_expr(None, Some(none_path)))),
) )

View file

@ -1,5 +1,6 @@
use std::iter::once; use std::iter::once;
use ide_db::helpers::node_ext::{is_pattern_cond, single_let};
use syntax::{ use syntax::{
ast::{ ast::{
self, self,
@ -48,25 +49,28 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
let cond = if_expr.condition()?; let cond = if_expr.condition()?;
// Check if there is an IfLet that we can handle. // Check if there is an IfLet that we can handle.
let if_let_pat = match cond.pat() { let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) {
None => None, // No IfLet, supported. let let_ = single_let(cond)?;
Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => { match let_.pat() {
let path = pat.path()?; Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
if path.qualifier().is_some() { let path = pat.path()?;
return None; if path.qualifier().is_some() {
} return None;
}
let bound_ident = pat.fields().next().unwrap(); let bound_ident = pat.fields().next().unwrap();
if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) { if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
return None; return None;
} }
Some((path, bound_ident)) (Some((path, bound_ident)), let_.expr()?)
}
_ => return None, // Unsupported IfLet.
} }
Some(_) => return None, // Unsupported IfLet. } else {
(None, cond)
}; };
let cond_expr = cond.expr()?;
let then_block = if_expr.then_branch()?; let then_block = if_expr.then_branch()?;
let then_block = then_block.stmt_list()?; let then_block = then_block.stmt_list()?;
@ -119,8 +123,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
let then_branch = let then_branch =
make::block_expr(once(make::expr_stmt(early_expression).into()), None); make::block_expr(once(make::expr_stmt(early_expression).into()), None);
let cond = invert_boolean_expression(cond_expr); let cond = invert_boolean_expression(cond_expr);
make::expr_if(make::condition(cond, None), then_branch, None) make::expr_if(cond, then_branch, None).indent(if_indent_level)
.indent(if_indent_level)
}; };
new_expr.syntax().clone_for_update() new_expr.syntax().clone_for_update()
} }

View file

@ -1,5 +1,6 @@
use std::iter::once; use std::iter::once;
use ide_db::helpers::node_ext::is_pattern_cond;
use syntax::{ use syntax::{
ast::{ ast::{
self, self,
@ -42,7 +43,6 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?; let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?;
let while_body = while_expr.loop_body()?; let while_body = while_expr.loop_body()?;
let while_cond = while_expr.condition()?; let while_cond = while_expr.condition()?;
let while_cond_expr = while_cond.expr()?;
let target = while_expr.syntax().text_range(); let target = while_expr.syntax().text_range();
acc.add( acc.add(
@ -55,19 +55,15 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O
let break_block = let break_block =
make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None) make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None)
.indent(while_indent_level); .indent(while_indent_level);
let block_expr = match while_cond.pat() { let block_expr = if is_pattern_cond(while_cond.clone()) {
Some(_) => { let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into())); let stmts = once(make::expr_stmt(if_expr).into());
let stmts = once(make::expr_stmt(if_expr).into()); make::block_expr(stmts, None)
make::block_expr(stmts, None) } else {
} let if_cond = invert_boolean_expression(while_cond);
None => { let if_expr = make::expr_if(if_cond, break_block, None);
let if_cond = make::condition(invert_boolean_expression(while_cond_expr), None); let stmts = once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
let if_expr = make::expr_if(if_cond, break_block, None); make::block_expr(stmts, while_body.tail_expr())
let stmts =
once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
make::block_expr(stmts, while_body.tail_expr())
}
}; };
let replacement = make::expr_loop(block_expr.indent(while_indent_level)); let replacement = make::expr_loop(block_expr.indent(while_indent_level));

View file

@ -1219,28 +1219,26 @@ impl FlowHandler {
let stmt = make::expr_stmt(action); let stmt = make::expr_stmt(action);
let block = make::block_expr(iter::once(stmt.into()), None); let block = make::block_expr(iter::once(stmt.into()), None);
let controlflow_break_path = make::path_from_text("ControlFlow::Break"); let controlflow_break_path = make::path_from_text("ControlFlow::Break");
let condition = make::condition( let condition = make::expr_let(
make::tuple_struct_pat(
controlflow_break_path,
iter::once(make::wildcard_pat().into()),
)
.into(),
call_expr, call_expr,
Some(
make::tuple_struct_pat(
controlflow_break_path,
iter::once(make::wildcard_pat().into()),
)
.into(),
),
); );
make::expr_if(condition, block, None) make::expr_if(condition.into(), block, None)
} }
FlowHandler::IfOption { action } => { FlowHandler::IfOption { action } => {
let path = make::ext::ident_path("Some"); let path = make::ext::ident_path("Some");
let value_pat = make::ext::simple_ident_pat(make::name("value")); let value_pat = make::ext::simple_ident_pat(make::name("value"));
let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
let cond = make::condition(call_expr, Some(pattern.into())); let cond = make::expr_let(pattern.into(), call_expr);
let value = make::expr_path(make::ext::ident_path("value")); let value = make::expr_path(make::ext::ident_path("value"));
let action_expr = action.make_result_handler(Some(value)); let action_expr = action.make_result_handler(Some(value));
let action_stmt = make::expr_stmt(action_expr); let action_stmt = make::expr_stmt(action_expr);
let then = make::block_expr(iter::once(action_stmt.into()), None); let then = make::block_expr(iter::once(action_stmt.into()), None);
make::expr_if(cond, then, None) make::expr_if(cond.into(), then, None)
} }
FlowHandler::MatchOption { none } => { FlowHandler::MatchOption { none } => {
let some_name = "value"; let some_name = "value";

View file

@ -1,3 +1,4 @@
use ide_db::helpers::node_ext::is_pattern_cond;
use syntax::{ use syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
T, T,
@ -34,12 +35,12 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
return None; return None;
} }
let cond = expr.condition()?;
// This assist should not apply for if-let. // This assist should not apply for if-let.
if expr.condition()?.is_pattern_cond() { if is_pattern_cond(cond.clone()) {
return None; return None;
} }
let cond = expr.condition()?.expr()?;
let then_node = expr.then_branch()?.syntax().clone(); let then_node = expr.then_branch()?.syntax().clone();
let else_block = match expr.else_branch()? { let else_block = match expr.else_branch()? {
ast::ElseBranch::Block(it) => it, ast::ElseBranch::Block(it) => it,

View file

@ -1,8 +1,5 @@
use syntax::{ use syntax::{
ast::{ ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat},
edit::AstNodeEdit, make, AstNode, BlockExpr, Condition, ElseBranch, Expr, IfExpr, MatchArm,
Pat,
},
SyntaxKind::WHITESPACE, SyntaxKind::WHITESPACE,
}; };
@ -44,18 +41,11 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
} }
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 let guard_condition = guard.condition()?;
if guard.let_token().is_some() {
return None;
}
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::condition(guard_condition, None), make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None)
make::block_expr(None, Some(arm_expr.clone())), .indent(arm_expr.indent_level());
None,
)
.indent(arm_expr.indent_level());
let target = guard.syntax().text_range(); let target = guard.syntax().text_range();
acc.add( acc.add(
@ -193,17 +183,13 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
) )
} }
// Parses an if-else-if chain to get the conditons and the then branches until we encounter an else // Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
// branch or the end. // branch or the end.
fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Condition, BlockExpr)>, Option<BlockExpr>)> { fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
let mut conds_blocks = Vec::new(); let mut conds_blocks = Vec::new();
let mut curr_if = if_expr; let mut curr_if = if_expr;
let tail = loop { let tail = loop {
let cond = curr_if.condition()?; let cond = curr_if.condition()?;
// Not support moving if let to arm guard
if cond.is_pattern_cond() {
return None;
}
conds_blocks.push((cond, curr_if.then_branch()?)); conds_blocks.push((cond, curr_if.then_branch()?));
match curr_if.else_branch() { match curr_if.else_branch() {
Some(ElseBranch::IfExpr(e)) => { Some(ElseBranch::IfExpr(e)) => {
@ -280,6 +266,31 @@ fn main() {
); );
} }
#[test]
fn move_let_guard_to_arm_body_works() {
check_assist(
move_guard_to_arm_body,
r#"
fn main() {
match 92 {
x $0if (let 1 = x) => false,
_ => true
}
}
"#,
r#"
fn main() {
match 92 {
x => if (let 1 = x) {
false
},
_ => true
}
}
"#,
);
}
#[test] #[test]
fn move_guard_to_arm_body_works_complex_match() { fn move_guard_to_arm_body_works_complex_match() {
check_assist( check_assist(
@ -440,13 +451,21 @@ fn main() {
} }
#[test] #[test]
fn move_arm_cond_to_match_guard_if_let_not_works() { fn move_arm_cond_to_match_guard_if_let_works() {
check_assist_not_applicable( check_assist(
move_arm_cond_to_match_guard, move_arm_cond_to_match_guard,
r#" r#"
fn main() { fn main() {
match 92 { match 92 {
x => if let 62 = x { $0false }, x => if let 62 = x && true { $0false },
_ => true
}
}
"#,
r#"
fn main() {
match 92 {
x if let 62 = x && true => false,
_ => true _ => true
} }
} }
@ -898,7 +917,7 @@ fn main() {
#[test] #[test]
fn move_arm_cond_to_match_guard_elseif_iflet() { fn move_arm_cond_to_match_guard_elseif_iflet() {
check_assist_not_applicable( check_assist(
move_arm_cond_to_match_guard, move_arm_cond_to_match_guard,
r#" r#"
fn main() { fn main() {
@ -915,9 +934,21 @@ fn main() {
4 4
}, },
} }
} }"#,
"#, r#"
) fn main() {
match 92 {
3 => 0,
x if x > 10 => 1,
x if x > 5 => 2,
x if let 4 = 4 => {
42;
3
}
x => 4,
}
}"#,
);
} }
#[test] #[test]

View file

@ -1,7 +1,12 @@
use std::iter::{self, successors}; use std::iter::{self, successors};
use either::Either; use either::Either;
use ide_db::{defs::NameClass, ty_filter::TryEnum, RootDatabase}; use ide_db::{
defs::NameClass,
helpers::node_ext::{is_pattern_cond, single_let},
ty_filter::TryEnum,
RootDatabase,
};
use syntax::{ use syntax::{
ast::{ ast::{
self, self,
@ -60,15 +65,22 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
None None
} }
}); });
let scrutinee_to_be_expr = if_expr.condition()?.expr()?; let scrutinee_to_be_expr = if_expr.condition()?;
let scrutinee_to_be_expr = match single_let(scrutinee_to_be_expr.clone()) {
Some(cond) => cond.expr()?,
None => scrutinee_to_be_expr,
};
let mut pat_seen = false; let mut pat_seen = false;
let mut cond_bodies = Vec::new(); let mut cond_bodies = Vec::new();
for if_expr in if_exprs { for if_expr in if_exprs {
let cond = if_expr.condition()?; let cond = if_expr.condition()?;
let expr = cond.expr()?; let cond = match single_let(cond.clone()) {
let cond = match cond.pat() { Some(let_) => {
Some(pat) => { let pat = let_.pat()?;
let expr = let_.expr()?;
// FIXME: If one `let` is wrapped in parentheses and the second is not,
// we'll exit here.
if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() { if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
// Only if all condition expressions are equal we can merge them into a match // Only if all condition expressions are equal we can merge them into a match
return None; return None;
@ -76,7 +88,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
pat_seen = true; pat_seen = true;
Either::Left(pat) Either::Left(pat)
} }
None => Either::Right(expr), // Multiple `let`, unsupported.
None if is_pattern_cond(cond.clone()) => return None,
None => Either::Right(cond),
}; };
let body = if_expr.then_branch()?; let body = if_expr.then_branch()?;
cond_bodies.push((cond, body)); cond_bodies.push((cond, body));
@ -217,11 +231,11 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
} }
} }
let condition = make::condition(scrutinee, Some(if_let_pat)); let condition = make::expr_let(if_let_pat, scrutinee);
let then_block = make_block_expr(then_expr.reset_indent()); let then_block = make_block_expr(then_expr.reset_indent());
let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
let if_let_expr = make::expr_if( let if_let_expr = make::expr_if(
condition, condition.into(),
then_block, then_block,
else_expr.map(make_block_expr).map(ast::ElseBranch::Block), else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
) )
@ -372,6 +386,18 @@ impl VariantData {
) )
} }
#[test]
fn test_if_let_with_match_let_chain() {
check_assist_not_applicable(
replace_if_let_with_match,
r#"
fn main() {
if $0let true = true && let Some(1) = None {}
}
"#,
)
}
#[test] #[test]
fn test_if_let_with_match_basic() { fn test_if_let_with_match_basic() {
check_assist( check_assist(

View file

@ -62,7 +62,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
let block = let block =
make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax())); make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax()));
let if_ = make::expr_if(make::condition(init, Some(pat)), block, None); let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None);
let stmt = make::expr_stmt(if_); let stmt = make::expr_stmt(if_);
edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));

View file

@ -575,6 +575,14 @@ impl<'a> CompletionContext<'a> {
(ty, name) (ty, name)
}, },
ast::LetExpr(it) => {
cov_mark::hit!(expected_type_if_let_without_leading_char);
let ty = it.pat()
.and_then(|pat| self.sema.type_of_pat(&pat))
.or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
.map(TypeInfo::original);
(ty, None)
},
ast::ArgList(_) => { ast::ArgList(_) => {
cov_mark::hit!(expected_type_fn_param); cov_mark::hit!(expected_type_fn_param);
ActiveParameter::at_token( ActiveParameter::at_token(
@ -641,9 +649,7 @@ impl<'a> CompletionContext<'a> {
(ty, None) (ty, None)
}, },
ast::IfExpr(it) => { ast::IfExpr(it) => {
cov_mark::hit!(expected_type_if_let_without_leading_char);
let ty = it.condition() let ty = it.condition()
.and_then(|cond| cond.expr())
.and_then(|e| self.sema.type_of_expr(&e)) .and_then(|e| self.sema.type_of_expr(&e))
.map(TypeInfo::original); .map(TypeInfo::original);
(ty, None) (ty, None)
@ -939,7 +945,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont
return (PatternRefutability::Irrefutable, has_type_ascription) return (PatternRefutability::Irrefutable, has_type_ascription)
}, },
ast::MatchArm(_) => PatternRefutability::Refutable, ast::MatchArm(_) => PatternRefutability::Refutable,
ast::Condition(_) => PatternRefutability::Refutable, ast::LetExpr(_) => PatternRefutability::Refutable,
ast::ForExpr(_) => PatternRefutability::Irrefutable, ast::ForExpr(_) => PatternRefutability::Irrefutable,
_ => PatternRefutability::Irrefutable, _ => PatternRefutability::Irrefutable,
} }

View file

@ -226,6 +226,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
| ast::Expr::TryExpr(_) | ast::Expr::TryExpr(_)
| ast::Expr::TupleExpr(_) | ast::Expr::TupleExpr(_)
| ast::Expr::WhileExpr(_) | ast::Expr::WhileExpr(_)
| ast::Expr::LetExpr(_)
| ast::Expr::YieldExpr(_) => cb(expr), | ast::Expr::YieldExpr(_) => cb(expr),
} }
} }

View file

@ -216,3 +216,29 @@ pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool {
_ => false, _ => false,
} }
} }
/// Returns the `let` only if there is exactly one (that is, `let pat = expr`
/// or `((let pat = expr))`, but not `let pat = expr && expr` or `non_let_expr`).
pub fn single_let(expr: ast::Expr) -> Option<ast::LetExpr> {
match expr {
ast::Expr::ParenExpr(expr) => expr.expr().and_then(single_let),
ast::Expr::LetExpr(expr) => Some(expr),
_ => None,
}
}
pub fn is_pattern_cond(expr: ast::Expr) -> bool {
match expr {
ast::Expr::BinExpr(expr)
if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) =>
{
expr.lhs()
.map(is_pattern_cond)
.or_else(|| expr.rhs().map(is_pattern_cond))
.unwrap_or(false)
}
ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, is_pattern_cond),
ast::Expr::LetExpr(_) => true,
_ => false,
}
}

View file

@ -29,6 +29,15 @@ fn expr_no_struct(p: &mut Parser) {
expr_bp(p, None, r, 1); expr_bp(p, None, r, 1);
} }
/// Parses the expression in `let pattern = expression`.
/// It needs to be parsed with lower precedence than `&&`, so that
/// `if let true = true && false` is parsed as `if (let true = true) && (true)`
/// and not `if let true = (true && true)`.
fn expr_let(p: &mut Parser) {
let r = Restrictions { forbid_structs: true, prefer_stmt: false };
expr_bp(p, None, r, 5);
}
pub(super) fn stmt(p: &mut Parser, semicolon: Semicolon) { pub(super) fn stmt(p: &mut Parser, semicolon: Semicolon) {
if p.eat(T![;]) { if p.eat(T![;]) {
return; return;
@ -185,6 +194,7 @@ fn current_op(p: &Parser) -> (u8, SyntaxKind) {
T![%] if p.at(T![%=]) => (1, T![%=]), T![%] if p.at(T![%=]) => (1, T![%=]),
T![%] => (11, T![%]), T![%] => (11, T![%]),
T![&] if p.at(T![&=]) => (1, T![&=]), T![&] if p.at(T![&=]) => (1, T![&=]),
// If you update this, remember to update `expr_let()` too.
T![&] if p.at(T![&&]) => (4, T![&&]), T![&] if p.at(T![&&]) => (4, T![&&]),
T![&] => (8, T![&]), T![&] => (8, T![&]),
T![/] if p.at(T![/=]) => (1, T![/=]), T![/] if p.at(T![/=]) => (1, T![/=]),

View file

@ -79,6 +79,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar
closure_expr(p) closure_expr(p)
} }
T![if] => if_expr(p), T![if] => if_expr(p),
T![let] => let_expr(p),
T![loop] => loop_expr(p, None), T![loop] => loop_expr(p, None),
T![box] => box_expr(p, None), T![box] => box_expr(p, None),
@ -286,7 +287,7 @@ fn if_expr(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]);
condition(p); expr_no_struct(p);
block_expr(p); block_expr(p);
if p.at(T![else]) { if p.at(T![else]) {
p.bump(T![else]); p.bump(T![else]);
@ -335,7 +336,7 @@ fn while_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
assert!(p.at(T![while])); assert!(p.at(T![while]));
let m = m.unwrap_or_else(|| p.start()); let m = m.unwrap_or_else(|| p.start());
p.bump(T![while]); p.bump(T![while]);
condition(p); expr_no_struct(p);
block_expr(p); block_expr(p);
m.complete(p, WHILE_EXPR) m.complete(p, WHILE_EXPR)
} }
@ -355,22 +356,18 @@ fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
m.complete(p, FOR_EXPR) m.complete(p, FOR_EXPR)
} }
// test cond // test let_expr
// fn foo() { if let Some(_) = None {} } // fn foo() {
// fn bar() { // if let Some(_) = None && true {}
// if let Some(_) | Some(_) = None {} // while 1 == 5 && (let None = None) {}
// if let | Some(_) = None {}
// while let Some(_) | Some(_) = None {}
// while let | Some(_) = None {}
// } // }
fn condition(p: &mut Parser) { fn let_expr(p: &mut Parser) -> CompletedMarker {
let m = p.start(); let m = p.start();
if p.eat(T![let]) { p.bump(T![let]);
patterns::pattern_top(p); patterns::pattern_top(p);
p.expect(T![=]); p.expect(T![=]);
} expr_let(p);
expr_no_struct(p); m.complete(p, LET_EXPR)
m.complete(p, CONDITION);
} }
// test match_expr // test match_expr
@ -482,10 +479,6 @@ 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

@ -178,7 +178,6 @@ pub enum SyntaxKind {
CLOSURE_EXPR, CLOSURE_EXPR,
IF_EXPR, IF_EXPR,
WHILE_EXPR, WHILE_EXPR,
CONDITION,
LOOP_EXPR, LOOP_EXPR,
FOR_EXPR, FOR_EXPR,
CONTINUE_EXPR, CONTINUE_EXPR,
@ -188,6 +187,7 @@ pub enum SyntaxKind {
STMT_LIST, STMT_LIST,
RETURN_EXPR, RETURN_EXPR,
YIELD_EXPR, YIELD_EXPR,
LET_EXPR,
MATCH_EXPR, MATCH_EXPR,
MATCH_ARM_LIST, MATCH_ARM_LIST,
MATCH_ARM, MATCH_ARM,

View file

@ -289,17 +289,19 @@ fn expr() {
TopEntryPoint::Expr, TopEntryPoint::Expr,
"let _ = 0;", "let _ = 0;",
expect![[r#" expect![[r#"
ERROR ERROR
LET_KW "let" LET_EXPR
WHITESPACE " " LET_KW "let"
UNDERSCORE "_" WHITESPACE " "
WHITESPACE " " WILDCARD_PAT
EQ "=" UNDERSCORE "_"
WHITESPACE " " WHITESPACE " "
INT_NUMBER "0" EQ "="
SEMICOLON ";" WHITESPACE " "
error 0: expected expression LITERAL
"#]], INT_NUMBER "0"
SEMICOLON ";"
"#]],
); );
} }

View file

@ -29,9 +29,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -1,5 +1,5 @@
fn foo() { fn foo() {
let foo = let foo = 11
let bar = 1; let bar = 1;
let let
let baz = 92; let baz = 92;

View file

@ -20,6 +20,9 @@ SOURCE_FILE
IDENT "foo" IDENT "foo"
WHITESPACE " " WHITESPACE " "
EQ "=" EQ "="
WHITESPACE " "
LITERAL
INT_NUMBER "11"
WHITESPACE "\n " WHITESPACE "\n "
LET_STMT LET_STMT
LET_KW "let" LET_KW "let"
@ -57,9 +60,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -73,9 +75,8 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -95,13 +96,12 @@ SOURCE_FILE
WHITESPACE "\n" WHITESPACE "\n"
R_CURLY "}" R_CURLY "}"
WHITESPACE "\n" WHITESPACE "\n"
error 24: expected expression error 27: expected SEMICOLON
error 24: expected SEMICOLON error 52: expected pattern
error 49: expected pattern error 52: expected SEMICOLON
error 49: expected SEMICOLON error 78: expected pattern
error 75: expected pattern error 78: expected SEMICOLON
error 75: expected SEMICOLON error 101: expected pattern
error 98: expected pattern error 101: expected SEMICOLON
error 98: expected SEMICOLON error 127: expected pattern
error 124: expected pattern error 127: expected SEMICOLON
error 124: expected SEMICOLON

View file

@ -180,116 +180,118 @@ SOURCE_FILE
ERROR ERROR
PLUS "+" PLUS "+"
WHITESPACE " " WHITESPACE " "
EXPR_STMT TUPLE_EXPR
TUPLE_EXPR L_PAREN "("
L_PAREN "(" FOR_EXPR
FOR_EXPR FOR_KW "for"
FOR_KW "for" PATH_PAT
PATH_PAT PATH
PATH PATH_SEGMENT
PATH_SEGMENT L_ANGLE "<"
L_ANGLE "<" ERROR
ERROR LIFETIME_IDENT "'a"
LIFETIME_IDENT "'a" R_ANGLE ">"
R_ANGLE ">" WHITESPACE " "
WHITESPACE " " BIN_EXPR
BIN_EXPR BIN_EXPR
BIN_EXPR BIN_EXPR
BIN_EXPR BIN_EXPR
BIN_EXPR
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "Trait"
L_ANGLE "<"
ERROR
LIFETIME_IDENT "'a"
R_ANGLE ">"
ERROR
R_PAREN ")"
WHITESPACE " "
PLUS "+"
WHITESPACE " "
PAREN_EXPR
L_PAREN "("
PATH_EXPR PATH_EXPR
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "Copy" IDENT "Trait"
L_ANGLE "<"
ERROR
LIFETIME_IDENT "'a"
R_ANGLE ">"
ERROR
R_PAREN ")" R_PAREN ")"
R_ANGLE ">" WHITESPACE " "
ERROR PLUS "+"
SEMICOLON ";" WHITESPACE " "
WHITESPACE "\n " PAREN_EXPR
LET_STMT L_PAREN "("
LET_KW "let" PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "Copy"
R_PAREN ")"
R_ANGLE ">"
ERROR
SEMICOLON ";"
WHITESPACE "\n "
LET_EXPR
LET_KW "let"
WHITESPACE " "
WILDCARD_PAT
UNDERSCORE "_"
ERROR
COLON ":"
WHITESPACE " " WHITESPACE " "
WILDCARD_PAT BIN_EXPR
UNDERSCORE "_" BIN_EXPR
COLON ":" PATH_EXPR
WHITESPACE " " PATH
DYN_TRAIT_TYPE PATH_SEGMENT
TYPE_BOUND_LIST NAME_REF
TYPE_BOUND IDENT "Box"
PATH_TYPE L_ANGLE "<"
PATH TUPLE_EXPR
PATH_SEGMENT L_PAREN "("
NAME_REF FOR_EXPR
IDENT "Box" FOR_KW "for"
GENERIC_ARG_LIST PATH_PAT
PATH
PATH_SEGMENT
L_ANGLE "<" L_ANGLE "<"
TYPE_ARG ERROR
PAREN_TYPE LIFETIME_IDENT "'a"
L_PAREN "(" R_ANGLE ">"
FOR_TYPE WHITESPACE " "
FOR_KW "for" BIN_EXPR
GENERIC_PARAM_LIST BIN_EXPR
L_ANGLE "<" BIN_EXPR
LIFETIME_PARAM BIN_EXPR
LIFETIME PATH_EXPR
LIFETIME_IDENT "'a" PATH
R_ANGLE ">" PATH_SEGMENT
WHITESPACE " " NAME_REF
PATH_TYPE IDENT "Trait"
PATH L_ANGLE "<"
PATH_SEGMENT ERROR
NAME_REF LIFETIME_IDENT "'a"
IDENT "Trait" R_ANGLE ">"
GENERIC_ARG_LIST ERROR
L_ANGLE "<" R_PAREN ")"
LIFETIME_ARG WHITESPACE " "
LIFETIME PLUS "+"
LIFETIME_IDENT "'a" WHITESPACE " "
R_ANGLE ">" PAREN_EXPR
R_PAREN ")" L_PAREN "("
WHITESPACE " " PATH_EXPR
PLUS "+" PATH
WHITESPACE " " PATH_SEGMENT
TYPE_BOUND NAME_REF
L_PAREN "(" IDENT "Copy"
PATH_TYPE R_PAREN ")"
PATH WHITESPACE " "
PATH_SEGMENT PLUS "+"
NAME_REF WHITESPACE " "
IDENT "Copy" PAREN_EXPR
R_PAREN ")" L_PAREN "("
WHITESPACE " " ERROR
PLUS "+" QUESTION "?"
WHITESPACE " " PATH_EXPR
TYPE_BOUND
L_PAREN "("
QUESTION "?"
PATH_TYPE
PATH PATH
PATH_SEGMENT PATH_SEGMENT
NAME_REF NAME_REF
IDENT "Sized" IDENT "Sized"
R_PAREN ")" R_PAREN ")"
ERROR R_ANGLE ">"
R_ANGLE ">" ERROR
SEMICOLON ";" SEMICOLON ";"
WHITESPACE "\n" WHITESPACE "\n"
R_CURLY "}" R_CURLY "}"
WHITESPACE "\n" WHITESPACE "\n"
@ -312,10 +314,18 @@ error 168: expected expression
error 179: expected expression error 179: expected expression
error 180: expected a block error 180: expected a block
error 180: expected COMMA error 180: expected COMMA
error 180: expected expression error 190: expected EQ
error 180: expected R_PAREN error 190: expected expression
error 180: expected SEMICOLON error 191: expected COMMA
error 215: expected COMMA error 201: expected type
error 215: expected R_ANGLE error 204: expected IN_KW
error 235: expected SEMICOLON error 211: expected expression
error 235: expected expression error 214: expected expression
error 228: expected expression
error 229: expected R_PAREN
error 229: expected a block
error 229: expected COMMA
error 236: expected expression
error 237: expected COMMA
error 237: expected expression
error 237: expected R_PAREN

View file

@ -1,209 +0,0 @@
SOURCE_FILE@0..197
FN@0..37
FN_KW@0..2 "fn"
WHITESPACE@2..3 " "
NAME@3..6
IDENT@3..6 "foo"
PARAM_LIST@6..8
L_PAREN@6..7 "("
R_PAREN@7..8 ")"
WHITESPACE@8..9 " "
BLOCK_EXPR@9..37
STMT_LIST@9..37
L_CURLY@9..10 "{"
WHITESPACE@10..11 " "
IF_EXPR@11..35
IF_KW@11..13 "if"
WHITESPACE@13..14 " "
CONDITION@14..32
LET_KW@14..17 "let"
WHITESPACE@17..18 " "
TUPLE_STRUCT_PAT@18..25
PATH@18..22
PATH_SEGMENT@18..22
NAME_REF@18..22
IDENT@18..22 "Some"
L_PAREN@22..23 "("
WILDCARD_PAT@23..24
UNDERSCORE@23..24 "_"
R_PAREN@24..25 ")"
WHITESPACE@25..26 " "
EQ@26..27 "="
WHITESPACE@27..28 " "
PATH_EXPR@28..32
PATH@28..32
PATH_SEGMENT@28..32
NAME_REF@28..32
IDENT@28..32 "None"
WHITESPACE@32..33 " "
BLOCK_EXPR@33..35
STMT_LIST@33..35
L_CURLY@33..34 "{"
R_CURLY@34..35 "}"
WHITESPACE@35..36 " "
R_CURLY@36..37 "}"
WHITESPACE@37..38 "\n"
FN@38..196
FN_KW@38..40 "fn"
WHITESPACE@40..41 " "
NAME@41..44
IDENT@41..44 "bar"
PARAM_LIST@44..46
L_PAREN@44..45 "("
R_PAREN@45..46 ")"
WHITESPACE@46..47 " "
BLOCK_EXPR@47..196
STMT_LIST@47..196
L_CURLY@47..48 "{"
WHITESPACE@48..53 "\n "
EXPR_STMT@53..87
IF_EXPR@53..87
IF_KW@53..55 "if"
WHITESPACE@55..56 " "
CONDITION@56..84
LET_KW@56..59 "let"
WHITESPACE@59..60 " "
OR_PAT@60..77
TUPLE_STRUCT_PAT@60..67
PATH@60..64
PATH_SEGMENT@60..64
NAME_REF@60..64
IDENT@60..64 "Some"
L_PAREN@64..65 "("
WILDCARD_PAT@65..66
UNDERSCORE@65..66 "_"
R_PAREN@66..67 ")"
WHITESPACE@67..68 " "
PIPE@68..69 "|"
WHITESPACE@69..70 " "
TUPLE_STRUCT_PAT@70..77
PATH@70..74
PATH_SEGMENT@70..74
NAME_REF@70..74
IDENT@70..74 "Some"
L_PAREN@74..75 "("
WILDCARD_PAT@75..76
UNDERSCORE@75..76 "_"
R_PAREN@76..77 ")"
WHITESPACE@77..78 " "
EQ@78..79 "="
WHITESPACE@79..80 " "
PATH_EXPR@80..84
PATH@80..84
PATH_SEGMENT@80..84
NAME_REF@80..84
IDENT@80..84 "None"
WHITESPACE@84..85 " "
BLOCK_EXPR@85..87
STMT_LIST@85..87
L_CURLY@85..86 "{"
R_CURLY@86..87 "}"
WHITESPACE@87..92 "\n "
EXPR_STMT@92..118
IF_EXPR@92..118
IF_KW@92..94 "if"
WHITESPACE@94..95 " "
CONDITION@95..115
LET_KW@95..98 "let"
WHITESPACE@98..99 " "
PIPE@99..100 "|"
WHITESPACE@100..101 " "
TUPLE_STRUCT_PAT@101..108
PATH@101..105
PATH_SEGMENT@101..105
NAME_REF@101..105
IDENT@101..105 "Some"
L_PAREN@105..106 "("
WILDCARD_PAT@106..107
UNDERSCORE@106..107 "_"
R_PAREN@107..108 ")"
WHITESPACE@108..109 " "
EQ@109..110 "="
WHITESPACE@110..111 " "
PATH_EXPR@111..115
PATH@111..115
PATH_SEGMENT@111..115
NAME_REF@111..115
IDENT@111..115 "None"
WHITESPACE@115..116 " "
BLOCK_EXPR@116..118
STMT_LIST@116..118
L_CURLY@116..117 "{"
R_CURLY@117..118 "}"
WHITESPACE@118..123 "\n "
EXPR_STMT@123..160
WHILE_EXPR@123..160
WHILE_KW@123..128 "while"
WHITESPACE@128..129 " "
CONDITION@129..157
LET_KW@129..132 "let"
WHITESPACE@132..133 " "
OR_PAT@133..150
TUPLE_STRUCT_PAT@133..140
PATH@133..137
PATH_SEGMENT@133..137
NAME_REF@133..137
IDENT@133..137 "Some"
L_PAREN@137..138 "("
WILDCARD_PAT@138..139
UNDERSCORE@138..139 "_"
R_PAREN@139..140 ")"
WHITESPACE@140..141 " "
PIPE@141..142 "|"
WHITESPACE@142..143 " "
TUPLE_STRUCT_PAT@143..150
PATH@143..147
PATH_SEGMENT@143..147
NAME_REF@143..147
IDENT@143..147 "Some"
L_PAREN@147..148 "("
WILDCARD_PAT@148..149
UNDERSCORE@148..149 "_"
R_PAREN@149..150 ")"
WHITESPACE@150..151 " "
EQ@151..152 "="
WHITESPACE@152..153 " "
PATH_EXPR@153..157
PATH@153..157
PATH_SEGMENT@153..157
NAME_REF@153..157
IDENT@153..157 "None"
WHITESPACE@157..158 " "
BLOCK_EXPR@158..160
STMT_LIST@158..160
L_CURLY@158..159 "{"
R_CURLY@159..160 "}"
WHITESPACE@160..165 "\n "
WHILE_EXPR@165..194
WHILE_KW@165..170 "while"
WHITESPACE@170..171 " "
CONDITION@171..191
LET_KW@171..174 "let"
WHITESPACE@174..175 " "
PIPE@175..176 "|"
WHITESPACE@176..177 " "
TUPLE_STRUCT_PAT@177..184
PATH@177..181
PATH_SEGMENT@177..181
NAME_REF@177..181
IDENT@177..181 "Some"
L_PAREN@181..182 "("
WILDCARD_PAT@182..183
UNDERSCORE@182..183 "_"
R_PAREN@183..184 ")"
WHITESPACE@184..185 " "
EQ@185..186 "="
WHITESPACE@186..187 " "
PATH_EXPR@187..191
PATH@187..191
PATH_SEGMENT@187..191
NAME_REF@187..191
IDENT@187..191 "None"
WHITESPACE@191..192 " "
BLOCK_EXPR@192..194
STMT_LIST@192..194
L_CURLY@192..193 "{"
R_CURLY@193..194 "}"
WHITESPACE@194..195 "\n"
R_CURLY@195..196 "}"
WHITESPACE@196..197 "\n"

View file

@ -1,7 +0,0 @@
fn foo() { if let Some(_) = None {} }
fn bar() {
if let Some(_) | Some(_) = None {}
if let | Some(_) = None {}
while let Some(_) | Some(_) = None {}
while let | Some(_) = None {}
}

View file

@ -1,209 +0,0 @@
SOURCE_FILE
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "foo"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE " "
IF_EXPR
IF_KW "if"
WHITESPACE " "
CONDITION
LET_KW "let"
WHITESPACE " "
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE " "
R_CURLY "}"
WHITESPACE "\n"
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "bar"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
IF_EXPR
IF_KW "if"
WHITESPACE " "
CONDITION
LET_KW "let"
WHITESPACE " "
OR_PAT
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
PIPE "|"
WHITESPACE " "
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n "
EXPR_STMT
IF_EXPR
IF_KW "if"
WHITESPACE " "
CONDITION
LET_KW "let"
WHITESPACE " "
PIPE "|"
WHITESPACE " "
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n "
EXPR_STMT
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
CONDITION
LET_KW "let"
WHITESPACE " "
OR_PAT
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
PIPE "|"
WHITESPACE " "
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n "
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
CONDITION
LET_KW "let"
WHITESPACE " "
PIPE "|"
WHITESPACE " "
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"

View file

@ -0,0 +1,90 @@
SOURCE_FILE
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "foo"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
IF_EXPR
IF_KW "if"
WHITESPACE " "
BIN_EXPR
LET_EXPR
LET_KW "let"
WHITESPACE " "
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
WHITESPACE " "
AMP2 "&&"
WHITESPACE " "
LITERAL
TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n "
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
BIN_EXPR
BIN_EXPR
LITERAL
INT_NUMBER "1"
WHITESPACE " "
EQ2 "=="
WHITESPACE " "
LITERAL
INT_NUMBER "5"
WHITESPACE " "
AMP2 "&&"
WHITESPACE " "
PAREN_EXPR
L_PAREN "("
LET_EXPR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "None"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"

View file

@ -0,0 +1,4 @@
fn foo() {
if let Some(_) = None && true {}
while 1 == 5 && (let None = None) {}
}

View file

@ -0,0 +1,90 @@
SOURCE_FILE
FN
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "foo"
PARAM_LIST
L_PAREN "("
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
WHITESPACE "\n "
EXPR_STMT
IF_EXPR
IF_KW "if"
WHITESPACE " "
BIN_EXPR
LET_EXPR
LET_KW "let"
WHITESPACE " "
TUPLE_STRUCT_PAT
PATH
PATH_SEGMENT
NAME_REF
IDENT "Some"
L_PAREN "("
WILDCARD_PAT
UNDERSCORE "_"
R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
WHITESPACE " "
AMP2 "&&"
WHITESPACE " "
LITERAL
TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n "
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
BIN_EXPR
BIN_EXPR
LITERAL
INT_NUMBER "1"
WHITESPACE " "
EQ2 "=="
WHITESPACE " "
LITERAL
INT_NUMBER "5"
WHITESPACE " "
AMP2 "&&"
WHITESPACE " "
PAREN_EXPR
L_PAREN "("
LET_EXPR
LET_KW "let"
WHITESPACE " "
IDENT_PAT
NAME
IDENT "None"
WHITESPACE " "
EQ "="
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "None"
R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"

View file

@ -16,9 +16,8 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -30,7 +29,7 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION LET_EXPR
LET_KW "let" LET_KW "let"
WHITESPACE " " WHITESPACE " "
TUPLE_STRUCT_PAT TUPLE_STRUCT_PAT
@ -69,15 +68,14 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION BLOCK_EXPR
BLOCK_EXPR STMT_LIST
STMT_LIST L_CURLY "{"
L_CURLY "{" WHITESPACE " "
WHITESPACE " " LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true" WHITESPACE " "
WHITESPACE " " R_CURLY "}"
R_CURLY "}"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -16,9 +16,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -30,9 +29,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -51,9 +49,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -65,9 +62,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL FALSE_KW "false"
FALSE_KW "false"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -86,12 +82,11 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "S"
IDENT "S"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -103,15 +98,14 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION BLOCK_EXPR
BLOCK_EXPR STMT_LIST
STMT_LIST L_CURLY "{"
L_CURLY "{" WHITESPACE " "
WHITESPACE " " LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true" WHITESPACE " "
WHITESPACE " " R_CURLY "}"
R_CURLY "}"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -15,9 +15,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION BREAK_EXPR
BREAK_EXPR BREAK_KW "break"
BREAK_KW "break"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -28,9 +27,8 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION BREAK_EXPR
BREAK_EXPR BREAK_KW "break"
BREAK_KW "break"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -16,9 +16,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -50,9 +49,8 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -35,9 +35,8 @@ SOURCE_FILE
WHITESPACE " " WHITESPACE " "
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -49,19 +49,20 @@ SOURCE_FILE
MATCH_GUARD MATCH_GUARD
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
LET_KW "let" LET_EXPR
WHITESPACE " " LET_KW "let"
IDENT_PAT WHITESPACE " "
NAME IDENT_PAT
IDENT "foo" NAME
WHITESPACE " " IDENT "foo"
EQ "=" WHITESPACE " "
WHITESPACE " " EQ "="
PATH_EXPR WHITESPACE " "
PATH PATH_EXPR
PATH_SEGMENT PATH
NAME_REF PATH_SEGMENT
IDENT "bar" NAME_REF
IDENT "bar"
WHITESPACE " " WHITESPACE " "
FAT_ARROW "=>" FAT_ARROW "=>"
WHITESPACE " " WHITESPACE " "

View file

@ -51,16 +51,15 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION CALL_EXPR
CALL_EXPR PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "condition_not_met"
IDENT "condition_not_met" ARG_LIST
ARG_LIST L_PAREN "("
L_PAREN "(" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -92,16 +91,15 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION CALL_EXPR
CALL_EXPR PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "condition_not_met"
IDENT "condition_not_met" ARG_LIST
ARG_LIST L_PAREN "("
L_PAREN "(" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -153,16 +151,15 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION CALL_EXPR
CALL_EXPR PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "foo"
IDENT "foo" ARG_LIST
ARG_LIST L_PAREN "("
L_PAREN "(" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -187,16 +184,15 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION CALL_EXPR
CALL_EXPR PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "bar"
IDENT "bar" ARG_LIST
ARG_LIST L_PAREN "("
L_PAREN "(" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -280,21 +280,20 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION PREFIX_EXPR
PREFIX_EXPR BANG "!"
BANG "!" METHOD_CALL_EXPR
METHOD_CALL_EXPR PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "x"
IDENT "x" DOT "."
DOT "." NAME_REF
NAME_REF IDENT "get"
IDENT "get" ARG_LIST
ARG_LIST L_PAREN "("
L_PAREN "(" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -443,12 +442,11 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION PAREN_EXPR
PAREN_EXPR L_PAREN "("
L_PAREN "(" RETURN_EXPR
RETURN_EXPR RETURN_KW "return"
RETURN_KW "return" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -457,12 +455,11 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION PAREN_EXPR
PAREN_EXPR L_PAREN "("
L_PAREN "(" RETURN_EXPR
RETURN_EXPR RETURN_KW "return"
RETURN_KW "return" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -495,12 +492,11 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION PAREN_EXPR
PAREN_EXPR L_PAREN "("
L_PAREN "(" RETURN_EXPR
RETURN_EXPR RETURN_KW "return"
RETURN_KW "return" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -549,12 +545,11 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION PAREN_EXPR
PAREN_EXPR L_PAREN "("
L_PAREN "(" RETURN_EXPR
RETURN_EXPR RETURN_KW "return"
RETURN_KW "return" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -572,12 +567,11 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION PAREN_EXPR
PAREN_EXPR L_PAREN "("
L_PAREN "(" RETURN_EXPR
RETURN_EXPR RETURN_KW "return"
RETURN_KW "return" R_PAREN ")"
R_PAREN ")"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -1037,9 +1031,8 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION BREAK_EXPR
BREAK_EXPR BREAK_KW "break"
BREAK_KW "break"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -1089,18 +1082,17 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION BIN_EXPR
BIN_EXPR PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "i"
IDENT "i" WHITESPACE " "
WHITESPACE " " EQ2 "=="
EQ2 "==" WHITESPACE " "
WHITESPACE " " LITERAL
LITERAL INT_NUMBER "1"
INT_NUMBER "1"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST
@ -1344,18 +1336,17 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION BIN_EXPR
BIN_EXPR PATH_EXPR
PATH_EXPR PATH
PATH PATH_SEGMENT
PATH_SEGMENT NAME_REF
NAME_REF IDENT "u8"
IDENT "u8" WHITESPACE " "
WHITESPACE " " NEQ "!="
NEQ "!=" WHITESPACE " "
WHITESPACE " " LITERAL
LITERAL INT_NUMBER "0u8"
INT_NUMBER "0u8"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -219,7 +219,7 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION LET_EXPR
LET_KW "let" LET_KW "let"
WHITESPACE " " WHITESPACE " "
TUPLE_STRUCT_PAT TUPLE_STRUCT_PAT

View file

@ -15,47 +15,46 @@ SOURCE_FILE
IF_EXPR IF_EXPR
IF_KW "if" IF_KW "if"
WHITESPACE " " WHITESPACE " "
CONDITION BIN_EXPR
BIN_EXPR CAST_EXPR
CAST_EXPR METHOD_CALL_EXPR
METHOD_CALL_EXPR LITERAL
LITERAL FLOAT_NUMBER "1.0f32"
FLOAT_NUMBER "1.0f32" DOT "."
DOT "." NAME_REF
NAME_REF IDENT "floor"
IDENT "floor" ARG_LIST
ARG_LIST L_PAREN "("
L_PAREN "(" R_PAREN ")"
R_PAREN ")"
WHITESPACE " "
AS_KW "as"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i64"
WHITESPACE " " WHITESPACE " "
NEQ "!=" AS_KW "as"
WHITESPACE " " WHITESPACE " "
CAST_EXPR PATH_TYPE
METHOD_CALL_EXPR PATH
LITERAL PATH_SEGMENT
FLOAT_NUMBER "1.0f32" NAME_REF
DOT "." IDENT "i64"
NAME_REF WHITESPACE " "
IDENT "floor" NEQ "!="
ARG_LIST WHITESPACE " "
L_PAREN "(" CAST_EXPR
R_PAREN ")" METHOD_CALL_EXPR
WHITESPACE " " LITERAL
AS_KW "as" FLOAT_NUMBER "1.0f32"
WHITESPACE " " DOT "."
PATH_TYPE NAME_REF
PATH IDENT "floor"
PATH_SEGMENT ARG_LIST
NAME_REF L_PAREN "("
IDENT "i64" R_PAREN ")"
WHITESPACE " "
AS_KW "as"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i64"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -86,9 +86,8 @@ SOURCE_FILE
WHILE_EXPR WHILE_EXPR
WHILE_KW "while" WHILE_KW "while"
WHITESPACE " " WHITESPACE " "
CONDITION LITERAL
LITERAL TRUE_KW "true"
TRUE_KW "true"
WHITESPACE " " WHITESPACE " "
BLOCK_EXPR BLOCK_EXPR
STMT_LIST STMT_LIST

View file

@ -30,7 +30,7 @@ rayon = "1"
expect-test = "1.2.0-pre.1" expect-test = "1.2.0-pre.1"
proc-macro2 = "1.0.8" proc-macro2 = "1.0.8"
quote = "1.0.2" quote = "1.0.2"
ungrammar = "=1.14.9" ungrammar = "=1.15.0"
test_utils = { path = "../test_utils" } test_utils = { path = "../test_utils" }
sourcegen = { path = "../sourcegen" } sourcegen = { path = "../sourcegen" }

View file

@ -884,7 +884,7 @@ pub struct IfExpr {
impl ast::HasAttrs for IfExpr {} impl ast::HasAttrs for IfExpr {}
impl IfExpr { impl IfExpr {
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 condition(&self) -> Option<Condition> { support::child(&self.syntax) } pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) } pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) }
} }
@ -1038,7 +1038,7 @@ impl ast::HasAttrs for WhileExpr {}
impl ast::HasLoopBody for WhileExpr {} impl ast::HasLoopBody for WhileExpr {}
impl WhileExpr { impl WhileExpr {
pub fn while_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![while]) } pub fn while_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![while]) }
pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) } pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -1051,6 +1051,18 @@ impl YieldExpr {
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)]
pub struct LetExpr {
pub(crate) syntax: SyntaxNode,
}
impl ast::HasAttrs for LetExpr {}
impl LetExpr {
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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList { pub struct StmtList {
pub(crate) syntax: SyntaxNode, pub(crate) syntax: SyntaxNode,
@ -1106,17 +1118,6 @@ impl ArgList {
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Condition {
pub(crate) syntax: SyntaxNode,
}
impl Condition {
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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MatchArmList { pub struct MatchArmList {
pub(crate) syntax: SyntaxNode, pub(crate) syntax: SyntaxNode,
@ -1147,10 +1148,7 @@ 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 condition(&self) -> Option<Expr> { support::child(&self.syntax) }
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)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -1524,6 +1522,7 @@ pub enum Expr {
TupleExpr(TupleExpr), TupleExpr(TupleExpr),
WhileExpr(WhileExpr), WhileExpr(WhileExpr),
YieldExpr(YieldExpr), YieldExpr(YieldExpr),
LetExpr(LetExpr),
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -2664,6 +2663,17 @@ impl AstNode for YieldExpr {
} }
fn syntax(&self) -> &SyntaxNode { &self.syntax } fn syntax(&self) -> &SyntaxNode { &self.syntax }
} }
impl AstNode for LetExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for StmtList { impl AstNode for StmtList {
fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST } fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> { fn cast(syntax: SyntaxNode) -> Option<Self> {
@ -2719,17 +2729,6 @@ impl AstNode for ArgList {
} }
fn syntax(&self) -> &SyntaxNode { &self.syntax } fn syntax(&self) -> &SyntaxNode { &self.syntax }
} }
impl AstNode for Condition {
fn can_cast(kind: SyntaxKind) -> bool { kind == CONDITION }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for MatchArmList { impl AstNode for MatchArmList {
fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST } fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> { fn cast(syntax: SyntaxNode) -> Option<Self> {
@ -3336,6 +3335,9 @@ impl From<WhileExpr> for Expr {
impl From<YieldExpr> for Expr { impl From<YieldExpr> for Expr {
fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) } fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) }
} }
impl From<LetExpr> for Expr {
fn from(node: LetExpr) -> Expr { Expr::LetExpr(node) }
}
impl AstNode for Expr { impl AstNode for Expr {
fn can_cast(kind: SyntaxKind) -> bool { fn can_cast(kind: SyntaxKind) -> bool {
match kind { match kind {
@ -3344,7 +3346,7 @@ impl AstNode for Expr {
| INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS | MATCH_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS | MATCH_EXPR
| METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
| RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR | RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR
| YIELD_EXPR => true, | YIELD_EXPR | LET_EXPR => true,
_ => false, _ => false,
} }
} }
@ -3381,6 +3383,7 @@ impl AstNode for Expr {
TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }), TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }),
WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }), WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }),
YIELD_EXPR => Expr::YieldExpr(YieldExpr { syntax }), YIELD_EXPR => Expr::YieldExpr(YieldExpr { syntax }),
LET_EXPR => Expr::LetExpr(LetExpr { syntax }),
_ => return None, _ => return None,
}; };
Some(res) Some(res)
@ -3418,6 +3421,7 @@ impl AstNode for Expr {
Expr::TupleExpr(it) => &it.syntax, Expr::TupleExpr(it) => &it.syntax,
Expr::WhileExpr(it) => &it.syntax, Expr::WhileExpr(it) => &it.syntax,
Expr::YieldExpr(it) => &it.syntax, Expr::YieldExpr(it) => &it.syntax,
Expr::LetExpr(it) => &it.syntax,
} }
} }
} }
@ -3883,6 +3887,7 @@ impl AstNode for AnyHasAttrs {
| TUPLE_EXPR | TUPLE_EXPR
| WHILE_EXPR | WHILE_EXPR
| YIELD_EXPR | YIELD_EXPR
| LET_EXPR
| STMT_LIST | STMT_LIST
| RECORD_EXPR_FIELD_LIST | RECORD_EXPR_FIELD_LIST
| RECORD_EXPR_FIELD | RECORD_EXPR_FIELD
@ -4537,6 +4542,11 @@ impl std::fmt::Display for YieldExpr {
std::fmt::Display::fmt(self.syntax(), f) std::fmt::Display::fmt(self.syntax(), f)
} }
} }
impl std::fmt::Display for LetExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for StmtList { impl std::fmt::Display for StmtList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f) std::fmt::Display::fmt(self.syntax(), f)
@ -4562,11 +4572,6 @@ impl std::fmt::Display for ArgList {
std::fmt::Display::fmt(self.syntax(), f) std::fmt::Display::fmt(self.syntax(), f)
} }
} }
impl std::fmt::Display for Condition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for MatchArmList { impl std::fmt::Display for MatchArmList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f) std::fmt::Display::fmt(self.syntax(), f)

View file

@ -397,7 +397,7 @@ pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Ex
expr_from_text(&format!("match {} {}", expr, match_arm_list)) expr_from_text(&format!("match {} {}", expr, match_arm_list))
} }
pub fn expr_if( pub fn expr_if(
condition: ast::Condition, condition: ast::Expr,
then_branch: ast::BlockExpr, then_branch: ast::BlockExpr,
else_branch: Option<ast::ElseBranch>, else_branch: Option<ast::ElseBranch>,
) -> ast::Expr { ) -> ast::Expr {
@ -456,14 +456,8 @@ pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
fn expr_from_text(text: &str) -> ast::Expr { fn expr_from_text(text: &str) -> ast::Expr {
ast_from_text(&format!("const C: () = {};", text)) ast_from_text(&format!("const C: () = {};", text))
} }
pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition { ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
match pattern {
None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
Some(pattern) => {
ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
}
}
} }
pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList { pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {

View file

@ -528,12 +528,6 @@ impl ast::Item {
} }
} }
impl ast::Condition {
pub fn is_pattern_cond(&self) -> bool {
self.let_token().is_some()
}
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldKind { pub enum FieldKind {
Name(ast::NameRef), Name(ast::NameRef),

View file

@ -133,7 +133,6 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
"CLOSURE_EXPR", "CLOSURE_EXPR",
"IF_EXPR", "IF_EXPR",
"WHILE_EXPR", "WHILE_EXPR",
"CONDITION",
"LOOP_EXPR", "LOOP_EXPR",
"FOR_EXPR", "FOR_EXPR",
"CONTINUE_EXPR", "CONTINUE_EXPR",
@ -143,6 +142,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
"STMT_LIST", "STMT_LIST",
"RETURN_EXPR", "RETURN_EXPR",
"YIELD_EXPR", "YIELD_EXPR",
"LET_EXPR",
"MATCH_EXPR", "MATCH_EXPR",
"MATCH_ARM_LIST", "MATCH_ARM_LIST",
"MATCH_ARM", "MATCH_ARM",

View file

@ -38,6 +38,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors), ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors), ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
ast::MacroRules(it) => validate_macro_rules(it, &mut errors), ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
ast::LetExpr(it) => validate_let_expr(it, &mut errors),
_ => (), _ => (),
} }
} }
@ -343,3 +344,33 @@ fn validate_const(const_: ast::Const, errors: &mut Vec<SyntaxError>) {
errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range())); errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range()));
} }
} }
fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec<SyntaxError>) {
let mut token = let_.syntax().clone();
loop {
token = match token.parent() {
Some(it) => it,
None => break,
};
if ast::ParenExpr::can_cast(token.kind()) {
continue;
} else if let Some(it) = ast::BinExpr::cast(token.clone()) {
if it.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) {
continue;
}
} else if ast::IfExpr::can_cast(token.kind())
|| ast::WhileExpr::can_cast(token.kind())
|| ast::MatchGuard::can_cast(token.kind())
{
// It must be part of the condition since the expressions are inside a block.
return;
}
break;
}
errors.push(SyntaxError::new(
"`let` expressions are not supported here",
let_.syntax().text_range(),
));
}

View file

@ -49,9 +49,8 @@ SOURCE_FILE@0..350
IF_EXPR@134..257 IF_EXPR@134..257
IF_KW@134..136 "if" IF_KW@134..136 "if"
WHITESPACE@136..137 " " WHITESPACE@136..137 " "
CONDITION@137..141 LITERAL@137..141
LITERAL@137..141 TRUE_KW@137..141 "true"
TRUE_KW@137..141 "true"
WHITESPACE@141..142 " " WHITESPACE@141..142 " "
BLOCK_EXPR@142..257 BLOCK_EXPR@142..257
STMT_LIST@142..257 STMT_LIST@142..257
@ -94,9 +93,8 @@ SOURCE_FILE@0..350
WHILE_EXPR@262..347 WHILE_EXPR@262..347
WHILE_KW@262..267 "while" WHILE_KW@262..267 "while"
WHITESPACE@267..268 " " WHITESPACE@267..268 " "
CONDITION@268..272 LITERAL@268..272
LITERAL@268..272 TRUE_KW@268..272 "true"
TRUE_KW@268..272 "true"
WHITESPACE@272..273 " " WHITESPACE@272..273 " "
BLOCK_EXPR@273..347 BLOCK_EXPR@273..347
STMT_LIST@273..347 STMT_LIST@273..347

View file

@ -0,0 +1,216 @@
SOURCE_FILE@0..282
FN@0..281
FN_KW@0..2 "fn"
WHITESPACE@2..3 " "
NAME@3..6
IDENT@3..6 "foo"
PARAM_LIST@6..8
L_PAREN@6..7 "("
R_PAREN@7..8 ")"
WHITESPACE@8..9 " "
BLOCK_EXPR@9..281
STMT_LIST@9..281
L_CURLY@9..10 "{"
WHITESPACE@10..15 "\n "
CONST@15..42
CONST_KW@15..20 "const"
WHITESPACE@20..21 " "
UNDERSCORE@21..22 "_"
COLON@22..23 ":"
WHITESPACE@23..24 " "
TUPLE_TYPE@24..26
L_PAREN@24..25 "("
R_PAREN@25..26 ")"
WHITESPACE@26..27 " "
EQ@27..28 "="
WHITESPACE@28..29 " "
LET_EXPR@29..41
LET_KW@29..32 "let"
WHITESPACE@32..33 " "
WILDCARD_PAT@33..34
UNDERSCORE@33..34 "_"
WHITESPACE@34..35 " "
EQ@35..36 "="
WHITESPACE@36..37 " "
PATH_EXPR@37..41
PATH@37..41
PATH_SEGMENT@37..41
NAME_REF@37..41
IDENT@37..41 "None"
SEMICOLON@41..42 ";"
WHITESPACE@42..48 "\n\n "
LET_STMT@48..83
LET_KW@48..51 "let"
WHITESPACE@51..52 " "
WILDCARD_PAT@52..53
UNDERSCORE@52..53 "_"
WHITESPACE@53..54 " "
EQ@54..55 "="
WHITESPACE@55..56 " "
IF_EXPR@56..82
IF_KW@56..58 "if"
WHITESPACE@58..59 " "
LITERAL@59..63
TRUE_KW@59..63 "true"
WHITESPACE@63..64 " "
BLOCK_EXPR@64..82
STMT_LIST@64..82
L_CURLY@64..65 "{"
WHITESPACE@65..66 " "
PAREN_EXPR@66..80
L_PAREN@66..67 "("
LET_EXPR@67..79
LET_KW@67..70 "let"
WHITESPACE@70..71 " "
WILDCARD_PAT@71..72
UNDERSCORE@71..72 "_"
WHITESPACE@72..73 " "
EQ@73..74 "="
WHITESPACE@74..75 " "
PATH_EXPR@75..79
PATH@75..79
PATH_SEGMENT@75..79
NAME_REF@75..79
IDENT@75..79 "None"
R_PAREN@79..80 ")"
WHITESPACE@80..81 " "
R_CURLY@81..82 "}"
SEMICOLON@82..83 ";"
WHITESPACE@83..89 "\n\n "
IF_EXPR@89..279
IF_KW@89..91 "if"
WHITESPACE@91..92 " "
BIN_EXPR@92..114
LITERAL@92..96
TRUE_KW@92..96 "true"
WHITESPACE@96..97 " "
AMP2@97..99 "&&"
WHITESPACE@99..100 " "
PAREN_EXPR@100..114
L_PAREN@100..101 "("
LET_EXPR@101..113
LET_KW@101..104 "let"
WHITESPACE@104..105 " "
WILDCARD_PAT@105..106
UNDERSCORE@105..106 "_"
WHITESPACE@106..107 " "
EQ@107..108 "="
WHITESPACE@108..109 " "
PATH_EXPR@109..113
PATH@109..113
PATH_SEGMENT@109..113
NAME_REF@109..113
IDENT@109..113 "None"
R_PAREN@113..114 ")"
WHITESPACE@114..115 " "
BLOCK_EXPR@115..279
STMT_LIST@115..279
L_CURLY@115..116 "{"
WHITESPACE@116..125 "\n "
EXPR_STMT@125..140
PAREN_EXPR@125..139
L_PAREN@125..126 "("
LET_EXPR@126..138
LET_KW@126..129 "let"
WHITESPACE@129..130 " "
WILDCARD_PAT@130..131
UNDERSCORE@130..131 "_"
WHITESPACE@131..132 " "
EQ@132..133 "="
WHITESPACE@133..134 " "
PATH_EXPR@134..138
PATH@134..138
PATH_SEGMENT@134..138
NAME_REF@134..138
IDENT@134..138 "None"
R_PAREN@138..139 ")"
SEMICOLON@139..140 ";"
WHITESPACE@140..149 "\n "
WHILE_EXPR@149..273
WHILE_KW@149..154 "while"
WHITESPACE@154..155 " "
LET_EXPR@155..167
LET_KW@155..158 "let"
WHITESPACE@158..159 " "
WILDCARD_PAT@159..160
UNDERSCORE@159..160 "_"
WHITESPACE@160..161 " "
EQ@161..162 "="
WHITESPACE@162..163 " "
PATH_EXPR@163..167
PATH@163..167
PATH_SEGMENT@163..167
NAME_REF@163..167
IDENT@163..167 "None"
WHITESPACE@167..168 " "
BLOCK_EXPR@168..273
STMT_LIST@168..273
L_CURLY@168..169 "{"
WHITESPACE@169..182 "\n "
MATCH_EXPR@182..263
MATCH_KW@182..187 "match"
WHITESPACE@187..188 " "
PATH_EXPR@188..192
PATH@188..192
PATH_SEGMENT@188..192
NAME_REF@188..192
IDENT@188..192 "None"
WHITESPACE@192..193 " "
MATCH_ARM_LIST@193..263
L_CURLY@193..194 "{"
WHITESPACE@194..211 "\n "
MATCH_ARM@211..249
WILDCARD_PAT@211..212
UNDERSCORE@211..212 "_"
WHITESPACE@212..213 " "
MATCH_GUARD@213..228
IF_KW@213..215 "if"
WHITESPACE@215..216 " "
LET_EXPR@216..228
LET_KW@216..219 "let"
WHITESPACE@219..220 " "
WILDCARD_PAT@220..221
UNDERSCORE@220..221 "_"
WHITESPACE@221..222 " "
EQ@222..223 "="
WHITESPACE@223..224 " "
PATH_EXPR@224..228
PATH@224..228
PATH_SEGMENT@224..228
NAME_REF@224..228
IDENT@224..228 "None"
WHITESPACE@228..229 " "
FAT_ARROW@229..231 "=>"
WHITESPACE@231..232 " "
BLOCK_EXPR@232..249
STMT_LIST@232..249
L_CURLY@232..233 "{"
WHITESPACE@233..234 " "
LET_STMT@234..247
LET_KW@234..237 "let"
WHITESPACE@237..238 " "
WILDCARD_PAT@238..239
UNDERSCORE@238..239 "_"
WHITESPACE@239..240 " "
EQ@240..241 "="
WHITESPACE@241..242 " "
PATH_EXPR@242..246
PATH@242..246
PATH_SEGMENT@242..246
NAME_REF@242..246
IDENT@242..246 "None"
SEMICOLON@246..247 ";"
WHITESPACE@247..248 " "
R_CURLY@248..249 "}"
WHITESPACE@249..262 "\n "
R_CURLY@262..263 "}"
WHITESPACE@263..272 "\n "
R_CURLY@272..273 "}"
WHITESPACE@273..278 "\n "
R_CURLY@278..279 "}"
WHITESPACE@279..280 "\n"
R_CURLY@280..281 "}"
WHITESPACE@281..282 "\n"
error 29..41: `let` expressions are not supported here
error 67..79: `let` expressions are not supported here
error 126..138: `let` expressions are not supported here

View file

@ -0,0 +1,14 @@
fn foo() {
const _: () = let _ = None;
let _ = if true { (let _ = None) };
if true && (let _ = None) {
(let _ = None);
while let _ = None {
match None {
_ if let _ = None => { let _ = None; }
}
}
}
}