feat: allow attributes on all expressions

Attrs are syntactically valid on any expression, even if they are not
allowed semantically everywhere yet.
This commit is contained in:
Aleksey Kladov 2021-09-25 19:51:54 +03:00
parent 7dc331faef
commit 56964c9bd3
20 changed files with 303 additions and 335 deletions

View file

@ -650,9 +650,11 @@ impl ExprCollector<'_> {
self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
}
ast::Stmt::ExprStmt(stmt) => {
if self.check_cfg(&stmt).is_none() {
if let Some(expr) = stmt.expr() {
if self.check_cfg(&expr).is_none() {
return;
}
}
let has_semi = stmt.semicolon_token().is_some();
// Note that macro could be expended to multiple statements
if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {

View file

@ -63,7 +63,7 @@ pub(crate) mod entry_points {
pub(crate) use types::type_;
pub(crate) fn expr(p: &mut Parser) {
let _ = expressions::expr_with_attrs(p);
let _ = expressions::expr(p);
}
pub(crate) fn stmt(p: &mut Parser) {

View file

@ -41,7 +41,7 @@ pub(super) fn meta(p: &mut Parser) {
match p.current() {
T![=] => {
p.bump(T![=]);
if expressions::expr(p).0.is_none() {
if !expressions::expr(p) {
p.error("expected expression");
}
}

View file

@ -13,35 +13,19 @@ pub(super) enum StmtWithSemi {
const EXPR_FIRST: TokenSet = LHS_FIRST;
pub(super) fn expr(p: &mut Parser) -> (Option<CompletedMarker>, BlockLike) {
pub(super) fn expr(p: &mut Parser) -> bool {
let r = Restrictions { forbid_structs: false, prefer_stmt: false };
expr_bp(p, r, 1)
expr_bp(p, None, r, 1).is_some()
}
pub(super) fn expr_with_attrs(p: &mut Parser) -> bool {
let m = p.start();
let has_attrs = p.at(T![#]);
attributes::outer_attrs(p);
let (cm, _block_like) = expr(p);
let success = cm.is_some();
match (has_attrs, cm) {
(true, Some(cm)) => cm.extend_to(p, m),
_ => m.abandon(p),
}
success
}
pub(super) fn expr_stmt(p: &mut Parser) -> (Option<CompletedMarker>, BlockLike) {
pub(super) fn expr_stmt(p: &mut Parser, m: Option<Marker>) -> Option<(CompletedMarker, BlockLike)> {
let r = Restrictions { forbid_structs: false, prefer_stmt: true };
expr_bp(p, r, 1)
expr_bp(p, m, r, 1)
}
fn expr_no_struct(p: &mut Parser) {
let r = Restrictions { forbid_structs: true, prefer_stmt: false };
expr_bp(p, r, 1);
expr_bp(p, None, r, 1);
}
pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
@ -53,7 +37,6 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
// #[C] #[D] {}
// #[D] return ();
// }
let has_attrs = p.at(T![#]);
attributes::outer_attrs(p);
if p.at(T![let]) {
@ -68,31 +51,8 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
Err(m) => m,
};
let (cm, blocklike) = expr_stmt(p);
let kind = cm.as_ref().map(|cm| cm.kind()).unwrap_or(ERROR);
if has_attrs {
if matches!(kind, BIN_EXPR | RANGE_EXPR) {
// test_err attr_on_expr_not_allowed
// fn foo() {
// #[A] 1 + 2;
// #[B] if true {};
// }
p.error(format!("attributes are not allowed on {:?}", kind));
}
}
if p.at(T!['}']) || (prefer_expr && p.at(EOF)) {
// test attr_on_last_expr_in_block
// fn foo() {
// { #[A] bar!()? }
// #[B] &()
// }
match cm {
Some(cm) => cm.extend_to(p, m),
None => m.abandon(p),
}
} else {
if let Some((cm, blocklike)) = expr_stmt(p, Some(m)) {
if !(p.at(T!['}']) || (prefer_expr && p.at(EOF))) {
// test no_semi_after_block
// fn foo() {
// if true {}
@ -107,7 +67,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
// }
// test!{}
// }
let m = cm.precede(p);
match with_semi {
StmtWithSemi::No => (),
StmtWithSemi::Optional => {
@ -124,6 +84,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
m.complete(p, EXPR_STMT);
}
}
// test let_stmt
// fn f() { let x: i32 = 92; }
@ -138,7 +99,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
if p.eat(T![=]) {
// test let_stmt_init
// fn f() { let x = 92; }
expressions::expr_with_attrs(p);
expressions::expr(p);
}
match with_semi {
@ -234,20 +195,34 @@ fn current_op(p: &Parser) -> (u8, SyntaxKind) {
}
// Parses expression with binding power of at least bp.
fn expr_bp(p: &mut Parser, mut r: Restrictions, bp: u8) -> (Option<CompletedMarker>, BlockLike) {
fn expr_bp(
p: &mut Parser,
m: Option<Marker>,
mut r: Restrictions,
bp: u8,
) -> Option<(CompletedMarker, BlockLike)> {
let m = m.unwrap_or_else(|| {
let m = p.start();
attributes::outer_attrs(p);
m
});
let mut lhs = match lhs(p, r) {
Some((lhs, blocklike)) => {
let lhs = lhs.extend_to(p, m);
if r.prefer_stmt && blocklike.is_block() {
// test stmt_bin_expr_ambiguity
// fn f() {
// let _ = {1} & 2;
// {1} &2;
// }
return (Some(lhs), BlockLike::Block);
return Some((lhs, BlockLike::Block));
}
lhs
}
None => return (None, BlockLike::NotBlock),
None => {
m.abandon(p);
return None;
}
};
loop {
@ -285,10 +260,10 @@ fn expr_bp(p: &mut Parser, mut r: Restrictions, bp: u8) -> (Option<CompletedMark
}
}
expr_bp(p, Restrictions { prefer_stmt: false, ..r }, op_bp + 1);
expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp + 1);
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
}
(Some(lhs), BlockLike::NotBlock)
Some((lhs, BlockLike::NotBlock))
}
const LHS_FIRST: TokenSet =
@ -341,9 +316,10 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)>
m = p.start();
p.bump(op);
if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])) {
expr_bp(p, r, 2);
expr_bp(p, None, r, 2);
}
return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock));
let cm = m.complete(p, RANGE_EXPR);
return Some((cm, BlockLike::NotBlock));
}
}
@ -353,12 +329,15 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)>
// {p}.x = 10;
// }
let (lhs, blocklike) = atom::atom_expr(p, r)?;
return Some(postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block())));
let (cm, block_like) =
postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block()));
return Some((cm, block_like));
}
};
// parse the interior of the unary expression
expr_bp(p, r, 255);
Some((m.complete(p, kind), BlockLike::NotBlock))
expr_bp(p, None, r, 255);
let cm = m.complete(p, kind);
Some((cm, BlockLike::NotBlock))
}
fn postfix_expr(
@ -536,7 +515,7 @@ fn arg_list(p: &mut Parser) {
// fn main() {
// foo(#[attr] 92)
// }
if !expr_with_attrs(p) {
if !expr(p) {
break;
}
if !p.at(T![')']) && !p.expect(T![,]) {

View file

@ -176,7 +176,7 @@ fn tuple_expr(p: &mut Parser) -> CompletedMarker {
// test tuple_attrs
// const A: (i64, i64) = (1, #[cfg(test)] 2);
if !expr_with_attrs(p) {
if !expr(p) {
break;
}
@ -209,7 +209,7 @@ fn array_expr(p: &mut Parser) -> CompletedMarker {
// test array_attrs
// const A: &[i64] = &[1, #[cfg(test)] 2];
if !expr_with_attrs(p) {
if !expr(p) {
break;
}
@ -438,7 +438,10 @@ fn match_arm(p: &mut Parser) {
match_guard(p);
}
p.expect(T![=>]);
let blocklike = expr_stmt(p).1;
let blocklike = match expr_stmt(p, None) {
Some((_, blocklike)) => blocklike,
None => BlockLike::NotBlock,
};
// test match_arms_commas
// fn foo() {
@ -619,14 +622,14 @@ fn meta_var_expr(p: &mut Parser) -> CompletedMarker {
assert!(p.at(L_DOLLAR));
let m = p.start();
p.bump(L_DOLLAR);
let (completed, _is_block) =
expr_bp(p, Restrictions { forbid_structs: false, prefer_stmt: false }, 1);
let expr = expr_bp(p, None, Restrictions { forbid_structs: false, prefer_stmt: false }, 1);
match (completed, p.current()) {
(Some(it), R_DOLLAR) => {
match (expr, p.current()) {
(Some((cm, _)), R_DOLLAR) => {
p.bump(R_DOLLAR);
// FIXME: this leaves the dollar hanging in the air...
m.abandon(p);
it
cm
}
_ => {
while !p.at(R_DOLLAR) {

View file

@ -339,7 +339,7 @@ impl CompletedMarker {
}
/// Extends this completed marker *to the left* up to `m`.
pub(crate) fn extend_to(self, p: &mut Parser, mut m: Marker) {
pub(crate) fn extend_to(self, p: &mut Parser, mut m: Marker) -> CompletedMarker {
m.bomb.defuse();
let idx = m.pos as usize;
match &mut p.events[idx] {
@ -348,6 +348,7 @@ impl CompletedMarker {
}
_ => unreachable!(),
}
self
}
pub(crate) fn kind(&self) -> SyntaxKind {

View file

@ -26,7 +26,6 @@ SOURCE_FILE@0..43
PATH_SEGMENT@23..24
NAME_REF@23..24
IDENT@23..24 "f"
EXPR_STMT@24..25
ERROR@24..25
COLON@24..25 ":"
WHITESPACE@25..26 " "
@ -55,4 +54,3 @@ error 15..15: expected an item
error 17..17: expected an item
error 24..24: expected SEMICOLON
error 24..24: expected expression
error 25..25: expected SEMICOLON

View file

@ -24,7 +24,6 @@ SOURCE_FILE@0..112
WHITESPACE@15..16 " "
ERROR@16..17
AT@16..17 "@"
EXPR_STMT@17..18
ERROR@17..18
COMMA@17..18 ","
WHITESPACE@18..19 " "
@ -71,13 +70,11 @@ SOURCE_FILE@0..112
WHITESPACE@52..53 " "
ERROR@53..54
AT@53..54 "@"
EXPR_STMT@54..55
ERROR@54..55
COMMA@54..55 ","
WHITESPACE@55..56 " "
IMPL@56..60
IMPL_KW@56..60 "impl"
EXPR_STMT@60..61
ERROR@60..61
COMMA@60..61 ","
WHITESPACE@61..62 " "
@ -122,14 +119,11 @@ SOURCE_FILE@0..112
WHITESPACE@91..92 " "
ERROR@92..93
AT@92..93 "@"
EXPR_STMT@93..94
ERROR@93..94
COMMA@93..94 ","
WHITESPACE@94..95 " "
EXPR_STMT@95..96
ERROR@95..96
R_BRACK@95..96 "]"
EXPR_STMT@96..97
ERROR@96..97
COMMA@96..97 ","
WHITESPACE@97..98 " "
@ -149,7 +143,6 @@ error 16..16: expected expression
error 17..17: expected R_BRACK
error 17..17: expected SEMICOLON
error 17..17: expected expression
error 18..18: expected SEMICOLON
error 25..25: expected a name
error 26..26: expected `;`, `{`, or `(`
error 30..30: expected pattern
@ -157,22 +150,17 @@ error 31..31: expected SEMICOLON
error 53..53: expected expression
error 54..54: expected SEMICOLON
error 54..54: expected expression
error 55..55: expected SEMICOLON
error 60..60: expected type
error 60..60: expected `{`
error 60..60: expected expression
error 61..61: expected SEMICOLON
error 65..65: expected pattern
error 65..65: expected SEMICOLON
error 65..65: expected expression
error 92..92: expected expression
error 93..93: expected SEMICOLON
error 93..93: expected expression
error 94..94: expected SEMICOLON
error 95..95: expected expression
error 96..96: expected SEMICOLON
error 96..96: expected expression
error 97..97: expected SEMICOLON
error 103..103: expected a name
error 104..104: expected `{`
error 108..108: expected pattern

View file

@ -143,7 +143,6 @@ SOURCE_FILE@0..240
LIFETIME_IDENT@117..119 "'a"
R_ANGLE@119..120 ">"
R_PAREN@120..121 ")"
EXPR_STMT@121..123
ERROR@121..122
R_ANGLE@121..122 ">"
SEMICOLON@122..123 ";"
@ -173,11 +172,9 @@ SOURCE_FILE@0..240
PATH_SEGMENT@141..146
NAME_REF@141..146
IDENT@141..146 "Sized"
EXPR_STMT@146..147
ERROR@146..147
R_PAREN@146..147 ")"
WHITESPACE@147..148 " "
EXPR_STMT@148..149
ERROR@148..149
PLUS@148..149 "+"
WHITESPACE@149..150 " "
@ -288,7 +285,6 @@ SOURCE_FILE@0..240
NAME_REF@229..234
IDENT@229..234 "Sized"
R_PAREN@234..235 ")"
EXPR_STMT@235..237
ERROR@235..236
R_ANGLE@235..236 ">"
SEMICOLON@236..237 ";"
@ -306,9 +302,7 @@ error 141..141: expected R_ANGLE
error 141..141: expected SEMICOLON
error 146..146: expected SEMICOLON
error 146..146: expected expression
error 147..147: expected SEMICOLON
error 148..148: expected expression
error 149..149: expected SEMICOLON
error 155..155: expected type
error 158..158: expected IN_KW
error 165..165: expected expression

View file

@ -51,7 +51,6 @@ SOURCE_FILE@0..83
BLOCK_EXPR@66..80
L_CURLY@66..67 "{"
WHITESPACE@67..68 " "
EXPR_STMT@68..75
ERROR@68..75
LABEL@68..75
LIFETIME@68..74
@ -69,4 +68,3 @@ error 24..24: expected existential, fn, trait or impl
error 41..41: expected existential, fn, trait or impl
error 56..56: expected a block
error 75..75: expected a loop
error 75..75: expected SEMICOLON

View file

@ -11,7 +11,6 @@ SOURCE_FILE@0..30
BLOCK_EXPR@10..29
L_CURLY@10..11 "{"
WHITESPACE@11..16 "\n "
EXPR_STMT@16..22
ERROR@16..22
LABEL@16..22
LIFETIME@16..21
@ -24,6 +23,5 @@ SOURCE_FILE@0..30
R_CURLY@28..29 "}"
WHITESPACE@29..30 "\n"
error 22..22: expected a loop
error 22..22: expected SEMICOLON
error 27..27: expected type
error 27..27: expected `{`

View file

@ -1,60 +0,0 @@
SOURCE_FILE@0..48
FN@0..47
FN_KW@0..2 "fn"
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_EXPR@9..47
L_CURLY@9..10 "{"
WHITESPACE@10..14 "\n "
EXPR_STMT@14..25
ATTR@14..18
POUND@14..15 "#"
L_BRACK@15..16 "["
META@16..17
PATH@16..17
PATH_SEGMENT@16..17
NAME_REF@16..17
IDENT@16..17 "A"
R_BRACK@17..18 "]"
WHITESPACE@18..19 " "
BIN_EXPR@19..24
LITERAL@19..20
INT_NUMBER@19..20 "1"
WHITESPACE@20..21 " "
PLUS@21..22 "+"
WHITESPACE@22..23 " "
LITERAL@23..24
INT_NUMBER@23..24 "2"
SEMICOLON@24..25 ";"
WHITESPACE@25..29 "\n "
EXPR_STMT@29..45
ATTR@29..33
POUND@29..30 "#"
L_BRACK@30..31 "["
META@31..32
PATH@31..32
PATH_SEGMENT@31..32
NAME_REF@31..32
IDENT@31..32 "B"
R_BRACK@32..33 "]"
WHITESPACE@33..34 " "
IF_EXPR@34..44
IF_KW@34..36 "if"
WHITESPACE@36..37 " "
CONDITION@37..41
LITERAL@37..41
TRUE_KW@37..41 "true"
WHITESPACE@41..42 " "
BLOCK_EXPR@42..44
L_CURLY@42..43 "{"
R_CURLY@43..44 "}"
SEMICOLON@44..45 ";"
WHITESPACE@45..46 "\n"
R_CURLY@46..47 "}"
WHITESPACE@47..48 "\n"
error 24..24: attributes are not allowed on BIN_EXPR

View file

@ -1,4 +0,0 @@
fn foo() {
#[A] 1 + 2;
#[B] if true {};
}

View file

@ -12,6 +12,7 @@ SOURCE_FILE@0..82
L_CURLY@9..10 "{"
WHITESPACE@10..15 "\n "
EXPR_STMT@15..26
CALL_EXPR@15..25
ATTR@15..19
POUND@15..16 "#"
L_BRACK@16..17 "["
@ -22,7 +23,6 @@ SOURCE_FILE@0..82
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
@ -34,6 +34,7 @@ SOURCE_FILE@0..82
SEMICOLON@25..26 ";"
WHITESPACE@26..31 "\n "
EXPR_STMT@31..42
MACRO_CALL@31..42
ATTR@31..35
POUND@31..32 "#"
L_BRACK@32..33 "["
@ -44,7 +45,6 @@ SOURCE_FILE@0..82
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
@ -55,6 +55,7 @@ SOURCE_FILE@0..82
R_CURLY@41..42 "}"
WHITESPACE@42..47 "\n "
EXPR_STMT@47..59
BLOCK_EXPR@47..59
ATTR@47..51
POUND@47..48 "#"
L_BRACK@48..49 "["
@ -75,11 +76,11 @@ SOURCE_FILE@0..82
IDENT@54..55 "D"
R_BRACK@55..56 "]"
WHITESPACE@56..57 " "
BLOCK_EXPR@57..59
L_CURLY@57..58 "{"
R_CURLY@58..59 "}"
WHITESPACE@59..64 "\n "
EXPR_STMT@64..79
RETURN_EXPR@64..78
ATTR@64..68
POUND@64..65 "#"
L_BRACK@65..66 "["
@ -90,7 +91,6 @@ SOURCE_FILE@0..82
IDENT@66..67 "D"
R_BRACK@67..68 "]"
WHITESPACE@68..69 " "
RETURN_EXPR@69..78
RETURN_KW@69..75 "return"
WHITESPACE@75..76 " "
TUPLE_EXPR@76..78

View file

@ -1,59 +0,0 @@
SOURCE_FILE@0..47
FN@0..46
FN_KW@0..2 "fn"
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_EXPR@9..46
L_CURLY@9..10 "{"
WHITESPACE@10..15 "\n "
EXPR_STMT@15..31
BLOCK_EXPR@15..31
L_CURLY@15..16 "{"
WHITESPACE@16..17 " "
TRY_EXPR@17..29
ATTR@17..21
POUND@17..18 "#"
L_BRACK@18..19 "["
META@19..20
PATH@19..20
PATH_SEGMENT@19..20
NAME_REF@19..20
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"
BANG@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 "\n "
REF_EXPR@36..44
ATTR@36..40
POUND@36..37 "#"
L_BRACK@37..38 "["
META@38..39
PATH@38..39
PATH_SEGMENT@38..39
NAME_REF@38..39
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 "\n"
R_CURLY@45..46 "}"
WHITESPACE@46..47 "\n"

View file

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

View file

@ -0,0 +1,58 @@
SOURCE_FILE@0..44
FN@0..43
FN_KW@0..2 "fn"
WHITESPACE@2..3 " "
NAME@3..4
IDENT@3..4 "f"
PARAM_LIST@4..6
L_PAREN@4..5 "("
R_PAREN@5..6 ")"
WHITESPACE@6..7 " "
BLOCK_EXPR@7..43
L_CURLY@7..8 "{"
WHITESPACE@8..13 "\n "
PAREN_EXPR@13..41
L_PAREN@13..14 "("
BIN_EXPR@14..40
TRY_EXPR@14..23
ATTR@14..18
POUND@14..15 "#"
L_BRACK@15..16 "["
META@16..17
PATH@16..17
PATH_SEGMENT@16..17
NAME_REF@16..17
IDENT@16..17 "a"
R_BRACK@17..18 "]"
WHITESPACE@18..19 " "
PATH_EXPR@19..22
PATH@19..22
PATH_SEGMENT@19..22
NAME_REF@19..22
IDENT@19..22 "lhs"
QUESTION@22..23 "?"
WHITESPACE@23..24 " "
PLUS@24..25 "+"
WHITESPACE@25..26 " "
AWAIT_EXPR@26..40
ATTR@26..30
POUND@26..27 "#"
L_BRACK@27..28 "["
META@28..29
PATH@28..29
PATH_SEGMENT@28..29
NAME_REF@28..29
IDENT@28..29 "b"
R_BRACK@29..30 "]"
WHITESPACE@30..31 " "
PATH_EXPR@31..34
PATH@31..34
PATH_SEGMENT@31..34
NAME_REF@31..34
IDENT@31..34 "rhs"
DOT@34..35 "."
AWAIT_KW@35..40 "await"
R_PAREN@40..41 ")"
WHITESPACE@41..42 "\n"
R_CURLY@42..43 "}"
WHITESPACE@43..44 "\n"

View file

@ -0,0 +1,3 @@
fn f() {
(#[a] lhs? + #[b] rhs.await)
}

View file

@ -0,0 +1,69 @@
SOURCE_FILE@0..52
FN@0..51
FN_KW@0..2 "fn"
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_EXPR@9..51
L_CURLY@9..10 "{"
WHITESPACE@10..15 "\n "
EXPR_STMT@15..36
BLOCK_EXPR@15..36
ATTR@15..19
POUND@15..16 "#"
L_BRACK@16..17 "["
META@17..18
PATH@17..18
PATH_SEGMENT@17..18
NAME_REF@17..18
IDENT@17..18 "A"
R_BRACK@18..19 "]"
WHITESPACE@19..20 " "
L_CURLY@20..21 "{"
WHITESPACE@21..22 " "
TRY_EXPR@22..34
ATTR@22..26
POUND@22..23 "#"
L_BRACK@23..24 "["
META@24..25
PATH@24..25
PATH_SEGMENT@24..25
NAME_REF@24..25
IDENT@24..25 "B"
R_BRACK@25..26 "]"
WHITESPACE@26..27 " "
MACRO_CALL@27..33
PATH@27..30
PATH_SEGMENT@27..30
NAME_REF@27..30
IDENT@27..30 "bar"
BANG@30..31 "!"
TOKEN_TREE@31..33
L_PAREN@31..32 "("
R_PAREN@32..33 ")"
QUESTION@33..34 "?"
WHITESPACE@34..35 " "
R_CURLY@35..36 "}"
WHITESPACE@36..41 "\n "
REF_EXPR@41..49
ATTR@41..45
POUND@41..42 "#"
L_BRACK@42..43 "["
META@43..44
PATH@43..44
PATH_SEGMENT@43..44
NAME_REF@43..44
IDENT@43..44 "C"
R_BRACK@44..45 "]"
WHITESPACE@45..46 " "
AMP@46..47 "&"
TUPLE_EXPR@47..49
L_PAREN@47..48 "("
R_PAREN@48..49 ")"
WHITESPACE@49..50 "\n"
R_CURLY@50..51 "}"
WHITESPACE@51..52 "\n"

View file

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