mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 12:33:33 +00:00
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:
parent
d6ed146a1c
commit
de8633f15f
7 changed files with 99 additions and 67 deletions
|
@ -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![/=]),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in a new issue