test case
This commit is contained in:
Asuka Minato 2025-12-20 05:04:58 +09:00
parent ea1d2998f2
commit a769fbdb87
4 changed files with 72 additions and 15 deletions

View File

@ -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

View File

@ -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,
}

View File

@ -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) {

View File

@ -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())
}