Allow attributes on top level expression

A top level expression is either
- a expression statement or
- the last expression in a block
This commit is contained in:
pcpthm 2019-03-19 17:24:02 +09:00
parent 1cd18f9237
commit ffed132e52
8 changed files with 231 additions and 35 deletions

View File

@ -105,7 +105,9 @@ pub(super) fn process(sink: &mut dyn TreeSink, mut events: Vec<Event>) {
// append `A`'s forward_parent `B`
fp = match mem::replace(&mut events[idx], Event::tombstone()) {
Event::Start { kind, forward_parent } => {
forward_parents.push(kind);
if kind != TOMBSTONE {
forward_parents.push(kind);
}
forward_parent
}
_ => unreachable!(),

View File

@ -8,10 +8,10 @@
pub(super) fn expr(p: &mut Parser) -> BlockLike {
let r = Restrictions { forbid_structs: false, prefer_stmt: false };
expr_bp(p, r, 1)
expr_bp(p, r, 1).1
}
pub(super) fn expr_stmt(p: &mut Parser) -> BlockLike {
pub(super) fn expr_stmt(p: &mut Parser) -> (Option<CompletedMarker>, BlockLike) {
let r = Restrictions { forbid_structs: false, prefer_stmt: true };
expr_bp(p, r, 1)
}
@ -55,7 +55,13 @@ pub(crate) fn expr_block_contents(p: &mut Parser) {
// test block_items
// fn a() { fn b() {} }
let m = p.start();
let has_attrs = p.at(POUND);
// test attr_on_expr_stmt
// fn foo() {
// #[A] foo();
// #[B] bar!{}
// #[C] #[D] {}
// #[D] return ();
// }
attributes::outer_attributes(p);
if p.at(LET_KW) {
let_stmt(p, m);
@ -67,35 +73,48 @@ pub(crate) fn expr_block_contents(p: &mut Parser) {
Err(m) => m,
};
if has_attrs {
m.abandon(p);
p.error("expected a let statement or an item after attributes in block");
} else {
let is_blocklike = expressions::expr_stmt(p) == BlockLike::Block;
if p.at(R_CURLY) {
m.abandon(p);
} else {
// test no_semi_after_block
// fn foo() {
// if true {}
// loop {}
// match () {}
// while true {}
// for _ in () {}
// {}
// {}
// macro_rules! test {
// () => {}
// }
// test!{}
// }
if is_blocklike {
p.eat(SEMI);
let (cm, blocklike) = expr_stmt(p);
let cm = match cm {
None => {
if p.at(R_CURLY) {
m.abandon(p);
} else {
p.expect(SEMI);
m.complete(p, EXPR_STMT);
}
m.complete(p, EXPR_STMT);
continue;
}
Some(cm) => cm,
};
if p.at(R_CURLY) {
// test attr_on_last_expr_in_block
// fn foo() {
// { #[A] bar!()? }
// #[B] &()
// }
m.contract_child(p, cm);
} else {
// test no_semi_after_block
// fn foo() {
// if true {}
// loop {}
// match () {}
// while true {}
// for _ in () {}
// {}
// {}
// macro_rules! test {
// () => {}
// }
// test!{}
// }
if blocklike.is_block() {
p.eat(SEMI);
} else {
p.expect(SEMI);
}
m.complete(p, EXPR_STMT);
}
}
@ -176,7 +195,7 @@ fn current_op(p: &Parser) -> (u8, Op) {
}
// Parses expression with binding power of at least bp.
fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> (Option<CompletedMarker>, BlockLike) {
let mut lhs = match lhs(p, r) {
Some((lhs, blocklike)) => {
// test stmt_bin_expr_ambiguity
@ -185,11 +204,11 @@ fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
// {1} &2;
// }
if r.prefer_stmt && blocklike.is_block() {
return BlockLike::Block;
return (Some(lhs), BlockLike::Block);
}
lhs
}
None => return BlockLike::NotBlock,
None => return (None, BlockLike::NotBlock),
};
loop {
@ -208,7 +227,7 @@ fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
expr_bp(p, r, op_bp + 1);
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
}
BlockLike::NotBlock
(Some(lhs), BlockLike::NotBlock)
}
const LHS_FIRST: TokenSet =

View File

@ -392,9 +392,9 @@ fn match_arm(p: &mut Parser) -> BlockLike {
match_guard(p);
}
p.expect(FAT_ARROW);
let ret = expr_stmt(p);
let blocklike = expr_stmt(p).1;
m.complete(p, MATCH_ARM);
ret
blocklike
}
// test match_guard

View File

@ -228,6 +228,29 @@ pub(crate) fn abandon(mut self, p: &mut Parser) {
}
}
}
/// Contract a node `cm` and complete as `cm`'s `kind`.
/// `cm` must be a child of `m` to work correctly.
/// ```
/// m--A m--A
/// +--cm--B -> +--B
/// +--C C
///
/// [m: TOMBSTONE, A, cm: Start(k), B, Finish, C]
/// [m: Start(k), A, cm: TOMBSTONE, B, Finish, C]
/// ```
pub(crate) fn contract_child(mut self, p: &mut Parser, cm: CompletedMarker) -> CompletedMarker {
self.bomb.defuse();
match p.events[self.pos as usize] {
Event::Start { kind: ref mut slot, .. } => *slot = cm.kind(),
_ => unreachable!(),
};
match p.events[cm.0 as usize] {
Event::Start { kind: ref mut slot, .. } => *slot = TOMBSTONE,
_ => unreachable!(),
};
CompletedMarker::new(self.pos, cm.kind())
}
}
pub(crate) struct CompletedMarker(u32, SyntaxKind);

View File

@ -0,0 +1,6 @@
fn foo() {
#[A] foo();
#[B] bar!{}
#[C] #[D] {}
#[D] return ();
}

View File

@ -0,0 +1,88 @@
SOURCE_FILE@[0; 82)
FN_DEF@[0; 81)
FN_KW@[0; 2)
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@[9; 81)
L_CURLY@[9; 10)
WHITESPACE@[10; 15)
EXPR_STMT@[15; 26)
ATTR@[15; 19)
POUND@[15; 16)
TOKEN_TREE@[16; 19)
L_BRACK@[16; 17)
IDENT@[17; 18) "A"
R_BRACK@[18; 19)
WHITESPACE@[19; 20)
CALL_EXPR@[20; 25)
PATH_EXPR@[20; 23)
PATH@[20; 23)
PATH_SEGMENT@[20; 23)
NAME_REF@[20; 23)
IDENT@[20; 23) "foo"
ARG_LIST@[23; 25)
L_PAREN@[23; 24)
R_PAREN@[24; 25)
SEMI@[25; 26)
WHITESPACE@[26; 31)
EXPR_STMT@[31; 42)
ATTR@[31; 35)
POUND@[31; 32)
TOKEN_TREE@[32; 35)
L_BRACK@[32; 33)
IDENT@[33; 34) "B"
R_BRACK@[34; 35)
WHITESPACE@[35; 36)
MACRO_CALL@[36; 42)
PATH@[36; 39)
PATH_SEGMENT@[36; 39)
NAME_REF@[36; 39)
IDENT@[36; 39) "bar"
EXCL@[39; 40)
TOKEN_TREE@[40; 42)
L_CURLY@[40; 41)
R_CURLY@[41; 42)
WHITESPACE@[42; 47)
EXPR_STMT@[47; 59)
ATTR@[47; 51)
POUND@[47; 48)
TOKEN_TREE@[48; 51)
L_BRACK@[48; 49)
IDENT@[49; 50) "C"
R_BRACK@[50; 51)
WHITESPACE@[51; 52)
ATTR@[52; 56)
POUND@[52; 53)
TOKEN_TREE@[53; 56)
L_BRACK@[53; 54)
IDENT@[54; 55) "D"
R_BRACK@[55; 56)
WHITESPACE@[56; 57)
BLOCK_EXPR@[57; 59)
BLOCK@[57; 59)
L_CURLY@[57; 58)
R_CURLY@[58; 59)
WHITESPACE@[59; 64)
EXPR_STMT@[64; 79)
ATTR@[64; 68)
POUND@[64; 65)
TOKEN_TREE@[65; 68)
L_BRACK@[65; 66)
IDENT@[66; 67) "D"
R_BRACK@[67; 68)
WHITESPACE@[68; 69)
RETURN_EXPR@[69; 78)
RETURN_KW@[69; 75)
WHITESPACE@[75; 76)
TUPLE_EXPR@[76; 78)
L_PAREN@[76; 77)
R_PAREN@[77; 78)
SEMI@[78; 79)
WHITESPACE@[79; 80)
R_CURLY@[80; 81)
WHITESPACE@[81; 82)

View File

@ -0,0 +1,4 @@
fn foo() {
{ #[A] bar!()? }
#[B] &()
}

View File

@ -0,0 +1,54 @@
SOURCE_FILE@[0; 47)
FN_DEF@[0; 46)
FN_KW@[0; 2)
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@[9; 46)
L_CURLY@[9; 10)
WHITESPACE@[10; 15)
EXPR_STMT@[15; 31)
BLOCK_EXPR@[15; 31)
BLOCK@[15; 31)
L_CURLY@[15; 16)
WHITESPACE@[16; 17)
TRY_EXPR@[17; 29)
ATTR@[17; 21)
POUND@[17; 18)
TOKEN_TREE@[18; 21)
L_BRACK@[18; 19)
IDENT@[19; 20) "A"
R_BRACK@[20; 21)
WHITESPACE@[21; 22)
MACRO_CALL@[22; 28)
PATH@[22; 25)
PATH_SEGMENT@[22; 25)
NAME_REF@[22; 25)
IDENT@[22; 25) "bar"
EXCL@[25; 26)
TOKEN_TREE@[26; 28)
L_PAREN@[26; 27)
R_PAREN@[27; 28)
QUESTION@[28; 29)
WHITESPACE@[29; 30)
R_CURLY@[30; 31)
WHITESPACE@[31; 36)
REF_EXPR@[36; 44)
ATTR@[36; 40)
POUND@[36; 37)
TOKEN_TREE@[37; 40)
L_BRACK@[37; 38)
IDENT@[38; 39) "B"
R_BRACK@[39; 40)
WHITESPACE@[40; 41)
AMP@[41; 42)
TUPLE_EXPR@[42; 44)
L_PAREN@[42; 43)
R_PAREN@[43; 44)
WHITESPACE@[44; 45)
R_CURLY@[45; 46)
WHITESPACE@[46; 47)