diff options
author | Ville Penttinen <[email protected]> | 2019-02-17 17:08:34 +0000 |
---|---|---|
committer | Ville Penttinen <[email protected]> | 2019-02-17 17:26:57 +0000 |
commit | 1c97c1ac11459d45f7bfc57dc72428d2b294520c (patch) | |
tree | 70638cabf61f96a7511f974cc66dae40175bfb22 /crates/ra_syntax/src | |
parent | 982f72c022b45629e6adbaef22884359d3495ecf (diff) |
Enable parsing of attributes inside a match block
We allow invalid inner attributes to be parsed, e.g. inner attributes that are
not directly after the opening brace of the match block.
Instead we run validation on `MatchArmList` to allow better reporting of errors.
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 14 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar/attributes.rs | 10 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar/expressions/atom.rs | 33 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_node/syntax_error.rs | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation/match_armlist.rs | 28 |
8 files changed, 96 insertions, 1 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 22105d6c9..350f01f33 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -153,6 +153,20 @@ impl FnDef { | |||
153 | } | 153 | } |
154 | 154 | ||
155 | impl Attr { | 155 | impl Attr { |
156 | pub fn is_inner(&self) -> bool { | ||
157 | let tt = match self.value() { | ||
158 | None => return false, | ||
159 | Some(tt) => tt, | ||
160 | }; | ||
161 | |||
162 | let prev = match tt.syntax().prev_sibling() { | ||
163 | None => return false, | ||
164 | Some(prev) => prev, | ||
165 | }; | ||
166 | |||
167 | prev.kind() == EXCL | ||
168 | } | ||
169 | |||
156 | pub fn as_atom(&self) -> Option<SmolStr> { | 170 | pub fn as_atom(&self) -> Option<SmolStr> { |
157 | let tt = self.value()?; | 171 | let tt = self.value()?; |
158 | let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?; | 172 | let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?; |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index dd91b5063..c7aaccc95 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1946,6 +1946,7 @@ impl ToOwned for MatchArm { | |||
1946 | } | 1946 | } |
1947 | 1947 | ||
1948 | 1948 | ||
1949 | impl ast::AttrsOwner for MatchArm {} | ||
1949 | impl MatchArm { | 1950 | impl MatchArm { |
1950 | pub fn pats(&self) -> impl Iterator<Item = &Pat> { | 1951 | pub fn pats(&self) -> impl Iterator<Item = &Pat> { |
1951 | super::children(self) | 1952 | super::children(self) |
@@ -1986,6 +1987,7 @@ impl ToOwned for MatchArmList { | |||
1986 | } | 1987 | } |
1987 | 1988 | ||
1988 | 1989 | ||
1990 | impl ast::AttrsOwner for MatchArmList {} | ||
1989 | impl MatchArmList { | 1991 | impl MatchArmList { |
1990 | pub fn arms(&self) -> impl Iterator<Item = &MatchArm> { | 1992 | pub fn arms(&self) -> impl Iterator<Item = &MatchArm> { |
1991 | super::children(self) | 1993 | super::children(self) |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 27a123681..193b563aa 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -413,13 +413,15 @@ Grammar( | |||
413 | ), | 413 | ), |
414 | "MatchArmList": ( | 414 | "MatchArmList": ( |
415 | collections: [ ["arms", "MatchArm"] ], | 415 | collections: [ ["arms", "MatchArm"] ], |
416 | traits: [ "AttrsOwner" ] | ||
416 | ), | 417 | ), |
417 | "MatchArm": ( | 418 | "MatchArm": ( |
418 | options: [ | 419 | options: [ |
419 | [ "guard", "MatchGuard" ], | 420 | [ "guard", "MatchGuard" ], |
420 | "Expr", | 421 | "Expr", |
421 | ], | 422 | ], |
422 | collections: [ [ "pats", "Pat" ] ] | 423 | collections: [ [ "pats", "Pat" ] ], |
424 | traits: [ "AttrsOwner" ] | ||
423 | ), | 425 | ), |
424 | "MatchGuard": (options: ["Expr"]), | 426 | "MatchGuard": (options: ["Expr"]), |
425 | "StructLit": (options: ["Path", "NamedFieldList", ["spread", "Expr"]]), | 427 | "StructLit": (options: ["Path", "NamedFieldList", ["spread", "Expr"]]), |
diff --git a/crates/ra_syntax/src/grammar/attributes.rs b/crates/ra_syntax/src/grammar/attributes.rs index cd30e8a45..2624d2e16 100644 --- a/crates/ra_syntax/src/grammar/attributes.rs +++ b/crates/ra_syntax/src/grammar/attributes.rs | |||
@@ -1,5 +1,15 @@ | |||
1 | use super::*; | 1 | use super::*; |
2 | 2 | ||
3 | /// Parses both inner & outer attributes. | ||
4 | /// | ||
5 | /// Allowing to run validation for reporting errors | ||
6 | /// regarding attributes | ||
7 | pub(super) fn all_attributes(p: &mut Parser) { | ||
8 | while p.at(POUND) { | ||
9 | attribute(p, p.nth(1) == EXCL) | ||
10 | } | ||
11 | } | ||
12 | |||
3 | pub(super) fn inner_attributes(p: &mut Parser) { | 13 | pub(super) fn inner_attributes(p: &mut Parser) { |
4 | while p.current() == POUND && p.nth(1) == EXCL { | 14 | while p.current() == POUND && p.nth(1) == EXCL { |
5 | attribute(p, true) | 15 | attribute(p, true) |
diff --git a/crates/ra_syntax/src/grammar/expressions/atom.rs b/crates/ra_syntax/src/grammar/expressions/atom.rs index 27ba87657..67cd7e6b0 100644 --- a/crates/ra_syntax/src/grammar/expressions/atom.rs +++ b/crates/ra_syntax/src/grammar/expressions/atom.rs | |||
@@ -313,11 +313,44 @@ pub(crate) fn match_arm_list(p: &mut Parser) { | |||
313 | assert!(p.at(L_CURLY)); | 313 | assert!(p.at(L_CURLY)); |
314 | let m = p.start(); | 314 | let m = p.start(); |
315 | p.eat(L_CURLY); | 315 | p.eat(L_CURLY); |
316 | |||
317 | // test match_arms_inner_attribute | ||
318 | // fn foo() { | ||
319 | // match () { | ||
320 | // #![doc("Inner attribute")] | ||
321 | // #![doc("Can be")] | ||
322 | // #![doc("Stacked")] | ||
323 | // _ => (), | ||
324 | // } | ||
325 | // } | ||
326 | attributes::inner_attributes(p); | ||
327 | |||
316 | while !p.at(EOF) && !p.at(R_CURLY) { | 328 | while !p.at(EOF) && !p.at(R_CURLY) { |
317 | if p.at(L_CURLY) { | 329 | if p.at(L_CURLY) { |
318 | error_block(p, "expected match arm"); | 330 | error_block(p, "expected match arm"); |
319 | continue; | 331 | continue; |
320 | } | 332 | } |
333 | |||
334 | // This may result in invalid attributes | ||
335 | // if there are inner attributes mixed in together | ||
336 | // with the outer attributes, but we allow parsing | ||
337 | // those so we can run validation and report better errors | ||
338 | |||
339 | // test match_arms_outer_attributes | ||
340 | // fn foo() { | ||
341 | // match () { | ||
342 | // #[cfg(feature = "some")] | ||
343 | // _ => (), | ||
344 | // #[cfg(feature = "other")] | ||
345 | // _ => (), | ||
346 | // #[cfg(feature = "many")] | ||
347 | // #[cfg(feature = "attributes")] | ||
348 | // #[cfg(feature = "before")] | ||
349 | // _ => (), | ||
350 | // } | ||
351 | // } | ||
352 | attributes::all_attributes(p); | ||
353 | |||
321 | // test match_arms_commas | 354 | // test match_arms_commas |
322 | // fn foo() { | 355 | // fn foo() { |
323 | // match () { | 356 | // match () { |
diff --git a/crates/ra_syntax/src/syntax_node/syntax_error.rs b/crates/ra_syntax/src/syntax_node/syntax_error.rs index 412cf82cc..4ff998090 100644 --- a/crates/ra_syntax/src/syntax_node/syntax_error.rs +++ b/crates/ra_syntax/src/syntax_node/syntax_error.rs | |||
@@ -92,6 +92,7 @@ pub enum SyntaxErrorKind { | |||
92 | UnclosedString, | 92 | UnclosedString, |
93 | InvalidSuffix, | 93 | InvalidSuffix, |
94 | InvalidBlockAttr, | 94 | InvalidBlockAttr, |
95 | InvalidMatchInnerAttr, | ||
95 | } | 96 | } |
96 | 97 | ||
97 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 98 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -136,6 +137,9 @@ impl fmt::Display for SyntaxErrorKind { | |||
136 | InvalidBlockAttr => { | 137 | InvalidBlockAttr => { |
137 | write!(f, "A block in this position cannot accept inner attributes") | 138 | write!(f, "A block in this position cannot accept inner attributes") |
138 | } | 139 | } |
140 | InvalidMatchInnerAttr => { | ||
141 | write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression") | ||
142 | } | ||
139 | ParseError(msg) => write!(f, "{}", msg.0), | 143 | ParseError(msg) => write!(f, "{}", msg.0), |
140 | } | 144 | } |
141 | } | 145 | } |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 10672d6bf..27a465a78 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -3,6 +3,7 @@ mod byte_string; | |||
3 | mod char; | 3 | mod char; |
4 | mod string; | 4 | mod string; |
5 | mod block; | 5 | mod block; |
6 | mod match_armlist; | ||
6 | 7 | ||
7 | use crate::{ | 8 | use crate::{ |
8 | SourceFile, syntax_node::SyntaxError, AstNode, | 9 | SourceFile, syntax_node::SyntaxError, AstNode, |
@@ -19,6 +20,7 @@ pub(crate) fn validate(file: &SourceFile) -> Vec<SyntaxError> { | |||
19 | .visit::<ast::Char, _>(self::char::validate_char_node) | 20 | .visit::<ast::Char, _>(self::char::validate_char_node) |
20 | .visit::<ast::String, _>(self::string::validate_string_node) | 21 | .visit::<ast::String, _>(self::string::validate_string_node) |
21 | .visit::<ast::Block, _>(self::block::validate_block_node) | 22 | .visit::<ast::Block, _>(self::block::validate_block_node) |
23 | .visit::<ast::MatchArmList, _>(self::match_armlist::validate_match_armlist) | ||
22 | .accept(node); | 24 | .accept(node); |
23 | } | 25 | } |
24 | errors | 26 | errors |
diff --git a/crates/ra_syntax/src/validation/match_armlist.rs b/crates/ra_syntax/src/validation/match_armlist.rs new file mode 100644 index 000000000..c43ed7092 --- /dev/null +++ b/crates/ra_syntax/src/validation/match_armlist.rs | |||
@@ -0,0 +1,28 @@ | |||
1 | use crate::{ | ||
2 | ast::{self, AttrsOwner, AstNode}, | ||
3 | syntax_node::{ | ||
4 | SyntaxError, | ||
5 | SyntaxErrorKind::*, | ||
6 | Direction, | ||
7 | }, | ||
8 | }; | ||
9 | |||
10 | pub(crate) fn validate_match_armlist(node: &ast::MatchArmList, errors: &mut Vec<SyntaxError>) { | ||
11 | // Report errors for any inner attribute | ||
12 | // which has a preceding matcharm or an outer attribute | ||
13 | for inner_attr in node.attrs().filter(|s| s.is_inner()) { | ||
14 | let any_errors = inner_attr.syntax().siblings(Direction::Prev).any(|s| { | ||
15 | match (ast::MatchArm::cast(s), ast::Attr::cast(s)) { | ||
16 | (Some(_), _) => true, | ||
17 | // Outer attributes which preceed an inner attribute are not allowed | ||
18 | (_, Some(a)) if !a.is_inner() => true, | ||
19 | (_, Some(_)) => false, | ||
20 | (None, None) => false, | ||
21 | } | ||
22 | }); | ||
23 | |||
24 | if any_errors { | ||
25 | errors.push(SyntaxError::new(InvalidMatchInnerAttr, inner_attr.syntax().range())); | ||
26 | } | ||
27 | } | ||
28 | } | ||