Unnamed repository; edit this file 'description' to name the repository.
feat: allow attributes on all expressions
Attrs are syntactically valid on any expression, even if they are not allowed semantically everywhere yet.
Aleksey Kladov 2021-09-26
parent 7dc331f · commit 56964c9
-rw-r--r--crates/hir_def/src/body/lower.rs6
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/parser/src/grammar/attributes.rs2
-rw-r--r--crates/parser/src/grammar/expressions.rs145
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs19
-rw-r--r--crates/parser/src/parser.rs3
-rw-r--r--crates/syntax/test_data/parser/err/0009_broken_struct_type_parameter.rast6
-rw-r--r--crates/syntax/test_data/parser/err/0022_bad_exprs.rast36
-rw-r--r--crates/syntax/test_data/parser/err/0024_many_type_parens.rast26
-rw-r--r--crates/syntax/test_data/parser/err/0043_weird_blocks.rast12
-rw-r--r--crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast12
-rw-r--r--crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast60
-rw-r--r--crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rs4
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast108
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast59
-rw-r--r--crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs4
-rw-r--r--crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rast58
-rw-r--r--crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rs3
-rw-r--r--crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rast69
-rw-r--r--crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rs4
20 files changed, 303 insertions, 335 deletions
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 759f2cc865..a34c18d6d0 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -650,8 +650,10 @@ impl ExprCollector<'_> {
self.statements_in_scope.push(Statement::Let { pat, type_ref, initializer });
}
ast::Stmt::ExprStmt(stmt) => {
- if self.check_cfg(&stmt).is_none() {
- return;
+ 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
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index d0b07f5931..3d0ad6735e 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -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) {
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index 80d7b09b3e..574629f31a 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -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");
}
}
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 30d3d4421a..645101e2f7 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -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,61 +51,39 @@ 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
+ 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() {
- // #[A] 1 + 2;
- // #[B] if true {};
+ // if true {}
+ // loop {}
+ // match () {}
+ // while true {}
+ // for _ in () {}
+ // {}
+ // {}
+ // macro_rules! test {
+ // () => {}
+ // }
+ // test!{}
// }
- 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 {
- // test no_semi_after_block
- // fn foo() {
- // if true {}
- // loop {}
- // match () {}
- // while true {}
- // for _ in () {}
- // {}
- // {}
- // macro_rules! test {
- // () => {}
- // }
- // test!{}
- // }
-
- match with_semi {
- StmtWithSemi::No => (),
- StmtWithSemi::Optional => {
- p.eat(T![;]);
- }
- StmtWithSemi::Yes => {
- if blocklike.is_block() {
+ let m = cm.precede(p);
+ match with_semi {
+ StmtWithSemi::No => (),
+ StmtWithSemi::Optional => {
p.eat(T![;]);
- } else {
- p.expect(T![;]);
+ }
+ StmtWithSemi::Yes => {
+ if blocklike.is_block() {
+ p.eat(T![;]);
+ } else {
+ p.expect(T![;]);
+ }
}
}
- }
- m.complete(p, EXPR_STMT);
+ m.complete(p, EXPR_STMT);
+ }
}
// test let_stmt
@@ -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![,]) {
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index f0048aa301..f6e9a5f1b3 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -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) {
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index ff2af130be..1f9961bb92 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -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 {
diff --git a/crates/syntax/test_data/parser/err/0009_broken_struct_type_parameter.rast b/crates/syntax/test_data/parser/err/0009_broken_struct_type_parameter.rast
index dacf71aa16..2d4c689c7c 100644
--- a/crates/syntax/test_data/parser/err/0009_broken_struct_type_parameter.rast
+++ b/crates/syntax/test_data/parser/err/0009_broken_struct_type_parameter.rast
@@ -26,9 +26,8 @@ [email protected]
@@ -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
diff --git a/crates/syntax/test_data/parser/err/0022_bad_exprs.rast b/crates/syntax/test_data/parser/err/0022_bad_exprs.rast
index 71fb19783a..7e6d82bfb4 100644
--- a/crates/syntax/test_data/parser/err/0022_bad_exprs.rast
+++ b/crates/syntax/test_data/parser/err/0022_bad_exprs.rast
@@ -24,9 +24,8 @@ [email protected]
@@ -71,15 +70,13 @@ [email protected]
@@ -122,16 +119,13 @@ [email protected]
@@ -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
diff --git a/crates/syntax/test_data/parser/err/0024_many_type_parens.rast b/crates/syntax/test_data/parser/err/0024_many_type_parens.rast
index be4a62940b..d29f12c1e5 100644
--- a/crates/syntax/test_data/parser/err/0024_many_type_parens.rast
+++ b/crates/syntax/test_data/parser/err/0024_many_type_parens.rast
@@ -143,10 +143,9 @@ [email protected]
@@ -173,13 +172,11 @@ [email protected]
@@ -288,10 +285,9 @@ [email protected]
@@ -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
diff --git a/crates/syntax/test_data/parser/err/0043_weird_blocks.rast b/crates/syntax/test_data/parser/err/0043_weird_blocks.rast
index e24f01e292..797a089648 100644
--- a/crates/syntax/test_data/parser/err/0043_weird_blocks.rast
+++ b/crates/syntax/test_data/parser/err/0043_weird_blocks.rast
@@ -51,12 +51,11 @@ [email protected]
@@ -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
diff --git a/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast b/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast
index 97bb5059d7..3774dd0733 100644
--- a/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast
+++ b/crates/syntax/test_data/parser/inline/err/0002_misplaced_label_err.rast
@@ -11,12 +11,11 @@ [email protected]
@@ -24,6 +23,5 @@ [email protected]
error 22..22: expected a loop
-error 22..22: expected SEMICOLON
error 27..27: expected type
error 27..27: expected `{`
diff --git a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast b/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
deleted file mode 100644
index 7b8b7284f9..0000000000
--- a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
+++ /dev/null
@@ -1,60 +0,0 @@
-error 24..24: attributes are not allowed on BIN_EXPR
diff --git a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rs b/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rs
deleted file mode 100644
index d725a07ce9..0000000000
--- a/crates/syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-fn foo() {
- #[A] 1 + 2;
- #[B] if true {};
-}
diff --git a/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast b/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast
index 178204fece..2a4a52eeb5 100644
--- a/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast
+++ b/crates/syntax/test_data/parser/inline/ok/0126_attr_on_expr_stmt.rast
@@ -12,17 +12,17 @@ [email protected]
@@ -34,17 +34,17 @@ [email protected]
@@ -55,42 +55,42 @@ [email protected]
diff --git a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast b/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast
deleted file mode 100644
index 9daac234ac..0000000000
--- a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rast
+++ /dev/null
@@ -1,59 +0,0 @@
diff --git a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs b/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs
deleted file mode 100644
index 9c5c8eb361..0000000000
--- a/crates/syntax/test_data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-fn foo() {
- { #[A] bar!()? }
- #[B] &()
-}
diff --git a/crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rast b/crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rast
new file mode 100644
index 0000000000..925fa4cbf6
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rast
@@ -0,0 +1,58 @@
diff --git a/crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rs b/crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rs
new file mode 100644
index 0000000000..d8b7a3832a
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0070_expr_attr_placement.rs
@@ -0,0 +1,3 @@
+fn f() {
+ (#[a] lhs? + #[b] rhs.await)
+}
diff --git a/crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rast b/crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rast
new file mode 100644
index 0000000000..3a00212e80
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rast
@@ -0,0 +1,69 @@
diff --git a/crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rs b/crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rs
new file mode 100644
index 0000000000..b4d5204bc0
--- /dev/null
+++ b/crates/syntax/test_data/parser/ok/0071_stmt_attr_placement.rs
@@ -0,0 +1,4 @@
+fn foo() {
+ #[A] { #[B] bar!()? }
+ #[C] &()
+}