mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
bevy_derive: Add #[deref]
attribute (#8552)
# Objective Bevy code tends to make heavy use of the [newtype]( https://doc.rust-lang.org/rust-by-example/generics/new_types.html) pattern, which is why we have a dedicated derive for [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) and [`DerefMut`](https://doc.rust-lang.org/std/ops/trait.DerefMut.html). This derive works for any struct with a single field: ```rust #[derive(Component, Deref, DerefMut)] struct MyNewtype(usize); ``` One reason for the single-field limitation is to prevent confusion and footguns related that would arise from allowing multi-field structs: <table align="center"> <tr> <th colspan="2"> Similar structs, different derefs </th> </tr> <tr> <td> ```rust #[derive(Deref, DerefMut)] struct MyStruct { foo: usize, // <- Derefs usize bar: String, } ``` </td> <td> ```rust #[derive(Deref, DerefMut)] struct MyStruct { bar: String, // <- Derefs String foo: usize, } ``` </td> </tr> <tr> <th colspan="2"> Why `.1`? </th> </tr> <tr> <td colspan="2"> ```rust #[derive(Deref, DerefMut)] struct MyStruct(Vec<usize>, Vec<f32>); let mut foo = MyStruct(vec![123], vec![1.23]); // Why can we skip the `.0` here? foo.push(456); // But not here? foo.1.push(4.56); ``` </td> </tr> </table> However, there are certainly cases where it's useful to allow for structs with multiple fields. Such as for structs with one "real" field and one `PhantomData` to allow for generics: ```rust #[derive(Deref, DerefMut)] struct MyStruct<T>( // We want use this field for the `Deref`/`DerefMut` impls String, // But we need this field so that we can make this struct generic PhantomData<T> ); // ERROR: Deref can only be derived for structs with a single field // ERROR: DerefMut can only be derived for structs with a single field ``` Additionally, the possible confusion and footguns are mainly an issue for newer Rust/Bevy users. Those familiar with `Deref` and `DerefMut` understand what adding the derive really means and can anticipate its behavior. ## Solution Allow users to opt into multi-field `Deref`/`DerefMut` derives using a `#[deref]` attribute: ```rust #[derive(Deref, DerefMut)] struct MyStruct<T>( // Use this field for the `Deref`/`DerefMut` impls #[deref] String, // We can freely include any other field without a compile error PhantomData<T> ); ``` This prevents the footgun pointed out in the first issue described in the previous section, but it still leaves the possible confusion surrounding `.0`-vs-`.#`. However, the idea is that by making this behavior explicit with an attribute, users will be more aware of it and can adapt appropriately. --- ## Changelog - Added `#[deref]` attribute to `Deref` and `DerefMut` derives
This commit is contained in:
parent
1da726e046
commit
56686a8962
33 changed files with 696 additions and 22 deletions
|
@ -16,6 +16,7 @@ rust-version = "1.67.0"
|
|||
exclude = [
|
||||
"benches",
|
||||
"crates/bevy_ecs_compile_fail_tests",
|
||||
"crates/bevy_macros_compile_fail_tests",
|
||||
"crates/bevy_reflect_compile_fail_tests",
|
||||
]
|
||||
members = [
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
use proc_macro::{Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Data, DeriveInput, Index, Member, Type};
|
||||
use syn::{parse_macro_input, Data, DeriveInput, Field, Index, Member, Type};
|
||||
|
||||
const DEREF: &str = "Deref";
|
||||
const DEREF_MUT: &str = "DerefMut";
|
||||
const DEREF_ATTR: &str = "deref";
|
||||
|
||||
pub fn derive_deref(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident = &ast.ident;
|
||||
let (field_member, field_type) = match get_inner_field(&ast, false) {
|
||||
let (field_member, field_type) = match get_deref_field(&ast, false) {
|
||||
Ok(items) => items,
|
||||
Err(err) => {
|
||||
return err.into_compile_error().into();
|
||||
|
@ -29,7 +33,7 @@ pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
|
|||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident = &ast.ident;
|
||||
let (field_member, _) = match get_inner_field(&ast, true) {
|
||||
let (field_member, _) = match get_deref_field(&ast, true) {
|
||||
Ok(items) => items,
|
||||
Err(err) => {
|
||||
return err.into_compile_error().into();
|
||||
|
@ -46,24 +50,62 @@ pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
|
|||
})
|
||||
}
|
||||
|
||||
fn get_inner_field(ast: &DeriveInput, is_mut: bool) -> syn::Result<(Member, &Type)> {
|
||||
fn get_deref_field(ast: &DeriveInput, is_mut: bool) -> syn::Result<(Member, &Type)> {
|
||||
let deref_kind = if is_mut { DEREF_MUT } else { DEREF };
|
||||
let deref_attr_str = format!("`#[{DEREF_ATTR}]`");
|
||||
|
||||
match &ast.data {
|
||||
Data::Struct(data_struct) if data_struct.fields.is_empty() => Err(syn::Error::new(
|
||||
Span::call_site().into(),
|
||||
format!("{deref_kind} cannot be derived on field-less structs"),
|
||||
)),
|
||||
Data::Struct(data_struct) if data_struct.fields.len() == 1 => {
|
||||
let field = data_struct.fields.iter().next().unwrap();
|
||||
let member = field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|name| Member::Named(name.clone()))
|
||||
.unwrap_or_else(|| Member::Unnamed(Index::from(0)));
|
||||
let member = to_member(field, 0);
|
||||
Ok((member, &field.ty))
|
||||
}
|
||||
_ => {
|
||||
let msg = if is_mut {
|
||||
"DerefMut can only be derived for structs with a single field"
|
||||
Data::Struct(data_struct) => {
|
||||
let mut selected_field: Option<(Member, &Type)> = None;
|
||||
for (index, field) in data_struct.fields.iter().enumerate() {
|
||||
for attr in &field.attrs {
|
||||
if !attr.meta.require_path_only()?.is_ident(DEREF_ATTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if selected_field.is_some() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
attr,
|
||||
format!(
|
||||
"{deref_attr_str} attribute can only be used on a single field"
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
let member = to_member(field, index);
|
||||
selected_field = Some((member, &field.ty));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(selected_field) = selected_field {
|
||||
Ok(selected_field)
|
||||
} else {
|
||||
"Deref can only be derived for structs with a single field"
|
||||
};
|
||||
Err(syn::Error::new(Span::call_site().into(), msg))
|
||||
Err(syn::Error::new(
|
||||
Span::call_site().into(),
|
||||
format!("deriving {deref_kind} on multi-field structs requires one field to have the {deref_attr_str} attribute"),
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(syn::Error::new(
|
||||
Span::call_site().into(),
|
||||
format!("{deref_kind} can only be derived on structs"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_member(field: &Field, index: usize) -> Member {
|
||||
field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(|name| Member::Named(name.clone()))
|
||||
.unwrap_or_else(|| Member::Unnamed(Index::from(index)))
|
||||
}
|
||||
|
|
|
@ -17,14 +17,20 @@ pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
|
|||
app_plugin::derive_dynamic_plugin(input)
|
||||
}
|
||||
|
||||
/// Implements [`Deref`] for _single-item_ structs. This is especially useful when
|
||||
/// utilizing the [newtype] pattern.
|
||||
/// Implements [`Deref`] for structs. This is especially useful when utilizing the [newtype] pattern.
|
||||
///
|
||||
/// For single-field structs, the implementation automatically uses that field.
|
||||
/// For multi-field structs, you must specify which field to use with the `#[deref]` attribute.
|
||||
///
|
||||
/// If you need [`DerefMut`] as well, consider using the other [derive] macro alongside
|
||||
/// this one.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ## Tuple Structs
|
||||
///
|
||||
/// Using a single-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// use bevy_derive::Deref;
|
||||
///
|
||||
|
@ -32,26 +38,83 @@ pub fn derive_dynamic_plugin(input: TokenStream) -> TokenStream {
|
|||
/// struct MyNewtype(String);
|
||||
///
|
||||
/// let foo = MyNewtype(String::from("Hello"));
|
||||
/// assert_eq!(5, foo.len());
|
||||
/// assert_eq!("Hello", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// Using a multi-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// # use std::marker::PhantomData;
|
||||
/// use bevy_derive::Deref;
|
||||
///
|
||||
/// #[derive(Deref)]
|
||||
/// struct MyStruct<T>(#[deref] String, PhantomData<T>);
|
||||
///
|
||||
/// let foo = MyStruct(String::from("Hello"), PhantomData::<usize>);
|
||||
/// assert_eq!("Hello", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// ## Named Structs
|
||||
///
|
||||
/// Using a single-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// use bevy_derive::{Deref, DerefMut};
|
||||
///
|
||||
/// #[derive(Deref, DerefMut)]
|
||||
/// struct MyStruct {
|
||||
/// value: String,
|
||||
/// }
|
||||
///
|
||||
/// let foo = MyStruct {
|
||||
/// value: String::from("Hello")
|
||||
/// };
|
||||
/// assert_eq!("Hello", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// Using a multi-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// # use std::marker::PhantomData;
|
||||
/// use bevy_derive::{Deref, DerefMut};
|
||||
///
|
||||
/// #[derive(Deref, DerefMut)]
|
||||
/// struct MyStruct<T> {
|
||||
/// #[deref]
|
||||
/// value: String,
|
||||
/// _phantom: PhantomData<T>,
|
||||
/// }
|
||||
///
|
||||
/// let foo = MyStruct {
|
||||
/// value:String::from("Hello"),
|
||||
/// _phantom:PhantomData::<usize>
|
||||
/// };
|
||||
/// assert_eq!("Hello", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// [`Deref`]: std::ops::Deref
|
||||
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
|
||||
/// [`DerefMut`]: std::ops::DerefMut
|
||||
/// [derive]: crate::derive_deref_mut
|
||||
#[proc_macro_derive(Deref)]
|
||||
#[proc_macro_derive(Deref, attributes(deref))]
|
||||
pub fn derive_deref(input: TokenStream) -> TokenStream {
|
||||
derefs::derive_deref(input)
|
||||
}
|
||||
|
||||
/// Implements [`DerefMut`] for _single-item_ structs. This is especially useful when
|
||||
/// utilizing the [newtype] pattern.
|
||||
/// Implements [`DerefMut`] for structs. This is especially useful when utilizing the [newtype] pattern.
|
||||
///
|
||||
/// For single-field structs, the implementation automatically uses that field.
|
||||
/// For multi-field structs, you must specify which field to use with the `#[deref]` attribute.
|
||||
///
|
||||
/// [`DerefMut`] requires a [`Deref`] implementation. You can implement it manually or use
|
||||
/// Bevy's [derive] macro for convenience.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ## Tuple Structs
|
||||
///
|
||||
/// Using a single-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// use bevy_derive::{Deref, DerefMut};
|
||||
///
|
||||
|
@ -63,11 +126,65 @@ pub fn derive_deref(input: TokenStream) -> TokenStream {
|
|||
/// assert_eq!("Hello World!", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// Using a multi-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// # use std::marker::PhantomData;
|
||||
/// use bevy_derive::{Deref, DerefMut};
|
||||
///
|
||||
/// #[derive(Deref, DerefMut)]
|
||||
/// struct MyStruct<T>(#[deref] String, PhantomData<T>);
|
||||
///
|
||||
/// let mut foo = MyStruct(String::from("Hello"), PhantomData::<usize>);
|
||||
/// foo.push_str(" World!");
|
||||
/// assert_eq!("Hello World!", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// ## Named Structs
|
||||
///
|
||||
/// Using a single-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// use bevy_derive::{Deref, DerefMut};
|
||||
///
|
||||
/// #[derive(Deref, DerefMut)]
|
||||
/// struct MyStruct {
|
||||
/// value: String,
|
||||
/// }
|
||||
///
|
||||
/// let mut foo = MyStruct {
|
||||
/// value: String::from("Hello")
|
||||
/// };
|
||||
/// foo.push_str(" World!");
|
||||
/// assert_eq!("Hello World!", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// Using a multi-field struct:
|
||||
///
|
||||
/// ```
|
||||
/// # use std::marker::PhantomData;
|
||||
/// use bevy_derive::{Deref, DerefMut};
|
||||
///
|
||||
/// #[derive(Deref, DerefMut)]
|
||||
/// struct MyStruct<T> {
|
||||
/// #[deref]
|
||||
/// value: String,
|
||||
/// _phantom: PhantomData<T>,
|
||||
/// }
|
||||
///
|
||||
/// let mut foo = MyStruct {
|
||||
/// value:String::from("Hello"),
|
||||
/// _phantom:PhantomData::<usize>
|
||||
/// };
|
||||
/// foo.push_str(" World!");
|
||||
/// assert_eq!("Hello World!", *foo);
|
||||
/// ```
|
||||
///
|
||||
/// [`DerefMut`]: std::ops::DerefMut
|
||||
/// [newtype]: https://doc.rust-lang.org/rust-by-example/generics/new_types.html
|
||||
/// [`Deref`]: std::ops::Deref
|
||||
/// [derive]: crate::derive_deref
|
||||
#[proc_macro_derive(DerefMut)]
|
||||
#[proc_macro_derive(DerefMut, attributes(deref))]
|
||||
pub fn derive_deref_mut(input: TokenStream) -> TokenStream {
|
||||
derefs::derive_deref_mut(input)
|
||||
}
|
||||
|
|
13
crates/bevy_macros_compile_fail_tests/Cargo.toml
Normal file
13
crates/bevy_macros_compile_fail_tests/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "bevy_macros_compile_fail_tests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Compile fail tests for Bevy Engine's various macros"
|
||||
homepage = "https://bevyengine.org"
|
||||
repository = "https://github.com/bevyengine/bevy"
|
||||
license = "MIT OR Apache-2.0"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
bevy_derive = { path = "../bevy_derive" }
|
||||
trybuild = "1.0.71"
|
6
crates/bevy_macros_compile_fail_tests/README.md
Normal file
6
crates/bevy_macros_compile_fail_tests/README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Compile fail tests for Bevy macros
|
||||
|
||||
This crate is not part of the Bevy workspace in order to not fail `crater` tests for Bevy.
|
||||
The tests assert on the exact compiler errors and can easily fail for new Rust versions due to updated compiler errors (e.g. changes in spans).
|
||||
|
||||
The `CI` workflow executes these tests on the stable rust toolchain (see [tools/ci](../../tools/ci/src/main.rs)).
|
1
crates/bevy_macros_compile_fail_tests/src/lib.rs
Normal file
1
crates/bevy_macros_compile_fail_tests/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
|||
// Nothing here, check out the integration tests
|
|
@ -0,0 +1,6 @@
|
|||
#[test]
|
||||
fn test() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/deref_derive/*.fail.rs");
|
||||
t.pass("tests/deref_derive/*.pass.rs");
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
use bevy_derive::Deref;
|
||||
|
||||
// Reason: `#[deref]` doesn't take any arguments
|
||||
|
||||
#[derive(Deref)]
|
||||
struct TupleStruct(usize, #[deref()] String);
|
||||
|
||||
#[derive(Deref)]
|
||||
struct Struct {
|
||||
foo: usize,
|
||||
#[deref()]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
error: unexpected token in attribute
|
||||
--> tests/deref_derive/invalid_attribute.fail.rs:6:34
|
||||
|
|
||||
6 | struct TupleStruct(usize, #[deref()] String);
|
||||
| ^
|
||||
|
||||
error: unexpected token in attribute
|
||||
--> tests/deref_derive/invalid_attribute.fail.rs:11:12
|
||||
|
|
||||
11 | #[deref()]
|
||||
| ^
|
|
@ -0,0 +1,9 @@
|
|||
use bevy_derive::Deref;
|
||||
|
||||
#[derive(Deref)]
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(Deref)]
|
||||
enum Enum {}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,15 @@
|
|||
error: Deref cannot be derived on field-less structs
|
||||
--> tests/deref_derive/invalid_item.fail.rs:3:10
|
||||
|
|
||||
3 | #[derive(Deref)]
|
||||
| ^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `Deref` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: Deref can only be derived on structs
|
||||
--> tests/deref_derive/invalid_item.fail.rs:6:10
|
||||
|
|
||||
6 | #[derive(Deref)]
|
||||
| ^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `Deref` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,12 @@
|
|||
use bevy_derive::Deref;
|
||||
|
||||
#[derive(Deref)]
|
||||
struct TupleStruct(usize, String);
|
||||
|
||||
#[derive(Deref)]
|
||||
struct Struct {
|
||||
foo: usize,
|
||||
bar: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,15 @@
|
|||
error: deriving Deref on multi-field structs requires one field to have the `#[deref]` attribute
|
||||
--> tests/deref_derive/missing_attribute.fail.rs:3:10
|
||||
|
|
||||
3 | #[derive(Deref)]
|
||||
| ^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `Deref` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: deriving Deref on multi-field structs requires one field to have the `#[deref]` attribute
|
||||
--> tests/deref_derive/missing_attribute.fail.rs:6:10
|
||||
|
|
||||
6 | #[derive(Deref)]
|
||||
| ^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `Deref` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,14 @@
|
|||
use bevy_derive::Deref;
|
||||
|
||||
#[derive(Deref)]
|
||||
struct TupleStruct(#[deref] usize, #[deref] String);
|
||||
|
||||
#[derive(Deref)]
|
||||
struct Struct {
|
||||
#[deref]
|
||||
foo: usize,
|
||||
#[deref]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
error: `#[deref]` attribute can only be used on a single field
|
||||
--> tests/deref_derive/multiple_attributes.fail.rs:4:36
|
||||
|
|
||||
4 | struct TupleStruct(#[deref] usize, #[deref] String);
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `#[deref]` attribute can only be used on a single field
|
||||
--> tests/deref_derive/multiple_attributes.fail.rs:10:5
|
||||
|
|
||||
10 | #[deref]
|
||||
| ^^^^^^^^
|
|
@ -0,0 +1,22 @@
|
|||
use bevy_derive::Deref;
|
||||
|
||||
#[derive(Deref)]
|
||||
struct TupleStruct(usize, #[deref] String);
|
||||
|
||||
#[derive(Deref)]
|
||||
struct Struct {
|
||||
foo: usize,
|
||||
#[deref]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let value = TupleStruct(123, "Hello world!".to_string());
|
||||
let _: &String = &*value;
|
||||
|
||||
let value = Struct {
|
||||
foo: 123,
|
||||
bar: "Hello world!".to_string(),
|
||||
};
|
||||
let _: &String = &*value;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
use bevy_derive::Deref;
|
||||
|
||||
#[derive(Deref)]
|
||||
struct TupleStruct(String);
|
||||
|
||||
#[derive(Deref)]
|
||||
struct Struct {
|
||||
bar: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let value = TupleStruct("Hello world!".to_string());
|
||||
let _: &String = &*value;
|
||||
|
||||
let value = Struct {
|
||||
bar: "Hello world!".to_string(),
|
||||
};
|
||||
let _: &String = &*value;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#[test]
|
||||
fn test() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.compile_fail("tests/deref_mut_derive/*.fail.rs");
|
||||
t.pass("tests/deref_mut_derive/*.pass.rs");
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
use bevy_derive::DerefMut;
|
||||
use std::ops::Deref;
|
||||
|
||||
// Reason: `#[deref]` doesn't take any arguments
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct TupleStruct(usize, #[deref()] String);
|
||||
|
||||
impl Deref for TupleStruct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct Struct {
|
||||
foo: usize,
|
||||
#[deref()]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Deref for Struct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bar
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
error: unexpected token in attribute
|
||||
--> tests/deref_mut_derive/invalid_attribute.fail.rs:7:34
|
||||
|
|
||||
7 | struct TupleStruct(usize, #[deref()] String);
|
||||
| ^
|
||||
|
||||
error: unexpected token in attribute
|
||||
--> tests/deref_mut_derive/invalid_attribute.fail.rs:20:12
|
||||
|
|
||||
20 | #[deref()]
|
||||
| ^
|
|
@ -0,0 +1,9 @@
|
|||
use bevy_derive::DerefMut;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct UnitStruct;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
enum Enum {}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,15 @@
|
|||
error: DerefMut cannot be derived on field-less structs
|
||||
--> tests/deref_mut_derive/invalid_item.fail.rs:3:10
|
||||
|
|
||||
3 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: DerefMut can only be derived on structs
|
||||
--> tests/deref_mut_derive/invalid_item.fail.rs:6:10
|
||||
|
|
||||
6 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,30 @@
|
|||
use bevy_derive::DerefMut;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct TupleStruct(#[deref] usize, String);
|
||||
|
||||
impl Deref for TupleStruct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct Struct {
|
||||
#[deref]
|
||||
foo: usize,
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Deref for Struct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bar
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,25 @@
|
|||
error[E0308]: mismatched types
|
||||
--> tests/deref_mut_derive/mismatched_target_type.fail.rs:4:10
|
||||
|
|
||||
4 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^
|
||||
| |
|
||||
| expected `&mut String`, found `&mut usize`
|
||||
| expected `&mut String` because of return type
|
||||
|
|
||||
= note: expected mutable reference `&mut String`
|
||||
found mutable reference `&mut usize`
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/deref_mut_derive/mismatched_target_type.fail.rs:15:10
|
||||
|
|
||||
15 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^
|
||||
| |
|
||||
| expected `&mut String`, found `&mut usize`
|
||||
| expected `&mut String` because of return type
|
||||
|
|
||||
= note: expected mutable reference `&mut String`
|
||||
found mutable reference `&mut usize`
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,29 @@
|
|||
use bevy_derive::DerefMut;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct TupleStruct(usize, String);
|
||||
|
||||
impl Deref for TupleStruct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct Struct {
|
||||
foo: usize,
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Deref for Struct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bar
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,15 @@
|
|||
error: deriving DerefMut on multi-field structs requires one field to have the `#[deref]` attribute
|
||||
--> tests/deref_mut_derive/missing_attribute.fail.rs:4:10
|
||||
|
|
||||
4 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: deriving DerefMut on multi-field structs requires one field to have the `#[deref]` attribute
|
||||
--> tests/deref_mut_derive/missing_attribute.fail.rs:15:10
|
||||
|
|
||||
15 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,13 @@
|
|||
use bevy_derive::DerefMut;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct TupleStruct(usize, #[deref] String);
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct Struct {
|
||||
foo: usize,
|
||||
#[deref]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,33 @@
|
|||
error[E0277]: the trait bound `TupleStruct: Deref` is not satisfied
|
||||
--> tests/deref_mut_derive/missing_deref.fail.rs:4:8
|
||||
|
|
||||
4 | struct TupleStruct(usize, #[deref] String);
|
||||
| ^^^^^^^^^^^ the trait `Deref` is not implemented for `TupleStruct`
|
||||
|
|
||||
note: required by a bound in `DerefMut`
|
||||
--> $RUST/core/src/ops/deref.rs
|
||||
|
||||
error[E0277]: the trait bound `Struct: Deref` is not satisfied
|
||||
--> tests/deref_mut_derive/missing_deref.fail.rs:7:8
|
||||
|
|
||||
7 | struct Struct {
|
||||
| ^^^^^^ the trait `Deref` is not implemented for `Struct`
|
||||
|
|
||||
note: required by a bound in `DerefMut`
|
||||
--> $RUST/core/src/ops/deref.rs
|
||||
|
||||
error[E0277]: the trait bound `TupleStruct: Deref` is not satisfied
|
||||
--> tests/deref_mut_derive/missing_deref.fail.rs:3:10
|
||||
|
|
||||
3 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^ the trait `Deref` is not implemented for `TupleStruct`
|
||||
|
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0277]: the trait bound `Struct: Deref` is not satisfied
|
||||
--> tests/deref_mut_derive/missing_deref.fail.rs:6:10
|
||||
|
|
||||
6 | #[derive(DerefMut)]
|
||||
| ^^^^^^^^ the trait `Deref` is not implemented for `Struct`
|
||||
|
|
||||
= note: this error originates in the derive macro `DerefMut` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -0,0 +1,31 @@
|
|||
use bevy_derive::DerefMut;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct TupleStruct(#[deref] usize, #[deref] String);
|
||||
|
||||
impl Deref for TupleStruct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct Struct {
|
||||
#[deref]
|
||||
foo: usize,
|
||||
#[deref]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Deref for Struct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bar
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,11 @@
|
|||
error: `#[deref]` attribute can only be used on a single field
|
||||
--> tests/deref_mut_derive/multiple_attributes.fail.rs:5:36
|
||||
|
|
||||
5 | struct TupleStruct(#[deref] usize, #[deref] String);
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `#[deref]` attribute can only be used on a single field
|
||||
--> tests/deref_mut_derive/multiple_attributes.fail.rs:19:5
|
||||
|
|
||||
19 | #[deref]
|
||||
| ^^^^^^^^
|
|
@ -0,0 +1,39 @@
|
|||
use bevy_derive::DerefMut;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct TupleStruct(usize, #[deref] String);
|
||||
|
||||
impl Deref for TupleStruct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct Struct {
|
||||
foo: usize,
|
||||
#[deref]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Deref for Struct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bar
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut value = TupleStruct(123, "Hello world!".to_string());
|
||||
let _: &mut String = &mut *value;
|
||||
|
||||
let mut value = Struct {
|
||||
foo: 123,
|
||||
bar: "Hello world!".to_string(),
|
||||
};
|
||||
let _: &mut String = &mut *value;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
use bevy_derive::DerefMut;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct TupleStruct(#[deref] String);
|
||||
|
||||
impl Deref for TupleStruct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DerefMut)]
|
||||
struct Struct {
|
||||
#[deref]
|
||||
bar: String,
|
||||
}
|
||||
|
||||
impl Deref for Struct {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.bar
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut value = TupleStruct("Hello world!".to_string());
|
||||
let _: &mut String = &mut *value;
|
||||
|
||||
let mut value = Struct {
|
||||
bar: "Hello world!".to_string(),
|
||||
};
|
||||
let _: &mut String = &mut *value;
|
||||
}
|
|
@ -106,6 +106,15 @@ fn main() {
|
|||
.run()
|
||||
.expect("Compiler errors of the Reflect compile fail tests seem to be different than expected! Check locally and compare rust versions.");
|
||||
}
|
||||
{
|
||||
// Macro Compile Fail Tests
|
||||
// Run tests (they do not get executed with the workspace tests)
|
||||
// - See crates/bevy_macros_compile_fail_tests/README.md
|
||||
let _subdir = sh.push_dir("crates/bevy_macros_compile_fail_tests");
|
||||
cmd!(sh, "cargo test --target-dir ../../target")
|
||||
.run()
|
||||
.expect("Compiler errors of the macros compile fail tests seem to be different than expected! Check locally and compare rust versions.");
|
||||
}
|
||||
}
|
||||
|
||||
if what_to_run.contains(Check::TEST) {
|
||||
|
|
Loading…
Reference in a new issue