diff options
author | Geoffry Song <[email protected]> | 2019-11-15 08:08:43 +0000 |
---|---|---|
committer | Geoffry Song <[email protected]> | 2019-11-15 08:18:28 +0000 |
commit | 989cebc99c02acd427f01724c4fa77d81691e886 (patch) | |
tree | 89c2ed0185257de734b60eb3db01be2f5270b7c3 /crates | |
parent | 3ad11973ac7596323f085a9fcb9530e47f021c41 (diff) |
Fix parsing of "postfix" range expressions.
Right now they are handled in `postfix_dot_expr`, but that doesn't allow it to
correctly handle precedence. Integrate it more tightly with the Pratt parser
instead.
Also includes a drive-by fix for parsing `match .. {}`.
Fixes #2242.
Diffstat (limited to 'crates')
7 files changed, 142 insertions, 30 deletions
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index 45f2e3de4..cf69da25a 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs | |||
@@ -290,6 +290,25 @@ fn expr_bp(p: &mut Parser, r: Restrictions, bp: u8) -> (Option<CompletedMarker>, | |||
290 | let m = lhs.precede(p); | 290 | let m = lhs.precede(p); |
291 | p.bump(op); | 291 | p.bump(op); |
292 | 292 | ||
293 | if is_range { | ||
294 | // test postfix_range | ||
295 | // fn foo() { | ||
296 | // let x = 1..; | ||
297 | // match 1.. { _ => () }; | ||
298 | // match a.b()..S { _ => () }; | ||
299 | // } | ||
300 | let has_trailing_expression = | ||
301 | p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])); | ||
302 | if !has_trailing_expression { | ||
303 | if op == T![..=] { | ||
304 | p.error("expected expression to end inclusive range"); | ||
305 | } | ||
306 | // no RHS | ||
307 | lhs = m.complete(p, RANGE_EXPR); | ||
308 | break; | ||
309 | } | ||
310 | } | ||
311 | |||
293 | expr_bp(p, r, op_bp + 1); | 312 | expr_bp(p, r, op_bp + 1); |
294 | lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR }); | 313 | lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR }); |
295 | } | 314 | } |
@@ -330,7 +349,7 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> | |||
330 | if p.at(op) { | 349 | if p.at(op) { |
331 | m = p.start(); | 350 | m = p.start(); |
332 | p.bump(op); | 351 | p.bump(op); |
333 | if p.at_ts(EXPR_FIRST) { | 352 | if p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])) { |
334 | expr_bp(p, r, 2); | 353 | expr_bp(p, r, 2); |
335 | } | 354 | } |
336 | return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock)); | 355 | return Some((m.complete(p, RANGE_EXPR), BlockLike::NotBlock)); |
@@ -344,13 +363,7 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> | |||
344 | // } | 363 | // } |
345 | // | 364 | // |
346 | let (lhs, blocklike) = atom::atom_expr(p, r)?; | 365 | let (lhs, blocklike) = atom::atom_expr(p, r)?; |
347 | return Some(postfix_expr( | 366 | return Some(postfix_expr(p, lhs, blocklike, !(r.prefer_stmt && blocklike.is_block()))); |
348 | p, | ||
349 | lhs, | ||
350 | blocklike, | ||
351 | !(r.prefer_stmt && blocklike.is_block()), | ||
352 | r.forbid_structs, | ||
353 | )); | ||
354 | } | 367 | } |
355 | }; | 368 | }; |
356 | // parse the interior of the unary expression | 369 | // parse the interior of the unary expression |
@@ -366,7 +379,6 @@ fn postfix_expr( | |||
366 | // `while true {break}; ();` | 379 | // `while true {break}; ();` |
367 | mut block_like: BlockLike, | 380 | mut block_like: BlockLike, |
368 | mut allow_calls: bool, | 381 | mut allow_calls: bool, |
369 | forbid_structs: bool, | ||
370 | ) -> (CompletedMarker, BlockLike) { | 382 | ) -> (CompletedMarker, BlockLike) { |
371 | loop { | 383 | loop { |
372 | lhs = match p.current() { | 384 | lhs = match p.current() { |
@@ -380,7 +392,7 @@ fn postfix_expr( | |||
380 | // } | 392 | // } |
381 | T!['('] if allow_calls => call_expr(p, lhs), | 393 | T!['('] if allow_calls => call_expr(p, lhs), |
382 | T!['['] if allow_calls => index_expr(p, lhs), | 394 | T!['['] if allow_calls => index_expr(p, lhs), |
383 | T![.] => match postfix_dot_expr(p, lhs, forbid_structs) { | 395 | T![.] => match postfix_dot_expr(p, lhs) { |
384 | Ok(it) => it, | 396 | Ok(it) => it, |
385 | Err(it) => { | 397 | Err(it) => { |
386 | lhs = it; | 398 | lhs = it; |
@@ -398,7 +410,6 @@ fn postfix_expr( | |||
398 | fn postfix_dot_expr( | 410 | fn postfix_dot_expr( |
399 | p: &mut Parser, | 411 | p: &mut Parser, |
400 | lhs: CompletedMarker, | 412 | lhs: CompletedMarker, |
401 | forbid_structs: bool, | ||
402 | ) -> Result<CompletedMarker, CompletedMarker> { | 413 | ) -> Result<CompletedMarker, CompletedMarker> { |
403 | assert!(p.at(T![.])); | 414 | assert!(p.at(T![.])); |
404 | if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) { | 415 | if p.nth(1) == IDENT && (p.nth(2) == T!['('] || p.nth_at(2, T![::])) { |
@@ -418,25 +429,8 @@ fn postfix_expr( | |||
418 | return Ok(m.complete(p, AWAIT_EXPR)); | 429 | return Ok(m.complete(p, AWAIT_EXPR)); |
419 | } | 430 | } |
420 | 431 | ||
421 | // test postfix_range | 432 | if p.at(T![..=]) || p.at(T![..]) { |
422 | // fn foo() { | 433 | return Err(lhs); |
423 | // let x = 1..; | ||
424 | // match 1.. { _ => () }; | ||
425 | // match a.b()..S { _ => () }; | ||
426 | // } | ||
427 | for &(op, la) in &[(T![..=], 3), (T![..], 2)] { | ||
428 | if p.at(op) { | ||
429 | let next_token = p.nth(la); | ||
430 | let has_trailing_expression = | ||
431 | !(forbid_structs && next_token == T!['{']) && EXPR_FIRST.contains(next_token); | ||
432 | return if has_trailing_expression { | ||
433 | Err(lhs) | ||
434 | } else { | ||
435 | let m = lhs.precede(p); | ||
436 | p.bump(op); | ||
437 | Ok(m.complete(p, RANGE_EXPR)) | ||
438 | }; | ||
439 | } | ||
440 | } | 434 | } |
441 | 435 | ||
442 | Ok(field_expr(p, lhs)) | 436 | Ok(field_expr(p, lhs)) |
diff --git a/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs new file mode 100644 index 000000000..ecd25afaf --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs | |||
@@ -0,0 +1,3 @@ | |||
1 | fn main() { | ||
2 | 0..=; | ||
3 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt new file mode 100644 index 000000000..3efe98164 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt | |||
@@ -0,0 +1,24 @@ | |||
1 | SOURCE_FILE@[0; 24) | ||
2 | FN_DEF@[0; 23) | ||
3 | FN_KW@[0; 2) "fn" | ||
4 | WHITESPACE@[2; 3) " " | ||
5 | NAME@[3; 7) | ||
6 | IDENT@[3; 7) "main" | ||
7 | PARAM_LIST@[7; 9) | ||
8 | L_PAREN@[7; 8) "(" | ||
9 | R_PAREN@[8; 9) ")" | ||
10 | WHITESPACE@[9; 10) " " | ||
11 | BLOCK_EXPR@[10; 23) | ||
12 | BLOCK@[10; 23) | ||
13 | L_CURLY@[10; 11) "{" | ||
14 | WHITESPACE@[11; 16) "\n " | ||
15 | EXPR_STMT@[16; 21) | ||
16 | RANGE_EXPR@[16; 20) | ||
17 | LITERAL@[16; 17) | ||
18 | INT_NUMBER@[16; 17) "0" | ||
19 | DOTDOTEQ@[17; 20) "..=" | ||
20 | SEMI@[20; 21) ";" | ||
21 | WHITESPACE@[21; 22) "\n" | ||
22 | R_CURLY@[22; 23) "}" | ||
23 | WHITESPACE@[23; 24) "\n" | ||
24 | error 20: expected expression to end inclusive range | ||
diff --git a/crates/ra_syntax/test_data/parser/ok/0060_as_range.rs b/crates/ra_syntax/test_data/parser/ok/0060_as_range.rs new file mode 100644 index 000000000..f063ffadb --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0060_as_range.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | fn main() { | ||
2 | 0 as usize ..; | ||
3 | 1 + 2 as usize ..; | ||
4 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/ok/0060_as_range.txt b/crates/ra_syntax/test_data/parser/ok/0060_as_range.txt new file mode 100644 index 000000000..ad0c4a3fe --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0060_as_range.txt | |||
@@ -0,0 +1,56 @@ | |||
1 | SOURCE_FILE@[0; 56) | ||
2 | FN_DEF@[0; 55) | ||
3 | FN_KW@[0; 2) "fn" | ||
4 | WHITESPACE@[2; 3) " " | ||
5 | NAME@[3; 7) | ||
6 | IDENT@[3; 7) "main" | ||
7 | PARAM_LIST@[7; 9) | ||
8 | L_PAREN@[7; 8) "(" | ||
9 | R_PAREN@[8; 9) ")" | ||
10 | WHITESPACE@[9; 10) " " | ||
11 | BLOCK_EXPR@[10; 55) | ||
12 | BLOCK@[10; 55) | ||
13 | L_CURLY@[10; 11) "{" | ||
14 | WHITESPACE@[11; 16) "\n " | ||
15 | EXPR_STMT@[16; 30) | ||
16 | RANGE_EXPR@[16; 29) | ||
17 | CAST_EXPR@[16; 26) | ||
18 | LITERAL@[16; 17) | ||
19 | INT_NUMBER@[16; 17) "0" | ||
20 | WHITESPACE@[17; 18) " " | ||
21 | AS_KW@[18; 20) "as" | ||
22 | WHITESPACE@[20; 21) " " | ||
23 | PATH_TYPE@[21; 26) | ||
24 | PATH@[21; 26) | ||
25 | PATH_SEGMENT@[21; 26) | ||
26 | NAME_REF@[21; 26) | ||
27 | IDENT@[21; 26) "usize" | ||
28 | WHITESPACE@[26; 27) " " | ||
29 | DOTDOT@[27; 29) ".." | ||
30 | SEMI@[29; 30) ";" | ||
31 | WHITESPACE@[30; 35) "\n " | ||
32 | EXPR_STMT@[35; 53) | ||
33 | RANGE_EXPR@[35; 52) | ||
34 | BIN_EXPR@[35; 49) | ||
35 | LITERAL@[35; 36) | ||
36 | INT_NUMBER@[35; 36) "1" | ||
37 | WHITESPACE@[36; 37) " " | ||
38 | PLUS@[37; 38) "+" | ||
39 | WHITESPACE@[38; 39) " " | ||
40 | CAST_EXPR@[39; 49) | ||
41 | LITERAL@[39; 40) | ||
42 | INT_NUMBER@[39; 40) "2" | ||
43 | WHITESPACE@[40; 41) " " | ||
44 | AS_KW@[41; 43) "as" | ||
45 | WHITESPACE@[43; 44) " " | ||
46 | PATH_TYPE@[44; 49) | ||
47 | PATH@[44; 49) | ||
48 | PATH_SEGMENT@[44; 49) | ||
49 | NAME_REF@[44; 49) | ||
50 | IDENT@[44; 49) "usize" | ||
51 | WHITESPACE@[49; 50) " " | ||
52 | DOTDOT@[50; 52) ".." | ||
53 | SEMI@[52; 53) ";" | ||
54 | WHITESPACE@[53; 54) "\n" | ||
55 | R_CURLY@[54; 55) "}" | ||
56 | WHITESPACE@[55; 56) "\n" | ||
diff --git a/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rs b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rs new file mode 100644 index 000000000..2c4ed11e1 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | fn main() { | ||
2 | match .. { | ||
3 | } | ||
4 | } | ||
diff --git a/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.txt b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.txt new file mode 100644 index 000000000..bdfac9b76 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.txt | |||
@@ -0,0 +1,27 @@ | |||
1 | SOURCE_FILE@[0; 35) | ||
2 | FN_DEF@[0; 34) | ||
3 | FN_KW@[0; 2) "fn" | ||
4 | WHITESPACE@[2; 3) " " | ||
5 | NAME@[3; 7) | ||
6 | IDENT@[3; 7) "main" | ||
7 | PARAM_LIST@[7; 9) | ||
8 | L_PAREN@[7; 8) "(" | ||
9 | R_PAREN@[8; 9) ")" | ||
10 | WHITESPACE@[9; 10) " " | ||
11 | BLOCK_EXPR@[10; 34) | ||
12 | BLOCK@[10; 34) | ||
13 | L_CURLY@[10; 11) "{" | ||
14 | WHITESPACE@[11; 16) "\n " | ||
15 | MATCH_EXPR@[16; 32) | ||
16 | MATCH_KW@[16; 21) "match" | ||
17 | WHITESPACE@[21; 22) " " | ||
18 | RANGE_EXPR@[22; 24) | ||
19 | DOTDOT@[22; 24) ".." | ||
20 | WHITESPACE@[24; 25) " " | ||
21 | MATCH_ARM_LIST@[25; 32) | ||
22 | L_CURLY@[25; 26) "{" | ||
23 | WHITESPACE@[26; 31) "\n " | ||
24 | R_CURLY@[31; 32) "}" | ||
25 | WHITESPACE@[32; 33) "\n" | ||
26 | R_CURLY@[33; 34) "}" | ||
27 | WHITESPACE@[34; 35) "\n" | ||