From de8633f15fbfb2255d93c51e630a1e1854b1b746 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 23 Jan 2022 04:21:09 +0200 Subject: [PATCH 1/8] Parse `let` expressions in order to support `let` chains We still need to reject freestanding `let` expressions: see https://github.com/rust-analyzer/rust-analyzer/issues/11320#issuecomment-1018212465. --- crates/parser/src/grammar/expressions.rs | 10 +++ crates/parser/src/grammar/expressions/atom.rs | 33 ++++----- crates/parser/src/syntax_kind/generated.rs | 2 +- crates/syntax/src/ast/generated/nodes.rs | 73 ++++++++++--------- crates/syntax/src/ast/make.rs | 12 +-- crates/syntax/src/ast/node_ext.rs | 34 ++++++++- crates/syntax/src/tests/ast_src.rs | 2 +- 7 files changed, 99 insertions(+), 67 deletions(-) diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 9dbba89c56..a40db15049 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -29,6 +29,15 @@ fn expr_no_struct(p: &mut Parser) { 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) { if p.eat(T![;]) { return; @@ -185,6 +194,7 @@ fn current_op(p: &Parser) -> (u8, SyntaxKind) { T![%] if p.at(T![%=]) => (1, T![%=]), T![%] => (11, 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![&] => (8, T![&]), T![/] if p.at(T![/=]) => (1, T![/=]), diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 4b7a1b31fb..e2c1b1fec5 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs @@ -79,6 +79,7 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar closure_expr(p) } T![if] => if_expr(p), + T![let] => let_expr(p), T![loop] => loop_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])); let m = p.start(); p.bump(T![if]); - condition(p); + expr_no_struct(p); block_expr(p); if p.at(T![else]) { p.bump(T![else]); @@ -335,7 +336,7 @@ fn while_expr(p: &mut Parser, m: Option) -> CompletedMarker { assert!(p.at(T![while])); let m = m.unwrap_or_else(|| p.start()); p.bump(T![while]); - condition(p); + expr_no_struct(p); block_expr(p); m.complete(p, WHILE_EXPR) } @@ -355,22 +356,18 @@ fn for_expr(p: &mut Parser, m: Option) -> CompletedMarker { m.complete(p, FOR_EXPR) } -// test cond -// 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 {} +// test let_expr +// fn foo() { +// if let Some(_) = None && true {} +// while 1 == 5 && (let None = None) {} // } -fn condition(p: &mut Parser) { +fn let_expr(p: &mut Parser) -> CompletedMarker { let m = p.start(); - if p.eat(T![let]) { - patterns::pattern_top(p); - p.expect(T![=]); - } - expr_no_struct(p); - m.complete(p, CONDITION); + p.bump(T![let]); + patterns::pattern_top(p); + p.expect(T![=]); + expr_let(p); + m.complete(p, LET_EXPR) } // test match_expr @@ -482,10 +479,6 @@ fn match_guard(p: &mut Parser) -> CompletedMarker { assert!(p.at(T![if])); let m = p.start(); p.bump(T![if]); - if p.eat(T![let]) { - patterns::pattern_top(p); - p.expect(T![=]); - } expr(p); m.complete(p, MATCH_GUARD) } diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 601a5792af..d04b5dbf00 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs @@ -178,7 +178,6 @@ pub enum SyntaxKind { CLOSURE_EXPR, IF_EXPR, WHILE_EXPR, - CONDITION, LOOP_EXPR, FOR_EXPR, CONTINUE_EXPR, @@ -188,6 +187,7 @@ pub enum SyntaxKind { STMT_LIST, RETURN_EXPR, YIELD_EXPR, + LET_EXPR, MATCH_EXPR, MATCH_ARM_LIST, MATCH_ARM, diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 09c5af210f..6c4729ef36 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -884,7 +884,7 @@ pub struct IfExpr { impl ast::HasAttrs for IfExpr {} impl IfExpr { pub fn if_token(&self) -> Option { support::token(&self.syntax, T![if]) } - pub fn condition(&self) -> Option { support::child(&self.syntax) } + pub fn condition(&self) -> Option { support::child(&self.syntax) } pub fn else_token(&self) -> Option { support::token(&self.syntax, T![else]) } } @@ -1038,7 +1038,7 @@ impl ast::HasAttrs for WhileExpr {} impl ast::HasLoopBody for WhileExpr {} impl WhileExpr { pub fn while_token(&self) -> Option { support::token(&self.syntax, T![while]) } - pub fn condition(&self) -> Option { support::child(&self.syntax) } + pub fn condition(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1051,6 +1051,18 @@ impl YieldExpr { pub fn expr(&self) -> Option { 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 { support::token(&self.syntax, T![let]) } + pub fn pat(&self) -> Option { support::child(&self.syntax) } + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } + pub fn expr(&self) -> Option { support::child(&self.syntax) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct StmtList { pub(crate) syntax: SyntaxNode, @@ -1106,17 +1118,6 @@ impl ArgList { pub fn r_paren_token(&self) -> Option { 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 { support::token(&self.syntax, T![let]) } - pub fn pat(&self) -> Option { support::child(&self.syntax) } - pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } - pub fn expr(&self) -> Option { support::child(&self.syntax) } -} - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct MatchArmList { pub(crate) syntax: SyntaxNode, @@ -1147,10 +1148,7 @@ pub struct MatchGuard { } impl MatchGuard { pub fn if_token(&self) -> Option { support::token(&self.syntax, T![if]) } - pub fn let_token(&self) -> Option { support::token(&self.syntax, T![let]) } - pub fn pat(&self) -> Option { support::child(&self.syntax) } - pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } - pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn condition(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1524,6 +1522,7 @@ pub enum Expr { TupleExpr(TupleExpr), WhileExpr(WhileExpr), YieldExpr(YieldExpr), + LetExpr(LetExpr), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -2664,6 +2663,17 @@ impl AstNode for YieldExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for LetExpr { + fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for StmtList { fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST } fn cast(syntax: SyntaxNode) -> Option { @@ -2719,17 +2729,6 @@ impl AstNode for ArgList { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl AstNode for Condition { - fn can_cast(kind: SyntaxKind) -> bool { kind == CONDITION } - fn cast(syntax: SyntaxNode) -> Option { - if Self::can_cast(syntax.kind()) { - Some(Self { syntax }) - } else { - None - } - } - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} impl AstNode for MatchArmList { fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST } fn cast(syntax: SyntaxNode) -> Option { @@ -3336,6 +3335,9 @@ impl From for Expr { impl From for Expr { fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) } } +impl From for Expr { + fn from(node: LetExpr) -> Expr { Expr::LetExpr(node) } +} impl AstNode for Expr { fn can_cast(kind: SyntaxKind) -> bool { match kind { @@ -3344,7 +3346,7 @@ impl AstNode for Expr { | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS | MATCH_EXPR | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR | RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR - | YIELD_EXPR => true, + | YIELD_EXPR | LET_EXPR => true, _ => false, } } @@ -3381,6 +3383,7 @@ impl AstNode for Expr { TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }), WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }), YIELD_EXPR => Expr::YieldExpr(YieldExpr { syntax }), + LET_EXPR => Expr::LetExpr(LetExpr { syntax }), _ => return None, }; Some(res) @@ -3418,6 +3421,7 @@ impl AstNode for Expr { Expr::TupleExpr(it) => &it.syntax, Expr::WhileExpr(it) => &it.syntax, Expr::YieldExpr(it) => &it.syntax, + Expr::LetExpr(it) => &it.syntax, } } } @@ -3883,6 +3887,7 @@ impl AstNode for AnyHasAttrs { | TUPLE_EXPR | WHILE_EXPR | YIELD_EXPR + | LET_EXPR | STMT_LIST | RECORD_EXPR_FIELD_LIST | RECORD_EXPR_FIELD @@ -4537,6 +4542,11 @@ impl std::fmt::Display for YieldExpr { 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -4562,11 +4572,6 @@ impl std::fmt::Display for ArgList { 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 8a1bcebbf6..1a754ef460 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -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)) } pub fn expr_if( - condition: ast::Condition, + condition: ast::Expr, then_branch: ast::BlockExpr, else_branch: Option, ) -> 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 { ast_from_text(&format!("const C: () = {};", text)) } - -pub fn condition(expr: ast::Expr, pattern: Option) -> ast::Condition { - match pattern { - None => ast_from_text(&format!("const _: () = while {} {{}};", expr)), - Some(pattern) => { - ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr)) - } - } +pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { + ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr)) } pub fn arg_list(args: impl IntoIterator) -> ast::ArgList { diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 067e13ee14..2915e7aab1 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -528,9 +528,39 @@ impl ast::Item { } } -impl ast::Condition { +impl ast::Expr { + /// 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(&self) -> Option { + return get_pat(self.clone()); + + fn get_pat(expr: ast::Expr) -> Option { + match expr { + ast::Expr::ParenExpr(expr) => expr.expr().and_then(get_pat), + ast::Expr::LetExpr(expr) => Some(expr), + _ => None, + } + } + } + pub fn is_pattern_cond(&self) -> bool { - self.let_token().is_some() + return contains_let(self.clone()); + + fn contains_let(expr: ast::Expr) -> bool { + match expr { + ast::Expr::BinExpr(expr) + if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) => + { + expr.lhs() + .map(contains_let) + .or_else(|| expr.rhs().map(contains_let)) + .unwrap_or(false) + } + ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, contains_let), + ast::Expr::LetExpr(_) => true, + _ => false, + } + } } } diff --git a/crates/syntax/src/tests/ast_src.rs b/crates/syntax/src/tests/ast_src.rs index c0f1d5ef56..aeff851ce4 100644 --- a/crates/syntax/src/tests/ast_src.rs +++ b/crates/syntax/src/tests/ast_src.rs @@ -133,7 +133,6 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { "CLOSURE_EXPR", "IF_EXPR", "WHILE_EXPR", - "CONDITION", "LOOP_EXPR", "FOR_EXPR", "CONTINUE_EXPR", @@ -143,6 +142,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc { "STMT_LIST", "RETURN_EXPR", "YIELD_EXPR", + "LET_EXPR", "MATCH_EXPR", "MATCH_ARM_LIST", "MATCH_ARM", From 6bf6f4ff1dd8d342c061708041810c64fe983ab8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 23 Jan 2022 05:39:26 +0200 Subject: [PATCH 2/8] Lower `let` expressions --- crates/hir_def/src/body/lower.rs | 73 ++----------- crates/hir_def/src/body/scope.rs | 102 +++++++++++------- crates/hir_def/src/expr.rs | 16 +-- .../mbe/tt_conversion.rs | 6 +- 4 files changed, 87 insertions(+), 110 deletions(-) diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 7cbeef1488..06ad7ce4cd 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -28,7 +28,7 @@ use crate::{ db::DefDatabase, expr::{ dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm, - MatchGuard, Pat, PatId, RecordFieldPat, RecordLitField, Statement, + Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, intern::Interned, item_scope::BuiltinShadowMode, @@ -155,9 +155,6 @@ impl ExprCollector<'_> { fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { 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 { self.alloc_expr_desugared(Expr::Missing) } @@ -215,33 +212,15 @@ impl ExprCollector<'_> { } }); - let condition = match 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), - ); - } - }, - }; + let condition = self.collect_expr_opt(e.condition()); 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() { Some(ast::BlockModifier::Try(_)) => { let body = self.collect_block(e); @@ -282,31 +261,7 @@ impl ExprCollector<'_> { let label = e.label().map(|label| self.collect_label(label)); let body = self.collect_block_opt(e.loop_body()); - let condition = match 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), - ); - } - }, - }; + let condition = self.collect_expr_opt(e.condition()); self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr) } @@ -352,15 +307,9 @@ impl ExprCollector<'_> { self.check_cfg(&arm).map(|()| MatchArm { pat: self.collect_pat_opt(arm.pat()), expr: self.collect_expr_opt(arm.expr()), - guard: arm.guard().map(|guard| match guard.pat() { - Some(pat) => MatchGuard::IfLet { - pat: self.collect_pat(pat), - expr: self.collect_expr_opt(guard.expr()), - }, - None => { - MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) } - } - }), + guard: arm + .guard() + .map(|guard| self.collect_expr_opt(guard.condition())), }) }) .collect() diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs index 2658eece8e..505d33fa48 100644 --- a/crates/hir_def/src/body/scope.rs +++ b/crates/hir_def/src/body/scope.rs @@ -8,7 +8,7 @@ use rustc_hash::FxHashMap; use crate::{ body::Body, db::DefDatabase, - expr::{Expr, ExprId, LabelId, MatchGuard, Pat, PatId, Statement}, + expr::{Expr, ExprId, LabelId, Pat, PatId, Statement}, BlockId, DefWithBodyId, }; @@ -53,9 +53,9 @@ impl ExprScopes { fn new(body: &Body) -> ExprScopes { let mut scopes = 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); - compute_expr_scopes(body.body_expr, body, &mut scopes, root); + compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root); scopes } @@ -151,32 +151,32 @@ fn compute_block_scopes( match stmt { Statement::Let { pat, initializer, else_branch, .. } => { 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 { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, &mut scope); } scope = scopes.new_scope(scope); scopes.add_bindings(body, scope, *pat); } Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, body, scopes, scope); + compute_expr_scopes(*expr, body, scopes, &mut scope); } } } 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 = |label: &Option| label.map(|label| (label, body.labels[label].name.clone())); - scopes.set_scope(expr, scope); + scopes.set_scope(expr, *scope); match &body[expr] { 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 // via the block itself (important for blocks that only contain items, no expressions). 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 } => { 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); - compute_expr_scopes(*body_expr, body, scopes, scope); + compute_expr_scopes(*body_expr, body, scopes, &mut scope); } Expr::While { condition, body: body_expr, label } => { - let scope = scopes.new_labeled_scope(scope, make_label(label)); - compute_expr_scopes(*condition, body, scopes, scope); - compute_expr_scopes(*body_expr, body, scopes, scope); + let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); + compute_expr_scopes(*condition, body, scopes, &mut scope); + compute_expr_scopes(*body_expr, body, scopes, &mut scope); } Expr::Loop { body: body_expr, label } => { - let scope = scopes.new_labeled_scope(scope, make_label(label)); - compute_expr_scopes(*body_expr, body, scopes, scope); + let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); + compute_expr_scopes(*body_expr, body, scopes, &mut scope); } 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); - compute_expr_scopes(*body_expr, body, scopes, scope); + compute_expr_scopes(*body_expr, body, scopes, &mut scope); } Expr::Match { expr, arms } => { compute_expr_scopes(*expr, body, scopes, scope); 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); - match arm.guard { - Some(MatchGuard::If { expr: guard }) => { - scopes.set_scope(guard, scope); - compute_expr_scopes(guard, body, scopes, scope); - } - Some(MatchGuard::IfLet { pat, expr: guard }) => { - scopes.set_scope(guard, scope); - compute_expr_scopes(guard, body, scopes, scope); - scope = scopes.new_scope(scope); - scopes.add_bindings(body, scope, pat); - } - _ => {} - }; - scopes.set_scope(arm.expr, scope); - compute_expr_scopes(arm.expr, body, scopes, scope); + if let Some(guard) = arm.guard { + scope = scopes.new_scope(scope); + compute_expr_scopes(guard, body, scopes, &mut scope); + } + compute_expr_scopes(arm.expr, body, scopes, &mut 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)), }; } @@ -500,8 +503,7 @@ fn foo() { } #[test] - fn while_let_desugaring() { - cov_mark::check!(infer_resolve_while_let); + fn while_let_adds_binding() { do_check_local_name( r#" fn test() { @@ -513,5 +515,31 @@ fn test() { "#, 75, ); + do_check_local_name( + r#" +fn test() { + let foo: Option = 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 = None; + match foo { + _ if let Option::Some(spam) = foo => spam$0, + } +} +"#, + 93, + ); } } diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs index 6534f970ee..4dca823888 100644 --- a/crates/hir_def/src/expr.rs +++ b/crates/hir_def/src/expr.rs @@ -59,6 +59,10 @@ pub enum Expr { then_branch: ExprId, else_branch: Option, }, + Let { + pat: PatId, + expr: ExprId, + }, Block { id: BlockId, statements: Box<[Statement]>, @@ -189,17 +193,10 @@ pub enum Array { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MatchArm { pub pat: PatId, - pub guard: Option, + pub guard: Option, pub expr: ExprId, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum MatchGuard { - If { expr: ExprId }, - - IfLet { pat: PatId, expr: ExprId }, -} - #[derive(Debug, Clone, Eq, PartialEq)] pub struct RecordLitField { pub name: Name, @@ -232,6 +229,9 @@ impl Expr { f(else_branch); } } + Expr::Let { expr, .. } => { + f(*expr); + } Expr::Block { statements, tail, .. } => { for stmt in statements.iter() { match stmt { diff --git a/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs index 5f4b7d6d0b..84cc3f3872 100644 --- a/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -108,18 +108,18 @@ fn expansion_does_not_parse_as_expression() { check( r#" macro_rules! stmts { - () => { let _ = 0; } + () => { fn foo() {} } } fn f() { let _ = stmts!/*+errors*/(); } "#, expect![[r#" macro_rules! stmts { - () => { let _ = 0; } + () => { fn foo() {} } } fn f() { let _ = /* parse error: expected expression */ -let _ = 0;; } +fn foo() {}; } "#]], ) } From fe1e324694425e9b683b72be5371a555601ffe6c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 23 Jan 2022 05:59:35 +0200 Subject: [PATCH 3/8] Type-inference for `let` expressions --- crates/hir_ty/src/infer/expr.rs | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 4f1bdee705..13f64d6825 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -8,10 +8,7 @@ use std::{ use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind}; use hir_def::{ - expr::{ - ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, MatchGuard, Ordering, Statement, - UnaryOp, - }, + expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp}, path::{GenericArg, GenericArgs}, resolver::resolver_for_expr, FieldId, FunctionId, ItemContainerId, Lookup, @@ -158,6 +155,11 @@ impl<'a> InferenceContext<'a> { 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: _ } => { let old_resolver = mem::replace( &mut self.resolver, @@ -378,20 +380,11 @@ impl<'a> InferenceContext<'a> { for arm in arms.iter() { self.diverges = Diverges::Maybe; let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); - match arm.guard { - Some(MatchGuard::If { expr: guard_expr }) => { - self.infer_expr( - guard_expr, - &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()); - } - _ => {} + if let Some(guard_expr) = arm.guard { + self.infer_expr( + guard_expr, + &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)), + ); } let arm_ty = self.infer_expr_inner(arm.expr, &expected); From 13ac5c349136bc2d86f8eeed2a8ac0253052510f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 24 Jan 2022 00:37:59 +0200 Subject: [PATCH 4/8] Fix various IDE features As a side benefit, we got `let` guard support for `move_guard` for free. --- crates/hir_expand/src/lib.rs | 8 ++--- crates/hir_ty/src/tests/patterns.rs | 10 ++++++ crates/hir_ty/src/tests/simple.rs | 1 + crates/ide/src/hover/render.rs | 4 +-- crates/ide/src/inlay_hints.rs | 15 +++----- .../src/handlers/convert_bool_then.rs | 3 +- .../src/handlers/convert_to_guarded_return.rs | 34 ++++++++++--------- .../src/handlers/convert_while_to_loop.rs | 23 +++++-------- .../src/handlers/extract_function.rs | 20 +++++------ crates/ide_assists/src/handlers/invert_if.rs | 4 +-- crates/ide_assists/src/handlers/move_guard.rs | 28 ++++----------- .../src/handlers/replace_if_let_with_match.rs | 23 +++++++++---- .../src/handlers/replace_let_with_if_let.rs | 2 +- crates/ide_completion/src/context.rs | 12 +++++-- crates/ide_db/src/helpers.rs | 1 + 15 files changed, 95 insertions(+), 93 deletions(-) diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index 56cf7aed8e..e33c2565c3 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -811,10 +811,10 @@ impl ExpandTo { MACRO_TYPE => ExpandTo::Type, 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 - | MATCH_GUARD | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR - | FIELD_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR - | BIN_EXPR => ExpandTo::Expr, + | CLOSURE_EXPR | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM | MATCH_GUARD + | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR + | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR + | LET_EXPR => ExpandTo::Expr, LET_STMT => { // FIXME: Handle LHS Pattern ExpandTo::Expr diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 5b08d65c46..acdd8f50ef 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs @@ -55,6 +55,7 @@ fn infer_pattern() { 139..140 'g': {unknown} 143..144 'e': {unknown} 157..204 'if let... }': () + 160..175 'let [val] = opt': bool 164..169 '[val]': [{unknown}] 165..168 'val': {unknown} 172..175 'opt': [{unknown}] @@ -62,6 +63,7 @@ fn infer_pattern() { 190..191 'h': {unknown} 194..197 'val': {unknown} 210..236 'if let...rue {}': () + 213..233 'let x ... &true': bool 217..225 'x @ true': &bool 221..225 'true': bool 221..225 'true': bool @@ -111,36 +113,42 @@ fn infer_literal_pattern() { 37..38 'x': &i32 46..208 '{ ...) {} }': () 52..75 'if let...y() {}': () + 55..72 'let "f... any()': bool 59..64 '"foo"': &str 59..64 '"foo"': &str 67..70 'any': fn any<&str>() -> &str 67..72 'any()': &str 73..75 '{}': () 80..99 'if let...y() {}': () + 83..96 'let 1 = any()': bool 87..88 '1': i32 87..88 '1': i32 91..94 'any': fn any() -> i32 91..96 'any()': i32 97..99 '{}': () 104..126 'if let...y() {}': () + 107..123 'let 1u... any()': bool 111..115 '1u32': u32 111..115 '1u32': u32 118..121 'any': fn any() -> u32 118..123 'any()': u32 124..126 '{}': () 131..153 'if let...y() {}': () + 134..150 'let 1f... any()': bool 138..142 '1f32': f32 138..142 '1f32': f32 145..148 'any': fn any() -> f32 145..150 'any()': f32 151..153 '{}': () 158..179 'if let...y() {}': () + 161..176 'let 1.0 = any()': bool 165..168 '1.0': f64 165..168 '1.0': f64 171..174 'any': fn any() -> f64 171..176 'any()': f64 177..179 '{}': () 184..206 'if let...y() {}': () + 187..203 'let tr... any()': bool 191..195 'true': bool 191..195 'true': bool 198..201 'any': fn any() -> bool @@ -163,10 +171,12 @@ fn infer_range_pattern() { 8..9 'x': &i32 17..75 '{ ...2 {} }': () 23..45 'if let...u32 {}': () + 26..42 'let 1....= 2u32': bool 30..35 '1..76': u32 38..42 '2u32': u32 43..45 '{}': () 50..73 'if let...u32 {}': () + 53..70 'let 1....= 2u32': bool 57..63 '1..=76': u32 66..70 '2u32': u32 71..73 '{}': () diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index f4d082ea8c..c11a70fa66 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -2248,6 +2248,7 @@ fn generic_default_in_struct_literal() { 176..193 'Thing ...1i32 }': Thing 187..191 '1i32': i32 199..240 'if let... }': () + 202..221 'let Th... } = z': bool 206..217 'Thing { t }': Thing 214..215 't': i32 220..221 'z': Thing diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index f94348ec58..ce9055c090 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -18,7 +18,7 @@ use syntax::{ algo, ast, display::{fn_as_proc_macro_label, macro_label}, match_ast, AstNode, Direction, - SyntaxKind::{CONDITION, LET_STMT}, + SyntaxKind::{LET_EXPR, LET_STMT}, SyntaxToken, T, }; @@ -484,7 +484,7 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option { let let_kw = if ident .syntax() .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 " } else { diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index cc304cb10a..2ca756cbe0 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -243,7 +243,7 @@ fn is_named_constructor( let expr = match_ast! { match let_node { ast::LetStmt(it) => it.initializer(), - ast::Condition(it) => it.expr(), + ast::LetExpr(it) => it.expr(), _ => None, } }?; @@ -372,15 +372,10 @@ fn should_not_display_type_hint( match node { ast::LetStmt(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::IfExpr(it) => { - return it.condition().and_then(|condition| condition.pat()).is_some() - && pat_is_enum_variant(db, bind_pat, pat_ty); - }, - ast::WhileExpr(it) => { - return it.condition().and_then(|condition| condition.pat()).is_some() - && pat_is_enum_variant(db, bind_pat, pat_ty); - }, + ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty), + ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty), + ast::IfExpr(_) => return false, + ast::WhileExpr(_) => return false, 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). // Type of expr should be iterable. diff --git a/crates/ide_assists/src/handlers/convert_bool_then.rs b/crates/ide_assists/src/handlers/convert_bool_then.rs index b8c55eb852..0bf2abe493 100644 --- a/crates/ide_assists/src/handlers/convert_bool_then.rs +++ b/crates/ide_assists/src/handlers/convert_bool_then.rs @@ -46,7 +46,6 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) -> } let cond = expr.condition().filter(|cond| !cond.is_pattern_cond())?; - let cond = cond.expr()?; let then = expr.then_branch()?; let else_ = match expr.else_branch()? { ast::ElseBranch::Block(b) => b, @@ -209,7 +208,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext) -> _ => receiver, }; let if_expr = make::expr_if( - make::condition(cond, None), + cond, closure_body.reset_indent(), Some(ast::ElseBranch::Block(make::block_expr(None, Some(none_path)))), ) diff --git a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs index 884905a918..9a80db6a83 100644 --- a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs +++ b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs @@ -48,25 +48,28 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) let cond = if_expr.condition()?; // Check if there is an IfLet that we can handle. - let if_let_pat = match cond.pat() { - None => None, // No IfLet, supported. - Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => { - let path = pat.path()?; - if path.qualifier().is_some() { - return None; - } + let (if_let_pat, cond_expr) = if cond.is_pattern_cond() { + let let_ = cond.single_let()?; + match let_.pat() { + Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => { + let path = pat.path()?; + if path.qualifier().is_some() { + return None; + } - let bound_ident = pat.fields().next().unwrap(); - if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) { - return None; - } + let bound_ident = pat.fields().next().unwrap(); + if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) { + 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 = then_block.stmt_list()?; @@ -119,8 +122,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) let then_branch = make::block_expr(once(make::expr_stmt(early_expression).into()), None); let cond = invert_boolean_expression(cond_expr); - make::expr_if(make::condition(cond, None), then_branch, None) - .indent(if_indent_level) + make::expr_if(cond, then_branch, None).indent(if_indent_level) }; new_expr.syntax().clone_for_update() } diff --git a/crates/ide_assists/src/handlers/convert_while_to_loop.rs b/crates/ide_assists/src/handlers/convert_while_to_loop.rs index 2bc64e77a3..30b4daec9d 100644 --- a/crates/ide_assists/src/handlers/convert_while_to_loop.rs +++ b/crates/ide_assists/src/handlers/convert_while_to_loop.rs @@ -42,7 +42,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_body = while_expr.loop_body()?; let while_cond = while_expr.condition()?; - let while_cond_expr = while_cond.expr()?; let target = while_expr.syntax().text_range(); acc.add( @@ -55,19 +54,15 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O let break_block = make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None) .indent(while_indent_level); - let block_expr = match while_cond.pat() { - Some(_) => { - let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into())); - let stmts = once(make::expr_stmt(if_expr).into()); - make::block_expr(stmts, None) - } - None => { - let if_cond = make::condition(invert_boolean_expression(while_cond_expr), None); - let if_expr = make::expr_if(if_cond, break_block, None); - let stmts = - once(make::expr_stmt(if_expr).into()).chain(while_body.statements()); - make::block_expr(stmts, while_body.tail_expr()) - } + let block_expr = if while_cond.is_pattern_cond() { + let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into())); + let stmts = once(make::expr_stmt(if_expr).into()); + make::block_expr(stmts, None) + } else { + let if_cond = invert_boolean_expression(while_cond); + let if_expr = make::expr_if(if_cond, break_block, None); + 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)); diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 877c5b0cef..21cfc76ac9 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs @@ -1219,28 +1219,26 @@ impl FlowHandler { let stmt = make::expr_stmt(action); let block = make::block_expr(iter::once(stmt.into()), None); 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, - 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 } => { let path = make::ext::ident_path("Some"); let value_pat = make::ext::simple_ident_pat(make::name("value")); 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 action_expr = action.make_result_handler(Some(value)); let action_stmt = make::expr_stmt(action_expr); 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 } => { let some_name = "value"; diff --git a/crates/ide_assists/src/handlers/invert_if.rs b/crates/ide_assists/src/handlers/invert_if.rs index 20f6b0c54c..8f99c4db67 100644 --- a/crates/ide_assists/src/handlers/invert_if.rs +++ b/crates/ide_assists/src/handlers/invert_if.rs @@ -34,12 +34,12 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { return None; } + let cond = expr.condition()?; // This assist should not apply for if-let. - if expr.condition()?.is_pattern_cond() { + if cond.is_pattern_cond() { return None; } - let cond = expr.condition()?.expr()?; let then_node = expr.then_branch()?.syntax().clone(); let else_block = match expr.else_branch()? { ast::ElseBranch::Block(it) => it, diff --git a/crates/ide_assists/src/handlers/move_guard.rs b/crates/ide_assists/src/handlers/move_guard.rs index 366f308f6e..6554940859 100644 --- a/crates/ide_assists/src/handlers/move_guard.rs +++ b/crates/ide_assists/src/handlers/move_guard.rs @@ -1,8 +1,5 @@ use syntax::{ - ast::{ - edit::AstNodeEdit, make, AstNode, BlockExpr, Condition, ElseBranch, Expr, IfExpr, MatchArm, - Pat, - }, + ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat}, 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(); - // FIXME: support `if let` guards too - if guard.let_token().is_some() { - return None; - } - let guard_condition = guard.expr()?; + let guard_condition = guard.condition()?; let arm_expr = match_arm.expr()?; - let if_expr = make::expr_if( - make::condition(guard_condition, None), - make::block_expr(None, Some(arm_expr.clone())), - None, - ) - .indent(arm_expr.indent_level()); + let if_expr = + make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None) + .indent(arm_expr.indent_level()); let target = guard.syntax().text_range(); 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. -fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Condition, BlockExpr)>, Option)> { +fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option)> { let mut conds_blocks = Vec::new(); let mut curr_if = if_expr; let tail = loop { 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()?)); match curr_if.else_branch() { Some(ElseBranch::IfExpr(e)) => { diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs index 7790934792..3be14d7d95 100644 --- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs @@ -60,15 +60,22 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) None } }); - let scrutinee_to_be_expr = if_expr.condition()?.expr()?; + let scrutinee_to_be_expr = if_expr.condition()?; + let scrutinee_to_be_expr = match scrutinee_to_be_expr.single_let() { + Some(cond) => cond.expr()?, + None => scrutinee_to_be_expr, + }; let mut pat_seen = false; let mut cond_bodies = Vec::new(); for if_expr in if_exprs { let cond = if_expr.condition()?; - let expr = cond.expr()?; - let cond = match cond.pat() { - Some(pat) => { + let cond = match cond.single_let() { + Some(let_) => { + 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() { // Only if all condition expressions are equal we can merge them into a match return None; @@ -76,7 +83,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) pat_seen = true; Either::Left(pat) } - None => Either::Right(expr), + // Multiple `let`, unsupported. + None if cond.is_pattern_cond() => return None, + None => Either::Right(cond), }; let body = if_expr.then_branch()?; cond_bodies.push((cond, body)); @@ -217,11 +226,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 else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) }; let if_let_expr = make::expr_if( - condition, + condition.into(), then_block, else_expr.map(make_block_expr).map(ast::ElseBranch::Block), ) diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs index 1062cc3953..a5fa8a110d 100644 --- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs @@ -62,7 +62,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> let block = 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_); edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index d711215491..e986c28b14 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs @@ -575,6 +575,14 @@ impl<'a> CompletionContext<'a> { (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(_) => { cov_mark::hit!(expected_type_fn_param); ActiveParameter::at_token( @@ -641,9 +649,7 @@ impl<'a> CompletionContext<'a> { (ty, None) }, ast::IfExpr(it) => { - cov_mark::hit!(expected_type_if_let_without_leading_char); let ty = it.condition() - .and_then(|cond| cond.expr()) .and_then(|e| self.sema.type_of_expr(&e)) .map(TypeInfo::original); (ty, None) @@ -939,7 +945,7 @@ fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternCont return (PatternRefutability::Irrefutable, has_type_ascription) }, ast::MatchArm(_) => PatternRefutability::Refutable, - ast::Condition(_) => PatternRefutability::Refutable, + ast::LetExpr(_) => PatternRefutability::Refutable, ast::ForExpr(_) => PatternRefutability::Irrefutable, _ => PatternRefutability::Irrefutable, } diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 2d3d640933..9c6d3775c7 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -226,6 +226,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) { | ast::Expr::TryExpr(_) | ast::Expr::TupleExpr(_) | ast::Expr::WhileExpr(_) + | ast::Expr::LetExpr(_) | ast::Expr::YieldExpr(_) => cb(expr), } } From a1b7169b483ff49ea420ec1ed3822d739589df58 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 24 Jan 2022 04:50:07 +0200 Subject: [PATCH 5/8] Update tests Unfortunately, we lost some recovery for expressions. --- crates/hir_def/src/body/scope.rs | 22 ++ crates/hir_ty/src/tests/macros.rs | 4 +- crates/ide_assists/src/handlers/move_guard.rs | 59 ++++- .../src/handlers/replace_if_let_with_match.rs | 12 + crates/parser/src/tests/top_entries.rs | 24 +- .../parser/err/0008_item_block_recovery.txt | 5 +- .../test_data/parser/err/0019_let_recover.rs | 2 +- .../test_data/parser/err/0019_let_recover.txt | 32 +-- .../parser/err/0024_many_type_parens.txt | 214 +++++++++--------- .../test_data/parser/inline/ok/0030_cond.rast | 209 ----------------- .../test_data/parser/inline/ok/0030_cond.rs | 7 - .../test_data/parser/inline/ok/0030_cond.txt | 209 ----------------- .../parser/inline/ok/0030_let_expr.rast | 90 ++++++++ .../parser/inline/ok/0030_let_expr.rs | 4 + .../parser/inline/ok/0030_let_expr.txt | 90 ++++++++ .../parser/inline/ok/0031_while_expr.txt | 24 +- .../parser/inline/ok/0064_if_expr.txt | 48 ++-- .../parser/inline/ok/0088_break_ambiguity.txt | 10 +- .../inline/ok/0096_no_semi_after_block.txt | 10 +- .../test_data/parser/inline/ok/0109_label.txt | 5 +- .../parser/inline/ok/0118_match_guard.txt | 27 +-- .../test_data/parser/ok/0033_label_break.txt | 76 +++---- .../test_data/parser/ok/0035_weird_exprs.txt | 135 ++++++----- .../parser/ok/0047_minus_in_inner_pattern.txt | 2 +- .../test_data/parser/ok/0056_neq_in_type.txt | 77 ++++--- .../parser/ok/0059_loops_in_parens.txt | 5 +- .../validation/0031_block_inner_attrs.rast | 10 +- 27 files changed, 616 insertions(+), 796 deletions(-) delete mode 100644 crates/parser/test_data/parser/inline/ok/0030_cond.rast delete mode 100644 crates/parser/test_data/parser/inline/ok/0030_cond.rs delete mode 100644 crates/parser/test_data/parser/inline/ok/0030_cond.txt create mode 100644 crates/parser/test_data/parser/inline/ok/0030_let_expr.rast create mode 100644 crates/parser/test_data/parser/inline/ok/0030_let_expr.rs create mode 100644 crates/parser/test_data/parser/inline/ok/0030_let_expr.txt diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs index 505d33fa48..fc36f1ae52 100644 --- a/crates/hir_def/src/body/scope.rs +++ b/crates/hir_def/src/body/scope.rs @@ -542,4 +542,26 @@ fn test() { 93, ); } + + #[test] + fn let_chains_can_reference_previous_lets() { + do_check_local_name( + r#" +fn test() { + let foo: Option = 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 = None; + if let Some(spam) = foo && spam > 1 && let Some(spam) = foo && sp$0am > 1 {} +} +"#, + 100, + ); + } } diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs index a61175f273..344e7293c5 100644 --- a/crates/hir_ty/src/tests/macros.rs +++ b/crates/hir_ty/src/tests/macros.rs @@ -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 39..442 '{ ...!(); }': () 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': () @@ -198,6 +197,7 @@ fn expr_macro_def_expanded_in_various_places() { 117..119 '{}': () 124..134 '|| spam!()': || -> isize 140..156 'while ...!() {}': () + 146..153 'spam!()': bool 154..156 '{}': () 161..174 'break 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 53..456 '{ ...!(); }': () 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': () @@ -279,6 +278,7 @@ fn expr_macro_rules_expanded_in_various_places() { 131..133 '{}': () 138..148 '|| spam!()': || -> isize 154..170 'while ...!() {}': () + 160..167 'spam!()': bool 168..170 '{}': () 175..188 'break spam!()': ! 194..208 'return spam!()': ! diff --git a/crates/ide_assists/src/handlers/move_guard.rs b/crates/ide_assists/src/handlers/move_guard.rs index 6554940859..5c05cb921d 100644 --- a/crates/ide_assists/src/handlers/move_guard.rs +++ b/crates/ide_assists/src/handlers/move_guard.rs @@ -266,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] fn move_guard_to_arm_body_works_complex_match() { check_assist( @@ -426,13 +451,21 @@ fn main() { } #[test] - fn move_arm_cond_to_match_guard_if_let_not_works() { - check_assist_not_applicable( + fn move_arm_cond_to_match_guard_if_let_works() { + check_assist( move_arm_cond_to_match_guard, r#" fn main() { 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 } } @@ -884,7 +917,7 @@ fn main() { #[test] fn move_arm_cond_to_match_guard_elseif_iflet() { - check_assist_not_applicable( + check_assist( move_arm_cond_to_match_guard, r#" fn main() { @@ -901,9 +934,21 @@ fn main() { 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] diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs index 3be14d7d95..2f35f74c5a 100644 --- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs @@ -381,6 +381,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] fn test_if_let_with_match_basic() { check_assist( diff --git a/crates/parser/src/tests/top_entries.rs b/crates/parser/src/tests/top_entries.rs index 24e41b46f8..eb640dc7fc 100644 --- a/crates/parser/src/tests/top_entries.rs +++ b/crates/parser/src/tests/top_entries.rs @@ -289,17 +289,19 @@ fn expr() { TopEntryPoint::Expr, "let _ = 0;", expect![[r#" - ERROR - LET_KW "let" - WHITESPACE " " - UNDERSCORE "_" - WHITESPACE " " - EQ "=" - WHITESPACE " " - INT_NUMBER "0" - SEMICOLON ";" - error 0: expected expression - "#]], + ERROR + LET_EXPR + LET_KW "let" + WHITESPACE " " + WILDCARD_PAT + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + SEMICOLON ";" + "#]], ); } diff --git a/crates/parser/test_data/parser/err/0008_item_block_recovery.txt b/crates/parser/test_data/parser/err/0008_item_block_recovery.txt index 6dd70e7cd9..60b2fe9875 100644 --- a/crates/parser/test_data/parser/err/0008_item_block_recovery.txt +++ b/crates/parser/test_data/parser/err/0008_item_block_recovery.txt @@ -29,9 +29,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/err/0019_let_recover.rs b/crates/parser/test_data/parser/err/0019_let_recover.rs index 48bf3d68bd..5108d5a49b 100644 --- a/crates/parser/test_data/parser/err/0019_let_recover.rs +++ b/crates/parser/test_data/parser/err/0019_let_recover.rs @@ -1,5 +1,5 @@ fn foo() { - let foo = + let foo = 11 let bar = 1; let let baz = 92; diff --git a/crates/parser/test_data/parser/err/0019_let_recover.txt b/crates/parser/test_data/parser/err/0019_let_recover.txt index 25722b1355..7d62e0cc14 100644 --- a/crates/parser/test_data/parser/err/0019_let_recover.txt +++ b/crates/parser/test_data/parser/err/0019_let_recover.txt @@ -20,6 +20,9 @@ SOURCE_FILE IDENT "foo" WHITESPACE " " EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "11" WHITESPACE "\n " LET_STMT LET_KW "let" @@ -57,9 +60,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -73,9 +75,8 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -95,13 +96,12 @@ SOURCE_FILE WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" -error 24: expected expression -error 24: expected SEMICOLON -error 49: expected pattern -error 49: expected SEMICOLON -error 75: expected pattern -error 75: expected SEMICOLON -error 98: expected pattern -error 98: expected SEMICOLON -error 124: expected pattern -error 124: expected SEMICOLON +error 27: expected SEMICOLON +error 52: expected pattern +error 52: expected SEMICOLON +error 78: expected pattern +error 78: expected SEMICOLON +error 101: expected pattern +error 101: expected SEMICOLON +error 127: expected pattern +error 127: expected SEMICOLON diff --git a/crates/parser/test_data/parser/err/0024_many_type_parens.txt b/crates/parser/test_data/parser/err/0024_many_type_parens.txt index 446e1a8233..82e6a11249 100644 --- a/crates/parser/test_data/parser/err/0024_many_type_parens.txt +++ b/crates/parser/test_data/parser/err/0024_many_type_parens.txt @@ -180,116 +180,118 @@ SOURCE_FILE ERROR PLUS "+" WHITESPACE " " - EXPR_STMT - TUPLE_EXPR - L_PAREN "(" - FOR_EXPR - FOR_KW "for" - PATH_PAT - PATH - PATH_SEGMENT - L_ANGLE "<" - ERROR - LIFETIME_IDENT "'a" - R_ANGLE ">" - WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + FOR_EXPR + FOR_KW "for" + PATH_PAT + PATH + PATH_SEGMENT + L_ANGLE "<" + ERROR + LIFETIME_IDENT "'a" + R_ANGLE ">" + WHITESPACE " " + 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 PATH_SEGMENT NAME_REF - IDENT "Copy" + IDENT "Trait" + L_ANGLE "<" + ERROR + LIFETIME_IDENT "'a" + R_ANGLE ">" + ERROR R_PAREN ")" - R_ANGLE ">" - ERROR - SEMICOLON ";" - WHITESPACE "\n " - LET_STMT - LET_KW "let" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + PAREN_EXPR + L_PAREN "(" + 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 " " - WILDCARD_PAT - UNDERSCORE "_" - COLON ":" - WHITESPACE " " - DYN_TRAIT_TYPE - TYPE_BOUND_LIST - TYPE_BOUND - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Box" - GENERIC_ARG_LIST + BIN_EXPR + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Box" + L_ANGLE "<" + TUPLE_EXPR + L_PAREN "(" + FOR_EXPR + FOR_KW "for" + PATH_PAT + PATH + PATH_SEGMENT L_ANGLE "<" - TYPE_ARG - PAREN_TYPE - L_PAREN "(" - FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Trait" - GENERIC_ARG_LIST - L_ANGLE "<" - LIFETIME_ARG - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" - R_PAREN ")" - WHITESPACE " " - PLUS "+" - WHITESPACE " " - TYPE_BOUND - L_PAREN "(" - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Copy" - R_PAREN ")" - WHITESPACE " " - PLUS "+" - WHITESPACE " " - TYPE_BOUND - L_PAREN "(" - QUESTION "?" - PATH_TYPE + ERROR + LIFETIME_IDENT "'a" + R_ANGLE ">" + WHITESPACE " " + 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 + PATH_SEGMENT + NAME_REF + IDENT "Copy" + R_PAREN ")" + WHITESPACE " " + PLUS "+" + WHITESPACE " " + PAREN_EXPR + L_PAREN "(" + ERROR + QUESTION "?" + PATH_EXPR PATH PATH_SEGMENT NAME_REF IDENT "Sized" R_PAREN ")" - ERROR - R_ANGLE ">" - SEMICOLON ";" + R_ANGLE ">" + ERROR + SEMICOLON ";" WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" @@ -312,10 +314,18 @@ error 168: expected expression error 179: expected expression error 180: expected a block error 180: expected COMMA -error 180: expected expression -error 180: expected R_PAREN -error 180: expected SEMICOLON -error 215: expected COMMA -error 215: expected R_ANGLE -error 235: expected SEMICOLON -error 235: expected expression +error 190: expected EQ +error 190: expected expression +error 191: expected COMMA +error 201: expected type +error 204: expected IN_KW +error 211: 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 diff --git a/crates/parser/test_data/parser/inline/ok/0030_cond.rast b/crates/parser/test_data/parser/inline/ok/0030_cond.rast deleted file mode 100644 index 3aa330f55d..0000000000 --- a/crates/parser/test_data/parser/inline/ok/0030_cond.rast +++ /dev/null @@ -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" diff --git a/crates/parser/test_data/parser/inline/ok/0030_cond.rs b/crates/parser/test_data/parser/inline/ok/0030_cond.rs deleted file mode 100644 index 2552a2621f..0000000000 --- a/crates/parser/test_data/parser/inline/ok/0030_cond.rs +++ /dev/null @@ -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 {} -} diff --git a/crates/parser/test_data/parser/inline/ok/0030_cond.txt b/crates/parser/test_data/parser/inline/ok/0030_cond.txt deleted file mode 100644 index 5b89937013..0000000000 --- a/crates/parser/test_data/parser/inline/ok/0030_cond.txt +++ /dev/null @@ -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" diff --git a/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast b/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast new file mode 100644 index 0000000000..dcffcb1ce2 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0030_let_expr.rast @@ -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" diff --git a/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs b/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs new file mode 100644 index 0000000000..0131d5e338 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0030_let_expr.rs @@ -0,0 +1,4 @@ +fn foo() { + if let Some(_) = None && true {} + while 1 == 5 && (let None = None) {} +} diff --git a/crates/parser/test_data/parser/inline/ok/0030_let_expr.txt b/crates/parser/test_data/parser/inline/ok/0030_let_expr.txt new file mode 100644 index 0000000000..dcffcb1ce2 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/0030_let_expr.txt @@ -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" diff --git a/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt b/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt index fc1ca4934c..16c522414a 100644 --- a/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt +++ b/crates/parser/test_data/parser/inline/ok/0031_while_expr.txt @@ -16,9 +16,8 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -30,7 +29,7 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION + LET_EXPR LET_KW "let" WHITESPACE " " TUPLE_STRUCT_PAT @@ -69,15 +68,14 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - LITERAL - TRUE_KW "true" - WHITESPACE " " - R_CURLY "}" + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + TRUE_KW "true" + WHITESPACE " " + R_CURLY "}" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt b/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt index c1f8381271..e2e964e44d 100644 --- a/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt +++ b/crates/parser/test_data/parser/inline/ok/0064_if_expr.txt @@ -16,9 +16,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -30,9 +29,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -51,9 +49,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -65,9 +62,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - LITERAL - FALSE_KW "false" + LITERAL + FALSE_KW "false" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -86,12 +82,11 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -103,15 +98,14 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE " " - LITERAL - TRUE_KW "true" - WHITESPACE " " - R_CURLY "}" + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE " " + LITERAL + TRUE_KW "true" + WHITESPACE " " + R_CURLY "}" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt b/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt index 50ce9933b9..cbf5e84e8c 100644 --- a/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt +++ b/crates/parser/test_data/parser/inline/ok/0088_break_ambiguity.txt @@ -15,9 +15,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - BREAK_EXPR - BREAK_KW "break" + BREAK_EXPR + BREAK_KW "break" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -28,9 +27,8 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - BREAK_EXPR - BREAK_KW "break" + BREAK_EXPR + BREAK_KW "break" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt b/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt index 2a85371393..e4e215593e 100644 --- a/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt +++ b/crates/parser/test_data/parser/inline/ok/0096_no_semi_after_block.txt @@ -16,9 +16,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -50,9 +49,8 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/inline/ok/0109_label.txt b/crates/parser/test_data/parser/inline/ok/0109_label.txt index bd57fa9d4a..48d0bde845 100644 --- a/crates/parser/test_data/parser/inline/ok/0109_label.txt +++ b/crates/parser/test_data/parser/inline/ok/0109_label.txt @@ -35,9 +35,8 @@ SOURCE_FILE WHITESPACE " " WHILE_KW "while" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt b/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt index a28b6ea5d1..96318b5219 100644 --- a/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt +++ b/crates/parser/test_data/parser/inline/ok/0118_match_guard.txt @@ -49,19 +49,20 @@ SOURCE_FILE MATCH_GUARD IF_KW "if" WHITESPACE " " - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "foo" - WHITESPACE " " - EQ "=" - WHITESPACE " " - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "bar" + LET_EXPR + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "foo" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "bar" WHITESPACE " " FAT_ARROW "=>" WHITESPACE " " diff --git a/crates/parser/test_data/parser/ok/0033_label_break.txt b/crates/parser/test_data/parser/ok/0033_label_break.txt index 9807bf0d9a..df1acd6b83 100644 --- a/crates/parser/test_data/parser/ok/0033_label_break.txt +++ b/crates/parser/test_data/parser/ok/0033_label_break.txt @@ -51,16 +51,15 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "condition_not_met" - ARG_LIST - L_PAREN "(" - R_PAREN ")" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "condition_not_met" + ARG_LIST + L_PAREN "(" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -92,16 +91,15 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "condition_not_met" - ARG_LIST - L_PAREN "(" - R_PAREN ")" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "condition_not_met" + ARG_LIST + L_PAREN "(" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -153,16 +151,15 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "foo" - ARG_LIST - L_PAREN "(" - R_PAREN ")" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "foo" + ARG_LIST + L_PAREN "(" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -187,16 +184,15 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "bar" - ARG_LIST - L_PAREN "(" - R_PAREN ")" + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "bar" + ARG_LIST + L_PAREN "(" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/ok/0035_weird_exprs.txt b/crates/parser/test_data/parser/ok/0035_weird_exprs.txt index 5f62748c47..4ec703e517 100644 --- a/crates/parser/test_data/parser/ok/0035_weird_exprs.txt +++ b/crates/parser/test_data/parser/ok/0035_weird_exprs.txt @@ -280,21 +280,20 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - PREFIX_EXPR - BANG "!" - METHOD_CALL_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "x" - DOT "." - NAME_REF - IDENT "get" - ARG_LIST - L_PAREN "(" - R_PAREN ")" + PREFIX_EXPR + BANG "!" + METHOD_CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + DOT "." + NAME_REF + IDENT "get" + ARG_LIST + L_PAREN "(" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -443,12 +442,11 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - PAREN_EXPR - L_PAREN "(" - RETURN_EXPR - RETURN_KW "return" - R_PAREN ")" + PAREN_EXPR + L_PAREN "(" + RETURN_EXPR + RETURN_KW "return" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -457,12 +455,11 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - PAREN_EXPR - L_PAREN "(" - RETURN_EXPR - RETURN_KW "return" - R_PAREN ")" + PAREN_EXPR + L_PAREN "(" + RETURN_EXPR + RETURN_KW "return" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -495,12 +492,11 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - PAREN_EXPR - L_PAREN "(" - RETURN_EXPR - RETURN_KW "return" - R_PAREN ")" + PAREN_EXPR + L_PAREN "(" + RETURN_EXPR + RETURN_KW "return" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -549,12 +545,11 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - PAREN_EXPR - L_PAREN "(" - RETURN_EXPR - RETURN_KW "return" - R_PAREN ")" + PAREN_EXPR + L_PAREN "(" + RETURN_EXPR + RETURN_KW "return" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -572,12 +567,11 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - PAREN_EXPR - L_PAREN "(" - RETURN_EXPR - RETURN_KW "return" - R_PAREN ")" + PAREN_EXPR + L_PAREN "(" + RETURN_EXPR + RETURN_KW "return" + R_PAREN ")" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -1037,9 +1031,8 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - BREAK_EXPR - BREAK_KW "break" + BREAK_EXPR + BREAK_KW "break" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -1089,18 +1082,17 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - BIN_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "i" - WHITESPACE " " - EQ2 "==" - WHITESPACE " " - LITERAL - INT_NUMBER "1" + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "i" + WHITESPACE " " + EQ2 "==" + WHITESPACE " " + LITERAL + INT_NUMBER "1" WHITESPACE " " BLOCK_EXPR STMT_LIST @@ -1344,18 +1336,17 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - BIN_EXPR - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "u8" - WHITESPACE " " - NEQ "!=" - WHITESPACE " " - LITERAL - INT_NUMBER "0u8" + BIN_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "u8" + WHITESPACE " " + NEQ "!=" + WHITESPACE " " + LITERAL + INT_NUMBER "0u8" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt b/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt index ac23e7d1d9..aecc71d482 100644 --- a/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt +++ b/crates/parser/test_data/parser/ok/0047_minus_in_inner_pattern.txt @@ -219,7 +219,7 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION + LET_EXPR LET_KW "let" WHITESPACE " " TUPLE_STRUCT_PAT diff --git a/crates/parser/test_data/parser/ok/0056_neq_in_type.txt b/crates/parser/test_data/parser/ok/0056_neq_in_type.txt index 2d78eaffc5..55ce31275f 100644 --- a/crates/parser/test_data/parser/ok/0056_neq_in_type.txt +++ b/crates/parser/test_data/parser/ok/0056_neq_in_type.txt @@ -15,47 +15,46 @@ SOURCE_FILE IF_EXPR IF_KW "if" WHITESPACE " " - CONDITION - BIN_EXPR - CAST_EXPR - METHOD_CALL_EXPR - LITERAL - FLOAT_NUMBER "1.0f32" - DOT "." - NAME_REF - IDENT "floor" - ARG_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "i64" + BIN_EXPR + CAST_EXPR + METHOD_CALL_EXPR + LITERAL + FLOAT_NUMBER "1.0f32" + DOT "." + NAME_REF + IDENT "floor" + ARG_LIST + L_PAREN "(" + R_PAREN ")" WHITESPACE " " - NEQ "!=" + AS_KW "as" WHITESPACE " " - CAST_EXPR - METHOD_CALL_EXPR - LITERAL - FLOAT_NUMBER "1.0f32" - DOT "." - NAME_REF - IDENT "floor" - ARG_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - AS_KW "as" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "i64" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i64" + WHITESPACE " " + NEQ "!=" + WHITESPACE " " + CAST_EXPR + METHOD_CALL_EXPR + LITERAL + FLOAT_NUMBER "1.0f32" + DOT "." + NAME_REF + IDENT "floor" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "i64" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt b/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt index 1eeb6c957f..79bc7f971d 100644 --- a/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt +++ b/crates/parser/test_data/parser/ok/0059_loops_in_parens.txt @@ -86,9 +86,8 @@ SOURCE_FILE WHILE_EXPR WHILE_KW "while" WHITESPACE " " - CONDITION - LITERAL - TRUE_KW "true" + LITERAL + TRUE_KW "true" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast index d4963979c8..50057a02d8 100644 --- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast +++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast @@ -49,9 +49,8 @@ SOURCE_FILE@0..350 IF_EXPR@134..257 IF_KW@134..136 "if" WHITESPACE@136..137 " " - CONDITION@137..141 - LITERAL@137..141 - TRUE_KW@137..141 "true" + LITERAL@137..141 + TRUE_KW@137..141 "true" WHITESPACE@141..142 " " BLOCK_EXPR@142..257 STMT_LIST@142..257 @@ -94,9 +93,8 @@ SOURCE_FILE@0..350 WHILE_EXPR@262..347 WHILE_KW@262..267 "while" WHITESPACE@267..268 " " - CONDITION@268..272 - LITERAL@268..272 - TRUE_KW@268..272 "true" + LITERAL@268..272 + TRUE_KW@268..272 "true" WHITESPACE@272..273 " " BLOCK_EXPR@273..347 STMT_LIST@273..347 From 821b791b6dfb7635930a31d6e14cab3abfe9dd80 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 30 Jan 2022 12:23:36 +0200 Subject: [PATCH 6/8] Validate `let` expressions Emit an error if they're found in an invalid position. --- crates/syntax/src/validation.rs | 31 +++ .../parser/validation/invalid_let_expr.rast | 216 ++++++++++++++++++ .../parser/validation/invalid_let_expr.rs | 14 ++ 3 files changed, 261 insertions(+) create mode 100644 crates/syntax/test_data/parser/validation/invalid_let_expr.rast create mode 100644 crates/syntax/test_data/parser/validation/invalid_let_expr.rs diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 8dc47e0bd3..3ea5844c95 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs @@ -38,6 +38,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec { 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::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) { errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range())); } } + +fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec) { + 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(), + )); +} diff --git a/crates/syntax/test_data/parser/validation/invalid_let_expr.rast b/crates/syntax/test_data/parser/validation/invalid_let_expr.rast new file mode 100644 index 0000000000..5b37b59783 --- /dev/null +++ b/crates/syntax/test_data/parser/validation/invalid_let_expr.rast @@ -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 diff --git a/crates/syntax/test_data/parser/validation/invalid_let_expr.rs b/crates/syntax/test_data/parser/validation/invalid_let_expr.rs new file mode 100644 index 0000000000..1515ae5334 --- /dev/null +++ b/crates/syntax/test_data/parser/validation/invalid_let_expr.rs @@ -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; } + } + } + } +} From 9881614db198813f47f64277c2d8ca674bcd3544 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 1 Feb 2022 11:35:36 +0200 Subject: [PATCH 7/8] Upgrade ungrammar to 1.15.0 --- Cargo.lock | 4 ++-- crates/syntax/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ef0bd5e3bc..64654c9961 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1832,9 +1832,9 @@ checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" [[package]] name = "ungrammar" -version = "1.14.9" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66be59c2fd880e3d76d1a6cf6d34114008f1d8af2748d4ad9d39ea712f14fda9" +checksum = "ed01567101450f7d600508e7680df6005ae4fe97119d79b0364cc5910ff39732" [[package]] name = "unicase" diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 43c1f2fa80..f59cd4f257 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -30,7 +30,7 @@ rayon = "1" expect-test = "1.2.0-pre.1" proc-macro2 = "1.0.8" quote = "1.0.2" -ungrammar = "=1.14.9" +ungrammar = "=1.15.0" test_utils = { path = "../test_utils" } sourcegen = { path = "../sourcegen" } From f70512cc1710656628b4173557795c1e60f0317d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 21 Feb 2022 08:15:21 +0200 Subject: [PATCH 8/8] Change `single_let()` and `is_pattern_cond()` to free functions --- .../src/handlers/convert_bool_then.rs | 4 +-- .../src/handlers/convert_to_guarded_return.rs | 5 +-- .../src/handlers/convert_while_to_loop.rs | 3 +- crates/ide_assists/src/handlers/invert_if.rs | 3 +- .../src/handlers/replace_if_let_with_match.rs | 13 ++++--- crates/ide_db/src/helpers/node_ext.rs | 26 ++++++++++++++ crates/syntax/src/ast/node_ext.rs | 36 ------------------- 7 files changed, 44 insertions(+), 46 deletions(-) diff --git a/crates/ide_assists/src/handlers/convert_bool_then.rs b/crates/ide_assists/src/handlers/convert_bool_then.rs index 0bf2abe493..274718e6ea 100644 --- a/crates/ide_assists/src/handlers/convert_bool_then.rs +++ b/crates/ide_assists/src/handlers/convert_bool_then.rs @@ -2,7 +2,7 @@ use hir::{known, AsAssocItem, Semantics}; use ide_db::{ helpers::{ for_each_tail_expr, - node_ext::{block_as_lone_tail, preorder_expr}, + node_ext::{block_as_lone_tail, is_pattern_cond, preorder_expr}, FamousDefs, }, RootDatabase, @@ -45,7 +45,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext) -> return None; } - let cond = expr.condition().filter(|cond| !cond.is_pattern_cond())?; + let cond = expr.condition().filter(|cond| !is_pattern_cond(cond.clone()))?; let then = expr.then_branch()?; let else_ = match expr.else_branch()? { ast::ElseBranch::Block(b) => b, diff --git a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs index 9a80db6a83..193d1cdfb2 100644 --- a/crates/ide_assists/src/handlers/convert_to_guarded_return.rs +++ b/crates/ide_assists/src/handlers/convert_to_guarded_return.rs @@ -1,5 +1,6 @@ use std::iter::once; +use ide_db::helpers::node_ext::{is_pattern_cond, single_let}; use syntax::{ ast::{ self, @@ -48,8 +49,8 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) let cond = if_expr.condition()?; // Check if there is an IfLet that we can handle. - let (if_let_pat, cond_expr) = if cond.is_pattern_cond() { - let let_ = cond.single_let()?; + let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) { + let let_ = single_let(cond)?; match let_.pat() { Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => { let path = pat.path()?; diff --git a/crates/ide_assists/src/handlers/convert_while_to_loop.rs b/crates/ide_assists/src/handlers/convert_while_to_loop.rs index 30b4daec9d..0fa2dcfbde 100644 --- a/crates/ide_assists/src/handlers/convert_while_to_loop.rs +++ b/crates/ide_assists/src/handlers/convert_while_to_loop.rs @@ -1,5 +1,6 @@ use std::iter::once; +use ide_db::helpers::node_ext::is_pattern_cond; use syntax::{ ast::{ self, @@ -54,7 +55,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext) -> O let break_block = make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None) .indent(while_indent_level); - let block_expr = if while_cond.is_pattern_cond() { + let block_expr = if is_pattern_cond(while_cond.clone()) { let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into())); let stmts = once(make::expr_stmt(if_expr).into()); make::block_expr(stmts, None) diff --git a/crates/ide_assists/src/handlers/invert_if.rs b/crates/ide_assists/src/handlers/invert_if.rs index 8f99c4db67..46f11f4af3 100644 --- a/crates/ide_assists/src/handlers/invert_if.rs +++ b/crates/ide_assists/src/handlers/invert_if.rs @@ -1,3 +1,4 @@ +use ide_db::helpers::node_ext::is_pattern_cond; use syntax::{ ast::{self, AstNode}, T, @@ -36,7 +37,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let cond = expr.condition()?; // This assist should not apply for if-let. - if cond.is_pattern_cond() { + if is_pattern_cond(cond.clone()) { return None; } diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs index 2f35f74c5a..b594c64c41 100644 --- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs @@ -1,7 +1,12 @@ use std::iter::{self, successors}; 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::{ ast::{ self, @@ -61,7 +66,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) } }); let scrutinee_to_be_expr = if_expr.condition()?; - let scrutinee_to_be_expr = match scrutinee_to_be_expr.single_let() { + let scrutinee_to_be_expr = match single_let(scrutinee_to_be_expr.clone()) { Some(cond) => cond.expr()?, None => scrutinee_to_be_expr, }; @@ -70,7 +75,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) let mut cond_bodies = Vec::new(); for if_expr in if_exprs { let cond = if_expr.condition()?; - let cond = match cond.single_let() { + let cond = match single_let(cond.clone()) { Some(let_) => { let pat = let_.pat()?; let expr = let_.expr()?; @@ -84,7 +89,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) Either::Left(pat) } // Multiple `let`, unsupported. - None if cond.is_pattern_cond() => return None, + None if is_pattern_cond(cond.clone()) => return None, None => Either::Right(cond), }; let body = if_expr.then_branch()?; diff --git a/crates/ide_db/src/helpers/node_ext.rs b/crates/ide_db/src/helpers/node_ext.rs index 82178ed749..5df3ed1366 100644 --- a/crates/ide_db/src/helpers/node_ext.rs +++ b/crates/ide_db/src/helpers/node_ext.rs @@ -216,3 +216,29 @@ pub fn vis_eq(this: &ast::Visibility, other: &ast::Visibility) -> bool { _ => 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 { + 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, + } +} diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 2915e7aab1..5ff6519c9c 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -528,42 +528,6 @@ impl ast::Item { } } -impl ast::Expr { - /// 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(&self) -> Option { - return get_pat(self.clone()); - - fn get_pat(expr: ast::Expr) -> Option { - match expr { - ast::Expr::ParenExpr(expr) => expr.expr().and_then(get_pat), - ast::Expr::LetExpr(expr) => Some(expr), - _ => None, - } - } - } - - pub fn is_pattern_cond(&self) -> bool { - return contains_let(self.clone()); - - fn contains_let(expr: ast::Expr) -> bool { - match expr { - ast::Expr::BinExpr(expr) - if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) => - { - expr.lhs() - .map(contains_let) - .or_else(|| expr.rhs().map(contains_let)) - .unwrap_or(false) - } - ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, contains_let), - ast::Expr::LetExpr(_) => true, - _ => false, - } - } - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub enum FieldKind { Name(ast::NameRef),