diff options
author | Edwin Cheng <[email protected]> | 2020-04-25 16:09:20 +0100 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2020-04-25 16:30:10 +0100 |
commit | d5eb43f246236b18646d9090ac2c4b6b04aee5b3 (patch) | |
tree | 3865dfd1c8a99d7a11fe40117cb4e23a0cd12fe4 /crates/ra_mbe | |
parent | 44e6c2cb54312e14b3c7bd9d64a9fa0547c17ad7 (diff) |
Checks no repetition for an empty token
Diffstat (limited to 'crates/ra_mbe')
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 50 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 37 |
2 files changed, 75 insertions, 12 deletions
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 535b7daa0..1a020398e 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -19,6 +19,7 @@ use crate::{ | |||
19 | #[derive(Debug, PartialEq, Eq)] | 19 | #[derive(Debug, PartialEq, Eq)] |
20 | pub enum ParseError { | 20 | pub enum ParseError { |
21 | Expected(String), | 21 | Expected(String), |
22 | RepetitionEmtpyTokenTree, | ||
22 | } | 23 | } |
23 | 24 | ||
24 | #[derive(Debug, PartialEq, Eq)] | 25 | #[derive(Debug, PartialEq, Eq)] |
@@ -194,20 +195,46 @@ impl Rule { | |||
194 | } | 195 | } |
195 | } | 196 | } |
196 | 197 | ||
198 | fn to_parse_error(e: ExpandError) -> ParseError { | ||
199 | let msg = match e { | ||
200 | ExpandError::InvalidRepeat => "invalid repeat".to_string(), | ||
201 | _ => "invalid macro definition".to_string(), | ||
202 | }; | ||
203 | ParseError::Expected(msg) | ||
204 | } | ||
205 | |||
197 | fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { | 206 | fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { |
198 | for op in parse_pattern(pattern) { | 207 | for op in parse_pattern(pattern) { |
199 | let op = match op { | 208 | let op = op.map_err(to_parse_error)?; |
200 | Ok(it) => it, | 209 | |
201 | Err(e) => { | ||
202 | let msg = match e { | ||
203 | ExpandError::InvalidRepeat => "invalid repeat".to_string(), | ||
204 | _ => "invalid macro definition".to_string(), | ||
205 | }; | ||
206 | return Err(ParseError::Expected(msg)); | ||
207 | } | ||
208 | }; | ||
209 | match op { | 210 | match op { |
210 | Op::TokenTree(tt::TokenTree::Subtree(subtree)) | Op::Repeat { subtree, .. } => { | 211 | Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?, |
212 | Op::Repeat { subtree, separator, .. } => { | ||
213 | // Checks that no repetition which could match an empty token | ||
214 | // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 | ||
215 | |||
216 | if separator.is_none() { | ||
217 | if parse_pattern(subtree).all(|child_op| { | ||
218 | match child_op.map_err(to_parse_error) { | ||
219 | Ok(Op::Var { kind, .. }) => { | ||
220 | // vis is optional | ||
221 | if kind.map_or(false, |it| it == "vis") { | ||
222 | return true; | ||
223 | } | ||
224 | } | ||
225 | Ok(Op::Repeat { kind, .. }) => { | ||
226 | return matches!( | ||
227 | kind, | ||
228 | parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne | ||
229 | ) | ||
230 | } | ||
231 | _ => {} | ||
232 | } | ||
233 | false | ||
234 | }) { | ||
235 | return Err(ParseError::RepetitionEmtpyTokenTree); | ||
236 | } | ||
237 | } | ||
211 | validate(subtree)? | 238 | validate(subtree)? |
212 | } | 239 | } |
213 | _ => (), | 240 | _ => (), |
@@ -216,6 +243,7 @@ fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { | |||
216 | Ok(()) | 243 | Ok(()) |
217 | } | 244 | } |
218 | 245 | ||
246 | #[derive(Debug)] | ||
219 | pub struct ExpandResult<T>(pub T, pub Option<ExpandError>); | 247 | pub struct ExpandResult<T>(pub T, pub Option<ExpandError>); |
220 | 248 | ||
221 | impl<T> ExpandResult<T> { | 249 | impl<T> ExpandResult<T> { |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 100ed41f2..6b139fb12 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1657,7 +1657,7 @@ impl MacroFixture { | |||
1657 | } | 1657 | } |
1658 | } | 1658 | } |
1659 | 1659 | ||
1660 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { | 1660 | fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { |
1661 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | 1661 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); |
1662 | let macro_definition = | 1662 | let macro_definition = |
1663 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 1663 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); |
@@ -1671,10 +1671,24 @@ pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { | |||
1671 | .0; | 1671 | .0; |
1672 | assert_eq!(definition_tt, parsed); | 1672 | assert_eq!(definition_tt, parsed); |
1673 | 1673 | ||
1674 | definition_tt | ||
1675 | } | ||
1676 | |||
1677 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { | ||
1678 | let definition_tt = parse_macro_to_tt(ra_fixture); | ||
1674 | let rules = MacroRules::parse(&definition_tt).unwrap(); | 1679 | let rules = MacroRules::parse(&definition_tt).unwrap(); |
1675 | MacroFixture { rules } | 1680 | MacroFixture { rules } |
1676 | } | 1681 | } |
1677 | 1682 | ||
1683 | pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { | ||
1684 | let definition_tt = parse_macro_to_tt(ra_fixture); | ||
1685 | |||
1686 | match MacroRules::parse(&definition_tt) { | ||
1687 | Ok(_) => panic!("Expect error"), | ||
1688 | Err(err) => err, | ||
1689 | } | ||
1690 | } | ||
1691 | |||
1678 | pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree { | 1692 | pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree { |
1679 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | 1693 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); |
1680 | let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0; | 1694 | let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0; |
@@ -1840,6 +1854,27 @@ fn test_no_space_after_semi_colon() { | |||
1840 | ); | 1854 | ); |
1841 | } | 1855 | } |
1842 | 1856 | ||
1857 | // https://github.com/rust-lang/rust/blob/master/src/test/ui/issues/issue-57597.rs | ||
1858 | #[test] | ||
1859 | fn test_rustc_issue_57597() { | ||
1860 | fn test_error(fixture: &str) { | ||
1861 | assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmtpyTokenTree); | ||
1862 | } | ||
1863 | |||
1864 | test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); | ||
1865 | test_error("macro_rules! foo { ($($($i:ident)?)*) => {}; }"); | ||
1866 | test_error("macro_rules! foo { ($($($i:ident)?)?) => {}; }"); | ||
1867 | test_error("macro_rules! foo { ($($($($i:ident)?)?)?) => {}; }"); | ||
1868 | test_error("macro_rules! foo { ($($($($i:ident)*)?)?) => {}; }"); | ||
1869 | test_error("macro_rules! foo { ($($($($i:ident)?)*)?) => {}; }"); | ||
1870 | test_error("macro_rules! foo { ($($($($i:ident)?)?)*) => {}; }"); | ||
1871 | test_error("macro_rules! foo { ($($($($i:ident)*)*)?) => {}; }"); | ||
1872 | test_error("macro_rules! foo { ($($($($i:ident)?)*)*) => {}; }"); | ||
1873 | test_error("macro_rules! foo { ($($($($i:ident)?)*)+) => {}; }"); | ||
1874 | test_error("macro_rules! foo { ($($($($i:ident)+)?)*) => {}; }"); | ||
1875 | test_error("macro_rules! foo { ($($($($i:ident)+)*)?) => {}; }"); | ||
1876 | } | ||
1877 | |||
1843 | #[test] | 1878 | #[test] |
1844 | fn test_expand_bad_literal() { | 1879 | fn test_expand_bad_literal() { |
1845 | parse_macro( | 1880 | parse_macro( |