internal: more reasonable grammar for blocks

Consider these expples

        { 92 }
  async { 92 }
    'a: { 92 }
   #[a] { 92 }

Previously the tree for them were

  BLOCK_EXPR
    { ... }

  EFFECT_EXPR
    async
    BLOCK_EXPR
      { ... }

  EFFECT_EXPR
    'a:
    BLOCK_EXPR
      { ... }

  BLOCK_EXPR
    #[a]
    { ... }

As you see, it gets progressively worse :) The last two items are
especially odd. The last one even violates the balanced curleys
invariant we have (#10357) The new approach is to say that the stuff in
`{}` is stmt_list, and the block is stmt_list + optional modifiers

  BLOCK_EXPR
    STMT_LIST
      { ... }

  BLOCK_EXPR
    async
    STMT_LIST
      { ... }

  BLOCK_EXPR
    'a:
    STMT_LIST
      { ... }

  BLOCK_EXPR
    #[a]
    STMT_LIST
      { ... }
This commit is contained in:
Aleksey Kladov 2021-09-26 12:12:57 +03:00
parent c51a3c78cf
commit 2bf81922f7
233 changed files with 11762 additions and 11343 deletions

View file

@ -18,7 +18,7 @@ use crate::{
};
pub use self::{
expr_ext::{ArrayExprKind, Effect, ElseBranch, LiteralKind},
expr_ext::{ArrayExprKind, BlockModifier, ElseBranch, LiteralKind},
generated::{nodes::*, tokens::*},
node_ext::{
AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,

View file

@ -451,7 +451,7 @@ impl ast::RecordExprFieldList {
}
}
impl ast::BlockExpr {
impl ast::StmtList {
pub fn push_front(&self, statement: ast::Stmt) {
ted::insert(Position::after(self.l_curly_token().unwrap()), statement.syntax());
}

View file

@ -25,7 +25,6 @@ impl ast::Expr {
| ast::Expr::WhileExpr(_)
| ast::Expr::BlockExpr(_)
| ast::Expr::MatchExpr(_)
| ast::Expr::EffectExpr(_)
)
}
}
@ -268,38 +267,23 @@ impl ast::Literal {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Effect {
pub enum BlockModifier {
Async(SyntaxToken),
Unsafe(SyntaxToken),
Try(SyntaxToken),
Const(SyntaxToken),
// Very much not an effect, but we stuff it into this node anyway
Label(ast::Label),
}
impl ast::EffectExpr {
pub fn effect(&self) -> Effect {
if let Some(token) = self.async_token() {
return Effect::Async(token);
}
if let Some(token) = self.unsafe_token() {
return Effect::Unsafe(token);
}
if let Some(token) = self.try_token() {
return Effect::Try(token);
}
if let Some(token) = self.const_token() {
return Effect::Const(token);
}
if let Some(label) = self.label() {
return Effect::Label(label);
}
unreachable!("ast::EffectExpr without Effect")
}
}
impl ast::BlockExpr {
pub fn modifier(&self) -> Option<BlockModifier> {
self.async_token()
.map(BlockModifier::Async)
.or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
.or_else(|| self.try_token().map(BlockModifier::Try))
.or_else(|| self.const_token().map(BlockModifier::Const))
.or_else(|| self.label().map(BlockModifier::Label))
}
/// false if the block is an intrinsic part of the syntax and can't be
/// replaced with arbitrary expression.
///
@ -312,7 +296,7 @@ impl ast::BlockExpr {
Some(it) => it,
None => return true,
};
!matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR | EFFECT_EXPR)
!matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR)
}
}

View file

@ -477,10 +477,12 @@ pub struct BlockExpr {
}
impl ast::AttrsOwner for BlockExpr {}
impl BlockExpr {
pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
pub fn tail_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) }
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
pub fn stmt_list(&self) -> Option<StmtList> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SelfParam {
@ -643,7 +645,6 @@ impl Meta {
pub struct ExprStmt {
pub(crate) syntax: SyntaxNode,
}
impl ast::AttrsOwner for ExprStmt {}
impl ExprStmt {
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
@ -753,19 +754,6 @@ impl ContinueExpr {
pub fn lifetime(&self) -> Option<Lifetime> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EffectExpr {
pub(crate) syntax: SyntaxNode,
}
impl ast::AttrsOwner for EffectExpr {}
impl EffectExpr {
pub fn label(&self) -> Option<Label> { support::child(&self.syntax) }
pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) }
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FieldExpr {
pub(crate) syntax: SyntaxNode,
}
@ -945,6 +933,17 @@ impl YieldExpr {
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList {
pub(crate) syntax: SyntaxNode,
}
impl ast::AttrsOwner for StmtList {}
impl StmtList {
pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) }
pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) }
pub fn tail_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Label {
pub(crate) syntax: SyntaxNode,
}
@ -1339,7 +1338,6 @@ pub enum Expr {
CastExpr(CastExpr),
ClosureExpr(ClosureExpr),
ContinueExpr(ContinueExpr),
EffectExpr(EffectExpr),
FieldExpr(FieldExpr),
ForExpr(ForExpr),
IfExpr(IfExpr),
@ -2255,17 +2253,6 @@ impl AstNode for ContinueExpr {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for EffectExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == EFFECT_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 FieldExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == FIELD_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@ -2475,6 +2462,17 @@ impl AstNode for YieldExpr {
}
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> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for Label {
fn can_cast(kind: SyntaxKind) -> bool { kind == LABEL }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@ -3073,9 +3071,6 @@ impl From<ClosureExpr> for Expr {
impl From<ContinueExpr> for Expr {
fn from(node: ContinueExpr) -> Expr { Expr::ContinueExpr(node) }
}
impl From<EffectExpr> for Expr {
fn from(node: EffectExpr) -> Expr { Expr::EffectExpr(node) }
}
impl From<FieldExpr> for Expr {
fn from(node: FieldExpr) -> Expr { Expr::FieldExpr(node) }
}
@ -3143,9 +3138,9 @@ impl AstNode for Expr {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BLOCK_EXPR | BOX_EXPR | BREAK_EXPR | CALL_EXPR
| CAST_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | EFFECT_EXPR | FIELD_EXPR | FOR_EXPR
| IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS
| MATCH_EXPR | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
| CAST_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | FIELD_EXPR | FOR_EXPR | IF_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,
_ => false,
@ -3163,7 +3158,6 @@ impl AstNode for Expr {
CAST_EXPR => Expr::CastExpr(CastExpr { syntax }),
CLOSURE_EXPR => Expr::ClosureExpr(ClosureExpr { syntax }),
CONTINUE_EXPR => Expr::ContinueExpr(ContinueExpr { syntax }),
EFFECT_EXPR => Expr::EffectExpr(EffectExpr { syntax }),
FIELD_EXPR => Expr::FieldExpr(FieldExpr { syntax }),
FOR_EXPR => Expr::ForExpr(ForExpr { syntax }),
IF_EXPR => Expr::IfExpr(IfExpr { syntax }),
@ -3201,7 +3195,6 @@ impl AstNode for Expr {
Expr::CastExpr(it) => &it.syntax,
Expr::ClosureExpr(it) => &it.syntax,
Expr::ContinueExpr(it) => &it.syntax,
Expr::EffectExpr(it) => &it.syntax,
Expr::FieldExpr(it) => &it.syntax,
Expr::ForExpr(it) => &it.syntax,
Expr::IfExpr(it) => &it.syntax,
@ -3660,7 +3653,6 @@ impl AstNode for DynAttrsOwner {
| CONST_PARAM
| LIFETIME_PARAM
| TYPE_PARAM
| EXPR_STMT
| LET_STMT
| ARRAY_EXPR
| AWAIT_EXPR
@ -3671,7 +3663,6 @@ impl AstNode for DynAttrsOwner {
| CAST_EXPR
| CLOSURE_EXPR
| CONTINUE_EXPR
| EFFECT_EXPR
| FIELD_EXPR
| FOR_EXPR
| IF_EXPR
@ -3690,6 +3681,7 @@ impl AstNode for DynAttrsOwner {
| TUPLE_EXPR
| WHILE_EXPR
| YIELD_EXPR
| STMT_LIST
| RECORD_EXPR_FIELD_LIST
| RECORD_EXPR_FIELD
| MATCH_ARM_LIST
@ -4222,11 +4214,6 @@ impl std::fmt::Display for ContinueExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for EffectExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for FieldExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@ -4322,6 +4309,11 @@ impl std::fmt::Display for YieldExpr {
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)
}
}
impl std::fmt::Display for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)

View file

@ -247,6 +247,7 @@ pub fn record_field(
ast_from_text(&format!("struct S {{ {}{}: {}, }}", visibility, name, ty))
}
// TODO
pub fn block_expr(
stmts: impl IntoIterator<Item = ast::Stmt>,
tail_expr: Option<ast::Expr>,

View file

@ -11,8 +11,8 @@ use rowan::{GreenNodeData, GreenTokenData};
use crate::{
ast::{
self, support, AstChildren, AstNode, AstToken, AttrsOwner, GenericParamsOwner, NameOwner,
SyntaxNode,
self, support, AstNode, AstToken, AttrsOwner, GenericParamsOwner, ModuleItemOwner,
NameOwner, SyntaxNode,
},
NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, TokenText, T,
};
@ -50,14 +50,23 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
}
}
impl ast::ModuleItemOwner for ast::StmtList {}
impl ast::BlockExpr {
pub fn items(&self) -> AstChildren<ast::Item> {
support::children(self.syntax())
// FIXME: remove all these methods, they belong to ast::StmtList
pub fn items(&self) -> impl Iterator<Item = ast::Item> {
self.stmt_list().into_iter().flat_map(|it| it.items())
}
pub fn is_empty(&self) -> bool {
self.statements().next().is_none() && self.tail_expr().is_none()
}
pub fn statements(&self) -> impl Iterator<Item = ast::Stmt> {
self.stmt_list().into_iter().flat_map(|it| it.statements())
}
pub fn tail_expr(&self) -> Option<ast::Expr> {
self.stmt_list()?.tail_expr()
}
}
#[derive(Debug, PartialEq, Eq, Clone)]

View file

@ -295,7 +295,8 @@ fn api_walkthrough() {
// Let's get the `1 + 1` expression!
let body: ast::BlockExpr = func.body().unwrap();
let expr: ast::Expr = body.tail_expr().unwrap();
let stmt_list: ast::StmtList = body.stmt_list().unwrap();
let expr: ast::Expr = stmt_list.tail_expr().unwrap();
// Enums are used to group related ast nodes together, and can be used for
// matching. However, because there are no public fields, it's possible to
@ -331,8 +332,8 @@ fn api_walkthrough() {
assert_eq!(text.to_string(), "1 + 1");
// There's a bunch of traversal methods on `SyntaxNode`:
assert_eq!(expr_syntax.parent().as_ref(), Some(body.syntax()));
assert_eq!(body.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
assert_eq!(expr_syntax.parent().as_ref(), Some(stmt_list.syntax()));
assert_eq!(stmt_list.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
assert_eq!(
expr_syntax.next_sibling_or_token().map(|it| it.kind()),
Some(SyntaxKind::WHITESPACE)

View file

@ -162,8 +162,8 @@ fn ws_before(position: &Position, new: &SyntaxElement) -> Option<SyntaxToken> {
}
if prev.kind() == T!['{'] && ast::Stmt::can_cast(new.kind()) {
if let Some(block_expr) = prev.parent().and_then(ast::BlockExpr::cast) {
let mut indent = IndentLevel::from_element(&block_expr.syntax().clone().into());
if let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast) {
let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into());
indent.0 += 1;
return Some(make::tokens::whitespace(&format!("\n{}", indent)));
}

View file

@ -149,6 +149,7 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
"BREAK_EXPR",
"LABEL",
"BLOCK_EXPR",
"STMT_LIST",
"RETURN_EXPR",
"YIELD_EXPR",
"MATCH_EXPR",
@ -158,7 +159,6 @@ pub(crate) const KINDS_SRC: KindsSrc = KindsSrc {
"RECORD_EXPR",
"RECORD_EXPR_FIELD_LIST",
"RECORD_EXPR_FIELD",
"EFFECT_EXPR",
"BOX_EXPR",
// postfix
"CALL_EXPR",

View file

@ -9,14 +9,16 @@ use crate::{
pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<SyntaxError>) {
if let Some(parent) = block.syntax().parent() {
match parent.kind() {
FN | EXPR_STMT | BLOCK_EXPR => return,
FN | EXPR_STMT | STMT_LIST => return,
_ => {}
}
}
errors.extend(block.attrs().filter(|attr| attr.kind().is_inner()).map(|attr| {
SyntaxError::new(
"A block in this position cannot accept inner attributes",
attr.syntax().text_range(),
)
}))
if let Some(stmt_list) = block.stmt_list() {
errors.extend(stmt_list.attrs().filter(|attr| attr.kind().is_inner()).map(|attr| {
SyntaxError::new(
"A block in this position cannot accept inner attributes",
attr.syntax().text_range(),
)
}))
}
}