use proc_macro::TokenStream; use proc_macro2::{Ident, Span, TokenStream as TokenStream2, TokenTree}; use quote::{quote, quote_spanned}; use syn::parse::{Nothing, ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{
parenthesized, parse_macro_input, token, Abi, Attribute, Data, DeriveInput, Error, Expr, Field,
Generics, Path, Result, Token, Type, Visibility,
};
/// Derive the `RefCast` trait. /// /// See the [crate-level documentation](./index.html) for usage examples! /// /// # Attributes /// /// Use the `#[trivial]` attribute to mark any zero-sized fields that are *not* /// the one that references are going to be converted from. /// /// ``` /// use ref_cast::RefCast; /// use std::marker::PhantomData; /// /// #[derive(RefCast)] /// #[repr(transparent)] /// pub struct Generic<T, U> { /// raw: Vec<U>, /// #[trivial] /// aux: Variance<T, U>, /// } /// /// type Variance<T, U> = PhantomData<fn(T) -> U>; /// ``` /// /// Fields with a type named `PhantomData` or `PhantomPinned` are automatically /// recognized and do not need to be marked with this attribute. /// /// ``` /// use ref_cast::RefCast; /// use std::marker::{PhantomData, PhantomPinned}; /// /// #[derive(RefCast)] // generates a conversion from &[u8] to &Bytes<'_> /// #[repr(transparent)] /// pub struct Bytes<'arena> { /// lifetime: PhantomData<&'arena ()>, /// pin: PhantomPinned, /// bytes: [u8], /// } /// ``` #[proc_macro_derive(RefCast, attributes(trivial))] pubfn derive_ref_cast(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput);
expand_ref_cast(&input)
.unwrap_or_else(Error::into_compile_error)
.into()
}
/// Derive that makes the `ref_cast_custom` attribute able to generate /// freestanding reference casting functions for a type. /// /// Please refer to the documentation of /// [`#[ref_cast_custom]`][macro@ref_cast_custom] where these two macros are /// documented together. #[proc_macro_derive(RefCastCustom, attributes(trivial))] pubfn derive_ref_cast_custom(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput);
expand_ref_cast_custom(&input)
.unwrap_or_else(Error::into_compile_error)
.into()
}
/// Create a function for a RefCast-style reference cast. Call site gets control /// of the visibility, function name, argument name, `const`ness, unsafety, and /// documentation. /// /// The `derive(RefCast)` macro produces a trait impl, which means the function /// names are predefined, and public if your type is public, and not callable in /// `const` (at least today on stable Rust). As an alternative to that, /// `derive(RefCastCustom)` exposes greater flexibility so that instead of a /// trait impl, the casting functions can be made associated functions or free /// functions, can be named what you want, documented, `const` or `unsafe` if /// you want, and have your exact choice of visibility. /// /// ```rust /// use ref_cast::{ref_cast_custom, RefCastCustom}; /// /// #[derive(RefCastCustom)] // does not generate any public API by itself /// #[repr(transparent)] /// pub struct Frame([u8]); /// /// impl Frame { /// #[ref_cast_custom] // requires derive(RefCastCustom) on the return type /// pub(crate) const fn new(bytes: &[u8]) -> &Self; /// /// #[ref_cast_custom] /// pub(crate) fn new_mut(bytes: &mut [u8]) -> &mut Self; /// } /// /// // example use of the const fn /// const FRAME: &Frame = Frame::new(b"..."); /// ``` /// /// The above shows associated functions, but you might alternatively want to /// generate free functions: /// /// ```rust /// # use ref_cast::{ref_cast_custom, RefCastCustom}; /// # /// # #[derive(RefCastCustom)] /// # #[repr(transparent)] /// # pub struct Frame([u8]); /// # /// impl Frame { /// pub fn new<T: AsRef<[u8]>>(bytes: &T) -> &Self { /// #[ref_cast_custom] /// fn ref_cast(bytes: &[u8]) -> &Frame; /// /// ref_cast(bytes.as_ref()) /// } /// } /// ``` #[proc_macro_attribute] pubfn ref_cast_custom(args: TokenStream, input: TokenStream) -> TokenStream { let input = TokenStream2::from(input); let expanded = match (|input: ParseStream| { let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let constness: Option<Token![const]> = input.parse()?; let asyncness: Option<Token![async]> = input.parse()?; let unsafety: Option<Token![unsafe]> = input.parse()?; let abi: Option<Abi> = input.parse()?; let fn_token: Token![fn] = input.parse()?; let ident: Ident = input.parse()?; letmut generics: Generics = input.parse()?;
let content; let paren_token = parenthesized!(content in input); let arg: Ident = content.parse()?; let colon_token: Token![:] = content.parse()?; let from_type: Type = content.parse()?; let _trailing_comma: Option<Token![,]> = content.parse()?; if !content.is_empty() { let rest: TokenStream2 = content.parse()?; return Err(Error::new_spanned(
rest, "ref_cast_custom function is required to have a single argument",
));
}
let arrow_token: Token![->] = input.parse()?; let to_type: Type = input.parse()?;
generics.where_clause = input.parse()?; let semi_token: Token![;] = input.parse()?;
let args = quote_spanned! {paren_token.span=>
(#arg#colon_token#from_type)
};
let allow_unused_unsafe = if unsafety.is_some() {
Some(quote!(#[allow(unused_unsafe)]))
} else {
None
};
letmut inline_attr = Some(quote!(#[inline])); for attr in &attrs { if attr.path().is_ident("inline") {
inline_attr = None; break;
}
}
// Apply a macro-generated span to the "unsafe" token for the unsafe block. // This is instead of reusing the caller's function signature's #unsafety // across both the generated function signature and generated unsafe block, // and instead of using `semi_token.span` like for the rest of the generated // code below, both of which would cause `forbid(unsafe_code)` located in // the caller to reject the expanded code. let macro_generated_unsafe = quote!(unsafe);
// check same crate let _ = ::ref_cast::__private::CurrentCrate::<#from_type, #to_type> {};
#allow_unused_unsafe// in case they are building with deny(unsafe_op_in_unsafe_fn) #[allow(clippy::transmute_ptr_to_ptr)] #macro_generated_unsafe {
::ref_cast::__private::transmute::<#from_type, #to_type>(#arg)
}
}
}
}
fn fields(input: &DeriveInput) -> Result<&Fields> { use syn::Fields;
match &input.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => Ok(&fields.named),
Fields::Unnamed(fields) => Ok(&fields.unnamed),
Fields::Unit => Err(Error::new(
Span::call_site(), "RefCast does not support unit structs",
)),
},
Data::Enum(_) => Err(Error::new(
Span::call_site(), "RefCast does not support enums",
)),
Data::Union(_) => Err(Error::new(
Span::call_site(), "RefCast does not support unions",
)),
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.