aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src
diff options
context:
space:
mode:
authorVille Penttinen <[email protected]>2019-02-17 17:08:34 +0000
committerVille Penttinen <[email protected]>2019-02-17 17:26:57 +0000
commit1c97c1ac11459d45f7bfc57dc72428d2b294520c (patch)
tree70638cabf61f96a7511f974cc66dae40175bfb22 /crates/ra_syntax/src
parent982f72c022b45629e6adbaef22884359d3495ecf (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.rs14
-rw-r--r--crates/ra_syntax/src/ast/generated.rs2
-rw-r--r--crates/ra_syntax/src/grammar.ron4
-rw-r--r--crates/ra_syntax/src/grammar/attributes.rs10
-rw-r--r--crates/ra_syntax/src/grammar/expressions/atom.rs33
-rw-r--r--crates/ra_syntax/src/syntax_node/syntax_error.rs4
-rw-r--r--crates/ra_syntax/src/validation.rs2
-rw-r--r--crates/ra_syntax/src/validation/match_armlist.rs28
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
155impl Attr { 155impl 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
1949impl ast::AttrsOwner for MatchArm {}
1949impl MatchArm { 1950impl 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
1990impl ast::AttrsOwner for MatchArmList {}
1989impl MatchArmList { 1991impl 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 @@
1use super::*; 1use super::*;
2 2
3/// Parses both inner & outer attributes.
4///
5/// Allowing to run validation for reporting errors
6/// regarding attributes
7pub(super) fn all_attributes(p: &mut Parser) {
8 while p.at(POUND) {
9 attribute(p, p.nth(1) == EXCL)
10 }
11}
12
3pub(super) fn inner_attributes(p: &mut Parser) { 13pub(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;
3mod char; 3mod char;
4mod string; 4mod string;
5mod block; 5mod block;
6mod match_armlist;
6 7
7use crate::{ 8use 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 @@
1use crate::{
2 ast::{self, AttrsOwner, AstNode},
3 syntax_node::{
4 SyntaxError,
5 SyntaxErrorKind::*,
6 Direction,
7 },
8};
9
10pub(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}