diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-11-15 20:15:29 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-11-15 20:15:29 +0000 |
commit | 86469d4195e7aeb93ae420d0c073593bfccc97f0 (patch) | |
tree | a2fd2d9a7be1b3310b842020c489197ebf2683c3 /crates/ra_syntax | |
parent | 9c3e35df3327e4798867a005d8d22daca99825a1 (diff) | |
parent | 5645c153e0379874d1f44ab149c3ec9257812692 (diff) |
Merge #2252
2252: Fix parsing of "postfix" range expressions. r=matklad a=goffrie
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.
Co-authored-by: Geoffry Song <[email protected]>
Diffstat (limited to 'crates/ra_syntax')
10 files changed, 186 insertions, 1 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 1ec9881b9..277532a8c 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -16,7 +16,7 @@ use crate::{ | |||
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub use self::{ | 18 | pub use self::{ |
19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp}, | 19 | expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, |
20 | extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, | 20 | extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, |
21 | generated::*, | 21 | generated::*, |
22 | tokens::*, | 22 | tokens::*, |
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs index 25dbd0bed..7c53aa934 100644 --- a/crates/ra_syntax/src/ast/expr_extensions.rs +++ b/crates/ra_syntax/src/ast/expr_extensions.rs | |||
@@ -189,6 +189,52 @@ impl ast::BinExpr { | |||
189 | } | 189 | } |
190 | } | 190 | } |
191 | 191 | ||
192 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
193 | pub enum RangeOp { | ||
194 | /// `..` | ||
195 | Exclusive, | ||
196 | /// `..=` | ||
197 | Inclusive, | ||
198 | } | ||
199 | |||
200 | impl ast::RangeExpr { | ||
201 | fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> { | ||
202 | self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| { | ||
203 | let token = child.into_token()?; | ||
204 | let bin_op = match token.kind() { | ||
205 | T![..] => RangeOp::Exclusive, | ||
206 | T![..=] => RangeOp::Inclusive, | ||
207 | _ => return None, | ||
208 | }; | ||
209 | Some((ix, token, bin_op)) | ||
210 | }) | ||
211 | } | ||
212 | |||
213 | pub fn op_kind(&self) -> Option<RangeOp> { | ||
214 | self.op_details().map(|t| t.2) | ||
215 | } | ||
216 | |||
217 | pub fn op_token(&self) -> Option<SyntaxToken> { | ||
218 | self.op_details().map(|t| t.1) | ||
219 | } | ||
220 | |||
221 | pub fn start(&self) -> Option<ast::Expr> { | ||
222 | let op_ix = self.op_details()?.0; | ||
223 | self.syntax() | ||
224 | .children_with_tokens() | ||
225 | .take(op_ix) | ||
226 | .find_map(|it| ast::Expr::cast(it.into_node()?)) | ||
227 | } | ||
228 | |||
229 | pub fn end(&self) -> Option<ast::Expr> { | ||
230 | let op_ix = self.op_details()?.0; | ||
231 | self.syntax() | ||
232 | .children_with_tokens() | ||
233 | .skip(op_ix + 1) | ||
234 | .find_map(|it| ast::Expr::cast(it.into_node()?)) | ||
235 | } | ||
236 | } | ||
237 | |||
192 | impl ast::IndexExpr { | 238 | impl ast::IndexExpr { |
193 | pub fn base(&self) -> Option<ast::Expr> { | 239 | pub fn base(&self) -> Option<ast::Expr> { |
194 | children(self).nth(0) | 240 | children(self).nth(0) |
diff --git a/crates/ra_syntax/src/syntax_error.rs b/crates/ra_syntax/src/syntax_error.rs index 1f60a7aab..6c171df8d 100644 --- a/crates/ra_syntax/src/syntax_error.rs +++ b/crates/ra_syntax/src/syntax_error.rs | |||
@@ -83,6 +83,7 @@ pub enum SyntaxErrorKind { | |||
83 | InvalidMatchInnerAttr, | 83 | InvalidMatchInnerAttr, |
84 | InvalidTupleIndexFormat, | 84 | InvalidTupleIndexFormat, |
85 | VisibilityNotAllowed, | 85 | VisibilityNotAllowed, |
86 | InclusiveRangeMissingEnd, | ||
86 | } | 87 | } |
87 | 88 | ||
88 | impl fmt::Display for SyntaxErrorKind { | 89 | impl fmt::Display for SyntaxErrorKind { |
@@ -103,6 +104,9 @@ impl fmt::Display for SyntaxErrorKind { | |||
103 | VisibilityNotAllowed => { | 104 | VisibilityNotAllowed => { |
104 | write!(f, "unnecessary visibility qualifier") | 105 | write!(f, "unnecessary visibility qualifier") |
105 | } | 106 | } |
107 | InclusiveRangeMissingEnd => { | ||
108 | write!(f, "An inclusive range must have an end expression") | ||
109 | } | ||
106 | } | 110 | } |
107 | } | 111 | } |
108 | } | 112 | } |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 2d596763e..222ac15f8 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -103,6 +103,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
103 | ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, | 103 | ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
104 | ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, | 104 | ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
105 | ast::Visibility(it) => { validate_visibility(it, &mut errors) }, | 105 | ast::Visibility(it) => { validate_visibility(it, &mut errors) }, |
106 | ast::RangeExpr(it) => { validate_range_expr(it, &mut errors) }, | ||
106 | _ => (), | 107 | _ => (), |
107 | } | 108 | } |
108 | } | 109 | } |
@@ -227,3 +228,12 @@ fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) { | |||
227 | .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range())) | 228 | .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range())) |
228 | } | 229 | } |
229 | } | 230 | } |
231 | |||
232 | fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) { | ||
233 | if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() { | ||
234 | errors.push(SyntaxError::new( | ||
235 | SyntaxErrorKind::InclusiveRangeMissingEnd, | ||
236 | expr.syntax().text_range(), | ||
237 | )); | ||
238 | } | ||
239 | } | ||
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..0b4ed7a2b --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | fn main() { | ||
2 | 0..=; | ||
3 | ..=; | ||
4 | } | ||
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..3810b9680 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt | |||
@@ -0,0 +1,30 @@ | |||
1 | SOURCE_FILE@[0; 33) | ||
2 | FN_DEF@[0; 32) | ||
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; 32) | ||
12 | BLOCK@[10; 32) | ||
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; 26) "\n " | ||
22 | EXPR_STMT@[26; 30) | ||
23 | RANGE_EXPR@[26; 29) | ||
24 | DOTDOTEQ@[26; 29) "..=" | ||
25 | SEMI@[29; 30) ";" | ||
26 | WHITESPACE@[30; 31) "\n" | ||
27 | R_CURLY@[31; 32) "}" | ||
28 | WHITESPACE@[32; 33) "\n" | ||
29 | error [16; 20): An inclusive range must have an end expression | ||
30 | error [26; 29): An inclusive range must have an end expression | ||
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" | ||