mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-06 18:40:34 +00:00

Used these commands and some manual searching ``` cargo clippy --fix --all-targets --workspace --exclude gstreamer-player --exclude i-slint-backend-linuxkms --exclude uefi-demo --exclude ffmpeg -- -A clippy::all -W clippy::uninlined_format_args cargo clippy --all-targets -- -A clippy::all -W clippy::uninlined_format_args cargo clippy --fix -- -A clippy::all -W clippy::uninlined_format_args ```
404 lines
12 KiB
Rust
404 lines
12 KiB
Rust
// Copyright © SixtyFPS GmbH <info@slint.dev>
|
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
/*!
|
|
This crate allow to get the offset of a field of a structure in a const or static context.
|
|
|
|
To be used re-exported from the `const_field_offset` crate
|
|
|
|
*/
|
|
extern crate proc_macro;
|
|
|
|
use proc_macro::TokenStream;
|
|
use quote::{format_ident, quote, quote_spanned};
|
|
use syn::{parse_macro_input, spanned::Spanned, DeriveInput};
|
|
#[cfg(feature = "field-offset-trait")]
|
|
use syn::{VisRestricted, Visibility};
|
|
|
|
/**
|
|
|
|
The macro FieldOffsets adds a `FIELD_OFFSETS` associated const to the struct. That
|
|
is an object which has fields with the same name as the fields of the original struct,
|
|
each field is of type `const_field_offset::FieldOffset`
|
|
|
|
```rust
|
|
use const_field_offset::FieldOffsets;
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets)]
|
|
struct Foo {
|
|
field_1 : u8,
|
|
field_2 : u32,
|
|
}
|
|
|
|
const FOO : usize = Foo::FIELD_OFFSETS.field_2.get_byte_offset();
|
|
assert_eq!(FOO, 4);
|
|
|
|
// This would not work on stable rust at the moment (rust 1.43)
|
|
// const FOO : usize = memoffsets::offsetof!(Foo, field_2);
|
|
```
|
|
|
|
*/
|
|
#[cfg_attr(
|
|
feature = "field-offset-trait",
|
|
doc = "
|
|
In addition, the macro also create a module `{ClassName}_field_offsets` which contains
|
|
zero-sized type that implement the `const_field_offset::ConstFieldOffset` trait
|
|
|
|
```rust
|
|
use const_field_offset::{FieldOffsets, FieldOffset, ConstFieldOffset};
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets)]
|
|
struct Foo {
|
|
field_1 : u8,
|
|
field_2 : u32,
|
|
}
|
|
|
|
const FOO : FieldOffset<Foo, u32> = Foo_field_offsets::field_2::OFFSET;
|
|
assert_eq!(FOO.get_byte_offset(), 4);
|
|
```
|
|
"
|
|
)]
|
|
/**
|
|
|
|
## Limitations
|
|
|
|
Only work with named #[repr(C)] structures.
|
|
|
|
## Attributes
|
|
|
|
### `pin`
|
|
|
|
Add a `AllowPin` to the FieldOffset.
|
|
|
|
In order for this to be safe, the macro will add code to prevent a
|
|
custom `Drop` or `Unpin` implementation.
|
|
|
|
```rust
|
|
use const_field_offset::*;
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets)]
|
|
#[pin]
|
|
struct Foo {
|
|
field_1 : u8,
|
|
field_2 : u32,
|
|
}
|
|
|
|
const FIELD_2 : FieldOffset<Foo, u32, AllowPin> = Foo::FIELD_OFFSETS.field_2;
|
|
let pin_box = Box::pin(Foo{field_1: 1, field_2: 2});
|
|
assert_eq!(*FIELD_2.apply_pin(pin_box.as_ref()), 2);
|
|
```
|
|
|
|
### `pin_drop`
|
|
|
|
This attribute works like the `pin` attribute but it does not prevent a custom
|
|
Drop implementation. Instead it provides a Drop implementation that forwards to
|
|
the [PinnedDrop](../const_field_offset/trait.PinnedDrop.html) trait that you need to implement for our type.
|
|
|
|
```rust
|
|
use const_field_offset::*;
|
|
use core::pin::Pin;
|
|
|
|
struct TypeThatRequiresSpecialDropHandling(); // ...
|
|
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets)]
|
|
#[pin_drop]
|
|
struct Foo {
|
|
field : TypeThatRequiresSpecialDropHandling,
|
|
}
|
|
|
|
impl PinnedDrop for Foo {
|
|
fn drop(self: Pin<&mut Self>) {
|
|
// Do you safe drop handling here
|
|
}
|
|
}
|
|
```
|
|
|
|
### `const-field-offset`
|
|
|
|
In case the `const-field-offset` crate is re-exported, it is possible to
|
|
specify the crate name using the `const_field_offset` attribute.
|
|
|
|
```rust
|
|
// suppose you re-export the const_field_offset create from a different module
|
|
mod xxx { pub use const_field_offset as cfo; }
|
|
#[repr(C)]
|
|
#[derive(xxx::cfo::FieldOffsets)]
|
|
#[const_field_offset(xxx::cfo)]
|
|
struct Foo {
|
|
field_1 : u8,
|
|
field_2 : u32,
|
|
}
|
|
```
|
|
|
|
*/
|
|
#[proc_macro_derive(FieldOffsets, attributes(const_field_offset, pin, pin_drop))]
|
|
pub fn const_field_offset(input: TokenStream) -> TokenStream {
|
|
let input = parse_macro_input!(input as DeriveInput);
|
|
|
|
let mut has_repr_c = false;
|
|
let mut crate_ = quote!(const_field_offset);
|
|
let mut pin = false;
|
|
let mut drop = false;
|
|
for a in &input.attrs {
|
|
if let Some(i) = a.path().get_ident() {
|
|
if i == "repr" {
|
|
let inner = a.parse_args::<syn::Ident>().map(|x| x.to_string());
|
|
match inner.as_ref().map(|x| x.as_str()) {
|
|
Ok("C") => has_repr_c = true,
|
|
Ok("packed") => {
|
|
return TokenStream::from(quote!(
|
|
compile_error! {"FieldOffsets does not work on #[repr(packed)]"}
|
|
))
|
|
}
|
|
_ => (),
|
|
}
|
|
} else if i == "const_field_offset" {
|
|
match a.parse_args::<syn::Path>() {
|
|
Ok(c) => crate_ = quote!(#c),
|
|
Err(_) => {
|
|
return TokenStream::from(
|
|
quote_spanned!(a.span()=> compile_error!{"const_field_offset attribute must be a crate name"}),
|
|
);
|
|
}
|
|
}
|
|
} else if i == "pin" {
|
|
pin = true;
|
|
} else if i == "pin_drop" {
|
|
drop = true;
|
|
pin = true;
|
|
}
|
|
}
|
|
}
|
|
if !has_repr_c {
|
|
return TokenStream::from(
|
|
quote! {compile_error!{"FieldOffsets only work for structures using repr(C)"}},
|
|
);
|
|
}
|
|
|
|
let struct_name = input.ident;
|
|
let struct_vis = input.vis;
|
|
let field_struct_name = quote::format_ident!("{}FieldsOffsets", struct_name);
|
|
|
|
let (fields, types, vis) = if let syn::Data::Struct(s) = &input.data {
|
|
if let syn::Fields::Named(n) = &s.fields {
|
|
let (f, tv): (Vec<_>, Vec<_>) =
|
|
n.named.iter().map(|f| (&f.ident, (&f.ty, &f.vis))).unzip();
|
|
let (t, v): (Vec<_>, Vec<_>) = tv.into_iter().unzip();
|
|
(f, t, v)
|
|
} else {
|
|
return TokenStream::from(quote! {compile_error!{"Only work for named fields"}});
|
|
}
|
|
} else {
|
|
return TokenStream::from(quote! {compile_error!("Only work for struct")});
|
|
};
|
|
|
|
let doc = format!(
|
|
"Helper struct containing the offsets of the fields of the struct [`{struct_name}`]\n\n\
|
|
Generated from the `#[derive(FieldOffsets)]` macro from the [`const-field-offset`]({crate_}) crate",
|
|
);
|
|
|
|
let (ensure_pin_safe, ensure_no_unpin, pin_flag, new_from_offset) = if !pin {
|
|
(None, None, quote!(#crate_::NotPinned), quote!(new_from_offset))
|
|
} else {
|
|
(
|
|
if drop {
|
|
None
|
|
} else {
|
|
let drop_trait_ident = format_ident!("{}MustNotImplDrop", struct_name);
|
|
Some(quote! {
|
|
/// Make sure that Drop is not implemented
|
|
#[allow(non_camel_case_types)]
|
|
trait #drop_trait_ident {}
|
|
impl<T: ::core::ops::Drop> #drop_trait_ident for T {}
|
|
impl #drop_trait_ident for #struct_name {}
|
|
|
|
})
|
|
},
|
|
Some(quote! {
|
|
const _ : () = {
|
|
/// Make sure that Unpin is not implemented
|
|
#[allow(dead_code)]
|
|
struct __MustNotImplUnpin<'__dummy_lifetime> (
|
|
::core::marker::PhantomData<&'__dummy_lifetime ()>
|
|
);
|
|
impl<'__dummy_lifetime> Unpin for #struct_name where __MustNotImplUnpin<'__dummy_lifetime> : Unpin {};
|
|
};
|
|
}),
|
|
quote!(#crate_::AllowPin),
|
|
quote!(new_from_offset_pinned),
|
|
)
|
|
};
|
|
|
|
let pinned_drop_impl = if drop {
|
|
Some(quote!(
|
|
impl Drop for #struct_name {
|
|
fn drop(&mut self) {
|
|
use #crate_::PinnedDrop;
|
|
self.do_safe_pinned_drop();
|
|
}
|
|
}
|
|
))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// Build the output, possibly using quasi-quotation
|
|
let expanded = quote! {
|
|
#[doc = #doc]
|
|
#[allow(missing_docs, non_camel_case_types, dead_code)]
|
|
#struct_vis struct #field_struct_name {
|
|
#(#vis #fields : #crate_::FieldOffset<#struct_name, #types, #pin_flag>,)*
|
|
}
|
|
|
|
#[allow(clippy::eval_order_dependence)] // The point of this code is to depend on the order!
|
|
impl #struct_name {
|
|
/// Return a struct containing the offset of for the fields of this struct
|
|
pub const FIELD_OFFSETS : #field_struct_name = {
|
|
#ensure_pin_safe;
|
|
let mut len = 0usize;
|
|
#field_struct_name {
|
|
#( #fields : {
|
|
let align = ::core::mem::align_of::<#types>();
|
|
// from Layout::padding_needed_for which is not yet stable
|
|
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
|
|
len = len_rounded_up + ::core::mem::size_of::<#types>();
|
|
/// Safety: According to the rules of repr(C), this is the right offset
|
|
unsafe { #crate_::FieldOffset::<#struct_name, #types, _>::#new_from_offset(len_rounded_up) }
|
|
}, )*
|
|
}
|
|
};
|
|
}
|
|
|
|
#pinned_drop_impl
|
|
#ensure_no_unpin
|
|
};
|
|
|
|
#[cfg(feature = "field-offset-trait")]
|
|
let module_name = quote::format_ident!("{}_field_offsets", struct_name);
|
|
|
|
#[cfg(feature = "field-offset-trait")]
|
|
let in_mod_vis = vis.iter().map(|vis| min_vis(vis, &struct_vis)).map(|vis| match vis {
|
|
Visibility::Public(_) => quote! {#vis},
|
|
Visibility::Restricted(VisRestricted { pub_token, path, .. }) => {
|
|
if quote!(#path).to_string().starts_with("super") {
|
|
quote!(#pub_token(in super::#path))
|
|
} else {
|
|
quote!(#vis)
|
|
}
|
|
}
|
|
Visibility::Inherited => quote!(pub(super)),
|
|
});
|
|
|
|
#[cfg(feature = "field-offset-trait")]
|
|
let expanded = quote! { #expanded
|
|
#[allow(non_camel_case_types)]
|
|
#[allow(non_snake_case)]
|
|
#[allow(missing_docs)]
|
|
#struct_vis mod #module_name {
|
|
#(
|
|
#[derive(Clone, Copy, Default)]
|
|
#in_mod_vis struct #fields;
|
|
)*
|
|
}
|
|
#(
|
|
impl #crate_::ConstFieldOffset for #module_name::#fields {
|
|
type Container = #struct_name;
|
|
type Field = #types;
|
|
type PinFlag = #pin_flag;
|
|
const OFFSET : #crate_::FieldOffset<#struct_name, #types, Self::PinFlag>
|
|
= #struct_name::FIELD_OFFSETS.#fields;
|
|
}
|
|
impl ::core::convert::Into<#crate_::FieldOffset<#struct_name, #types, #pin_flag>> for #module_name::#fields {
|
|
fn into(self) -> #crate_::FieldOffset<#struct_name, #types, #pin_flag> {
|
|
#struct_name::FIELD_OFFSETS.#fields
|
|
}
|
|
}
|
|
impl<Other> ::core::ops::Add<Other> for #module_name::#fields
|
|
where Other : #crate_::ConstFieldOffset<Container = #types>
|
|
{
|
|
type Output = #crate_::ConstFieldOffsetSum<Self, Other>;
|
|
fn add(self, other: Other) -> Self::Output {
|
|
#crate_::ConstFieldOffsetSum(self, other)
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
|
|
// Hand the output tokens back to the compiler
|
|
TokenStream::from(expanded)
|
|
}
|
|
|
|
#[cfg(feature = "field-offset-trait")]
|
|
/// Returns the most restricted visibility
|
|
fn min_vis<'a>(a: &'a Visibility, b: &'a Visibility) -> &'a Visibility {
|
|
match (a, b) {
|
|
(Visibility::Public(_), _) => b,
|
|
(_, Visibility::Public(_)) => a,
|
|
(Visibility::Inherited, _) => a,
|
|
(_, Visibility::Inherited) => b,
|
|
// FIXME: compare two paths
|
|
_ => a,
|
|
}
|
|
}
|
|
|
|
/**
|
|
```compile_fail
|
|
use const_field_offset::*;
|
|
#[derive(FieldOffsets)]
|
|
struct Foo {
|
|
x: u32,
|
|
}
|
|
```
|
|
*/
|
|
#[cfg(doctest)]
|
|
const _NO_REPR_C: u32 = 0;
|
|
|
|
/**
|
|
```compile_fail
|
|
use const_field_offset::*;
|
|
#[derive(FieldOffsets)]
|
|
#[repr(C)]
|
|
#[repr(packed)]
|
|
struct Foo {
|
|
x: u32,
|
|
}
|
|
```
|
|
*/
|
|
#[cfg(doctest)]
|
|
const _REPR_PACKED: u32 = 0;
|
|
|
|
/**
|
|
```compile_fail
|
|
use const_field_offset::*;
|
|
#[derive(FieldOffsets)]
|
|
#[repr(C)]
|
|
#[pin]
|
|
struct Foo {
|
|
x: u32,
|
|
}
|
|
|
|
impl Drop for Foo {
|
|
fn drop(&mut self) {}
|
|
}
|
|
```
|
|
*/
|
|
#[cfg(doctest)]
|
|
const _PIN_NO_DROP: u32 = 0;
|
|
|
|
/**
|
|
```compile_fail
|
|
use const_field_offset::*;
|
|
#[derive(FieldOffsets)]
|
|
#[repr(C)]
|
|
#[pin]
|
|
struct Foo {
|
|
q: std::marker::PhantomPinned,
|
|
x: u32,
|
|
}
|
|
|
|
impl Unpin for Foo {}
|
|
```
|
|
*/
|
|
#[cfg(doctest)]
|
|
const _PIN_NO_UNPIN: u32 = 0;
|