Skip to content

Commit 5671bac

Browse files
Merge #4139
4139: Add check for rules that no repetition which could match an empty token r=matklad a=edwin0cheng Fix #4103 for `/ui/issues/issue-57597.rs` This is `ParseError` of the macro rules , because it is how rustc handle it : https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 Co-authored-by: Edwin Cheng <[email protected]>
2 parents 67afeeb + d5eb43f commit 5671bac

File tree

2 files changed

+75
-12
lines changed

2 files changed

+75
-12
lines changed

crates/ra_mbe/src/lib.rs

+39-11
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{
1919
#[derive(Debug, PartialEq, Eq)]
2020
pub enum ParseError {
2121
Expected(String),
22+
RepetitionEmtpyTokenTree,
2223
}
2324

2425
#[derive(Debug, PartialEq, Eq)]
@@ -194,20 +195,46 @@ impl Rule {
194195
}
195196
}
196197

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+
197206
fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
198207
for op in parse_pattern(pattern) {
199-
let op = match op {
200-
Ok(it) => it,
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-
};
208+
let op = op.map_err(to_parse_error)?;
209+
209210
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+
}
211238
validate(subtree)?
212239
}
213240
_ => (),
@@ -216,6 +243,7 @@ fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
216243
Ok(())
217244
}
218245

246+
#[derive(Debug)]
219247
pub struct ExpandResult<T>(pub T, pub Option<ExpandError>);
220248

221249
impl<T> ExpandResult<T> {

crates/ra_mbe/src/tests.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -1657,7 +1657,7 @@ impl MacroFixture {
16571657
}
16581658
}
16591659

1660-
pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
1660+
fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree {
16611661
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
16621662
let macro_definition =
16631663
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
@@ -1671,10 +1671,24 @@ pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
16711671
.0;
16721672
assert_eq!(definition_tt, parsed);
16731673

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);
16741679
let rules = MacroRules::parse(&definition_tt).unwrap();
16751680
MacroFixture { rules }
16761681
}
16771682

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+
16781692
pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree {
16791693
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
16801694
let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0;
@@ -1840,6 +1854,27 @@ fn test_no_space_after_semi_colon() {
18401854
);
18411855
}
18421856

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+
18431878
#[test]
18441879
fn test_expand_bad_literal() {
18451880
parse_macro(

0 commit comments

Comments
 (0)