Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/parser/src/grammar/expressions.rs10
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs33
-rw-r--r--crates/parser/src/syntax_kind/generated.rs2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs73
-rw-r--r--crates/syntax/src/ast/make.rs12
-rw-r--r--crates/syntax/src/ast/node_ext.rs34
-rw-r--r--crates/syntax/src/tests/ast_src.rs2
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<Marker>) -> 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<Marker>) -> 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<SyntaxToken> { support::token(&self.syntax, T![if]) }
- pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) }
+ pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) }
}
@@ -1038,7 +1038,7 @@ impl ast::HasAttrs for WhileExpr {}
impl ast::HasLoopBody for WhileExpr {}
impl WhileExpr {
pub fn while_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![while]) }
- pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) }
+ pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -1052,6 +1052,18 @@ impl YieldExpr {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct LetExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for LetExpr {}
+impl LetExpr {
+ pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
+ pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList {
pub(crate) syntax: SyntaxNode,
}
@@ -1107,17 +1119,6 @@ impl ArgList {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Condition {
- pub(crate) syntax: SyntaxNode,
-}
-impl Condition {
- pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
- pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
- pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MatchArmList {
pub(crate) syntax: SyntaxNode,
}
@@ -1147,10 +1148,7 @@ pub struct MatchGuard {
}
impl MatchGuard {
pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
- pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
- pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
- pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn condition(&self) -> Option<Expr> { 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<Self> {
+ 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<Self> {
@@ -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<Self> {
- 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<Self> {
@@ -3336,6 +3335,9 @@ impl From<WhileExpr> for Expr {
impl From<YieldExpr> for Expr {
fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) }
}
+impl From<LetExpr> for Expr {
+ fn from(node: LetExpr) -> Expr { Expr::LetExpr(node) }
+}
impl AstNode for Expr {
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::ElseBranch>,
) -> 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::Pat>) -> 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<Item = ast::Expr>) -> 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<ast::LetExpr> {
+ return get_pat(self.clone());
+
+ fn get_pat(expr: ast::Expr) -> Option<ast::LetExpr> {
+ 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",