Skip to content

Commit cb402f2

Browse files
committed
Add support for repetition to proc_macro::quote
1 parent 7b84c9e commit cb402f2

File tree

3 files changed

+354
-2
lines changed

3 files changed

+354
-2
lines changed

library/proc_macro/src/lib.rs

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mod diagnostic;
4444
mod escape;
4545
mod to_tokens;
4646

47+
use core::ops::BitOr;
4748
use std::ffi::CStr;
4849
use std::ops::{Range, RangeBounds};
4950
use std::path::PathBuf;
@@ -1613,3 +1614,196 @@ pub mod tracked_path {
16131614
crate::bridge::client::FreeFunctions::track_path(path);
16141615
}
16151616
}
1617+
1618+
#[doc(hidden)]
1619+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1620+
#[derive(Debug)]
1621+
pub struct HasIterator;
1622+
#[doc(hidden)]
1623+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1624+
#[derive(Debug)]
1625+
pub struct ThereIsNoIteratorInRepetition;
1626+
1627+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1628+
impl BitOr<ThereIsNoIteratorInRepetition> for ThereIsNoIteratorInRepetition {
1629+
type Output = ThereIsNoIteratorInRepetition;
1630+
fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition {
1631+
ThereIsNoIteratorInRepetition
1632+
}
1633+
}
1634+
1635+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1636+
impl BitOr<ThereIsNoIteratorInRepetition> for HasIterator {
1637+
type Output = HasIterator;
1638+
fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator {
1639+
HasIterator
1640+
}
1641+
}
1642+
1643+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1644+
impl BitOr<HasIterator> for ThereIsNoIteratorInRepetition {
1645+
type Output = HasIterator;
1646+
fn bitor(self, _rhs: HasIterator) -> HasIterator {
1647+
HasIterator
1648+
}
1649+
}
1650+
1651+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1652+
impl BitOr<HasIterator> for HasIterator {
1653+
type Output = HasIterator;
1654+
fn bitor(self, _rhs: HasIterator) -> HasIterator {
1655+
HasIterator
1656+
}
1657+
}
1658+
1659+
/// Extension traits used by the implementation of `quote!`. These are defined
1660+
/// in separate traits, rather than as a single trait due to ambiguity issues.
1661+
///
1662+
/// These traits expose a `quote_into_iter` method which should allow calling
1663+
/// whichever impl happens to be applicable. Calling that method repeatedly on
1664+
/// the returned value should be idempotent.
1665+
#[doc(hidden)]
1666+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1667+
pub mod ext {
1668+
use std::collections::btree_set::{self, BTreeSet};
1669+
use std::slice;
1670+
1671+
use super::{
1672+
HasIterator as HasIter, RepInterp, ThereIsNoIteratorInRepetition as DoesNotHaveIter,
1673+
};
1674+
use crate::ToTokens;
1675+
1676+
/// Extension trait providing the `quote_into_iter` method on iterators.
1677+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1678+
pub trait RepIteratorExt: Iterator + Sized {
1679+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1680+
fn quote_into_iter(self) -> (Self, HasIter) {
1681+
(self, HasIter)
1682+
}
1683+
}
1684+
1685+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1686+
impl<T: Iterator> RepIteratorExt for T {}
1687+
1688+
/// Extension trait providing the `quote_into_iter` method for
1689+
/// non-iterable types. These types interpolate the same value in each
1690+
/// iteration of the repetition.
1691+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1692+
pub trait RepToTokensExt {
1693+
/// Pretend to be an iterator for the purposes of `quote_into_iter`.
1694+
/// This allows repeated calls to `quote_into_iter` to continue
1695+
/// correctly returning DoesNotHaveIter.
1696+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1697+
fn next(&self) -> Option<&Self> {
1698+
Some(self)
1699+
}
1700+
1701+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1702+
fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) {
1703+
(self, DoesNotHaveIter)
1704+
}
1705+
}
1706+
1707+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1708+
impl<T: ToTokens + ?Sized> RepToTokensExt for T {}
1709+
1710+
/// Extension trait providing the `quote_into_iter` method for types that
1711+
/// can be referenced as an iterator.
1712+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1713+
pub trait RepAsIteratorExt<'q> {
1714+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1715+
type Iter: Iterator;
1716+
1717+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1718+
fn quote_into_iter(&'q self) -> (Self::Iter, HasIter);
1719+
}
1720+
1721+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1722+
impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a T {
1723+
type Iter = T::Iter;
1724+
1725+
fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
1726+
<T as RepAsIteratorExt>::quote_into_iter(*self)
1727+
}
1728+
}
1729+
1730+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1731+
impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a mut T {
1732+
type Iter = T::Iter;
1733+
1734+
fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
1735+
<T as RepAsIteratorExt>::quote_into_iter(*self)
1736+
}
1737+
}
1738+
1739+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1740+
impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] {
1741+
type Iter = slice::Iter<'q, T>;
1742+
1743+
fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
1744+
(self.iter(), HasIter)
1745+
}
1746+
}
1747+
1748+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1749+
impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec<T> {
1750+
type Iter = slice::Iter<'q, T>;
1751+
1752+
fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
1753+
(self.iter(), HasIter)
1754+
}
1755+
}
1756+
1757+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1758+
impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet<T> {
1759+
type Iter = btree_set::Iter<'q, T>;
1760+
1761+
fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
1762+
(self.iter(), HasIter)
1763+
}
1764+
}
1765+
1766+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1767+
impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp<T> {
1768+
type Iter = T::Iter;
1769+
1770+
fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) {
1771+
self.0.quote_into_iter()
1772+
}
1773+
}
1774+
}
1775+
1776+
// Helper type used within interpolations to allow for repeated binding names.
1777+
// Implements the relevant traits, and exports a dummy `next()` method.
1778+
#[derive(Copy, Clone)]
1779+
#[doc(hidden)]
1780+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1781+
pub struct RepInterp<T>(pub T);
1782+
1783+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1784+
impl<T> RepInterp<T> {
1785+
// This method is intended to look like `Iterator::next`, and is called when
1786+
// a name is bound multiple times, as the previous binding will shadow the
1787+
// original `Iterator` object. This allows us to avoid advancing the
1788+
// iterator multiple times per iteration.
1789+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1790+
pub fn next(self) -> Option<T> {
1791+
Some(self.0)
1792+
}
1793+
}
1794+
1795+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1796+
impl<T: Iterator> Iterator for RepInterp<T> {
1797+
type Item = T::Item;
1798+
1799+
fn next(&mut self) -> Option<Self::Item> {
1800+
self.0.next()
1801+
}
1802+
}
1803+
1804+
#[unstable(feature = "proc_macro_quote", issue = "54722")]
1805+
impl<T: ToTokens> ToTokens for RepInterp<T> {
1806+
fn to_tokens(&self, tokens: &mut TokenStream) {
1807+
self.0.to_tokens(tokens);
1808+
}
1809+
}

library/proc_macro/src/quote.rs

Lines changed: 136 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ macro_rules! minimal_quote_tt {
2020
(>) => { Punct::new('>', Spacing::Alone) };
2121
(&) => { Punct::new('&', Spacing::Alone) };
2222
(=) => { Punct::new('=', Spacing::Alone) };
23+
(#) => { Punct::new('#', Spacing::Alone) };
24+
(|) => { Punct::new('|', Spacing::Alone) };
25+
(:) => { Punct::new(':', Spacing::Alone) };
26+
(*) => { Punct::new('*', Spacing::Alone) };
27+
(_) => { Ident::new("_", Span::def_site()) };
2328
($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
29+
($lit:literal) => { stringify!($lit).parse::<Literal>().unwrap() };
2430
}
2531

2632
macro_rules! minimal_quote_ts {
@@ -36,6 +42,39 @@ macro_rules! minimal_quote_ts {
3642
[c.0, c.1].into_iter().collect::<TokenStream>()
3743
}
3844
};
45+
(=>) => {
46+
{
47+
let mut c = (
48+
TokenTree::from(Punct::new('=', Spacing::Joint)),
49+
TokenTree::from(Punct::new('>', Spacing::Alone))
50+
);
51+
c.0.set_span(Span::def_site());
52+
c.1.set_span(Span::def_site());
53+
[c.0, c.1].into_iter().collect::<TokenStream>()
54+
}
55+
};
56+
(+=) => {
57+
{
58+
let mut c = (
59+
TokenTree::from(Punct::new('+', Spacing::Joint)),
60+
TokenTree::from(Punct::new('=', Spacing::Alone))
61+
);
62+
c.0.set_span(Span::def_site());
63+
c.1.set_span(Span::def_site());
64+
[c.0, c.1].into_iter().collect::<TokenStream>()
65+
}
66+
};
67+
(!=) => {
68+
{
69+
let mut c = (
70+
TokenTree::from(Punct::new('!', Spacing::Joint)),
71+
TokenTree::from(Punct::new('=', Spacing::Alone))
72+
);
73+
c.0.set_span(Span::def_site());
74+
c.1.set_span(Span::def_site());
75+
[c.0, c.1].into_iter().collect::<TokenStream>()
76+
}
77+
};
3978
($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) };
4079
}
4180

@@ -71,10 +110,82 @@ pub fn quote(stream: TokenStream) -> TokenStream {
71110
let mut after_dollar = false;
72111

73112
let mut tokens = crate::TokenStream::new();
74-
for tree in stream {
113+
let mut iter = stream.into_iter().peekable();
114+
while let Some(tree) = iter.next() {
75115
if after_dollar {
76116
after_dollar = false;
77117
match tree {
118+
TokenTree::Group(inner) => {
119+
let content = inner.stream();
120+
121+
let sep_opt = match iter.peek() {
122+
Some(TokenTree::Punct(_)) => Some(iter.next().unwrap()),
123+
_ => None,
124+
};
125+
126+
if let Some(TokenTree::Punct(p3)) = iter.peek() {
127+
if p3.as_char() == '*' {
128+
let _ = iter.next();
129+
130+
let meta_vars = collect_meta_vars(content.clone());
131+
minimal_quote!(
132+
use crate::ext::*;
133+
let mut _i = 0usize;
134+
let mut has_iter: crate::ThereIsNoIteratorInRepetition = crate::ThereIsNoIteratorInRepetition;
135+
).to_tokens(&mut tokens);
136+
for meta_var in &meta_vars {
137+
minimal_quote!(
138+
#[allow(unused_mut)]
139+
let (mut (@ meta_var), i) = (@ meta_var).quote_into_iter();
140+
has_iter = has_iter | i;
141+
)
142+
.to_tokens(&mut tokens);
143+
}
144+
minimal_quote!(
145+
let _: crate::HasIterator = has_iter;
146+
)
147+
.to_tokens(&mut tokens);
148+
149+
let while_ident =
150+
TokenTree::Ident(Ident::new("while", Span::call_site()));
151+
let true_literal =
152+
TokenTree::Ident(Ident::new("true", Span::call_site()));
153+
let mut inner_tokens = TokenStream::new();
154+
for meta_var in &meta_vars {
155+
minimal_quote!(
156+
let (@ meta_var) = match (@ meta_var).next() {
157+
Some(_x) => crate::RepInterp(_x),
158+
None => break,
159+
};
160+
)
161+
.to_tokens(&mut inner_tokens);
162+
}
163+
if let Some(TokenTree::Punct(_)) = sep_opt {
164+
let sep: TokenTree = sep_opt.unwrap();
165+
minimal_quote!(
166+
if _i > 0 {
167+
(@ quote(sep.into_token_stream())).to_tokens(&mut ts);
168+
}
169+
_i += 1;
170+
)
171+
.to_tokens(&mut inner_tokens);
172+
};
173+
minimal_quote!(
174+
(@ quote(content.clone())).to_tokens(&mut ts);
175+
)
176+
.to_tokens(&mut inner_tokens);
177+
let block =
178+
TokenTree::Group(Group::new(Delimiter::Brace, inner_tokens));
179+
180+
let while_tokens =
181+
TokenStream::from_iter(vec![while_ident, true_literal, block]);
182+
minimal_quote!((@ while_tokens)).to_tokens(&mut tokens);
183+
} else {
184+
panic! {"XXX"};
185+
}
186+
}
187+
continue;
188+
}
78189
TokenTree::Ident(_) => {
79190
minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);)
80191
.to_tokens(&mut tokens);
@@ -155,6 +266,30 @@ pub fn quote(stream: TokenStream) -> TokenStream {
155266
}
156267
}
157268

269+
fn collect_meta_vars(stream: TokenStream) -> Vec<Ident> {
270+
fn helper(stream: TokenStream, out: &mut Vec<Ident>) {
271+
let mut iter = stream.into_iter().peekable();
272+
while let Some(tree) = iter.next() {
273+
match &tree {
274+
TokenTree::Punct(tt) if tt.as_char() == '$' => {
275+
if let Some(TokenTree::Ident(id)) = iter.peek() {
276+
out.push(id.clone());
277+
iter.next();
278+
}
279+
}
280+
TokenTree::Group(tt) => {
281+
helper(tt.stream(), out);
282+
}
283+
_ => {}
284+
}
285+
}
286+
}
287+
288+
let mut vars = Vec::new();
289+
helper(stream, &mut vars);
290+
vars
291+
}
292+
158293
/// Quote a `Span` into a `TokenStream`.
159294
/// This is needed to implement a custom quoter.
160295
#[unstable(feature = "proc_macro_quote", issue = "54722")]

0 commit comments

Comments
 (0)