Pretend to have `span` field on punctuation with single span
The punctuation tokens are structured as:
pub struct $name {
pub spans: [Span; $len]
}
It is good that this is consistent across punctuation with single and multiple
spans. Accessing spans as shl.spans[0], shl.spans[1] is fine for something like
Token![<<]. But for tokens with a single span like Token![;] it would be nice to
allow referring to semi.span as an alternative to semi.spans[0].
This commit adds Deref and DerefMut impls to make that work:
use proc_macro2::Span;
use syn::Token;
fn main() {
let mut semi = Token);
println!("{:?}", semi.spans[0]);
// New:
semi.span = Span::call_site();
println!("{:?}", semi.span);
}
I suspect many people (because that includes me) write code like this already
without paying attention. Now instead of compile errors it will just do the
right thing.
diff --git a/src/token.rs b/src/token.rs
index f1a2331..a636b16 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -95,6 +95,7 @@
use std::fmt::{self, Debug};
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
+use std::ops::{Deref, DerefMut};
#[cfg(feature = "parsing")]
use proc_macro2::Delimiter;
@@ -106,6 +107,7 @@
#[cfg(feature = "printing")]
use quote::{ToTokens, TokenStreamExt};
+use self::private::WithSpan;
#[cfg(feature = "parsing")]
use buffer::Cursor;
#[cfg(feature = "parsing")]
@@ -136,9 +138,18 @@
fn display() -> &'static str;
}
-#[cfg(feature = "parsing")]
mod private {
+ use proc_macro2::Span;
+
+ #[cfg(feature = "parsing")]
pub trait Sealed {}
+
+ /// Support writing `token.span` rather than `token.spans[0]` on tokens that
+ /// hold a single span.
+ #[repr(C)]
+ pub struct WithSpan {
+ pub span: Span,
+ }
}
#[cfg(feature = "parsing")]
@@ -304,10 +315,31 @@
};
}
+macro_rules! impl_deref_if_len_is_1 {
+ ($name:ident/1) => {
+ impl Deref for $name {
+ type Target = WithSpan;
+
+ fn deref(&self) -> &Self::Target {
+ unsafe { &*(self as *const Self as *const WithSpan) }
+ }
+ }
+
+ impl DerefMut for $name {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ unsafe { &mut *(self as *mut Self as *mut WithSpan) }
+ }
+ }
+ };
+
+ ($name:ident/$len:tt) => {};
+}
+
macro_rules! define_punctuation_structs {
($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
$(
#[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
+ #[repr(C)]
#[$doc]
///
/// Don't try to remember the name of this type -- use the [`Token!`]
@@ -355,6 +387,8 @@
impl Hash for $name {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
+
+ impl_deref_if_len_is_1!($name/$len);
)*
};
}