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.
This commit is contained in:
Chayim Refael Friedman 2022-01-23 04:21:09 +02:00
parent d6ed146a1c
commit de8633f15f
7 changed files with 99 additions and 67 deletions

View file

@ -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![/=]),

View file

@ -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]) {
p.bump(T![let]);
patterns::pattern_top(p);
p.expect(T![=]);
}
expr_no_struct(p);
m.complete(p, CONDITION);
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)
}

View file

@ -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,

View file

@ -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)]
@ -1051,6 +1051,18 @@ impl YieldExpr {
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LetExpr {
pub(crate) syntax: SyntaxNode,
}
impl ast::HasAttrs for LetExpr {}
impl LetExpr {
pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList {
pub(crate) syntax: SyntaxNode,
@ -1106,17 +1118,6 @@ impl ArgList {
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Condition {
pub(crate) syntax: SyntaxNode,
}
impl Condition {
pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
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)

View file

@ -397,7 +397,7 @@ pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Ex
expr_from_text(&format!("match {} {}", expr, match_arm_list))
}
pub fn expr_if(
condition: ast::Condition,
condition: ast::Expr,
then_branch: ast::BlockExpr,
else_branch: Option<ast::ElseBranch>,
) -> ast::Expr {
@ -456,15 +456,9 @@ 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) => {
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 {
ast_from_text(&format!("fn main() {{ ()({}) }}", args.into_iter().format(", ")))

View file

@ -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,
}
}
}
}

View file

@ -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",