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()));
}
}
}

View File

@ -0,0 +1,20 @@
fn foo() {
match () {
_ => (),
#![doc("Not allowed here")]
_ => (),
}
match () {
_ => (),
_ => (),
#![doc("Nor here")]
}
match () {
#[cfg(test)]
#![doc("Nor here")]
_ => (),
_ => (),
}
}

View File

@ -0,0 +1,173 @@
SOURCE_FILE@[0; 293)
FN_DEF@[0; 292)
FN_KW@[0; 2)
WHITESPACE@[2; 3)
NAME@[3; 6)
IDENT@[3; 6) "foo"
PARAM_LIST@[6; 8)
L_PAREN@[6; 7)
R_PAREN@[7; 8)
WHITESPACE@[8; 9)
BLOCK@[9; 292)
L_CURLY@[9; 10)
WHITESPACE@[10; 15)
EXPR_STMT@[15; 101)
MATCH_EXPR@[15; 101)
MATCH_KW@[15; 20)
WHITESPACE@[20; 21)
TUPLE_EXPR@[21; 23)
L_PAREN@[21; 22)
R_PAREN@[22; 23)
WHITESPACE@[23; 24)
MATCH_ARM_LIST@[24; 101)
L_CURLY@[24; 25)
WHITESPACE@[25; 34)
MATCH_ARM@[34; 41)
PLACEHOLDER_PAT@[34; 35)
UNDERSCORE@[34; 35)
WHITESPACE@[35; 36)
FAT_ARROW@[36; 38)
WHITESPACE@[38; 39)
TUPLE_EXPR@[39; 41)
L_PAREN@[39; 40)
R_PAREN@[40; 41)
COMMA@[41; 42)
WHITESPACE@[42; 51)
err: `Inner attributes are only allowed directly after the opening brace of the match expression`
ATTR@[51; 78)
POUND@[51; 52)
EXCL@[52; 53)
TOKEN_TREE@[53; 78)
L_BRACK@[53; 54)
IDENT@[54; 57) "doc"
TOKEN_TREE@[57; 77)
L_PAREN@[57; 58)
STRING@[58; 76)
R_PAREN@[76; 77)
R_BRACK@[77; 78)
WHITESPACE@[78; 87)
MATCH_ARM@[87; 94)
PLACEHOLDER_PAT@[87; 88)
UNDERSCORE@[87; 88)
WHITESPACE@[88; 89)
FAT_ARROW@[89; 91)
WHITESPACE@[91; 92)
TUPLE_EXPR@[92; 94)
L_PAREN@[92; 93)
R_PAREN@[93; 94)
COMMA@[94; 95)
WHITESPACE@[95; 100)
R_CURLY@[100; 101)
WHITESPACE@[101; 107)
EXPR_STMT@[107; 185)
MATCH_EXPR@[107; 185)
MATCH_KW@[107; 112)
WHITESPACE@[112; 113)
TUPLE_EXPR@[113; 115)
L_PAREN@[113; 114)
R_PAREN@[114; 115)
WHITESPACE@[115; 116)
MATCH_ARM_LIST@[116; 185)
L_CURLY@[116; 117)
WHITESPACE@[117; 126)
MATCH_ARM@[126; 133)
PLACEHOLDER_PAT@[126; 127)
UNDERSCORE@[126; 127)
WHITESPACE@[127; 128)
FAT_ARROW@[128; 130)
WHITESPACE@[130; 131)
TUPLE_EXPR@[131; 133)
L_PAREN@[131; 132)
R_PAREN@[132; 133)
COMMA@[133; 134)
WHITESPACE@[134; 143)
MATCH_ARM@[143; 150)
PLACEHOLDER_PAT@[143; 144)
UNDERSCORE@[143; 144)
WHITESPACE@[144; 145)
FAT_ARROW@[145; 147)
WHITESPACE@[147; 148)
TUPLE_EXPR@[148; 150)
L_PAREN@[148; 149)
R_PAREN@[149; 150)
COMMA@[150; 151)
WHITESPACE@[151; 160)
err: `Inner attributes are only allowed directly after the opening brace of the match expression`
ATTR@[160; 179)
POUND@[160; 161)
EXCL@[161; 162)
TOKEN_TREE@[162; 179)
L_BRACK@[162; 163)
IDENT@[163; 166) "doc"
TOKEN_TREE@[166; 178)
L_PAREN@[166; 167)
STRING@[167; 177)
R_PAREN@[177; 178)
R_BRACK@[178; 179)
WHITESPACE@[179; 184)
err: `expected pattern`
err: `expected FAT_ARROW`
err: `expected expression`
MATCH_ARM@[184; 184)
R_CURLY@[184; 185)
WHITESPACE@[185; 191)
MATCH_EXPR@[191; 290)
MATCH_KW@[191; 196)
WHITESPACE@[196; 197)
TUPLE_EXPR@[197; 199)
L_PAREN@[197; 198)
R_PAREN@[198; 199)
WHITESPACE@[199; 200)
MATCH_ARM_LIST@[200; 290)
L_CURLY@[200; 201)
WHITESPACE@[201; 210)
ATTR@[210; 222)
POUND@[210; 211)
TOKEN_TREE@[211; 222)
L_BRACK@[211; 212)
IDENT@[212; 215) "cfg"
TOKEN_TREE@[215; 221)
L_PAREN@[215; 216)
IDENT@[216; 220) "test"
R_PAREN@[220; 221)
R_BRACK@[221; 222)
WHITESPACE@[222; 231)
err: `Inner attributes are only allowed directly after the opening brace of the match expression`
ATTR@[231; 250)
POUND@[231; 232)
EXCL@[232; 233)
TOKEN_TREE@[233; 250)
L_BRACK@[233; 234)
IDENT@[234; 237) "doc"
TOKEN_TREE@[237; 249)
L_PAREN@[237; 238)
STRING@[238; 248)
R_PAREN@[248; 249)
R_BRACK@[249; 250)
WHITESPACE@[250; 259)
MATCH_ARM@[259; 266)
PLACEHOLDER_PAT@[259; 260)
UNDERSCORE@[259; 260)
WHITESPACE@[260; 261)
FAT_ARROW@[261; 263)
WHITESPACE@[263; 264)
TUPLE_EXPR@[264; 266)
L_PAREN@[264; 265)
R_PAREN@[265; 266)
COMMA@[266; 267)
WHITESPACE@[267; 276)
MATCH_ARM@[276; 283)
PLACEHOLDER_PAT@[276; 277)
UNDERSCORE@[276; 277)
WHITESPACE@[277; 278)
FAT_ARROW@[278; 280)
WHITESPACE@[280; 281)
TUPLE_EXPR@[281; 283)
L_PAREN@[281; 282)
R_PAREN@[282; 283)
COMMA@[283; 284)
WHITESPACE@[284; 289)
R_CURLY@[289; 290)
WHITESPACE@[290; 291)
R_CURLY@[291; 292)
WHITESPACE@[292; 293)

View File

@ -0,0 +1,7 @@
fn foo() {
match () {
_ => (),
_ => (),
#[cfg(test)]
}
}

View File

@ -0,0 +1,64 @@
SOURCE_FILE@[0; 89)
FN_DEF@[0; 88)
FN_KW@[0; 2)
WHITESPACE@[2; 3)
NAME@[3; 6)
IDENT@[3; 6) "foo"
PARAM_LIST@[6; 8)
L_PAREN@[6; 7)
R_PAREN@[7; 8)
WHITESPACE@[8; 9)
BLOCK@[9; 88)
L_CURLY@[9; 10)
WHITESPACE@[10; 15)
MATCH_EXPR@[15; 86)
MATCH_KW@[15; 20)
WHITESPACE@[20; 21)
TUPLE_EXPR@[21; 23)
L_PAREN@[21; 22)
R_PAREN@[22; 23)
WHITESPACE@[23; 24)
MATCH_ARM_LIST@[24; 86)
L_CURLY@[24; 25)
WHITESPACE@[25; 34)
MATCH_ARM@[34; 41)
PLACEHOLDER_PAT@[34; 35)
UNDERSCORE@[34; 35)
WHITESPACE@[35; 36)
FAT_ARROW@[36; 38)
WHITESPACE@[38; 39)
TUPLE_EXPR@[39; 41)
L_PAREN@[39; 40)
R_PAREN@[40; 41)
COMMA@[41; 42)
WHITESPACE@[42; 51)
MATCH_ARM@[51; 58)
PLACEHOLDER_PAT@[51; 52)
UNDERSCORE@[51; 52)
WHITESPACE@[52; 53)
FAT_ARROW@[53; 55)
WHITESPACE@[55; 56)
TUPLE_EXPR@[56; 58)
L_PAREN@[56; 57)
R_PAREN@[57; 58)
COMMA@[58; 59)
WHITESPACE@[59; 68)
ATTR@[68; 80)
POUND@[68; 69)
TOKEN_TREE@[69; 80)
L_BRACK@[69; 70)
IDENT@[70; 73) "cfg"
TOKEN_TREE@[73; 79)
L_PAREN@[73; 74)
IDENT@[74; 78) "test"
R_PAREN@[78; 79)
R_BRACK@[79; 80)
WHITESPACE@[80; 85)
err: `expected pattern`
err: `expected FAT_ARROW`
err: `expected expression`
MATCH_ARM@[85; 85)
R_CURLY@[85; 86)
WHITESPACE@[86; 87)
R_CURLY@[87; 88)
WHITESPACE@[88; 89)

View File

@ -0,0 +1,8 @@
fn foo() {
match () {
#![doc("Inner attribute")]
#![doc("Can be")]
#![doc("Stacked")]
_ => (),
}
}

View File

@ -0,0 +1,74 @@
SOURCE_FILE@[0; 139)
FN_DEF@[0; 138)
FN_KW@[0; 2)
WHITESPACE@[2; 3)
NAME@[3; 6)
IDENT@[3; 6) "foo"
PARAM_LIST@[6; 8)
L_PAREN@[6; 7)
R_PAREN@[7; 8)
WHITESPACE@[8; 9)
BLOCK@[9; 138)
L_CURLY@[9; 10)
WHITESPACE@[10; 15)
MATCH_EXPR@[15; 136)
MATCH_KW@[15; 20)
WHITESPACE@[20; 21)
TUPLE_EXPR@[21; 23)
L_PAREN@[21; 22)
R_PAREN@[22; 23)
WHITESPACE@[23; 24)
MATCH_ARM_LIST@[24; 136)
L_CURLY@[24; 25)
WHITESPACE@[25; 34)
ATTR@[34; 60)
POUND@[34; 35)
EXCL@[35; 36)
TOKEN_TREE@[36; 60)
L_BRACK@[36; 37)
IDENT@[37; 40) "doc"
TOKEN_TREE@[40; 59)
L_PAREN@[40; 41)
STRING@[41; 58)
R_PAREN@[58; 59)
R_BRACK@[59; 60)
WHITESPACE@[60; 69)
ATTR@[69; 86)
POUND@[69; 70)
EXCL@[70; 71)
TOKEN_TREE@[71; 86)
L_BRACK@[71; 72)
IDENT@[72; 75) "doc"
TOKEN_TREE@[75; 85)
L_PAREN@[75; 76)
STRING@[76; 84)
R_PAREN@[84; 85)
R_BRACK@[85; 86)
WHITESPACE@[86; 95)
ATTR@[95; 113)
POUND@[95; 96)
EXCL@[96; 97)
TOKEN_TREE@[97; 113)
L_BRACK@[97; 98)
IDENT@[98; 101) "doc"
TOKEN_TREE@[101; 112)
L_PAREN@[101; 102)
STRING@[102; 111)
R_PAREN@[111; 112)
R_BRACK@[112; 113)
WHITESPACE@[113; 122)
MATCH_ARM@[122; 129)
PLACEHOLDER_PAT@[122; 123)
UNDERSCORE@[122; 123)
WHITESPACE@[123; 124)
FAT_ARROW@[124; 126)
WHITESPACE@[126; 127)
TUPLE_EXPR@[127; 129)
L_PAREN@[127; 128)
R_PAREN@[128; 129)
COMMA@[129; 130)
WHITESPACE@[130; 135)
R_CURLY@[135; 136)
WHITESPACE@[136; 137)
R_CURLY@[137; 138)
WHITESPACE@[138; 139)

View File

@ -0,0 +1,12 @@
fn foo() {
match () {
#[cfg(feature = "some")]
_ => (),
#[cfg(feature = "other")]
_ => (),
#[cfg(feature = "many")]
#[cfg(feature = "attributes")]
#[cfg(feature = "before")]
_ => (),
}
}

View File

@ -0,0 +1,135 @@
SOURCE_FILE@[0; 259)
FN_DEF@[0; 258)
FN_KW@[0; 2)
WHITESPACE@[2; 3)
NAME@[3; 6)
IDENT@[3; 6) "foo"
PARAM_LIST@[6; 8)
L_PAREN@[6; 7)
R_PAREN@[7; 8)
WHITESPACE@[8; 9)
BLOCK@[9; 258)
L_CURLY@[9; 10)
WHITESPACE@[10; 15)
MATCH_EXPR@[15; 256)
MATCH_KW@[15; 20)
WHITESPACE@[20; 21)
TUPLE_EXPR@[21; 23)
L_PAREN@[21; 22)
R_PAREN@[22; 23)
WHITESPACE@[23; 24)
MATCH_ARM_LIST@[24; 256)
L_CURLY@[24; 25)
WHITESPACE@[25; 34)
ATTR@[34; 58)
POUND@[34; 35)
TOKEN_TREE@[35; 58)
L_BRACK@[35; 36)
IDENT@[36; 39) "cfg"
TOKEN_TREE@[39; 57)
L_PAREN@[39; 40)
IDENT@[40; 47) "feature"
WHITESPACE@[47; 48)
EQ@[48; 49)
WHITESPACE@[49; 50)
STRING@[50; 56)
R_PAREN@[56; 57)
R_BRACK@[57; 58)
WHITESPACE@[58; 67)
MATCH_ARM@[67; 74)
PLACEHOLDER_PAT@[67; 68)
UNDERSCORE@[67; 68)
WHITESPACE@[68; 69)
FAT_ARROW@[69; 71)
WHITESPACE@[71; 72)
TUPLE_EXPR@[72; 74)
L_PAREN@[72; 73)
R_PAREN@[73; 74)
COMMA@[74; 75)
WHITESPACE@[75; 84)
ATTR@[84; 109)
POUND@[84; 85)
TOKEN_TREE@[85; 109)
L_BRACK@[85; 86)
IDENT@[86; 89) "cfg"
TOKEN_TREE@[89; 108)
L_PAREN@[89; 90)
IDENT@[90; 97) "feature"
WHITESPACE@[97; 98)
EQ@[98; 99)
WHITESPACE@[99; 100)
STRING@[100; 107)
R_PAREN@[107; 108)
R_BRACK@[108; 109)
WHITESPACE@[109; 118)
MATCH_ARM@[118; 125)
PLACEHOLDER_PAT@[118; 119)
UNDERSCORE@[118; 119)
WHITESPACE@[119; 120)
FAT_ARROW@[120; 122)
WHITESPACE@[122; 123)
TUPLE_EXPR@[123; 125)
L_PAREN@[123; 124)
R_PAREN@[124; 125)
COMMA@[125; 126)
WHITESPACE@[126; 135)
ATTR@[135; 159)
POUND@[135; 136)
TOKEN_TREE@[136; 159)
L_BRACK@[136; 137)
IDENT@[137; 140) "cfg"
TOKEN_TREE@[140; 158)
L_PAREN@[140; 141)
IDENT@[141; 148) "feature"
WHITESPACE@[148; 149)
EQ@[149; 150)
WHITESPACE@[150; 151)
STRING@[151; 157)
R_PAREN@[157; 158)
R_BRACK@[158; 159)
WHITESPACE@[159; 168)
ATTR@[168; 198)
POUND@[168; 169)
TOKEN_TREE@[169; 198)
L_BRACK@[169; 170)
IDENT@[170; 173) "cfg"
TOKEN_TREE@[173; 197)
L_PAREN@[173; 174)
IDENT@[174; 181) "feature"
WHITESPACE@[181; 182)
EQ@[182; 183)
WHITESPACE@[183; 184)
STRING@[184; 196)
R_PAREN@[196; 197)
R_BRACK@[197; 198)
WHITESPACE@[198; 207)
ATTR@[207; 233)
POUND@[207; 208)
TOKEN_TREE@[208; 233)
L_BRACK@[208; 209)
IDENT@[209; 212) "cfg"
TOKEN_TREE@[212; 232)
L_PAREN@[212; 213)
IDENT@[213; 220) "feature"
WHITESPACE@[220; 221)
EQ@[221; 222)
WHITESPACE@[222; 223)
STRING@[223; 231)
R_PAREN@[231; 232)
R_BRACK@[232; 233)
WHITESPACE@[233; 242)
MATCH_ARM@[242; 249)
PLACEHOLDER_PAT@[242; 243)
UNDERSCORE@[242; 243)
WHITESPACE@[243; 244)
FAT_ARROW@[244; 246)
WHITESPACE@[246; 247)
TUPLE_EXPR@[247; 249)
L_PAREN@[247; 248)
R_PAREN@[248; 249)
COMMA@[249; 250)
WHITESPACE@[250; 255)
R_CURLY@[255; 256)
WHITESPACE@[256; 257)
R_CURLY@[257; 258)
WHITESPACE@[258; 259)