aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_parser/src/event.rs4
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs83
-rw-r--r--crates/ra_parser/src/grammar/expressions/atom.rs4
-rw-r--r--crates/ra_parser/src/parser.rs23
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.rs6
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.txt88
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs4
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.txt54
8 files changed, 231 insertions, 35 deletions
diff --git a/crates/ra_parser/src/event.rs b/crates/ra_parser/src/event.rs
index 6361d5d86..c1773e8e0 100644
--- a/crates/ra_parser/src/event.rs
+++ b/crates/ra_parser/src/event.rs
@@ -105,7 +105,9 @@ pub(super) fn process(sink: &mut dyn TreeSink, mut events: Vec<Event>) {
105 // append `A`'s forward_parent `B` 105 // append `A`'s forward_parent `B`
106 fp = match mem::replace(&mut events[idx], Event::tombstone()) { 106 fp = match mem::replace(&mut events[idx], Event::tombstone()) {
107 Event::Start { kind, forward_parent } => { 107 Event::Start { kind, forward_parent } => {
108 forward_parents.push(kind); 108 if kind != TOMBSTONE {
109 forward_parents.push(kind);
110 }
109 forward_parent 111 forward_parent
110 } 112 }
111 _ => unreachable!(), 113 _ => unreachable!(),
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index 83812e938..e35c46a5e 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -8,10 +8,10 @@ const EXPR_FIRST: TokenSet = LHS_FIRST;
8 8
9pub(super) fn expr(p: &mut Parser) -> BlockLike { 9pub(super) fn expr(p: &mut Parser) -> BlockLike {
10 let r = Restrictions { forbid_structs: false, prefer_stmt: false }; 10 let r = Restrictions { forbid_structs: false, prefer_stmt: false };
11 expr_bp(p, r, 1) 11 expr_bp(p, r, 1).1
12} 12}
13 13
14pub(super) fn expr_stmt(p: &mut Parser) -> BlockLike { 14pub(super) fn expr_stmt(p: &mut Parser) -> (Option<CompletedMarker>, BlockLike) {
15 let r = Restrictions { forbid_structs: false, prefer_stmt: true }; 15 let r = Restrictions { forbid_structs: false, prefer_stmt: true };
16 expr_bp(p, r, 1) 16 expr_bp(p, r, 1)
17} 17}
@@ -55,7 +55,13 @@ pub(crate) fn expr_block_contents(p: &mut Parser) {
55 // test block_items 55 // test block_items
56 // fn a() { fn b() {} } 56 // fn a() { fn b() {} }
57 let m = p.start(); 57 let m = p.start();
58 let has_attrs = p.at(POUND); 58 // test attr_on_expr_stmt
59 // fn foo() {
60 // #[A] foo();
61 // #[B] bar!{}
62 // #[C] #[D] {}
63 // #[D] return ();
64 // }
59 attributes::outer_attributes(p); 65 attributes::outer_attributes(p);
60 if p.at(LET_KW) { 66 if p.at(LET_KW) {
61 let_stmt(p, m); 67 let_stmt(p, m);
@@ -67,35 +73,48 @@ pub(crate) fn expr_block_contents(p: &mut Parser) {
67 Err(m) => m, 73 Err(m) => m,
68 }; 74 };
69 75
70 if has_attrs { 76 let (cm, blocklike) = expr_stmt(p);
71 m.abandon(p); 77 let cm = match cm {
72 p.error("expected a let statement or an item after attributes in block"); 78 None => {
73 } else { 79 if p.at(R_CURLY) {
74 let is_blocklike = expressions::expr_stmt(p) == BlockLike::Block; 80 m.abandon(p);
75 if p.at(R_CURLY) {
76 m.abandon(p);
77 } else {
78 // test no_semi_after_block
79 // fn foo() {
80 // if true {}
81 // loop {}
82 // match () {}
83 // while true {}
84 // for _ in () {}
85 // {}
86 // {}
87 // macro_rules! test {
88 // () => {}
89 // }
90 // test!{}
91 // }
92 if is_blocklike {
93 p.eat(SEMI);
94 } else { 81 } else {
95 p.expect(SEMI); 82 p.expect(SEMI);
83 m.complete(p, EXPR_STMT);
96 } 84 }
97 m.complete(p, EXPR_STMT); 85 continue;
86 }
87 Some(cm) => cm,
88 };
89
90 if p.at(R_CURLY) {
91 // test attr_on_last_expr_in_block
92 // fn foo() {
93 // { #[A] bar!()? }
94 // #[B] &()
95 // }
96 m.contract_child(p, cm);
97 } else {
98 // test no_semi_after_block
99 // fn foo() {
100 // if true {}
101 // loop {}
102 // match () {}
103 // while true {}
104 // for _ in () {}
105 // {}
106 // {}
107 // macro_rules! test {
108 // () => {}
109 // }
110 // test!{}
111 // }
112 if blocklike.is_block() {
113 p.eat(SEMI);
114 } else {
115 p.expect(SEMI);
98 } 116 }
117 m.complete(p, EXPR_STMT);
99 } 118 }
100 } 119 }
101 120
@@ -176,7 +195,7 @@ fn current_op(p: &Parser) -> (u8, Op) {
176} 195}
177 196
178// Parses expression with binding power of at least bp. 197// Parses expression with binding power of at least bp.
179fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike { 198fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> (Option<CompletedMarker>, BlockLike) {
180 let mut lhs = match lhs(p, r) { 199 let mut lhs = match lhs(p, r) {
181 Some((lhs, blocklike)) => { 200 Some((lhs, blocklike)) => {
182 // test stmt_bin_expr_ambiguity 201 // test stmt_bin_expr_ambiguity
@@ -185,11 +204,11 @@ fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
185 // {1} &2; 204 // {1} &2;
186 // } 205 // }
187 if r.prefer_stmt && blocklike.is_block() { 206 if r.prefer_stmt && blocklike.is_block() {
188 return BlockLike::Block; 207 return (Some(lhs), BlockLike::Block);
189 } 208 }
190 lhs 209 lhs
191 } 210 }
192 None => return BlockLike::NotBlock, 211 None => return (None, BlockLike::NotBlock),
193 }; 212 };
194 213
195 loop { 214 loop {
@@ -208,7 +227,7 @@ fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike {
208 expr_bp(p, r, op_bp + 1); 227 expr_bp(p, r, op_bp + 1);
209 lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR }); 228 lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
210 } 229 }
211 BlockLike::NotBlock 230 (Some(lhs), BlockLike::NotBlock)
212} 231}
213 232
214const LHS_FIRST: TokenSet = 233const LHS_FIRST: TokenSet =
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs
index d933288cd..a23977bfb 100644
--- a/crates/ra_parser/src/grammar/expressions/atom.rs
+++ b/crates/ra_parser/src/grammar/expressions/atom.rs
@@ -392,9 +392,9 @@ fn match_arm(p: &mut Parser) -> BlockLike {
392 match_guard(p); 392 match_guard(p);
393 } 393 }
394 p.expect(FAT_ARROW); 394 p.expect(FAT_ARROW);
395 let ret = expr_stmt(p); 395 let blocklike = expr_stmt(p).1;
396 m.complete(p, MATCH_ARM); 396 m.complete(p, MATCH_ARM);
397 ret 397 blocklike
398} 398}
399 399
400// test match_guard 400// test match_guard
diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs
index a18458148..702669eb9 100644
--- a/crates/ra_parser/src/parser.rs
+++ b/crates/ra_parser/src/parser.rs
@@ -228,6 +228,29 @@ impl Marker {
228 } 228 }
229 } 229 }
230 } 230 }
231
232 /// Contract a node `cm` and complete as `cm`'s `kind`.
233 /// `cm` must be a child of `m` to work correctly.
234 /// ```
235 /// m--A m--A
236 /// +--cm--B -> +--B
237 /// +--C C
238 ///
239 /// [m: TOMBSTONE, A, cm: Start(k), B, Finish, C]
240 /// [m: Start(k), A, cm: TOMBSTONE, B, Finish, C]
241 /// ```
242 pub(crate) fn contract_child(mut self, p: &mut Parser, cm: CompletedMarker) -> CompletedMarker {
243 self.bomb.defuse();
244 match p.events[self.pos as usize] {
245 Event::Start { kind: ref mut slot, .. } => *slot = cm.kind(),
246 _ => unreachable!(),
247 };
248 match p.events[cm.0 as usize] {
249 Event::Start { kind: ref mut slot, .. } => *slot = TOMBSTONE,
250 _ => unreachable!(),
251 };
252 CompletedMarker::new(self.pos, cm.kind())
253 }
231} 254}
232 255
233pub(crate) struct CompletedMarker(u32, SyntaxKind); 256pub(crate) struct CompletedMarker(u32, SyntaxKind);
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.rs
new file mode 100644
index 000000000..b28c078f9
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.rs
@@ -0,0 +1,6 @@
1fn foo() {
2 #[A] foo();
3 #[B] bar!{}
4 #[C] #[D] {}
5 #[D] return ();
6}
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.txt
new file mode 100644
index 000000000..7cd525cc7
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/inline/ok/0126_attr_on_expr_stmt.txt
@@ -0,0 +1,88 @@
1SOURCE_FILE@[0; 82)
2 FN_DEF@[0; 81)
3 FN_KW@[0; 2)
4 WHITESPACE@[2; 3)
5 NAME@[3; 6)
6 IDENT@[3; 6) "foo"
7 PARAM_LIST@[6; 8)
8 L_PAREN@[6; 7)
9 R_PAREN@[7; 8)
10 WHITESPACE@[8; 9)
11 BLOCK@[9; 81)
12 L_CURLY@[9; 10)
13 WHITESPACE@[10; 15)
14 EXPR_STMT@[15; 26)
15 ATTR@[15; 19)
16 POUND@[15; 16)
17 TOKEN_TREE@[16; 19)
18 L_BRACK@[16; 17)
19 IDENT@[17; 18) "A"
20 R_BRACK@[18; 19)
21 WHITESPACE@[19; 20)
22 CALL_EXPR@[20; 25)
23 PATH_EXPR@[20; 23)
24 PATH@[20; 23)
25 PATH_SEGMENT@[20; 23)
26 NAME_REF@[20; 23)
27 IDENT@[20; 23) "foo"
28 ARG_LIST@[23; 25)
29 L_PAREN@[23; 24)
30 R_PAREN@[24; 25)
31 SEMI@[25; 26)
32 WHITESPACE@[26; 31)
33 EXPR_STMT@[31; 42)
34 ATTR@[31; 35)
35 POUND@[31; 32)
36 TOKEN_TREE@[32; 35)
37 L_BRACK@[32; 33)
38 IDENT@[33; 34) "B"
39 R_BRACK@[34; 35)
40 WHITESPACE@[35; 36)
41 MACRO_CALL@[36; 42)
42 PATH@[36; 39)
43 PATH_SEGMENT@[36; 39)
44 NAME_REF@[36; 39)
45 IDENT@[36; 39) "bar"
46 EXCL@[39; 40)
47 TOKEN_TREE@[40; 42)
48 L_CURLY@[40; 41)
49 R_CURLY@[41; 42)
50 WHITESPACE@[42; 47)
51 EXPR_STMT@[47; 59)
52 ATTR@[47; 51)
53 POUND@[47; 48)
54 TOKEN_TREE@[48; 51)
55 L_BRACK@[48; 49)
56 IDENT@[49; 50) "C"
57 R_BRACK@[50; 51)
58 WHITESPACE@[51; 52)
59 ATTR@[52; 56)
60 POUND@[52; 53)
61 TOKEN_TREE@[53; 56)
62 L_BRACK@[53; 54)
63 IDENT@[54; 55) "D"
64 R_BRACK@[55; 56)
65 WHITESPACE@[56; 57)
66 BLOCK_EXPR@[57; 59)
67 BLOCK@[57; 59)
68 L_CURLY@[57; 58)
69 R_CURLY@[58; 59)
70 WHITESPACE@[59; 64)
71 EXPR_STMT@[64; 79)
72 ATTR@[64; 68)
73 POUND@[64; 65)
74 TOKEN_TREE@[65; 68)
75 L_BRACK@[65; 66)
76 IDENT@[66; 67) "D"
77 R_BRACK@[67; 68)
78 WHITESPACE@[68; 69)
79 RETURN_EXPR@[69; 78)
80 RETURN_KW@[69; 75)
81 WHITESPACE@[75; 76)
82 TUPLE_EXPR@[76; 78)
83 L_PAREN@[76; 77)
84 R_PAREN@[77; 78)
85 SEMI@[78; 79)
86 WHITESPACE@[79; 80)
87 R_CURLY@[80; 81)
88 WHITESPACE@[81; 82)
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs
new file mode 100644
index 000000000..9c5c8eb36
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.rs
@@ -0,0 +1,4 @@
1fn foo() {
2 { #[A] bar!()? }
3 #[B] &()
4}
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.txt
new file mode 100644
index 000000000..4af64559c
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/inline/ok/0127_attr_on_last_expr_in_block.txt
@@ -0,0 +1,54 @@
1SOURCE_FILE@[0; 47)
2 FN_DEF@[0; 46)
3 FN_KW@[0; 2)
4 WHITESPACE@[2; 3)
5 NAME@[3; 6)
6 IDENT@[3; 6) "foo"
7 PARAM_LIST@[6; 8)
8 L_PAREN@[6; 7)
9 R_PAREN@[7; 8)
10 WHITESPACE@[8; 9)
11 BLOCK@[9; 46)
12 L_CURLY@[9; 10)
13 WHITESPACE@[10; 15)
14 EXPR_STMT@[15; 31)
15 BLOCK_EXPR@[15; 31)
16 BLOCK@[15; 31)
17 L_CURLY@[15; 16)
18 WHITESPACE@[16; 17)
19 TRY_EXPR@[17; 29)
20 ATTR@[17; 21)
21 POUND@[17; 18)
22 TOKEN_TREE@[18; 21)
23 L_BRACK@[18; 19)
24 IDENT@[19; 20) "A"
25 R_BRACK@[20; 21)
26 WHITESPACE@[21; 22)
27 MACRO_CALL@[22; 28)
28 PATH@[22; 25)
29 PATH_SEGMENT@[22; 25)
30 NAME_REF@[22; 25)
31 IDENT@[22; 25) "bar"
32 EXCL@[25; 26)
33 TOKEN_TREE@[26; 28)
34 L_PAREN@[26; 27)
35 R_PAREN@[27; 28)
36 QUESTION@[28; 29)
37 WHITESPACE@[29; 30)
38 R_CURLY@[30; 31)
39 WHITESPACE@[31; 36)
40 REF_EXPR@[36; 44)
41 ATTR@[36; 40)
42 POUND@[36; 37)
43 TOKEN_TREE@[37; 40)
44 L_BRACK@[37; 38)
45 IDENT@[38; 39) "B"
46 R_BRACK@[39; 40)
47 WHITESPACE@[40; 41)
48 AMP@[41; 42)
49 TUPLE_EXPR@[42; 44)
50 L_PAREN@[42; 43)
51 R_PAREN@[43; 44)
52 WHITESPACE@[44; 45)
53 R_CURLY@[45; 46)
54 WHITESPACE@[46; 47)