From a769fbdb875d43a424eebf80ee531fb68cd0dc4f Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Sat, 20 Dec 2025 05:04:58 +0900 Subject: [PATCH] fix test case --- .../macro_expansion_tests/mbe/regression.rs | 36 +++++++++++++++++++ crates/mbe/src/expander.rs | 13 +++++-- crates/mbe/src/expander/matcher.rs | 27 ++++++++------ crates/mbe/src/expander/transcriber.rs | 11 ++++-- 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 e2022c7967d8..ddabb50251a4 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -108,6 +108,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 6510fefcb6ad..274b779c1b2e 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 4da8b309f005..a21468fbb0d5 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 3e4ab8bdc1d8..006ef1af806e 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()) }