Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21304 from asukaminato0721/19168
internal: Improve recursive mbe parsing behavior
Lukas Wirth 4 months ago
parent 7fdd04b · parent a769fbd · commit 82d4f8b
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/regression.rs36
-rw-r--r--crates/mbe/src/expander.rs13
-rw-r--r--crates/mbe/src/expander/matcher.rs27
-rw-r--r--crates/mbe/src/expander/transcriber.rs11
4 files changed, 72 insertions, 15 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index e2022c7967..ddabb50251 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -109,6 +109,42 @@ fn main() {
}
#[test]
+fn ty_fragment_followed_by_expr() {
+ check(
+ r#"
+macro_rules! a {
+ ($t:tt) => {};
+}
+
+macro_rules! b {
+ ($t:ty) => {
+ a!($t);
+ };
+}
+
+fn main() {
+ b!(&'static str);
+}
+"#,
+ expect![[r#"
+macro_rules! a {
+ ($t:tt) => {};
+}
+
+macro_rules! b {
+ ($t:ty) => {
+ a!($t);
+ };
+}
+
+fn main() {
+ a!(&'static str);;
+}
+"#]],
+ );
+}
+
+#[test]
fn test_winapi_struct() {
// from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366
diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs
index 6510fefcb6..274b779c1b 100644
--- a/crates/mbe/src/expander.rs
+++ b/crates/mbe/src/expander.rs
@@ -128,7 +128,10 @@ enum Fragment<'a> {
#[default]
Empty,
/// token fragments are just copy-pasted into the output
- Tokens(tt::TokenTreesView<'a, Span>),
+ Tokens {
+ tree: tt::TokenTreesView<'a, Span>,
+ origin: TokensOrigin,
+ },
/// Expr ast fragments are surrounded with `()` on transcription to preserve precedence.
/// Note that this impl is different from the one currently in `rustc` --
/// `rustc` doesn't translate fragments into token trees at all.
@@ -156,10 +159,16 @@ impl Fragment<'_> {
fn is_empty(&self) -> bool {
match self {
Fragment::Empty => true,
- Fragment::Tokens(it) => it.len() == 0,
+ Fragment::Tokens { tree, .. } => tree.len() == 0,
Fragment::Expr(it) => it.len() == 0,
Fragment::Path(it) => it.len() == 0,
Fragment::TokensOwned(it) => it.0.is_empty(),
}
}
}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum TokensOrigin {
+ Raw,
+ Ast,
+}
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 4da8b309f0..a21468fbb0 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -71,7 +71,7 @@ use tt::{
use crate::{
ExpandError, ExpandErrorKind, MetaTemplate, ValueResult,
- expander::{Binding, Bindings, ExpandResult, Fragment},
+ expander::{Binding, Bindings, ExpandResult, Fragment, TokensOrigin},
expect_fragment,
parser::{ExprKind, MetaVarKind, Op, RepeatKind, Separator},
};
@@ -842,18 +842,23 @@ fn match_meta_var<'t>(
}
.err();
let tt_result = input.from_savepoint(savepoint);
- return ValueResult { value: Fragment::Tokens(tt_result), err };
+ return ValueResult {
+ value: Fragment::Tokens { tree: tt_result, origin: TokensOrigin::Raw },
+ err,
+ };
}
- MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
- MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop,
- MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
- MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
- MetaVarKind::Block => parser::PrefixEntryPoint::Block,
- MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
- MetaVarKind::Item => parser::PrefixEntryPoint::Item,
- MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
+ MetaVarKind::Ty => (parser::PrefixEntryPoint::Ty, TokensOrigin::Ast),
+ MetaVarKind::Pat => (parser::PrefixEntryPoint::PatTop, TokensOrigin::Ast),
+ MetaVarKind::PatParam => (parser::PrefixEntryPoint::Pat, TokensOrigin::Ast),
+ MetaVarKind::Stmt => (parser::PrefixEntryPoint::Stmt, TokensOrigin::Ast),
+ MetaVarKind::Block => (parser::PrefixEntryPoint::Block, TokensOrigin::Ast),
+ MetaVarKind::Meta => (parser::PrefixEntryPoint::MetaItem, TokensOrigin::Ast),
+ MetaVarKind::Item => (parser::PrefixEntryPoint::Item, TokensOrigin::Ast),
+ MetaVarKind::Vis => (parser::PrefixEntryPoint::Vis, TokensOrigin::Ast),
};
- expect_fragment(db, input, fragment, delim_span).map(Fragment::Tokens)
+ let (entry_point, origin) = fragment;
+ expect_fragment(db, input, entry_point, delim_span)
+ .map(|tree| Fragment::Tokens { tree, origin })
}
fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) {
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index 3e4ab8bdc1..006ef1af80 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -5,6 +5,7 @@ use intern::{Symbol, sym};
use span::{Edition, Span};
use tt::{Delimiter, TopSubtreeBuilder, iter::TtElement};
+use super::TokensOrigin;
use crate::{
ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate,
expander::{Binding, Bindings, Fragment},
@@ -313,7 +314,7 @@ fn expand_subtree(
}
};
let values = match &var_value {
- Fragment::Tokens(tokens) => {
+ Fragment::Tokens { tree: tokens, .. } => {
let mut iter = tokens.iter();
(iter.next(), iter.next())
}
@@ -393,7 +394,13 @@ fn expand_var(
// rustc spacing is not like ours. Ours is like proc macros', it dictates how puncts will actually be joined.
// rustc uses them mostly for pretty printing. So we have to deviate a bit from what rustc does here.
// Basically, a metavariable can never be joined with whatever after it.
- Fragment::Tokens(tt) => builder.extend_with_tt_alone(tt.strip_invisible()),
+ Fragment::Tokens { tree, origin } => {
+ let view = match origin {
+ TokensOrigin::Raw => tree.strip_invisible(),
+ TokensOrigin::Ast => tree,
+ };
+ builder.extend_with_tt_alone(view);
+ }
Fragment::TokensOwned(tt) => {
builder.extend_with_tt_alone(tt.view().strip_invisible())
}