Skip to content

Commit 8574a8a

Browse files
committed
Make derived queries immutable by default
1 parent 3cb3fb6 commit 8574a8a

File tree

4 files changed

+159
-177
lines changed

4 files changed

+159
-177
lines changed

crates/bevy_ecs/macros/src/fetch.rs

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use syn::{
99

1010
use crate::bevy_ecs_path;
1111

12-
static READ_ONLY_ATTRIBUTE_NAME: &str = "read_only";
12+
static MUTABLE_ATTRIBUTE_NAME: &str = "mutable";
1313
static READ_ONLY_DERIVE_ATTRIBUTE_NAME: &str = "read_only_derive";
1414

1515
pub fn derive_fetch_impl(input: TokenStream) -> TokenStream {
@@ -26,7 +26,7 @@ pub fn derive_fetch_impl(input: TokenStream) -> TokenStream {
2626
ty_generics,
2727
where_clause,
2828
has_world_lifetime,
29-
has_read_only_attr,
29+
has_mutable_attr,
3030
world_lifetime,
3131
state_lifetime,
3232
phantom_field_idents,
@@ -47,8 +47,8 @@ pub fn derive_fetch_impl(input: TokenStream) -> TokenStream {
4747
.map_or(false, |ident| ident == READ_ONLY_DERIVE_ATTRIBUTE_NAME)
4848
});
4949
let read_only_derive_macro_call = if let Some(read_only_derive_attr) = read_only_derive_attr {
50-
if has_read_only_attr {
51-
panic!("Attributes `read_only` and `read_only_derive` are mutually exclusive");
50+
if !has_mutable_attr {
51+
panic!("Attribute `read_only_derive` can only be be used for a struct marked with the `mutable` attribute");
5252
}
5353
let derive_args = &read_only_derive_attr.tokens;
5454
quote! { #[derive #derive_args] }
@@ -74,25 +74,10 @@ pub fn derive_fetch_impl(input: TokenStream) -> TokenStream {
7474

7575
let path = bevy_ecs_path();
7676

77-
let struct_read_only_declaration = if has_read_only_attr {
77+
let struct_read_only_declaration = if has_mutable_attr {
7878
quote! {
79-
// Statically checks that the safety guarantee actually holds true. We need this to make
80-
// sure that we don't compile `ReadOnlyFetch` if our struct contains nested `WorldQuery`
81-
// members that don't implement it.
82-
#[allow(dead_code)]
83-
const _: () = {
84-
fn assert_readonly<T: #path::query::ReadOnlyFetch>() {}
85-
86-
// We generate a readonly assertion for every struct member.
87-
fn assert_all #impl_generics () #where_clause {
88-
#(assert_readonly::<<#query_types as #path::query::WorldQuery>::Fetch>();)*
89-
}
90-
};
91-
}
92-
} else {
93-
quote! {
94-
// TODO: it would be great to be able to dedup this by just deriving `Fetch` again with `read_only` attribute,
95-
// but supporting QSelf types is tricky.
79+
// TODO: it would be great to be able to dedup this by just deriving `Fetch` again
80+
// without the `mutable` attribute, but supporting QSelf types is tricky.
9681
#read_only_derive_macro_call
9782
struct #struct_name_read_only #impl_generics #where_clause {
9883
#(#field_idents: <<#query_types as #path::query::WorldQuery>::ReadOnlyFetch as #path::query::Fetch<#world_lifetime, #world_lifetime>>::Item,)*
@@ -154,6 +139,21 @@ pub fn derive_fetch_impl(input: TokenStream) -> TokenStream {
154139
type ReadOnlyFetch = #read_only_fetch_struct_name #ty_generics;
155140
}
156141
}
142+
} else {
143+
quote! {
144+
// Statically checks that the safety guarantee actually holds true. We need this to make
145+
// sure that we don't compile `ReadOnlyFetch` if our struct contains nested `WorldQuery`
146+
// members that don't implement it.
147+
#[allow(dead_code)]
148+
const _: () = {
149+
fn assert_readonly<T: #path::query::ReadOnlyFetch>() {}
150+
151+
// We generate a readonly assertion for every struct member.
152+
fn assert_all #impl_generics () #where_clause {
153+
#(assert_readonly::<<#query_types as #path::query::WorldQuery>::Fetch>();)*
154+
}
155+
};
156+
}
157157
};
158158

159159
let tokens = TokenStream::from(quote! {
@@ -265,7 +265,7 @@ pub fn derive_filter_fetch_impl(input: TokenStream) -> TokenStream {
265265
ty_generics,
266266
where_clause,
267267
world_lifetime,
268-
has_read_only_attr: _,
268+
has_mutable_attr: _,
269269
has_world_lifetime,
270270
state_lifetime,
271271
phantom_field_idents,
@@ -383,7 +383,7 @@ pub fn derive_filter_fetch_impl(input: TokenStream) -> TokenStream {
383383
// This struct is used to share common tokens between `Fetch` and `FilterFetch` implementations.
384384
struct FetchImplTokens<'a> {
385385
struct_name: Ident,
386-
// Equals `struct_name` if `has_read_only_attr` is true.
386+
// Equals `struct_name` if `has_mutable_attr` is false.
387387
struct_name_read_only: Ident,
388388
fetch_struct_name: Ident,
389389
state_struct_name: Ident,
@@ -392,7 +392,7 @@ struct FetchImplTokens<'a> {
392392
impl_generics: ImplGenerics<'a>,
393393
ty_generics: TypeGenerics<'a>,
394394
where_clause: Option<&'a WhereClause>,
395-
has_read_only_attr: bool,
395+
has_mutable_attr: bool,
396396
has_world_lifetime: bool,
397397
world_lifetime: GenericParam,
398398
state_lifetime: GenericParam,
@@ -405,10 +405,10 @@ struct FetchImplTokens<'a> {
405405
}
406406

407407
fn fetch_impl_tokens(ast: &DeriveInput) -> FetchImplTokens {
408-
let has_read_only_attr = ast.attrs.iter().any(|attr| {
408+
let has_mutable_attr = ast.attrs.iter().any(|attr| {
409409
attr.path
410410
.get_ident()
411-
.map_or(false, |ident| ident == READ_ONLY_ATTRIBUTE_NAME)
411+
.map_or(false, |ident| ident == MUTABLE_ATTRIBUTE_NAME)
412412
});
413413

414414
let world_lifetime = ast.generics.params.first().and_then(|param| match param {
@@ -429,17 +429,17 @@ fn fetch_impl_tokens(ast: &DeriveInput) -> FetchImplTokens {
429429
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
430430

431431
let struct_name = ast.ident.clone();
432-
let struct_name_read_only = if has_read_only_attr {
433-
ast.ident.clone()
434-
} else {
432+
let struct_name_read_only = if has_mutable_attr {
435433
Ident::new(&format!("{}ReadOnly", struct_name), Span::call_site())
434+
} else {
435+
ast.ident.clone()
436436
};
437437
let fetch_struct_name = Ident::new(&format!("{}Fetch", struct_name), Span::call_site());
438438
let state_struct_name = Ident::new(&format!("{}State", struct_name), Span::call_site());
439-
let read_only_fetch_struct_name = if has_read_only_attr {
440-
fetch_struct_name.clone()
441-
} else {
439+
let read_only_fetch_struct_name = if has_mutable_attr {
442440
Ident::new(&format!("{}ReadOnlyFetch", struct_name), Span::call_site())
441+
} else {
442+
fetch_struct_name.clone()
443443
};
444444

445445
let fields = match &ast.data {
@@ -496,7 +496,7 @@ fn fetch_impl_tokens(ast: &DeriveInput) -> FetchImplTokens {
496496
impl_generics,
497497
ty_generics,
498498
where_clause,
499-
has_read_only_attr,
499+
has_mutable_attr,
500500
has_world_lifetime,
501501
world_lifetime,
502502
state_lifetime,

crates/bevy_ecs/macros/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
428428
}
429429

430430
/// Implement `WorldQuery` to use a struct as a parameter in a query
431-
#[proc_macro_derive(Fetch, attributes(read_only, read_only_derive))]
431+
#[proc_macro_derive(Fetch, attributes(mutable, read_only_derive))]
432432
pub fn derive_fetch(input: TokenStream) -> TokenStream {
433433
derive_fetch_impl(input)
434434
}

0 commit comments

Comments
 (0)