diff --git a/crates/ra_syntax/src/grammar/expressions.rs b/crates/ra_syntax/src/grammar/expressions.rs index 1604d9b5aca9..d27eb8b7e126 100644 --- a/crates/ra_syntax/src/grammar/expressions.rs +++ b/crates/ra_syntax/src/grammar/expressions.rs @@ -45,7 +45,6 @@ pub(crate) fn block(p: &mut Parser) { while !p.at(EOF) && !p.at(R_CURLY) { match p.current() { - LET_KW => let_stmt(p), // test nocontentexpr // fn foo(){ // ;;;some_expr();;;;{;;;};;;;Ok(()) @@ -55,41 +54,54 @@ pub(crate) fn block(p: &mut Parser) { // test block_items // fn a() { fn b() {} } let m = p.start(); - match items::maybe_item(p, items::ItemFlavor::Mod) { - items::MaybeItem::Item(kind) => { - m.complete(p, kind); - } - items::MaybeItem::Modifiers => { - m.abandon(p); - p.error("expected an item"); - } - // test pub_expr - // fn foo() { pub 92; } //FIXME - items::MaybeItem::None => { - let is_blocklike = expressions::expr_stmt(p) == BlockLike::Block; - if p.at(R_CURLY) { + let has_attrs = p.at(POUND); + attributes::outer_attributes(p); + if p.at(LET_KW) { + let_stmt(p, m); + } else { + match items::maybe_item(p, items::ItemFlavor::Mod) { + items::MaybeItem::Item(kind) => { + m.complete(p, kind); + } + items::MaybeItem::Modifiers => { 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); + p.error("expected an item"); + } + // test pub_expr + // fn foo() { pub 92; } //FIXME + items::MaybeItem::None => { + if has_attrs { + m.abandon(p); + p.error( + "expected a let statement or an item after attributes in block", + ); } else { - p.expect(SEMI); + 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); + } else { + p.expect(SEMI); + } + m.complete(p, EXPR_STMT); + } } - m.complete(p, EXPR_STMT); } } } @@ -106,9 +118,8 @@ pub(crate) fn block(p: &mut Parser) { // let c = 92; // let d: i32 = 92; // } - fn let_stmt(p: &mut Parser) { + fn let_stmt(p: &mut Parser, m: Marker) { assert!(p.at(LET_KW)); - let m = p.start(); p.bump(); patterns::pattern(p); if p.at(COLON) { diff --git a/crates/ra_syntax/src/grammar/items.rs b/crates/ra_syntax/src/grammar/items.rs index 265e84570d51..18039cd3f8da 100644 --- a/crates/ra_syntax/src/grammar/items.rs +++ b/crates/ra_syntax/src/grammar/items.rs @@ -36,6 +36,7 @@ pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) { let m = p.start(); + attributes::outer_attributes(p); match maybe_item(p, flavor) { MaybeItem::Item(kind) => { m.complete(p, kind); @@ -79,7 +80,6 @@ pub(super) enum MaybeItem { } pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { - attributes::outer_attributes(p); opt_visibility(p); if let Some(kind) = items_without_modifiers(p) { return MaybeItem::Item(kind); diff --git a/crates/ra_syntax/tests/data/parser/ok/0044_let_attrs.rs b/crates/ra_syntax/tests/data/parser/ok/0044_let_attrs.rs new file mode 100644 index 000000000000..325a97aebdcb --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0044_let_attrs.rs @@ -0,0 +1,5 @@ +// https://github.com/rust-analyzer/rust-analyzer/issues/677 +fn main() { + #[cfg(feature = "backtrace")] + let exit_code = panic::catch_unwind(move || main()); +} diff --git a/crates/ra_syntax/tests/data/parser/ok/0044_let_attrs.txt b/crates/ra_syntax/tests/data/parser/ok/0044_let_attrs.txt new file mode 100644 index 000000000000..1f52f699b3fd --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0044_let_attrs.txt @@ -0,0 +1,73 @@ +SOURCE_FILE@[0; 166) + FN_DEF@[0; 165) + COMMENT@[0; 60) + WHITESPACE@[60; 61) + FN_KW@[61; 63) + WHITESPACE@[63; 64) + NAME@[64; 68) + IDENT@[64; 68) "main" + PARAM_LIST@[68; 70) + L_PAREN@[68; 69) + R_PAREN@[69; 70) + WHITESPACE@[70; 71) + BLOCK@[71; 165) + L_CURLY@[71; 72) + WHITESPACE@[72; 77) + LET_STMT@[77; 163) + ATTR@[77; 106) + POUND@[77; 78) + TOKEN_TREE@[78; 106) + L_BRACK@[78; 79) + IDENT@[79; 82) "cfg" + TOKEN_TREE@[82; 105) + L_PAREN@[82; 83) + IDENT@[83; 90) "feature" + WHITESPACE@[90; 91) + EQ@[91; 92) + WHITESPACE@[92; 93) + STRING@[93; 104) + R_PAREN@[104; 105) + R_BRACK@[105; 106) + WHITESPACE@[106; 111) + LET_KW@[111; 114) + WHITESPACE@[114; 115) + BIND_PAT@[115; 124) + NAME@[115; 124) + IDENT@[115; 124) "exit_code" + WHITESPACE@[124; 125) + EQ@[125; 126) + WHITESPACE@[126; 127) + CALL_EXPR@[127; 162) + PATH_EXPR@[127; 146) + PATH@[127; 146) + PATH@[127; 132) + PATH_SEGMENT@[127; 132) + NAME_REF@[127; 132) + IDENT@[127; 132) "panic" + COLONCOLON@[132; 134) + PATH_SEGMENT@[134; 146) + NAME_REF@[134; 146) + IDENT@[134; 146) "catch_unwind" + ARG_LIST@[146; 162) + L_PAREN@[146; 147) + LAMBDA_EXPR@[147; 161) + MOVE_KW@[147; 151) + WHITESPACE@[151; 152) + PARAM_LIST@[152; 154) + PIPE@[152; 153) + PIPE@[153; 154) + WHITESPACE@[154; 155) + CALL_EXPR@[155; 161) + PATH_EXPR@[155; 159) + PATH@[155; 159) + PATH_SEGMENT@[155; 159) + NAME_REF@[155; 159) + IDENT@[155; 159) "main" + ARG_LIST@[159; 161) + L_PAREN@[159; 160) + R_PAREN@[160; 161) + R_PAREN@[161; 162) + SEMI@[162; 163) + WHITESPACE@[163; 164) + R_CURLY@[164; 165) + WHITESPACE@[165; 166)