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.
This commit is contained in:
Ville Penttinen 2019-02-17 19:08:34 +02:00
parent 982f72c022
commit 1c97c1ac11
16 changed files with 589 additions and 1 deletions

View file

@ -153,6 +153,20 @@ impl FnDef {
}
impl Attr {
pub fn is_inner(&self) -> bool {
let tt = match self.value() {
None => return false,
Some(tt) => tt,
};
let prev = match tt.syntax().prev_sibling() {
None => return false,
Some(prev) => prev,
};
prev.kind() == EXCL
}
pub fn as_atom(&self) -> Option<SmolStr> {
let tt = self.value()?;
let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?;

View file

@ -1946,6 +1946,7 @@ impl ToOwned for MatchArm {
}
impl ast::AttrsOwner for MatchArm {}
impl MatchArm {
pub fn pats(&self) -> impl Iterator<Item = &Pat> {
super::children(self)
@ -1986,6 +1987,7 @@ impl ToOwned for MatchArmList {
}
impl ast::AttrsOwner for MatchArmList {}
impl MatchArmList {
pub fn arms(&self) -> impl Iterator<Item = &MatchArm> {
super::children(self)

View file

@ -413,13 +413,15 @@ Grammar(
),
"MatchArmList": (
collections: [ ["arms", "MatchArm"] ],
traits: [ "AttrsOwner" ]
),
"MatchArm": (
options: [
[ "guard", "MatchGuard" ],
"Expr",
],
collections: [ [ "pats", "Pat" ] ]
collections: [ [ "pats", "Pat" ] ],
traits: [ "AttrsOwner" ]
),
"MatchGuard": (options: ["Expr"]),
"StructLit": (options: ["Path", "NamedFieldList", ["spread", "Expr"]]),

View file

@ -1,5 +1,15 @@
use super::*;
/// Parses both inner & outer attributes.
///
/// Allowing to run validation for reporting errors
/// regarding attributes
pub(super) fn all_attributes(p: &mut Parser) {
while p.at(POUND) {
attribute(p, p.nth(1) == EXCL)
}
}
pub(super) fn inner_attributes(p: &mut Parser) {
while p.current() == POUND && p.nth(1) == EXCL {
attribute(p, true)

View file

@ -313,11 +313,44 @@ pub(crate) fn match_arm_list(p: &mut Parser) {
assert!(p.at(L_CURLY));
let m = p.start();
p.eat(L_CURLY);
// test match_arms_inner_attribute
// fn foo() {
// match () {
// #![doc("Inner attribute")]
// #![doc("Can be")]
// #![doc("Stacked")]
// _ => (),
// }
// }
attributes::inner_attributes(p);
while !p.at(EOF) && !p.at(R_CURLY) {
if p.at(L_CURLY) {
error_block(p, "expected match arm");
continue;
}
// This may result in invalid attributes
// if there are inner attributes mixed in together
// with the outer attributes, but we allow parsing
// those so we can run validation and report better errors
// test match_arms_outer_attributes
// fn foo() {
// match () {
// #[cfg(feature = "some")]
// _ => (),
// #[cfg(feature = "other")]
// _ => (),
// #[cfg(feature = "many")]
// #[cfg(feature = "attributes")]
// #[cfg(feature = "before")]
// _ => (),
// }
// }
attributes::all_attributes(p);
// test match_arms_commas
// fn foo() {
// match () {

View file

@ -92,6 +92,7 @@ pub enum SyntaxErrorKind {
UnclosedString,
InvalidSuffix,
InvalidBlockAttr,
InvalidMatchInnerAttr,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -136,6 +137,9 @@ impl fmt::Display for SyntaxErrorKind {
InvalidBlockAttr => {
write!(f, "A block in this position cannot accept inner attributes")
}
InvalidMatchInnerAttr => {
write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
}
ParseError(msg) => write!(f, "{}", msg.0),
}
}

View file

@ -3,6 +3,7 @@ mod byte_string;
mod char;
mod string;
mod block;
mod match_armlist;
use crate::{
SourceFile, syntax_node::SyntaxError, AstNode,
@ -19,6 +20,7 @@ pub(crate) fn validate(file: &SourceFile) -> Vec<SyntaxError> {
.visit::<ast::Char, _>(self::char::validate_char_node)
.visit::<ast::String, _>(self::string::validate_string_node)
.visit::<ast::Block, _>(self::block::validate_block_node)
.visit::<ast::MatchArmList, _>(self::match_armlist::validate_match_armlist)
.accept(node);
}
errors

View file

@ -0,0 +1,28 @@
use crate::{
ast::{self, AttrsOwner, AstNode},
syntax_node::{
SyntaxError,
SyntaxErrorKind::*,
Direction,
},
};
pub(crate) fn validate_match_armlist(node: &ast::MatchArmList, errors: &mut Vec<SyntaxError>) {
// Report errors for any inner attribute
// which has a preceding matcharm or an outer attribute
for inner_attr in node.attrs().filter(|s| s.is_inner()) {
let any_errors = inner_attr.syntax().siblings(Direction::Prev).any(|s| {
match (ast::MatchArm::cast(s), ast::Attr::cast(s)) {
(Some(_), _) => true,
// Outer attributes which preceed an inner attribute are not allowed
(_, Some(a)) if !a.is_inner() => true,
(_, Some(_)) => false,
(None, None) => false,
}
});
if any_errors {
errors.push(SyntaxError::new(InvalidMatchInnerAttr, inner_attr.syntax().range()));
}
}
}