aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_mbe/src/lib.rs50
-rw-r--r--crates/ra_mbe/src/tests.rs37
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)]
20pub enum ParseError { 20pub 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
198fn 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
197fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { 206fn 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)]
219pub struct ExpandResult<T>(pub T, pub Option<ExpandError>); 247pub struct ExpandResult<T>(pub T, pub Option<ExpandError>);
220 248
221impl<T> ExpandResult<T> { 249impl<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
1660pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { 1660fn 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
1677pub(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
1683pub(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
1678pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree { 1692pub(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]
1859fn 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]
1844fn test_expand_bad_literal() { 1879fn test_expand_bad_literal() {
1845 parse_macro( 1880 parse_macro(