
# Objective - Fixes #6370 - Closes #6581 ## Solution - Added the following lints to the workspace: - `std_instead_of_core` - `std_instead_of_alloc` - `alloc_instead_of_core` - Used `cargo +nightly fmt` with [item level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Item%5C%3A) to split all `use` statements into single items. - Used `cargo clippy --workspace --all-targets --all-features --fix --allow-dirty` to _attempt_ to resolve the new linting issues, and intervened where the lint was unable to resolve the issue automatically (usually due to needing an `extern crate alloc;` statement in a crate root). - Manually removed certain uses of `std` where negative feature gating prevented `--all-features` from finding the offending uses. - Used `cargo +nightly fmt` with [crate level use formatting](https://rust-lang.github.io/rustfmt/?version=v1.6.0&search=#Crate%5C%3A) to re-merge all `use` statements matching Bevy's previous styling. - Manually fixed cases where the `fmt` tool could not re-merge `use` statements due to conditional compilation attributes. ## Testing - Ran CI locally ## Migration Guide The MSRV is now 1.81. Please update to this version or higher. ## Notes - This is a _massive_ change to try and push through, which is why I've outlined the semi-automatic steps I used to create this PR, in case this fails and someone else tries again in the future. - Making this change has no impact on user code, but does mean Bevy contributors will be warned to use `core` and `alloc` instead of `std` where possible. - This lint is a critical first step towards investigating `no_std` options for Bevy. --------- Co-authored-by: François Mockers <francois.mockers@vleue.com>
278 lines
9.2 KiB
Rust
278 lines
9.2 KiB
Rust
use crate::{
|
|
derive_data::ReflectEnum, derive_data::StructField, field_attributes::DefaultBehavior,
|
|
ident::ident_or_index,
|
|
};
|
|
use bevy_macro_utils::fq_std::{FQDefault, FQOption};
|
|
use proc_macro2::{Ident, TokenStream};
|
|
use quote::{format_ident, quote};
|
|
|
|
pub(crate) struct EnumVariantOutputData {
|
|
/// The names of each variant as a string.
|
|
///
|
|
/// For example, `Some` and `None` for the `Option` enum.
|
|
pub variant_names: Vec<String>,
|
|
/// The constructor portion of each variant.
|
|
///
|
|
/// For example, `Option::Some { 0: value }` and `Option::None {}` for the `Option` enum.
|
|
pub variant_constructors: Vec<TokenStream>,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub(crate) struct VariantField<'a, 'b> {
|
|
/// The alias for the field.
|
|
///
|
|
/// This should be used whenever the field needs to be referenced in a token stream.
|
|
pub alias: &'a Ident,
|
|
/// The name of the variant that contains the field.
|
|
pub variant_name: &'a str,
|
|
/// The field data.
|
|
pub field: &'a StructField<'b>,
|
|
}
|
|
|
|
/// Trait used to control how enum variants are built.
|
|
pub(crate) trait VariantBuilder: Sized {
|
|
/// Returns the enum data.
|
|
fn reflect_enum(&self) -> &ReflectEnum;
|
|
|
|
/// Returns a token stream that accesses a field of a variant as an `Option<dyn Reflect>`.
|
|
///
|
|
/// The default implementation of this method will return a token stream
|
|
/// which gets the field dynamically so as to support `dyn Enum`.
|
|
///
|
|
/// # Parameters
|
|
/// * `this`: The identifier of the enum
|
|
/// * `field`: The field to access
|
|
fn access_field(&self, this: &Ident, field: VariantField) -> TokenStream {
|
|
match &field.field.data.ident {
|
|
Some(field_ident) => {
|
|
let name = field_ident.to_string();
|
|
quote!(#this.field(#name))
|
|
}
|
|
None => {
|
|
if let Some(field_index) = field.field.reflection_index {
|
|
quote!(#this.field_at(#field_index))
|
|
} else {
|
|
quote!(::core::compile_error!(
|
|
"internal bevy_reflect error: field should be active"
|
|
))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns a token stream that unwraps a field of a variant as a `&dyn Reflect`
|
|
/// (from an `Option<dyn Reflect>`).
|
|
///
|
|
/// # Parameters
|
|
/// * `field`: The field to access
|
|
fn unwrap_field(&self, field: VariantField) -> TokenStream;
|
|
|
|
/// Returns a token stream that constructs a field of a variant as a concrete type
|
|
/// (from a `&dyn Reflect`).
|
|
///
|
|
/// # Parameters
|
|
/// * `field`: The field to access
|
|
fn construct_field(&self, field: VariantField) -> TokenStream;
|
|
|
|
/// Returns a token stream that constructs an instance of an active field.
|
|
///
|
|
/// # Parameters
|
|
/// * `this`: The identifier of the enum
|
|
/// * `field`: The field to access
|
|
fn on_active_field(&self, this: &Ident, field: VariantField) -> TokenStream {
|
|
let bevy_reflect_path = self.reflect_enum().meta().bevy_reflect_path();
|
|
let field_accessor = self.access_field(this, field);
|
|
|
|
let alias = field.alias;
|
|
let field_ty = field.field.reflected_type();
|
|
let field_constructor = self.construct_field(field);
|
|
|
|
let construction = match &field.field.attrs.default {
|
|
DefaultBehavior::Func(path) => quote! {
|
|
if let #FQOption::Some(#alias) = #field_accessor {
|
|
#field_constructor
|
|
} else {
|
|
#path()
|
|
}
|
|
},
|
|
DefaultBehavior::Default => quote! {
|
|
if let #FQOption::Some(#alias) = #field_accessor {
|
|
#field_constructor
|
|
} else {
|
|
#FQDefault::default()
|
|
}
|
|
},
|
|
DefaultBehavior::Required => {
|
|
let field_unwrapper = self.unwrap_field(field);
|
|
|
|
quote! {{
|
|
// `#alias` is used by both the unwrapper and constructor
|
|
let #alias = #field_accessor;
|
|
let #alias = #field_unwrapper;
|
|
#field_constructor
|
|
}}
|
|
}
|
|
};
|
|
|
|
if field.field.attrs().remote.is_some() {
|
|
quote! {
|
|
<#field_ty as #bevy_reflect_path::ReflectRemote>::into_remote(#construction)
|
|
}
|
|
} else {
|
|
construction
|
|
}
|
|
}
|
|
|
|
/// Returns a token stream that constructs an instance of an ignored field.
|
|
///
|
|
/// # Parameters
|
|
/// * `field`: The field to access
|
|
fn on_ignored_field(&self, field: VariantField) -> TokenStream {
|
|
match &field.field.attrs.default {
|
|
DefaultBehavior::Func(path) => quote! { #path() },
|
|
_ => quote! { #FQDefault::default() },
|
|
}
|
|
}
|
|
|
|
/// Builds the enum variant output data.
|
|
fn build(&self, this: &Ident) -> EnumVariantOutputData {
|
|
let variants = self.reflect_enum().variants();
|
|
|
|
let mut variant_names = Vec::with_capacity(variants.len());
|
|
let mut variant_constructors = Vec::with_capacity(variants.len());
|
|
|
|
for variant in variants {
|
|
let variant_ident = &variant.data.ident;
|
|
let variant_name = variant_ident.to_string();
|
|
let variant_path = self.reflect_enum().get_unit(variant_ident);
|
|
|
|
let fields = variant.fields();
|
|
|
|
let field_constructors = fields.iter().map(|field| {
|
|
let member = ident_or_index(field.data.ident.as_ref(), field.declaration_index);
|
|
let alias = format_ident!("_{}", member);
|
|
|
|
let variant_field = VariantField {
|
|
alias: &alias,
|
|
variant_name: &variant_name,
|
|
field,
|
|
};
|
|
|
|
let value = if field.attrs.ignore.is_ignored() {
|
|
self.on_ignored_field(variant_field)
|
|
} else {
|
|
self.on_active_field(this, variant_field)
|
|
};
|
|
|
|
let constructor = quote! {
|
|
#member: #value
|
|
};
|
|
|
|
constructor
|
|
});
|
|
|
|
let constructor = quote! {
|
|
#variant_path {
|
|
#( #field_constructors ),*
|
|
}
|
|
};
|
|
|
|
variant_names.push(variant_name);
|
|
variant_constructors.push(constructor);
|
|
}
|
|
|
|
EnumVariantOutputData {
|
|
variant_names,
|
|
variant_constructors,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Generates the enum variant output data needed to build the `FromReflect::from_reflect` implementation.
|
|
pub(crate) struct FromReflectVariantBuilder<'a> {
|
|
reflect_enum: &'a ReflectEnum<'a>,
|
|
}
|
|
|
|
impl<'a> FromReflectVariantBuilder<'a> {
|
|
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
|
|
Self { reflect_enum }
|
|
}
|
|
}
|
|
|
|
impl<'a> VariantBuilder for FromReflectVariantBuilder<'a> {
|
|
fn reflect_enum(&self) -> &ReflectEnum {
|
|
self.reflect_enum
|
|
}
|
|
|
|
fn unwrap_field(&self, field: VariantField) -> TokenStream {
|
|
let alias = field.alias;
|
|
quote!(#alias?)
|
|
}
|
|
|
|
fn construct_field(&self, field: VariantField) -> TokenStream {
|
|
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
|
|
let field_ty = field.field.reflected_type();
|
|
let alias = field.alias;
|
|
|
|
quote! {
|
|
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)?
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Generates the enum variant output data needed to build the `PartialReflect::try_apply` implementation.
|
|
pub(crate) struct TryApplyVariantBuilder<'a> {
|
|
reflect_enum: &'a ReflectEnum<'a>,
|
|
}
|
|
|
|
impl<'a> TryApplyVariantBuilder<'a> {
|
|
pub fn new(reflect_enum: &'a ReflectEnum) -> Self {
|
|
Self { reflect_enum }
|
|
}
|
|
}
|
|
|
|
impl<'a> VariantBuilder for TryApplyVariantBuilder<'a> {
|
|
fn reflect_enum(&self) -> &ReflectEnum {
|
|
self.reflect_enum
|
|
}
|
|
|
|
fn unwrap_field(&self, field: VariantField) -> TokenStream {
|
|
let VariantField {
|
|
alias,
|
|
variant_name,
|
|
field,
|
|
..
|
|
} = field;
|
|
|
|
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
|
|
|
|
let field_name = match &field.data.ident {
|
|
Some(ident) => format!("{ident}"),
|
|
None => format!(".{}", field.declaration_index),
|
|
};
|
|
|
|
quote! {
|
|
#alias.ok_or(#bevy_reflect_path::ApplyError::MissingEnumField {
|
|
variant_name: ::core::convert::Into::into(#variant_name),
|
|
field_name: ::core::convert::Into::into(#field_name)
|
|
})?
|
|
}
|
|
}
|
|
|
|
fn construct_field(&self, field: VariantField) -> TokenStream {
|
|
let bevy_reflect_path = self.reflect_enum.meta().bevy_reflect_path();
|
|
let alias = field.alias;
|
|
let field_ty = field.field.reflected_type();
|
|
|
|
quote! {
|
|
<#field_ty as #bevy_reflect_path::FromReflect>::from_reflect(#alias)
|
|
.ok_or(#bevy_reflect_path::ApplyError::MismatchedTypes {
|
|
from_type: ::core::convert::Into::into(
|
|
#bevy_reflect_path::DynamicTypePath::reflect_type_path(#alias)
|
|
),
|
|
to_type: ::core::convert::Into::into(<#field_ty as #bevy_reflect_path::TypePath>::type_path())
|
|
})?
|
|
}
|
|
}
|
|
}
|