diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-19 21:22:00 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2018-12-19 21:22:00 +0000 |
commit | ef1e107df147a82b74089ab9797ce35effe24e5e (patch) | |
tree | e54a25733ffc2c3b19385b299779d763615cb81e /crates/ra_syntax/src/grammar/expressions.rs | |
parent | 0e1c01cdb8e6b346edd5d68d9cc72cbce1ce9793 (diff) | |
parent | a3b842fb8b7b5503b1c4fc49355edd4f2fe0d28d (diff) |
Merge #273
273: Add a test to ensure that we can parse each file r=matklad a=DJMcNab
Note that this has a non-spurious failure in ra_analysis/src/mock_analysis.
Probably fixes #195.
If my understanding is correct, fixes #214 and fixes #225.
Co-authored-by: DJMcNab <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src/grammar/expressions.rs')
-rw-r--r-- | crates/ra_syntax/src/grammar/expressions.rs | 72 |
1 files changed, 39 insertions, 33 deletions
diff --git a/crates/ra_syntax/src/grammar/expressions.rs b/crates/ra_syntax/src/grammar/expressions.rs index a9449c7bf..4f8c46ab3 100644 --- a/crates/ra_syntax/src/grammar/expressions.rs +++ b/crates/ra_syntax/src/grammar/expressions.rs | |||
@@ -64,6 +64,20 @@ pub(crate) fn block(p: &mut Parser) { | |||
64 | if p.at(R_CURLY) { | 64 | if p.at(R_CURLY) { |
65 | m.abandon(p); | 65 | m.abandon(p); |
66 | } else { | 66 | } else { |
67 | // test no_semi_after_block | ||
68 | // fn foo() { | ||
69 | // if true {} | ||
70 | // loop {} | ||
71 | // match () {} | ||
72 | // while true {} | ||
73 | // for _ in () {} | ||
74 | // {} | ||
75 | // {} | ||
76 | // macro_rules! test { | ||
77 | // () => {} | ||
78 | // } | ||
79 | // test!{} | ||
80 | // } | ||
67 | if is_blocklike { | 81 | if is_blocklike { |
68 | p.eat(SEMI); | 82 | p.eat(SEMI); |
69 | } else { | 83 | } else { |
@@ -143,7 +157,7 @@ fn current_op(p: &Parser) -> (u8, Op) { | |||
143 | 157 | ||
144 | let bp = match p.current() { | 158 | let bp = match p.current() { |
145 | EQ => 1, | 159 | EQ => 1, |
146 | DOTDOT => 2, | 160 | DOTDOT | DOTDOTEQ => 2, |
147 | EQEQ | NEQ | L_ANGLE | R_ANGLE => 5, | 161 | EQEQ | NEQ | L_ANGLE | R_ANGLE => 5, |
148 | PIPE => 6, | 162 | PIPE => 6, |
149 | CARET => 7, | 163 | CARET => 7, |
@@ -158,13 +172,13 @@ fn current_op(p: &Parser) -> (u8, Op) { | |||
158 | // Parses expression with binding power of at least bp. | 172 | // Parses expression with binding power of at least bp. |
159 | fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike { | 173 | fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike { |
160 | let mut lhs = match lhs(p, r) { | 174 | let mut lhs = match lhs(p, r) { |
161 | Some(lhs) => { | 175 | Some((lhs, blocklike)) => { |
162 | // test stmt_bin_expr_ambiguity | 176 | // test stmt_bin_expr_ambiguity |
163 | // fn foo() { | 177 | // fn foo() { |
164 | // let _ = {1} & 2; | 178 | // let _ = {1} & 2; |
165 | // {1} &2; | 179 | // {1} &2; |
166 | // } | 180 | // } |
167 | if r.prefer_stmt && is_block(lhs.kind()) { | 181 | if r.prefer_stmt && blocklike.is_block() { |
168 | return BlockLike::Block; | 182 | return BlockLike::Block; |
169 | } | 183 | } |
170 | lhs | 184 | lhs |
@@ -173,7 +187,7 @@ fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike { | |||
173 | }; | 187 | }; |
174 | 188 | ||
175 | loop { | 189 | loop { |
176 | let is_range = p.current() == DOTDOT; | 190 | let is_range = p.current() == DOTDOT || p.current() == DOTDOTEQ; |
177 | let (op_bp, op) = current_op(p); | 191 | let (op_bp, op) = current_op(p); |
178 | if op_bp < bp { | 192 | if op_bp < bp { |
179 | break; | 193 | break; |
@@ -191,29 +205,12 @@ fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> BlockLike { | |||
191 | BlockLike::NotBlock | 205 | BlockLike::NotBlock |
192 | } | 206 | } |
193 | 207 | ||
194 | // test no_semi_after_block | ||
195 | // fn foo() { | ||
196 | // if true {} | ||
197 | // loop {} | ||
198 | // match () {} | ||
199 | // while true {} | ||
200 | // for _ in () {} | ||
201 | // {} | ||
202 | // {} | ||
203 | // } | ||
204 | fn is_block(kind: SyntaxKind) -> bool { | ||
205 | match kind { | ||
206 | IF_EXPR | WHILE_EXPR | FOR_EXPR | LOOP_EXPR | MATCH_EXPR | BLOCK_EXPR => true, | ||
207 | _ => false, | ||
208 | } | ||
209 | } | ||
210 | |||
211 | const LHS_FIRST: TokenSet = token_set_union![ | 208 | const LHS_FIRST: TokenSet = token_set_union![ |
212 | token_set![AMP, STAR, EXCL, DOTDOT, MINUS], | 209 | token_set![AMP, STAR, EXCL, DOTDOT, MINUS], |
213 | atom::ATOM_EXPR_FIRST, | 210 | atom::ATOM_EXPR_FIRST, |
214 | ]; | 211 | ]; |
215 | 212 | ||
216 | fn lhs(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> { | 213 | fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> { |
217 | let m; | 214 | let m; |
218 | let kind = match p.current() { | 215 | let kind = match p.current() { |
219 | // test ref_expr | 216 | // test ref_expr |
@@ -246,19 +243,28 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> { | |||
246 | if p.at_ts(EXPR_FIRST) { | 243 | if p.at_ts(EXPR_FIRST) { |
247 | expr_bp(p, r, 2); | 244 | expr_bp(p, r, 2); |
248 | } | 245 | } |
249 | return Some(m.complete(p, RANGE_EXPR)); | 246 | return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock)); |
250 | } | 247 | } |
251 | _ => { | 248 | _ => { |
252 | let lhs = atom::atom_expr(p, r)?; | 249 | let (lhs, blocklike) = atom::atom_expr(p, r)?; |
253 | return Some(postfix_expr(p, r, lhs)); | 250 | return Some(( |
251 | postfix_expr(p, lhs, !(r.prefer_stmt && blocklike.is_block())), | ||
252 | blocklike, | ||
253 | )); | ||
254 | } | 254 | } |
255 | }; | 255 | }; |
256 | expr_bp(p, r, 255); | 256 | expr_bp(p, r, 255); |
257 | Some(m.complete(p, kind)) | 257 | Some((m.complete(p, kind), BlockLike::NotBlock)) |
258 | } | 258 | } |
259 | 259 | ||
260 | fn postfix_expr(p: &mut Parser, r: Restrictions, mut lhs: CompletedMarker) -> CompletedMarker { | 260 | fn postfix_expr( |
261 | let mut allow_calls = !r.prefer_stmt || !is_block(lhs.kind()); | 261 | p: &mut Parser, |
262 | mut lhs: CompletedMarker, | ||
263 | // Calls are disallowed if the type is a block and we prefer statements because the call cannot be disambiguated from a tuple | ||
264 | // E.g. `while true {break}();` is parsed as | ||
265 | // `while true {break}; ();` | ||
266 | mut allow_calls: bool, | ||
267 | ) -> CompletedMarker { | ||
262 | loop { | 268 | loop { |
263 | lhs = match p.current() { | 269 | lhs = match p.current() { |
264 | // test stmt_postfix_expr_ambiguity | 270 | // test stmt_postfix_expr_ambiguity |
@@ -406,20 +412,20 @@ fn arg_list(p: &mut Parser) { | |||
406 | // let _ = ::a::<b>; | 412 | // let _ = ::a::<b>; |
407 | // let _ = format!(); | 413 | // let _ = format!(); |
408 | // } | 414 | // } |
409 | fn path_expr(p: &mut Parser, r: Restrictions) -> CompletedMarker { | 415 | fn path_expr(p: &mut Parser, r: Restrictions) -> (CompletedMarker, BlockLike) { |
410 | assert!(paths::is_path_start(p) || p.at(L_ANGLE)); | 416 | assert!(paths::is_path_start(p) || p.at(L_ANGLE)); |
411 | let m = p.start(); | 417 | let m = p.start(); |
412 | paths::expr_path(p); | 418 | paths::expr_path(p); |
413 | match p.current() { | 419 | match p.current() { |
414 | L_CURLY if !r.forbid_structs => { | 420 | L_CURLY if !r.forbid_structs => { |
415 | named_field_list(p); | 421 | named_field_list(p); |
416 | m.complete(p, STRUCT_LIT) | 422 | (m.complete(p, STRUCT_LIT), BlockLike::Block) |
417 | } | 423 | } |
418 | EXCL => { | 424 | EXCL => { |
419 | items::macro_call_after_excl(p); | 425 | let block_like = items::macro_call_after_excl(p); |
420 | m.complete(p, MACRO_CALL) | 426 | return (m.complete(p, MACRO_CALL), block_like); |
421 | } | 427 | } |
422 | _ => m.complete(p, PATH_EXPR), | 428 | _ => (m.complete(p, PATH_EXPR), BlockLike::NotBlock), |
423 | } | 429 | } |
424 | } | 430 | } |
425 | 431 | ||