mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Checks no repetition for an empty token
This commit is contained in:
parent
44e6c2cb54
commit
d5eb43f246
2 changed files with 75 additions and 12 deletions
|
@ -19,6 +19,7 @@ use crate::{
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
Expected(String),
|
Expected(String),
|
||||||
|
RepetitionEmtpyTokenTree,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -194,20 +195,46 @@ impl Rule {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_parse_error(e: ExpandError) -> ParseError {
|
||||||
|
let msg = match e {
|
||||||
|
ExpandError::InvalidRepeat => "invalid repeat".to_string(),
|
||||||
|
_ => "invalid macro definition".to_string(),
|
||||||
|
};
|
||||||
|
ParseError::Expected(msg)
|
||||||
|
}
|
||||||
|
|
||||||
fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
|
fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
|
||||||
for op in parse_pattern(pattern) {
|
for op in parse_pattern(pattern) {
|
||||||
let op = match op {
|
let op = op.map_err(to_parse_error)?;
|
||||||
Ok(it) => it,
|
|
||||||
Err(e) => {
|
|
||||||
let msg = match e {
|
|
||||||
ExpandError::InvalidRepeat => "invalid repeat".to_string(),
|
|
||||||
_ => "invalid macro definition".to_string(),
|
|
||||||
};
|
|
||||||
return Err(ParseError::Expected(msg));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match op {
|
match op {
|
||||||
Op::TokenTree(tt::TokenTree::Subtree(subtree)) | Op::Repeat { subtree, .. } => {
|
Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?,
|
||||||
|
Op::Repeat { subtree, separator, .. } => {
|
||||||
|
// Checks that no repetition which could match an empty token
|
||||||
|
// https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
|
||||||
|
|
||||||
|
if separator.is_none() {
|
||||||
|
if parse_pattern(subtree).all(|child_op| {
|
||||||
|
match child_op.map_err(to_parse_error) {
|
||||||
|
Ok(Op::Var { kind, .. }) => {
|
||||||
|
// vis is optional
|
||||||
|
if kind.map_or(false, |it| it == "vis") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Op::Repeat { kind, .. }) => {
|
||||||
|
return matches!(
|
||||||
|
kind,
|
||||||
|
parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}) {
|
||||||
|
return Err(ParseError::RepetitionEmtpyTokenTree);
|
||||||
|
}
|
||||||
|
}
|
||||||
validate(subtree)?
|
validate(subtree)?
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -216,6 +243,7 @@ fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ExpandResult<T>(pub T, pub Option<ExpandError>);
|
pub struct ExpandResult<T>(pub T, pub Option<ExpandError>);
|
||||||
|
|
||||||
impl<T> ExpandResult<T> {
|
impl<T> ExpandResult<T> {
|
||||||
|
|
|
@ -1657,7 +1657,7 @@ impl MacroFixture {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
|
fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree {
|
||||||
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
|
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
|
||||||
let macro_definition =
|
let macro_definition =
|
||||||
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||||
|
@ -1671,10 +1671,24 @@ pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
|
||||||
.0;
|
.0;
|
||||||
assert_eq!(definition_tt, parsed);
|
assert_eq!(definition_tt, parsed);
|
||||||
|
|
||||||
|
definition_tt
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
|
||||||
|
let definition_tt = parse_macro_to_tt(ra_fixture);
|
||||||
let rules = MacroRules::parse(&definition_tt).unwrap();
|
let rules = MacroRules::parse(&definition_tt).unwrap();
|
||||||
MacroFixture { rules }
|
MacroFixture { rules }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError {
|
||||||
|
let definition_tt = parse_macro_to_tt(ra_fixture);
|
||||||
|
|
||||||
|
match MacroRules::parse(&definition_tt) {
|
||||||
|
Ok(_) => panic!("Expect error"),
|
||||||
|
Err(err) => err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree {
|
pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree {
|
||||||
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
|
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
|
||||||
let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0;
|
let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0;
|
||||||
|
@ -1840,6 +1854,27 @@ fn test_no_space_after_semi_colon() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/rust/blob/master/src/test/ui/issues/issue-57597.rs
|
||||||
|
#[test]
|
||||||
|
fn test_rustc_issue_57597() {
|
||||||
|
fn test_error(fixture: &str) {
|
||||||
|
assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmtpyTokenTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($i:ident)?)*) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($i:ident)?)?) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)?)?)?) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)*)?)?) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)?)*)?) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)?)?)*) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)*)*)?) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)?)*)*) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)?)*)+) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)+)?)*) => {}; }");
|
||||||
|
test_error("macro_rules! foo { ($($($($i:ident)+)*)?) => {}; }");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_expand_bad_literal() {
|
fn test_expand_bad_literal() {
|
||||||
parse_macro(
|
parse_macro(
|
||||||
|
|
Loading…
Reference in a new issue