mirror of
https://github.com/bevyengine/bevy
synced 2024-12-22 02:53:07 +00:00
fd329c0426
# Objective > Rust 1.81 released the #[expect(...)] attribute, which works like #[allow(...)] but throws a warning if the lint isn't raised. This is preferred to #[allow(...)] because it tells us when it can be removed. - Adopts the parts of #15118 that are complete, and updates the branch so it can be merged. - There were a few conflicts, let me know if I misjudged any of 'em. Alice's [recommendation](https://github.com/bevyengine/bevy/issues/15059#issuecomment-2349263900) seems well-taken, let's do this crate by crate now that @BD103 has done the lion's share of this! (Relates to, but doesn't yet completely finish #15059.) Crates this _doesn't_ cover: - bevy_input - bevy_gilrs - bevy_window - bevy_winit - bevy_state - bevy_render - bevy_picking - bevy_core_pipeline - bevy_sprite - bevy_text - bevy_pbr - bevy_ui - bevy_gltf - bevy_gizmos - bevy_dev_tools - bevy_internal - bevy_dylib --------- Co-authored-by: BD103 <59022059+BD103@users.noreply.github.com> Co-authored-by: Ben Frankel <ben.frankel7@gmail.com> Co-authored-by: Antony <antony.m.3012@gmail.com>
125 lines
5 KiB
Rust
125 lines
5 KiB
Rust
// FIXME(15321): solve CI failures, then replace with `#![expect()]`.
|
|
#![allow(missing_docs, reason = "Not all docs are written yet, see #3492.")]
|
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
|
|
|
use bevy_macro_utils::BevyManifest;
|
|
use proc_macro::{Span, TokenStream};
|
|
use quote::{format_ident, quote};
|
|
use syn::{parse_macro_input, Data, DeriveInput, Path};
|
|
|
|
pub(crate) fn bevy_asset_path() -> Path {
|
|
BevyManifest::default().get_path("bevy_asset")
|
|
}
|
|
|
|
const DEPENDENCY_ATTRIBUTE: &str = "dependency";
|
|
|
|
#[proc_macro_derive(Asset, attributes(dependency))]
|
|
pub fn derive_asset(input: TokenStream) -> TokenStream {
|
|
let ast = parse_macro_input!(input as DeriveInput);
|
|
let bevy_asset_path: Path = bevy_asset_path();
|
|
|
|
let struct_name = &ast.ident;
|
|
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
|
|
let dependency_visitor = match derive_dependency_visitor_internal(&ast, &bevy_asset_path) {
|
|
Ok(dependency_visitor) => dependency_visitor,
|
|
Err(err) => return err.into_compile_error().into(),
|
|
};
|
|
|
|
TokenStream::from(quote! {
|
|
impl #impl_generics #bevy_asset_path::Asset for #struct_name #type_generics #where_clause { }
|
|
#dependency_visitor
|
|
})
|
|
}
|
|
|
|
#[proc_macro_derive(VisitAssetDependencies, attributes(dependency))]
|
|
pub fn derive_asset_dependency_visitor(input: TokenStream) -> TokenStream {
|
|
let ast = parse_macro_input!(input as DeriveInput);
|
|
let bevy_asset_path: Path = bevy_asset_path();
|
|
match derive_dependency_visitor_internal(&ast, &bevy_asset_path) {
|
|
Ok(dependency_visitor) => TokenStream::from(dependency_visitor),
|
|
Err(err) => err.into_compile_error().into(),
|
|
}
|
|
}
|
|
|
|
fn derive_dependency_visitor_internal(
|
|
ast: &DeriveInput,
|
|
bevy_asset_path: &Path,
|
|
) -> Result<proc_macro2::TokenStream, syn::Error> {
|
|
let struct_name = &ast.ident;
|
|
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
|
|
|
|
let visit_dep = |to_read| quote!(#bevy_asset_path::VisitAssetDependencies::visit_dependencies(#to_read, visit););
|
|
let is_dep_attribute = |a: &syn::Attribute| a.path().is_ident(DEPENDENCY_ATTRIBUTE);
|
|
let field_has_dep = |f: &syn::Field| f.attrs.iter().any(is_dep_attribute);
|
|
|
|
let body = match &ast.data {
|
|
Data::Struct(data_struct) => {
|
|
let fields = data_struct.fields.iter();
|
|
let field_visitors = fields.enumerate().filter(|(_, f)| field_has_dep(f));
|
|
let field_visitors = field_visitors.map(|(i, field)| match &field.ident {
|
|
Some(ident) => visit_dep(quote!(&self.#ident)),
|
|
None => {
|
|
let index = syn::Index::from(i);
|
|
visit_dep(quote!(&self.#index))
|
|
}
|
|
});
|
|
Some(quote!( #(#field_visitors)* ))
|
|
}
|
|
Data::Enum(data_enum) => {
|
|
let variant_has_dep = |v: &syn::Variant| v.fields.iter().any(field_has_dep);
|
|
let any_case_required = data_enum.variants.iter().any(variant_has_dep);
|
|
let cases = data_enum.variants.iter().filter(|v| variant_has_dep(v));
|
|
let cases = cases.map(|variant| {
|
|
let ident = &variant.ident;
|
|
let fields = &variant.fields;
|
|
|
|
let field_visitors = fields.iter().enumerate().filter(|(_, f)| field_has_dep(f));
|
|
|
|
let field_visitors = field_visitors.map(|(i, field)| match &field.ident {
|
|
Some(ident) => visit_dep(quote!(#ident)),
|
|
None => {
|
|
let ident = format_ident!("member{i}");
|
|
visit_dep(quote!(#ident))
|
|
}
|
|
});
|
|
let fields = match fields {
|
|
syn::Fields::Named(fields) => {
|
|
let named = fields.named.iter().map(|f| f.ident.as_ref());
|
|
quote!({ #(#named,)* .. })
|
|
}
|
|
syn::Fields::Unnamed(fields) => {
|
|
let named = (0..fields.unnamed.len()).map(|i| format_ident!("member{i}"));
|
|
quote!( ( #(#named,)* ) )
|
|
}
|
|
syn::Fields::Unit => unreachable!("Can't pass filter is_dep_attribute"),
|
|
};
|
|
quote!(Self::#ident #fields => {
|
|
#(#field_visitors)*
|
|
})
|
|
});
|
|
|
|
any_case_required.then(|| quote!(match self { #(#cases)*, _ => {} }))
|
|
}
|
|
Data::Union(_) => {
|
|
return Err(syn::Error::new(
|
|
Span::call_site().into(),
|
|
"Asset derive currently doesn't work on unions",
|
|
));
|
|
}
|
|
};
|
|
|
|
// prevent unused variable warning in case there are no dependencies
|
|
let visit = if body.is_none() {
|
|
quote! { _visit }
|
|
} else {
|
|
quote! { visit }
|
|
};
|
|
|
|
Ok(quote! {
|
|
impl #impl_generics #bevy_asset_path::VisitAssetDependencies for #struct_name #type_generics #where_clause {
|
|
fn visit_dependencies(&self, #visit: &mut impl FnMut(#bevy_asset_path::UntypedAssetId)) {
|
|
#body
|
|
}
|
|
}
|
|
})
|
|
}
|