Support Macro v2 in mbe

This commit is contained in:
Edwin Cheng 2021-01-26 05:15:47 +08:00
parent 2c735ed734
commit ce4eeec1bf
4 changed files with 241 additions and 102 deletions

View file

@ -6,7 +6,7 @@ use syntax::{
SyntaxKind::{ERROR, IDENT},
SyntaxNode, WalkEvent, T,
};
use test_utils::assert_eq_text;
use test_utils::{assert_eq_text, mark};
use super::*;
@ -675,6 +675,36 @@ fn test_match_literal() {
.assert_expand_items("foo! ['('];", "fn foo () {}");
}
#[test]
fn test_parse_macro_def_simple() {
mark::check!(parse_macro_def_simple);
parse_macro2(
r#"
macro foo($id:ident) {
fn $id() {}
}
"#,
)
.assert_expand_items("foo!(bar);", "fn bar () {}");
}
#[test]
fn test_parse_macro_def_rules() {
mark::check!(parse_macro_def_rules);
parse_macro2(
r#"
macro foo {
($id:ident) => {
fn $id() {}
}
}
"#,
)
.assert_expand_items("foo!(bar);", "fn bar () {}");
}
// The following tests are port from intellij-rust directly
// https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt
@ -1699,95 +1729,122 @@ pub(crate) struct MacroFixture {
rules: MacroRules,
}
impl MacroFixture {
pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree {
self.try_expand_tt(invocation).unwrap()
}
fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> {
let source_file = ast::SourceFile::parse(invocation).tree();
let macro_invocation =
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap())
.ok_or_else(|| ExpandError::ConversionError)?;
self.rules.expand(&invocation_tt).result()
}
fn assert_expand_err(&self, invocation: &str, err: &ExpandError) {
assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err));
}
fn expand_items(&self, invocation: &str) -> SyntaxNode {
let expanded = self.expand_tt(invocation);
token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node()
}
fn expand_statements(&self, invocation: &str) -> SyntaxNode {
let expanded = self.expand_tt(invocation);
token_tree_to_syntax_node(&expanded, FragmentKind::Statements).unwrap().0.syntax_node()
}
fn expand_expr(&self, invocation: &str) -> SyntaxNode {
let expanded = self.expand_tt(invocation);
token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node()
}
fn assert_expand_tt(&self, invocation: &str, expected: &str) {
let expansion = self.expand_tt(invocation);
assert_eq!(expansion.to_string(), expected);
}
fn assert_expand(&self, invocation: &str, expected: &str) {
let expansion = self.expand_tt(invocation);
let actual = format!("{:?}", expansion);
test_utils::assert_eq_text!(&expected.trim(), &actual.trim());
}
fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture {
self.assert_expansion(FragmentKind::Items, invocation, expected);
self
}
fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &MacroFixture {
self.assert_expansion(FragmentKind::Statements, invocation, expected);
self
}
fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) {
let expanded = self.expand_tt(invocation);
assert_eq!(expanded.to_string(), expected);
let expected = expected.replace("$crate", "C_C__C");
// wrap the given text to a macro call
let expected = {
let wrapped = format!("wrap_macro!( {} )", expected);
let wrapped = ast::SourceFile::parse(&wrapped);
let wrapped =
wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0;
wrapped.delimiter = None;
wrapped
};
let expanded_tree = token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node();
let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string();
let expected_tree = token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node();
let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string();
let expected_tree = expected_tree.replace("C_C__C", "$crate");
assert_eq!(
expanded_tree, expected_tree,
"\nleft:\n{}\nright:\n{}",
expanded_tree, expected_tree,
);
}
pub(crate) struct MacroFixture2 {
rules: MacroDef,
}
fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree {
macro_rules! impl_fixture {
($name:ident) => {
impl $name {
pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree {
self.try_expand_tt(invocation).unwrap()
}
fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> {
let source_file = ast::SourceFile::parse(invocation).tree();
let macro_invocation =
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap())
.ok_or_else(|| ExpandError::ConversionError)?;
self.rules.expand(&invocation_tt).result()
}
#[allow(unused)]
fn assert_expand_err(&self, invocation: &str, err: &ExpandError) {
assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err));
}
#[allow(unused)]
fn expand_items(&self, invocation: &str) -> SyntaxNode {
let expanded = self.expand_tt(invocation);
token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node()
}
#[allow(unused)]
fn expand_statements(&self, invocation: &str) -> SyntaxNode {
let expanded = self.expand_tt(invocation);
token_tree_to_syntax_node(&expanded, FragmentKind::Statements)
.unwrap()
.0
.syntax_node()
}
#[allow(unused)]
fn expand_expr(&self, invocation: &str) -> SyntaxNode {
let expanded = self.expand_tt(invocation);
token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node()
}
#[allow(unused)]
fn assert_expand_tt(&self, invocation: &str, expected: &str) {
let expansion = self.expand_tt(invocation);
assert_eq!(expansion.to_string(), expected);
}
#[allow(unused)]
fn assert_expand(&self, invocation: &str, expected: &str) {
let expansion = self.expand_tt(invocation);
let actual = format!("{:?}", expansion);
test_utils::assert_eq_text!(&expected.trim(), &actual.trim());
}
fn assert_expand_items(&self, invocation: &str, expected: &str) -> &$name {
self.assert_expansion(FragmentKind::Items, invocation, expected);
self
}
#[allow(unused)]
fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &$name {
self.assert_expansion(FragmentKind::Statements, invocation, expected);
self
}
fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) {
let expanded = self.expand_tt(invocation);
assert_eq!(expanded.to_string(), expected);
let expected = expected.replace("$crate", "C_C__C");
// wrap the given text to a macro call
let expected = {
let wrapped = format!("wrap_macro!( {} )", expected);
let wrapped = ast::SourceFile::parse(&wrapped);
let wrapped = wrapped
.tree()
.syntax()
.descendants()
.find_map(ast::TokenTree::cast)
.unwrap();
let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0;
wrapped.delimiter = None;
wrapped
};
let expanded_tree =
token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node();
let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string();
let expected_tree =
token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node();
let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string();
let expected_tree = expected_tree.replace("C_C__C", "$crate");
assert_eq!(
expanded_tree, expected_tree,
"\nleft:\n{}\nright:\n{}",
expanded_tree, expected_tree,
);
}
}
};
}
impl_fixture!(MacroFixture);
impl_fixture!(MacroFixture2);
fn parse_macro_rules_to_tt(ra_fixture: &str) -> tt::Subtree {
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
let macro_definition =
source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap();
@ -1804,14 +1861,36 @@ fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree {
definition_tt
}
fn parse_macro_def_to_tt(ra_fixture: &str) -> tt::Subtree {
let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
let macro_definition =
source_file.syntax().descendants().find_map(ast::MacroDef::cast).unwrap();
let (definition_tt, _) = ast_to_token_tree(&macro_definition.body().unwrap()).unwrap();
let parsed =
parse_to_token_tree(&ra_fixture[macro_definition.body().unwrap().syntax().text_range()])
.unwrap()
.0;
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 definition_tt = parse_macro_rules_to_tt(ra_fixture);
let rules = MacroRules::parse(&definition_tt).unwrap();
MacroFixture { rules }
}
pub(crate) fn parse_macro2(ra_fixture: &str) -> MacroFixture2 {
let definition_tt = parse_macro_def_to_tt(ra_fixture);
let rules = MacroDef::parse(&definition_tt).unwrap();
MacroFixture2 { rules }
}
pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError {
let definition_tt = parse_macro_to_tt(ra_fixture);
let definition_tt = parse_macro_rules_to_tt(ra_fixture);
match MacroRules::parse(&definition_tt) {
Ok(_) => panic!("Expect error"),