Skip to content

Commit 1d1771c

Browse files
authored
Merge pull request #19501 from ChayimFriedman2/macro-expansion
fix: Fix a bug in MBE expansion that arose from incorrect fixing of an older bug in MBE
2 parents 94c3384 + 3953b60 commit 1d1771c

File tree

7 files changed

+107
-11
lines changed

7 files changed

+107
-11
lines changed

crates/hir-def/src/macro_expansion_tests/mbe.rs

+48
Original file line numberDiff line numberDiff line change
@@ -1979,3 +1979,51 @@ fn f() {
19791979
"#]],
19801980
);
19811981
}
1982+
1983+
#[test]
1984+
fn semicolon_does_not_glue() {
1985+
check(
1986+
r#"
1987+
macro_rules! bug {
1988+
($id: expr) => {
1989+
true
1990+
};
1991+
($id: expr; $($attr: ident),*) => {
1992+
true
1993+
};
1994+
($id: expr; $($attr: ident),*; $norm: expr) => {
1995+
true
1996+
};
1997+
($id: expr; $($attr: ident),*;; $print: expr) => {
1998+
true
1999+
};
2000+
($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => {
2001+
true
2002+
};
2003+
}
2004+
2005+
let _ = bug!(a;;;test);
2006+
"#,
2007+
expect![[r#"
2008+
macro_rules! bug {
2009+
($id: expr) => {
2010+
true
2011+
};
2012+
($id: expr; $($attr: ident),*) => {
2013+
true
2014+
};
2015+
($id: expr; $($attr: ident),*; $norm: expr) => {
2016+
true
2017+
};
2018+
($id: expr; $($attr: ident),*;; $print: expr) => {
2019+
true
2020+
};
2021+
($id: expr; $($attr: ident),*; $norm: expr; $print: expr) => {
2022+
true
2023+
};
2024+
}
2025+
2026+
let _ = true;
2027+
"#]],
2028+
);
2029+
}

crates/hir-def/src/macro_expansion_tests/mbe/regression.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -582,8 +582,8 @@ macro_rules! arbitrary {
582582
}
583583
584584
impl <A: Arbitrary> $crate::arbitrary::Arbitrary for Vec<A> {
585-
type Parameters = RangedParams1<A::Parameters>;
586-
type Strategy = VecStrategy<A::Strategy>;
585+
type Parameters = RangedParams1<A::Parameters> ;
586+
type Strategy = VecStrategy<A::Strategy> ;
587587
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { {
588588
let product_unpack![range, a] = args;
589589
vec(any_with::<A>(a), range)

crates/ide/src/expand_macro.rs

+22
Original file line numberDiff line numberDiff line change
@@ -677,4 +677,26 @@ crate::Foo;
677677
crate::Foo;"#]],
678678
);
679679
}
680+
681+
#[test]
682+
fn semi_glueing() {
683+
check(
684+
r#"
685+
macro_rules! __log_value {
686+
($key:ident :$capture:tt =) => {};
687+
}
688+
689+
macro_rules! __log {
690+
($key:tt $(:$capture:tt)? $(= $value:expr)?; $($arg:tt)+) => {
691+
__log_value!($key $(:$capture)* = $($value)*);
692+
};
693+
}
694+
695+
__log!(written:%; "Test"$0);
696+
"#,
697+
expect![[r#"
698+
__log!
699+
"#]],
700+
);
701+
}
680702
}

crates/mbe/src/expander/transcriber.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,13 @@ fn expand_var(
389389
match ctx.bindings.get_fragment(v, id, &mut ctx.nesting, marker) {
390390
Ok(fragment) => {
391391
match fragment {
392-
Fragment::Tokens(tt) => builder.extend_with_tt(tt.strip_invisible()),
393-
Fragment::TokensOwned(tt) => builder.extend_with_tt(tt.view().strip_invisible()),
392+
// rustc spacing is not like ours. Ours is like proc macros', it dictates how puncts will actually be joined.
393+
// rustc uses them mostly for pretty printing. So we have to deviate a bit from what rustc does here.
394+
// Basically, a metavariable can never be joined with whatever after it.
395+
Fragment::Tokens(tt) => builder.extend_with_tt_alone(tt.strip_invisible()),
396+
Fragment::TokensOwned(tt) => {
397+
builder.extend_with_tt_alone(tt.view().strip_invisible())
398+
}
394399
Fragment::Expr(sub) => {
395400
let sub = sub.strip_invisible();
396401
let mut span = id;
@@ -402,7 +407,7 @@ fn expand_var(
402407
if wrap_in_parens {
403408
builder.open(tt::DelimiterKind::Parenthesis, span);
404409
}
405-
builder.extend_with_tt(sub);
410+
builder.extend_with_tt_alone(sub);
406411
if wrap_in_parens {
407412
builder.close(span);
408413
}

crates/mbe/src/parser.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use std::sync::Arc;
66
use arrayvec::ArrayVec;
77
use intern::{Symbol, sym};
88
use span::{Edition, Span, SyntaxContext};
9-
use tt::iter::{TtElement, TtIter};
9+
use tt::{
10+
MAX_GLUED_PUNCT_LEN,
11+
iter::{TtElement, TtIter},
12+
};
1013

1114
use crate::ParseError;
1215

@@ -96,7 +99,7 @@ pub(crate) enum Op {
9699
delimiter: tt::Delimiter<Span>,
97100
},
98101
Literal(tt::Literal<Span>),
99-
Punct(Box<ArrayVec<tt::Punct<Span>, 3>>),
102+
Punct(Box<ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>>),
100103
Ident(tt::Ident<Span>),
101104
}
102105

@@ -151,7 +154,7 @@ pub(crate) enum MetaVarKind {
151154
pub(crate) enum Separator {
152155
Literal(tt::Literal<Span>),
153156
Ident(tt::Ident<Span>),
154-
Puncts(ArrayVec<tt::Punct<Span>, 3>),
157+
Puncts(ArrayVec<tt::Punct<Span>, MAX_GLUED_PUNCT_LEN>),
155158
}
156159

157160
// Note that when we compare a Separator, we just care about its textual value.

crates/tt/src/iter.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::fmt;
66
use arrayvec::ArrayVec;
77
use intern::sym;
88

9-
use crate::{Ident, Leaf, Punct, Spacing, Subtree, TokenTree, TokenTreesView};
9+
use crate::{Ident, Leaf, MAX_GLUED_PUNCT_LEN, Punct, Spacing, Subtree, TokenTree, TokenTreesView};
1010

1111
#[derive(Clone)]
1212
pub struct TtIter<'a, S> {
@@ -111,7 +111,7 @@ impl<'a, S: Copy> TtIter<'a, S> {
111111
///
112112
/// This method currently may return a single quotation, which is part of lifetime ident and
113113
/// conceptually not a punct in the context of mbe. Callers should handle this.
114-
pub fn expect_glued_punct(&mut self) -> Result<ArrayVec<Punct<S>, 3>, ()> {
114+
pub fn expect_glued_punct(&mut self) -> Result<ArrayVec<Punct<S>, MAX_GLUED_PUNCT_LEN>, ()> {
115115
let TtElement::Leaf(&Leaf::Punct(first)) = self.next().ok_or(())? else {
116116
return Err(());
117117
};
@@ -145,7 +145,6 @@ impl<'a, S: Copy> TtIter<'a, S> {
145145
}
146146
('-' | '!' | '*' | '/' | '&' | '%' | '^' | '+' | '<' | '=' | '>' | '|', '=', _)
147147
| ('-' | '=' | '>', '>', _)
148-
| (_, _, Some(';'))
149148
| ('<', '-', _)
150149
| (':', ':', _)
151150
| ('.', '.', _)

crates/tt/src/lib.rs

+19
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use stdx::{impl_from, itertools::Itertools as _};
2222

2323
pub use text_size::{TextRange, TextSize};
2424

25+
pub const MAX_GLUED_PUNCT_LEN: usize = 3;
26+
2527
#[derive(Clone, PartialEq, Debug)]
2628
pub struct Lit {
2729
pub kind: LitKind,
@@ -243,6 +245,23 @@ impl<S: Copy> TopSubtreeBuilder<S> {
243245
self.token_trees.extend(tt.0.iter().cloned());
244246
}
245247

248+
/// Like [`Self::extend_with_tt()`], but makes sure the new tokens will never be
249+
/// joint with whatever comes after them.
250+
pub fn extend_with_tt_alone(&mut self, tt: TokenTreesView<'_, S>) {
251+
if let Some((last, before_last)) = tt.0.split_last() {
252+
self.token_trees.reserve(tt.0.len());
253+
self.token_trees.extend(before_last.iter().cloned());
254+
let last = if let TokenTree::Leaf(Leaf::Punct(last)) = last {
255+
let mut last = *last;
256+
last.spacing = Spacing::Alone;
257+
TokenTree::Leaf(Leaf::Punct(last))
258+
} else {
259+
last.clone()
260+
};
261+
self.token_trees.push(last);
262+
}
263+
}
264+
246265
pub fn expected_delimiters(&self) -> impl Iterator<Item = &Delimiter<S>> {
247266
self.unclosed_subtree_indices.iter().rev().map(|&subtree_idx| {
248267
let TokenTree::Subtree(subtree) = &self.token_trees[subtree_idx] else {

0 commit comments

Comments
 (0)