mirror of
https://github.com/openharmony/third_party_rust_quote.git
synced 2026-07-19 13:53:59 -04:00
Merge pull request 109 from mystor/dup_name_rep
This commit is contained in:
+113
-79
@@ -132,20 +132,6 @@ pub mod __rt;
|
||||
/// - `#( struct #var; )*` — the repetition can contain other tokens
|
||||
/// - `#( #k => println!("{}", #v), )*` — even multiple interpolations
|
||||
///
|
||||
/// There are two limitations around interpolations in a repetition:
|
||||
///
|
||||
/// - Every interpolation inside of a repetition must be a distinct variable.
|
||||
/// That is, `#(#a #a)*` is not allowed. Work around this by collecting `a`
|
||||
/// into a vector and taking references `a1 = &a` and `a2 = &a` which you use
|
||||
/// inside the repetition: `#(#a1 #a2)*`. Where possible, use meaningful names
|
||||
/// that indicate the distinct role of each copy.
|
||||
///
|
||||
/// - Every interpolation inside of a repetition must be iterable. If we have
|
||||
/// `vec` which is a vector and `ident` which is a single identifier,
|
||||
/// `#(#ident #vec)*` is not allowed. Work around this by using
|
||||
/// `std::iter::repeat(ident)` to produce an iterable that can be used from
|
||||
/// within the repetition.
|
||||
///
|
||||
/// # Hygiene
|
||||
///
|
||||
/// Any interpolated tokens preserve the `Span` information provided by their
|
||||
@@ -443,99 +429,131 @@ macro_rules! quote_spanned {
|
||||
|
||||
// Extract the names of all #metavariables and pass them to the $finish macro.
|
||||
//
|
||||
// in: pounded_var_names!(then () a #b c #( #d )* #e)
|
||||
// in: pounded_var_names!(then!(()) () a #b c #( #d )* #e)
|
||||
// out: then!(() b d e)
|
||||
#[macro_export(local_inner_macros)]
|
||||
#[doc(hidden)]
|
||||
macro_rules! pounded_var_names {
|
||||
($finish:ident ($($found:ident)*) # ( $($inner:tt)* ) $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) # ( $($inner:tt)* ) $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)*) $($inner)* $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*) # [ $($inner:tt)* ] $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) # [ $($inner:tt)* ] $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)*) $($inner)* $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*) # { $($inner:tt)* } $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) # { $($inner:tt)* } $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)*) $($inner)* $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*) # $first:ident $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)* $first) $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) # $first:ident $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)* $first) $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*) ( $($inner:tt)* ) $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) ( $($inner:tt)* ) $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)*) $($inner)* $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*) [ $($inner:tt)* ] $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) [ $($inner:tt)* ] $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)*) $($inner)* $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*) { $($inner:tt)* } $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)*) $($inner)* $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) { $($inner:tt)* } $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)*) $($inner)* $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*) $ignore:tt $($rest:tt)*) => {
|
||||
pounded_var_names!($finish ($($found)*) $($rest)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*) $ignore:tt $($rest:tt)*) => {
|
||||
pounded_var_names!($finish!($($extra)*) ($($found)*) $($rest)*)
|
||||
};
|
||||
|
||||
($finish:ident ($($found:ident)*)) => {
|
||||
$finish!(() $($found)*)
|
||||
($finish:ident!($($extra:tt)*) ($($found:ident)*)) => {
|
||||
$finish!($($extra)* $($found)*)
|
||||
};
|
||||
}
|
||||
|
||||
// in: nested_tuples_pat!(() a b c d e)
|
||||
// out: ((((a b) c) d) e)
|
||||
// in: quote_bind_into_iter!(has_iter {} a b c d e)
|
||||
// out: #[allow(unused_mut)]
|
||||
// let mut a = a.__quote_into_iter(&mut has_iter);
|
||||
// #[allow(unused_mut)]
|
||||
// let mut b = b.__quote_into_iter(&mut has_iter);
|
||||
// #[allow(unused_mut)]
|
||||
// let mut c = b.__quote_into_iter(&mut has_iter);
|
||||
// #[allow(unused_mut)]
|
||||
// let mut d = b.__quote_into_iter(&mut has_iter);
|
||||
// #[allow(unused_mut)]
|
||||
// let mut e = b.__quote_into_iter(&mut has_iter);
|
||||
//
|
||||
// in: nested_tuples_pat!(() a)
|
||||
// out: a
|
||||
// in: quote_bind_into_iter!(has_iter {} a)
|
||||
// out: #[allow(unused_mut)]
|
||||
// let mut a = a.__quote_into_iter(&mut has_iter);
|
||||
#[macro_export(local_inner_macros)]
|
||||
#[doc(hidden)]
|
||||
macro_rules! nested_tuples_pat {
|
||||
(()) => {
|
||||
&()
|
||||
macro_rules! quote_bind_into_iter {
|
||||
($has_iter:ident {$($acc:tt)*} $next:ident $($rest:ident)*) => {
|
||||
quote_bind_into_iter!(
|
||||
$has_iter
|
||||
{
|
||||
$($acc)*
|
||||
// `mut` may be unused if $next occurs multiple times in the list.
|
||||
#[allow(unused_mut)]
|
||||
let mut $next = $next.__quote_into_iter(&mut $has_iter);
|
||||
}
|
||||
$($rest)*
|
||||
);
|
||||
};
|
||||
|
||||
(() $first:ident $($rest:ident)*) => {
|
||||
nested_tuples_pat!(($first) $($rest)*)
|
||||
};
|
||||
|
||||
(($pat:pat) $first:ident $($rest:ident)*) => {
|
||||
nested_tuples_pat!((($pat, $first)) $($rest)*)
|
||||
};
|
||||
|
||||
(($done:pat)) => {
|
||||
$done
|
||||
($has_iter:ident {$($done:tt)*}) => {
|
||||
$($done)*
|
||||
};
|
||||
}
|
||||
|
||||
// in: multi_zip_expr!(() a b c d e)
|
||||
// out: a.into_iter().zip(b).zip(c).zip(d).zip(e)
|
||||
// in: quote_bind_next_or_break!({} a b c d e)
|
||||
// out: let a = match a.next() {
|
||||
// Some(_x) => $crate::__rt::RepInterp(_x),
|
||||
// None => break,
|
||||
// };
|
||||
// let b = match b.next() {
|
||||
// Some(_x) => $crate::__rt::RepInterp(_x),
|
||||
// None => break,
|
||||
// };
|
||||
// let c = match c.next() {
|
||||
// Some(_x) => $crate::__rt::RepInterp(_x),
|
||||
// None => break,
|
||||
// };
|
||||
// let d = match d.next() {
|
||||
// Some(_x) => $crate::__rt::RepInterp(_x),
|
||||
// None => break,
|
||||
// };
|
||||
// let e = match e.next() {
|
||||
// Some(_x) => $crate::__rt::RepInterp(_x),
|
||||
// None => break,
|
||||
// };
|
||||
//
|
||||
// in: multi_zip_iter!(() a)
|
||||
// out: a
|
||||
// in: quote_bind_next_or_break!({} a)
|
||||
// out: let a = match a.next() {
|
||||
// Some(_x) => $crate::__rt::RepInterp(_x),
|
||||
// None => break,
|
||||
// };
|
||||
//
|
||||
// in: quote_bind_next_or_break!({})
|
||||
// out: break;
|
||||
#[macro_export(local_inner_macros)]
|
||||
#[doc(hidden)]
|
||||
macro_rules! multi_zip_expr {
|
||||
(()) => {
|
||||
&[]
|
||||
macro_rules! quote_bind_next_or_break {
|
||||
({}) => { break; };
|
||||
|
||||
({$($acc:tt)*} $next:ident $($rest:ident)*) => {
|
||||
quote_bind_next_or_break!({
|
||||
$($acc)*
|
||||
let $next = match $next.next() {
|
||||
Some(_x) => $crate::__rt::RepInterp(_x),
|
||||
None => break,
|
||||
};
|
||||
} $($rest)*);
|
||||
};
|
||||
|
||||
(() $single:ident) => {
|
||||
$single
|
||||
};
|
||||
|
||||
(() $first:ident $($rest:ident)*) => {
|
||||
multi_zip_expr!(($first.into_iter()) $($rest)*)
|
||||
};
|
||||
|
||||
(($zips:expr) $first:ident $($rest:ident)*) => {
|
||||
multi_zip_expr!(($zips.zip($first)) $($rest)*)
|
||||
};
|
||||
|
||||
(($done:expr)) => {
|
||||
$done
|
||||
({$($done:tt)*}) => {
|
||||
$($done)*
|
||||
};
|
||||
}
|
||||
|
||||
@@ -551,20 +569,36 @@ macro_rules! quote_each_token {
|
||||
};
|
||||
|
||||
($tokens:ident $span:ident # ( $($inner:tt)* ) * $($rest:tt)*) => {
|
||||
for pounded_var_names!(nested_tuples_pat () $($inner)*)
|
||||
in pounded_var_names!(multi_zip_expr () $($inner)*) {
|
||||
quote_each_token!($tokens $span $($inner)*);
|
||||
{
|
||||
use $crate::__rt::ext::*;
|
||||
let mut has_iter = false;
|
||||
pounded_var_names!(quote_bind_into_iter!(has_iter {}) () $($inner)*);
|
||||
if has_iter {
|
||||
loop {
|
||||
pounded_var_names!(quote_bind_next_or_break!({}) () $($inner)*);
|
||||
quote_each_token!($tokens $span $($inner)*);
|
||||
}
|
||||
}
|
||||
}
|
||||
quote_each_token!($tokens $span $($rest)*);
|
||||
};
|
||||
|
||||
($tokens:ident $span:ident # ( $($inner:tt)* ) $sep:tt * $($rest:tt)*) => {
|
||||
for (_i, pounded_var_names!(nested_tuples_pat () $($inner)*))
|
||||
in pounded_var_names!(multi_zip_expr () $($inner)*).into_iter().enumerate() {
|
||||
if _i > 0 {
|
||||
quote_each_token!($tokens $span $sep);
|
||||
{
|
||||
use $crate::__rt::ext::*;
|
||||
let mut _i = 0usize;
|
||||
let mut has_iter = false;
|
||||
pounded_var_names!(quote_bind_into_iter!(has_iter {}) () $($inner)*);
|
||||
if has_iter {
|
||||
loop {
|
||||
pounded_var_names!(quote_bind_next_or_break!({}) () $($inner)*);
|
||||
if _i > 0 {
|
||||
quote_each_token!($tokens $span $sep);
|
||||
}
|
||||
_i += 1;
|
||||
quote_each_token!($tokens $span $($inner)*);
|
||||
}
|
||||
}
|
||||
quote_each_token!($tokens $span $($inner)*);
|
||||
}
|
||||
quote_each_token!($tokens $span $($rest)*);
|
||||
};
|
||||
|
||||
+158
-1
@@ -1,5 +1,162 @@
|
||||
use ext::TokenStreamExt;
|
||||
pub use proc_macro2::*;
|
||||
use ToTokens;
|
||||
|
||||
/// Extension traits used by the implementation of `quote!`. These are defined
|
||||
/// in separate traits, rather than as a single trait due to ambiguity issues.
|
||||
///
|
||||
/// These traits expose a `__quote_into_iter` method which should allow calling
|
||||
/// whichever impl happens to be applicable. Calling that method repeatedly on
|
||||
/// the returned value should be idempotent.
|
||||
pub mod ext {
|
||||
use std::slice;
|
||||
use ToTokens;
|
||||
|
||||
/// Extension trait providing the `__quote_into_iter` method on iterators.
|
||||
pub trait RepIteratorExt: Iterator + Sized {
|
||||
#[inline]
|
||||
fn __quote_into_iter(self, has_iter: &mut bool) -> Self {
|
||||
*has_iter = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Iterator> RepIteratorExt for T {}
|
||||
|
||||
/// Extension trait providing the `__quote_into_iter` method for
|
||||
/// non-iterable types. These types don't produce an iterator and don't set
|
||||
/// the `_has_iter` outparameter.
|
||||
pub trait RepToTokensExt {
|
||||
/// Pretend to be an iterator for the purposes of `__quote_into_iter`.
|
||||
/// This allows repeated calls to `__quote_into_iter` to not set the
|
||||
/// `has_iter` outparameter.
|
||||
#[inline]
|
||||
fn next(&self) -> Option<&Self> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn __quote_into_iter<'a>(&'a self, _has_iter: &mut bool) -> &'a Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToTokens + ?Sized> RepToTokensExt for T {}
|
||||
|
||||
/// Extension trait providing the `__quote_into_iter` method for types
|
||||
/// convertable into slices.
|
||||
///
|
||||
/// NOTE: This is implemented manually, rather than by using a blanket impl
|
||||
/// over `AsRef<[T]>` to reduce the chance of ambiguity conflicts with other
|
||||
/// `__quote_into_iter` methods from this module.
|
||||
pub trait RepSliceExt {
|
||||
type Item;
|
||||
|
||||
fn as_slice(&self) -> &[Self::Item];
|
||||
|
||||
#[inline]
|
||||
fn __quote_into_iter<'a>(&'a self, has_iter: &mut bool) -> slice::Iter<'a, Self::Item> {
|
||||
*has_iter = true;
|
||||
self.as_slice().iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RepSliceExt + ?Sized> RepSliceExt for &'a T {
|
||||
type Item = T::Item;
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[Self::Item] {
|
||||
<T as RepSliceExt>::as_slice(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: RepSliceExt + ?Sized> RepSliceExt for &'a mut T {
|
||||
type Item = T::Item;
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[Self::Item] {
|
||||
<T as RepSliceExt>::as_slice(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RepSliceExt for [T] {
|
||||
type Item = T;
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[Self::Item] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> RepSliceExt for Vec<T> {
|
||||
type Item = T;
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[Self::Item] {
|
||||
&self[..]
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! array_rep_slice {
|
||||
($($l:tt)*) => {
|
||||
$(
|
||||
impl<T> RepSliceExt for [T; $l] {
|
||||
type Item = T;
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[Self::Item] {
|
||||
&self[..]
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
array_rep_slice!(
|
||||
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
||||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
||||
);
|
||||
}
|
||||
|
||||
// Helper type used within interpolations to allow for repeated binding names.
|
||||
// Implements the relevant traits, and exports a dummy `next()` method.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct RepInterp<T>(pub T);
|
||||
|
||||
impl<T> RepInterp<T> {
|
||||
// This method is intended to look like `Iterator::next`, and is called when
|
||||
// a name is bound multiple times, as the previous binding will shadow the
|
||||
// original `Iterator` object. This allows us to avoid advancing the
|
||||
// iterator multiple times per iteration.
|
||||
#[inline]
|
||||
pub fn next(self) -> Option<T> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ext::RepSliceExt> ext::RepSliceExt for RepInterp<T> {
|
||||
type Item = T::Item;
|
||||
|
||||
#[inline]
|
||||
fn as_slice(&self) -> &[Self::Item] {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Iterator> Iterator for RepInterp<T> {
|
||||
type Item = T::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToTokens> ToTokens for RepInterp<T> {
|
||||
#[inline]
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.0.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ident_start(c: u8) -> bool {
|
||||
(b'a' <= c && c <= b'z') || (b'A' <= c && c <= b'Z') || c == b'_'
|
||||
@@ -12,7 +169,7 @@ fn is_ident_continue(c: u8) -> bool {
|
||||
fn is_ident(token: &str) -> bool {
|
||||
let mut iter = token.bytes();
|
||||
let first_ok = iter.next().map(is_ident_start).unwrap_or(false);
|
||||
|
||||
|
||||
first_ok && iter.all(is_ident_continue)
|
||||
}
|
||||
|
||||
|
||||
@@ -234,10 +234,36 @@ fn test_nested_fancy_repetition() {
|
||||
|
||||
#[test]
|
||||
fn test_empty_repetition() {
|
||||
#[allow(unreachable_code)]
|
||||
let tokens = quote!(#(a b)* #(c d),*);
|
||||
assert_eq!("", tokens.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicate_name_repetition() {
|
||||
let foo = &["a", "b"];
|
||||
|
||||
let tokens = quote! {
|
||||
#(#foo: #foo),*
|
||||
#(#foo: #foo),*
|
||||
};
|
||||
|
||||
let expected = r#""a" : "a" , "b" : "b" "a" : "a" , "b" : "b""#;
|
||||
assert_eq!(expected, tokens.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duplicate_name_repetition_no_copy() {
|
||||
let foo = vec!["a".to_owned(), "b".to_owned()];
|
||||
|
||||
let tokens = quote! {
|
||||
#(#foo: #foo),*
|
||||
};
|
||||
|
||||
let expected = r#""a" : "a" , "b" : "b""#;
|
||||
assert_eq!(expected, tokens.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_name_conflict() {
|
||||
// The implementation of `#(...),*` uses the variable `_i` but it should be
|
||||
@@ -248,6 +274,43 @@ fn test_variable_name_conflict() {
|
||||
assert_eq!(expected, tokens.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonrep_in_repetition() {
|
||||
let rep = vec!["a", "b"];
|
||||
let nonrep = "c";
|
||||
|
||||
let tokens = quote! {
|
||||
#(#rep #rep : #nonrep #nonrep),*
|
||||
};
|
||||
|
||||
let expected = r#""a" "a" : "c" "c" , "b" "b" : "c" "c""#;
|
||||
assert_eq!(expected, tokens.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonrep_only_repetition() {
|
||||
let nonrep = "a";
|
||||
|
||||
// Without the `has_iter` parameter to `__quote_into_iter()`, this would
|
||||
// loop infinitely.
|
||||
let tokens = quote!( #(#nonrep)* );
|
||||
let expected = "";
|
||||
|
||||
assert_eq!(expected, tokens.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonrep_only_repetition_dup() {
|
||||
let nonrep = "a";
|
||||
|
||||
// If the `__quote_into_iter()` method for `nonrep` produced a real
|
||||
// iterator, this would loop infinitely.
|
||||
let tokens = quote!( #(#nonrep #nonrep)* );
|
||||
let expected = "";
|
||||
|
||||
assert_eq!(expected, tokens.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_quote() {
|
||||
let tokens = quote!();
|
||||
|
||||
Reference in New Issue
Block a user