From 8b4ee1475d74b16906705433d2f2ab4fcef839f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9l=C3=A8ne=20Amanita?= Date: Sun, 2 Jul 2023 09:19:41 +0100 Subject: [PATCH 1/5] Fix 9011 (clarify doc of get_component) and draft for 8917 (add informations with the Query[...]Error types) --- crates/bevy_ecs/src/query/state.rs | 87 ++++++++++++--- crates/bevy_ecs/src/system/mod.rs | 10 +- crates/bevy_ecs/src/system/query.rs | 163 +++++++++++++++++++++++----- 3 files changed, 217 insertions(+), 43 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 267c7903893ba..d55bed4c03995 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -447,12 +447,24 @@ impl QueryState { let location = world .entities() .get(entity) - .ok_or(QueryEntityError::NoSuchEntity(entity))?; + .ok_or(QueryEntityError::NoSuchEntity( + QueryEntityErrorDetails { + requested_entity: entity, + query_type: std::any::type_name::(), + } + ))?; if !self .matched_archetypes .contains(location.archetype_id.index()) { - return Err(QueryEntityError::QueryDoesNotMatch(entity)); + todo!(); + return Err(QueryEntityError::QueryDoesNotMatch( + "", //location.archetype_id? + QueryEntityErrorDetails { + requested_entity: entity, + query_type: std::any::type_name::(), + } + )); } let archetype = world .archetypes() @@ -473,7 +485,14 @@ impl QueryState { if F::filter_fetch(&mut filter, entity, location.table_row) { Ok(Q::fetch(&mut fetch, entity, location.table_row)) } else { - Err(QueryEntityError::QueryDoesNotMatch(entity)) + todo!(); + Err(QueryEntityError::QueryDoesNotMatch( + "", + QueryEntityErrorDetails { + requested_entity: entity, + query_type: std::any::type_name::(), + } + )) } } @@ -530,7 +549,12 @@ impl QueryState { for i in 0..N { for j in 0..i { if entities[i] == entities[j] { - return Err(QueryEntityError::AliasedMutability(entities[i])); + return Err(QueryEntityError::AliasedMutability( + QueryEntityErrorDetails { + requested_entity: entities[i], + query_type: std::any::type_name::(), + } + )); } } } @@ -1281,18 +1305,24 @@ impl QueryState { /// An error that occurs when retrieving a specific [`Entity`]'s query result from [`Query`](crate::system::Query) or [`QueryState`]. // TODO: return the type_name as part of this error -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum QueryEntityError { /// The given [`Entity`]'s components do not match the query. /// /// Either it does not have a requested component, or it has a component which the query filters out. - QueryDoesNotMatch(Entity), + QueryDoesNotMatch(&'static str, QueryEntityErrorDetails), /// The given [`Entity`] does not exist. - NoSuchEntity(Entity), + NoSuchEntity(QueryEntityErrorDetails), /// The [`Entity`] was requested mutably more than once. /// /// See [`QueryState::get_many_mut`] for an example. - AliasedMutability(Entity), + AliasedMutability(QueryEntityErrorDetails), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct QueryEntityErrorDetails { + pub requested_entity: Entity, + pub query_type: &'static str, } impl std::error::Error for QueryEntityError {} @@ -1300,12 +1330,20 @@ impl std::error::Error for QueryEntityError {} impl fmt::Display for QueryEntityError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - QueryEntityError::QueryDoesNotMatch(_) => { + QueryEntityError::QueryDoesNotMatch( + components, + QueryEntityErrorDetails {requested_entity, query_type} + ) => { + todo!(); write!(f, "The given entity's components do not match the query.") } - QueryEntityError::NoSuchEntity(_) => write!(f, "The requested entity does not exist."), - QueryEntityError::AliasedMutability(_) => { - write!(f, "The entity was requested mutably more than once.") + QueryEntityError::NoSuchEntity( + QueryEntityErrorDetails {requested_entity, ..} + ) => write!(f, "The requested entity {} does not exist.", requested_entity.index()), + QueryEntityError::AliasedMutability( + QueryEntityErrorDetails {requested_entity, ..} + ) => { + write!(f, "The entity {} was requested mutably more than once.", requested_entity.index()) } } } @@ -1313,7 +1351,7 @@ impl fmt::Display for QueryEntityError { #[cfg(test)] mod tests { - use crate::{prelude::*, query::QueryEntityError}; + use crate::{prelude::*, query::QueryEntityError, query::QueryEntityErrorDetails}; #[test] fn get_many_unchecked_manual_uniqueness() { @@ -1355,7 +1393,12 @@ mod tests { ) .unwrap_err() }, - QueryEntityError::AliasedMutability(entities[0]) + QueryEntityError::AliasedMutability( + QueryEntityErrorDetails { + requested_entity: entities[0], + query_type: "QueryState", + } + ) ); assert_eq!( @@ -1370,7 +1413,12 @@ mod tests { ) .unwrap_err() }, - QueryEntityError::AliasedMutability(entities[0]) + QueryEntityError::AliasedMutability( + QueryEntityErrorDetails { + requested_entity: entities[0], + query_type: "QueryState", + } + ) ); assert_eq!( @@ -1385,7 +1433,12 @@ mod tests { ) .unwrap_err() }, - QueryEntityError::AliasedMutability(entities[9]) + QueryEntityError::AliasedMutability( + QueryEntityErrorDetails { + requested_entity: entities[9], + query_type: "QueryState", + } + ) ); } @@ -1422,6 +1475,8 @@ mod tests { /// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`] as a single expected result via /// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut). +/// +/// The `&str` inside is a string representation of the `Query` type. #[derive(Debug)] pub enum QuerySingleError { /// No entity fits the query. diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 3900dd68ac6ba..725c9c67a39b3 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -511,7 +511,7 @@ mod tests { }, system::{ adapter::new, Commands, In, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, - QueryComponentError, Res, ResMut, Resource, System, SystemState, + QueryComponentError, QueryComponentErrorDetails, Res, ResMut, Resource, System, SystemState, }, world::{FromWorld, World}, }; @@ -1726,7 +1726,13 @@ mod tests { run_system(&mut world, move |q: Query<&mut W>| { let mut rq = q.to_readonly(); assert_eq!( - QueryComponentError::MissingWriteAccess, + QueryComponentError::MissingWriteAccess( + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: "W", + query_type: "Query<'_, '_, &W>", + } + ), rq.get_component_mut::>(entity).unwrap_err(), ); }); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index fe12856744edb..5b77b15b7b615 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1062,7 +1062,9 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns a shared reference to the component `T` of the given [`Entity`]. /// - /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead. + /// In case of a nonexisting entity, if the entity doesn't have the requested component, + /// or if the query doesn't have read access to this component, + /// a [`QueryEntityError`] is returned instead. /// /// # Example /// @@ -1096,15 +1098,38 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { let world = self.world; let entity_ref = world .get_entity(entity) - .ok_or(QueryComponentError::NoSuchEntity)?; + .ok_or(QueryComponentError::NoSuchEntity( + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + ))?; + todo!(); let component_id = world .components() .get_id(TypeId::of::()) - .ok_or(QueryComponentError::MissingComponent)?; + .ok_or(QueryComponentError::MissingComponent( + "", + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + ))?; + + todo!(); let archetype_component = entity_ref .archetype() .get_archetype_component_id(component_id) - .ok_or(QueryComponentError::MissingComponent)?; + .ok_or(QueryComponentError::MissingComponent( + "", + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + ))?; if self .state .archetype_component_access @@ -1112,15 +1137,31 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { { // SAFETY: `self.world` must have access to the component `T` for this entity, // since it was registered in `self.state`'s archetype component access set. - unsafe { entity_ref.get::() }.ok_or(QueryComponentError::MissingComponent) + todo!(); + unsafe { entity_ref.get::() }.ok_or(QueryComponentError::MissingComponent( + "", + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + )) } else { - Err(QueryComponentError::MissingReadAccess) + Err(QueryComponentError::MissingReadAccess( + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + )) } } /// Returns a mutable reference to the component `T` of the given entity. /// - /// In case of a nonexisting entity or mismatched component, a [`QueryComponentError`] is returned instead. + /// In case of a nonexisting entity, if the entity doesn't have the requested component, + /// or if the query doesn't have read access to this component, + /// a [`QueryEntityError`] is returned instead. /// /// # Example /// @@ -1174,30 +1215,72 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { // SAFETY: this check is required to ensure soundness in the case of `to_readonly().get_component_mut()` // See the comments on the `force_read_only_component_access` field for more info. if self.force_read_only_component_access { - return Err(QueryComponentError::MissingWriteAccess); + return Err(QueryComponentError::MissingWriteAccess( + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + )); } let world = self.world; let entity_ref = world .get_entity(entity) - .ok_or(QueryComponentError::NoSuchEntity)?; + .ok_or(QueryComponentError::NoSuchEntity( + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + ))?; + todo!(); let component_id = world .components() .get_id(TypeId::of::()) - .ok_or(QueryComponentError::MissingComponent)?; + .ok_or(QueryComponentError::MissingComponent( + "", + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + ))?; + todo!(); let archetype_component = entity_ref .archetype() .get_archetype_component_id(component_id) - .ok_or(QueryComponentError::MissingComponent)?; + .ok_or(QueryComponentError::MissingComponent( + "", + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + ))?; if self .state .archetype_component_access .has_write(archetype_component) { + todo!(); entity_ref .get_mut_using_ticks::(self.last_run, self.this_run) - .ok_or(QueryComponentError::MissingComponent) + .ok_or(QueryComponentError::MissingComponent( + "", + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + )) } else { - Err(QueryComponentError::MissingWriteAccess) + Err(QueryComponentError::MissingWriteAccess( + QueryComponentErrorDetails { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + )) } } @@ -1458,7 +1541,7 @@ pub enum QueryComponentError { /// } /// # bevy_ecs::system::assert_is_system(get_missing_read_access_error); /// ``` - MissingReadAccess, + MissingReadAccess(QueryComponentErrorDetails), /// The [`Query`] does not have write access to the requested component. /// /// This error occurs when the requested component is not included in the original query, or the mutability of the requested component is mismatched with the original query. @@ -1485,11 +1568,18 @@ pub enum QueryComponentError { /// } /// # bevy_ecs::system::assert_is_system(get_missing_write_access_error); /// ``` - MissingWriteAccess, + MissingWriteAccess(QueryComponentErrorDetails), /// The given [`Entity`] does not have the requested component. - MissingComponent, + MissingComponent(&'static str, QueryComponentErrorDetails), /// The requested [`Entity`] does not exist. - NoSuchEntity, + NoSuchEntity(QueryComponentErrorDetails), +} + +#[derive(Debug, PartialEq, Eq)] +pub struct QueryComponentErrorDetails { + pub requested_entity: Entity, + pub requested_component: &'static str, + pub query_type: &'static str, } impl std::error::Error for QueryComponentError {} @@ -1497,23 +1587,46 @@ impl std::error::Error for QueryComponentError {} impl std::fmt::Display for QueryComponentError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - QueryComponentError::MissingReadAccess => { + QueryComponentError::MissingReadAccess( + QueryComponentErrorDetails{requested_component, query_type, ..} + ) => { write!( f, - "This query does not have read access to the requested component." + "The query {} does not have read access to the requested component {}.", + requested_component, + query_type ) } - QueryComponentError::MissingWriteAccess => { + QueryComponentError::MissingWriteAccess( + QueryComponentErrorDetails{requested_component, query_type, ..} + ) => { write!( f, - "This query does not have write access to the requested component." + "The query {} does not have write access to the requested component {}.", + requested_component, + query_type ) } - QueryComponentError::MissingComponent => { - write!(f, "The given entity does not have the requested component.") + QueryComponentError::MissingComponent( + component, + QueryComponentErrorDetails{requested_entity, requested_component, ..} + ) => { + todo!(); + write!( + f, + "The given entity {} does not have the requested component {}.", + requested_entity.index(), + requested_component + ) } - QueryComponentError::NoSuchEntity => { - write!(f, "The requested entity does not exist.") + QueryComponentError::NoSuchEntity( + QueryComponentErrorDetails{requested_entity, ..} + ) => { + write!( + f, + "The requested entity {} does not exist.", + requested_entity.index(), + ) } } } From d5eda2cf3bb39bfbeacfd7a03dd5dc7b2f869f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9l=C3=A8ne=20Amanita?= Date: Tue, 4 Jul 2023 15:02:58 +0100 Subject: [PATCH 2/5] Add QueryEntityMismatchDetail and the logic to diagnose ComponentMismatch --- crates/bevy_ecs/src/query/access.rs | 8 +- crates/bevy_ecs/src/query/state.rs | 234 ++++++++++++++++++++-------- crates/bevy_ecs/src/system/mod.rs | 15 +- crates/bevy_ecs/src/system/query.rs | 206 +++++++++++------------- 4 files changed, 271 insertions(+), 192 deletions(-) diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 41a23020ac5f4..fa7b5d6403927 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -229,7 +229,7 @@ pub struct FilteredAccess { access: Access, // An array of filter sets to express `With` or `Without` clauses in disjunctive normal form, for example: `Or<(With, With)>`. // Filters like `(With, Or<(With, Without)>` are expanded into `Or<((With, With), (With, Without))>`. - filter_sets: Vec>, + pub(crate) filter_sets: Vec>, } impl Default for FilteredAccess { @@ -380,9 +380,9 @@ impl FilteredAccess { } #[derive(Clone, Eq, PartialEq)] -struct AccessFilters { - with: FixedBitSet, - without: FixedBitSet, +pub(crate) struct AccessFilters { + pub(crate) with: FixedBitSet, + pub(crate) without: FixedBitSet, _index_type: PhantomData, } diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index d55bed4c03995..cc8eeb700d731 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId}, - component::{ComponentId, Tick}, - entity::Entity, + component::{ComponentId, Components, Tick}, + entity::{Entity, EntityLocation}, prelude::FromWorld, query::{ Access, BatchingStrategy, DebugCheckedUnwrap, FilteredAccess, QueryCombinationIter, @@ -231,7 +231,9 @@ impl QueryState { /// Gets the query result for the given [`World`] and [`Entity`]. /// - /// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries. + /// In case of a nonexisting entity or mismatched components, a [`QueryEntityError`] is returned instead. + /// + /// This can only be called for read-only access to the component, see [`Self::get_mut`] for write access. #[inline] pub fn get<'w>( &mut self, @@ -252,7 +254,7 @@ impl QueryState { /// Returns the read-only query results for the given array of [`Entity`]. /// - /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is + /// In case of a nonexisting entity or mismatched components, a [`QueryEntityError`] is /// returned instead. /// /// Note that the unlike [`QueryState::get_many_mut`], the entities passed in do not need to be unique. @@ -302,6 +304,9 @@ impl QueryState { } /// Gets the query result for the given [`World`] and [`Entity`]. + /// + /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is + /// returned instead. #[inline] pub fn get_mut<'w>( &mut self, @@ -447,29 +452,32 @@ impl QueryState { let location = world .entities() .get(entity) - .ok_or(QueryEntityError::NoSuchEntity( - QueryEntityErrorDetails { - requested_entity: entity, - query_type: std::any::type_name::(), - } - ))?; + .ok_or(QueryEntityError::NoSuchEntity(QueryEntityErrorDetail { + requested_entity: entity, + query_type: std::any::type_name::(), + }))?; + let archetype = world + .archetypes() + .get(location.archetype_id) + .debug_checked_unwrap(); + if !self .matched_archetypes .contains(location.archetype_id.index()) { - todo!(); return Err(QueryEntityError::QueryDoesNotMatch( - "", //location.archetype_id? - QueryEntityErrorDetails { + QueryEntityMismatchDetail::ComponentMismatch(self.get_mismatches_detail( + world.components(), + location, + archetype, + )), + QueryEntityErrorDetail { requested_entity: entity, query_type: std::any::type_name::(), - } + }, )); } - let archetype = world - .archetypes() - .get(location.archetype_id) - .debug_checked_unwrap(); + let mut fetch = Q::init_fetch(world, &self.fetch_state, last_run, this_run); let mut filter = F::init_fetch(world, &self.filter_state, last_run, this_run); @@ -485,13 +493,12 @@ impl QueryState { if F::filter_fetch(&mut filter, entity, location.table_row) { Ok(Q::fetch(&mut fetch, entity, location.table_row)) } else { - todo!(); Err(QueryEntityError::QueryDoesNotMatch( - "", - QueryEntityErrorDetails { + QueryEntityMismatchDetail::ChangeDetectionMismatch, + QueryEntityErrorDetail { requested_entity: entity, query_type: std::any::type_name::(), - } + }, )) } } @@ -550,10 +557,10 @@ impl QueryState { for j in 0..i { if entities[i] == entities[j] { return Err(QueryEntityError::AliasedMutability( - QueryEntityErrorDetails { + QueryEntityErrorDetail { requested_entity: entities[i], query_type: std::any::type_name::(), - } + }, )); } } @@ -1301,49 +1308,148 @@ impl QueryState { >())), } } + + /// Gets the reasons an [`Entity`] might not match this query, see [`QueryEntityMismatchDetail`] + pub fn get_mismatches_detail( + &self, + components: &Components, + entity_location: EntityLocation, + archetype: &Archetype, + ) -> Vec { + let mut mismatch_details = Vec::new(); + + for components_access in self.component_access.filter_sets.iter() { + let query_with_components = components_access.with.clone(); + let query_without_components = components_access.without.clone(); + let mut query_unmatched_components = query_with_components.clone(); + + let mut query_without_components_in_entity = Vec::new(); + for component_id in archetype.components() { + if query_without_components.contains(component_id.index()) { + query_without_components_in_entity.push(( + components.get_info(component_id).unwrap().name().to_owned(), + component_id, + )); + } else if query_with_components.contains(component_id.index()) { + query_unmatched_components.set(component_id.index(), false); + } + } + + let query_with_components_not_in_entity = query_unmatched_components + .ones() + .map(|component_id| { + let component_id = ComponentId::new(component_id); + ( + components.get_info(component_id).unwrap().name().to_owned(), + component_id, + ) + }) + .collect(); + + mismatch_details.push(QueryEntityComponentMismatch { + query_with_components_not_in_entity, + query_without_components_in_entity, + entity_archetype: entity_location.archetype_id, + query_with_components, + query_without_components, + }); + } + + mismatch_details + } } /// An error that occurs when retrieving a specific [`Entity`]'s query result from [`Query`](crate::system::Query) or [`QueryState`]. -// TODO: return the type_name as part of this error #[derive(Debug, PartialEq, Eq, Clone)] pub enum QueryEntityError { - /// The given [`Entity`]'s components do not match the query. - /// - /// Either it does not have a requested component, or it has a component which the query filters out. - QueryDoesNotMatch(&'static str, QueryEntityErrorDetails), + /// The given [`Entity`]'s components do not match the query, see [`QueryEntityMismatchDetail`]. + QueryDoesNotMatch(QueryEntityMismatchDetail, QueryEntityErrorDetail), /// The given [`Entity`] does not exist. - NoSuchEntity(QueryEntityErrorDetails), + NoSuchEntity(QueryEntityErrorDetail), /// The [`Entity`] was requested mutably more than once. /// /// See [`QueryState::get_many_mut`] for an example. - AliasedMutability(QueryEntityErrorDetails), + AliasedMutability(QueryEntityErrorDetail), } +/// Additional information in the context of a [`QueryEntityError`]. #[derive(Debug, PartialEq, Eq, Clone)] -pub struct QueryEntityErrorDetails { +pub struct QueryEntityErrorDetail { + /// Specific entity which was requested when encountering the error. pub requested_entity: Entity, + /// Representation of the query's type. pub query_type: &'static str, } +/// Diagnosis about why an [`Entity`] and a [`Query`](crate::system::Query) or [`QueryState`] don't match. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum QueryEntityMismatchDetail { + /// The entity is missing components required by the query, + /// or it has components that are filtered out by the query. + ComponentMismatch(Vec), + /// The entity has the correct components, but some of them are requested + /// with change detection ([`Added`](crate::query::Added) or [`Changed`](crate::query::Changed)) + /// and the component for that entity is not in that change state. + ChangeDetectionMismatch, +} + +/// Represents which [`Entity`]'s component do not match a [`Query`](crate::system::Query) or [`QueryState`]. +/// +/// Will be returned automatically with [`QueryEntityMismatchDetail::ComponentMismatch`], +/// or can be requested manually with [`QueryState::get_mismatch_details_unchecked`]. +/// +/// This is usually used in an array because of a query like `QueryState, With>` +/// is separated into two queries `QueryState>` and `QueryState>`, +/// in that case a single `QueryEntityMismatchDetail` represents the reasons the entity +/// doesn't match one of those "sub-queries". +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct QueryEntityComponentMismatch { + /// Components that the query requires but the entity doesn't have, + /// with a representation of their name. + pub query_with_components_not_in_entity: Vec<(String, ComponentId)>, + /// Components that the query filters out but the entity have, + /// with a representation of their name. + pub query_without_components_in_entity: Vec<(String, ComponentId)>, + /// [`Archetype`] of the entity, used to retrieve its components. + pub entity_archetype: ArchetypeId, + /// Components that the query requires. + pub query_with_components: FixedBitSet, + /// Components that the query filters out. + pub query_without_components: FixedBitSet, +} + impl std::error::Error for QueryEntityError {} impl fmt::Display for QueryEntityError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { QueryEntityError::QueryDoesNotMatch( - components, - QueryEntityErrorDetails {requested_entity, query_type} - ) => { - todo!(); - write!(f, "The given entity's components do not match the query.") - } - QueryEntityError::NoSuchEntity( - QueryEntityErrorDetails {requested_entity, ..} - ) => write!(f, "The requested entity {} does not exist.", requested_entity.index()), - QueryEntityError::AliasedMutability( - QueryEntityErrorDetails {requested_entity, ..} - ) => { - write!(f, "The entity {} was requested mutably more than once.", requested_entity.index()) + QueryEntityMismatchDetail::ComponentMismatch(_), + .., + ) => write!(f, "The given entity's components do not match the query."), + QueryEntityError::QueryDoesNotMatch( + QueryEntityMismatchDetail::ChangeDetectionMismatch, + .., + ) => write!( + f, + "The given entity's components are not in the change state required by the query." + ), + QueryEntityError::NoSuchEntity(QueryEntityErrorDetail { + requested_entity, .. + }) => write!( + f, + "The requested entity {} does not exist.", + requested_entity.index() + ), + QueryEntityError::AliasedMutability(QueryEntityErrorDetail { + requested_entity, + .. + }) => { + write!( + f, + "The entity {} was requested mutably more than once.", + requested_entity.index() + ) } } } @@ -1351,7 +1457,7 @@ impl fmt::Display for QueryEntityError { #[cfg(test)] mod tests { - use crate::{prelude::*, query::QueryEntityError, query::QueryEntityErrorDetails}; + use crate::{prelude::*, query::QueryEntityError, query::QueryEntityErrorDetail}; #[test] fn get_many_unchecked_manual_uniqueness() { @@ -1393,12 +1499,10 @@ mod tests { ) .unwrap_err() }, - QueryEntityError::AliasedMutability( - QueryEntityErrorDetails { - requested_entity: entities[0], - query_type: "QueryState", - } - ) + QueryEntityError::AliasedMutability(QueryEntityErrorDetail { + requested_entity: entities[0], + query_type: "QueryState", + }) ); assert_eq!( @@ -1413,12 +1517,10 @@ mod tests { ) .unwrap_err() }, - QueryEntityError::AliasedMutability( - QueryEntityErrorDetails { - requested_entity: entities[0], - query_type: "QueryState", - } - ) + QueryEntityError::AliasedMutability(QueryEntityErrorDetail { + requested_entity: entities[0], + query_type: "QueryState", + }) ); assert_eq!( @@ -1433,12 +1535,10 @@ mod tests { ) .unwrap_err() }, - QueryEntityError::AliasedMutability( - QueryEntityErrorDetails { - requested_entity: entities[9], - query_type: "QueryState", - } - ) + QueryEntityError::AliasedMutability(QueryEntityErrorDetail { + requested_entity: entities[9], + query_type: "QueryState", + }) ); } @@ -1475,13 +1575,15 @@ mod tests { /// An error that occurs when evaluating a [`Query`](crate::system::Query) or [`QueryState`] as a single expected result via /// [`get_single`](crate::system::Query::get_single) or [`get_single_mut`](crate::system::Query::get_single_mut). -/// -/// The `&str` inside is a string representation of the `Query` type. #[derive(Debug)] pub enum QuerySingleError { /// No entity fits the query. + /// + /// Provides a representation of the query's type. NoEntities(&'static str), /// Multiple entities fit the query. + /// + /// Provides a representation of the query's type. MultipleEntities(&'static str), } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 725c9c67a39b3..7432d85684593 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -511,7 +511,8 @@ mod tests { }, system::{ adapter::new, Commands, In, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, - QueryComponentError, QueryComponentErrorDetails, Res, ResMut, Resource, System, SystemState, + QueryComponentError, QueryComponentErrorDetails, Res, ResMut, Resource, System, + SystemState, }, world::{FromWorld, World}, }; @@ -1726,13 +1727,11 @@ mod tests { run_system(&mut world, move |q: Query<&mut W>| { let mut rq = q.to_readonly(); assert_eq!( - QueryComponentError::MissingWriteAccess( - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: "W", - query_type: "Query<'_, '_, &W>", - } - ), + QueryComponentError::MissingWriteAccess(QueryComponentErrorDetails { + requested_entity: entity, + requested_component: "W", + query_type: "Query<'_, '_, &W>", + }), rq.get_component_mut::>(entity).unwrap_err(), ); }); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 5b77b15b7b615..e0e784579728d 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -814,7 +814,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns the read-only query item for the given [`Entity`]. /// - /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead. + /// In case of a nonexisting entity or mismatched components, a [`QueryEntityError`] is returned instead. /// /// # Example /// @@ -860,9 +860,10 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns the read-only query items for the given array of [`Entity`]. /// /// The returned query items are in the same order as the input. - /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead. /// The elements of the array do not need to be unique, unlike `get_many_mut`. /// + /// In case of a nonexisting entity or mismatched components, a [`QueryEntityError`] is returned instead. + /// /// # See also /// /// - [`get_many_mut`](Self::get_many_mut) to get mutable query items. @@ -887,7 +888,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// /// # Panics /// - /// This method panics if there is a query mismatch or a non-existing entity. + /// This method panics if there are mismatched components or a non-existing entity. /// /// # Examples /// ```rust, no_run @@ -931,7 +932,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns the query item for the given [`Entity`]. /// - /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead. + /// In case of a nonexisting entity or mismatched components, a [`QueryEntityError`] is returned instead. /// /// # Example /// @@ -969,7 +970,9 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns the query items for the given array of [`Entity`]. /// /// The returned query items are in the same order as the input. - /// In case of a nonexisting entity, duplicate entities or mismatched component, a [`QueryEntityError`] is returned instead. + /// + /// In case of a nonexisting entity, duplicate entities or mismatched components, + /// a [`QueryEntityError`] is returned instead. /// /// # See also /// @@ -991,7 +994,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// /// # Panics /// - /// This method panics if there is a query mismatch, a non-existing entity, or the same `Entity` is included more than once in the array. + /// This method panics if there are mismatched components, a non-existing entity, + /// or the same `Entity` is included more than once in the array. /// /// # Examples /// @@ -1042,7 +1046,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns the query item for the given [`Entity`]. /// - /// In case of a nonexisting entity or mismatched component, a [`QueryEntityError`] is returned instead. + /// In case of a nonexisting entity or mismatched components, a [`QueryEntityError`] is returned instead. /// /// # Safety /// @@ -1064,7 +1068,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// /// In case of a nonexisting entity, if the entity doesn't have the requested component, /// or if the query doesn't have read access to this component, - /// a [`QueryEntityError`] is returned instead. + /// a [`QueryComponentError`] is returned instead. /// /// # Example /// @@ -1099,36 +1103,19 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { let entity_ref = world .get_entity(entity) .ok_or(QueryComponentError::NoSuchEntity( - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), ))?; - todo!(); - let component_id = world - .components() - .get_id(TypeId::of::()) - .ok_or(QueryComponentError::MissingComponent( - "", - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } - ))?; - - todo!(); + let component_id = world.components().get_id(TypeId::of::()).ok_or( + QueryComponentError::MissingComponent(QueryComponentErrorDetail::from::( + entity, + )), + )?; + let archetype_component = entity_ref .archetype() .get_archetype_component_id(component_id) .ok_or(QueryComponentError::MissingComponent( - "", - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), ))?; if self .state @@ -1137,22 +1124,12 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { { // SAFETY: `self.world` must have access to the component `T` for this entity, // since it was registered in `self.state`'s archetype component access set. - todo!(); unsafe { entity_ref.get::() }.ok_or(QueryComponentError::MissingComponent( - "", - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), )) } else { Err(QueryComponentError::MissingReadAccess( - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), )) } } @@ -1160,8 +1137,8 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns a mutable reference to the component `T` of the given entity. /// /// In case of a nonexisting entity, if the entity doesn't have the requested component, - /// or if the query doesn't have read access to this component, - /// a [`QueryEntityError`] is returned instead. + /// or if the query doesn't have write access to this component, + /// a [`QueryComponentError`] is returned instead. /// /// # Example /// @@ -1197,7 +1174,9 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns a mutable reference to the component `T` of the given entity. /// - /// In case of a nonexisting entity or mismatched component, a [`QueryComponentError`] is returned instead. + /// In case of a nonexisting entity, if the entity doesn't have the requested component, + /// or if the query doesn't have write access to this component, + /// a [`QueryComponentError`] is returned instead. /// /// # Safety /// @@ -1216,70 +1195,39 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { // See the comments on the `force_read_only_component_access` field for more info. if self.force_read_only_component_access { return Err(QueryComponentError::MissingWriteAccess( - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), )); } let world = self.world; let entity_ref = world .get_entity(entity) .ok_or(QueryComponentError::NoSuchEntity( - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), ))?; - todo!(); - let component_id = world - .components() - .get_id(TypeId::of::()) - .ok_or(QueryComponentError::MissingComponent( - "", - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } - ))?; - todo!(); + let component_id = world.components().get_id(TypeId::of::()).ok_or( + QueryComponentError::MissingComponent(QueryComponentErrorDetail::from::( + entity, + )), + )?; let archetype_component = entity_ref .archetype() .get_archetype_component_id(component_id) .ok_or(QueryComponentError::MissingComponent( - "", - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), ))?; if self .state .archetype_component_access .has_write(archetype_component) { - todo!(); entity_ref .get_mut_using_ticks::(self.last_run, self.this_run) .ok_or(QueryComponentError::MissingComponent( - "", - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), )) } else { Err(QueryComponentError::MissingWriteAccess( - QueryComponentErrorDetails { - requested_entity: entity, - requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), - } + QueryComponentErrorDetail::from::(entity), )) } } @@ -1535,13 +1483,19 @@ pub enum QueryComponentError { /// fn get_missing_read_access_error(query: Query<&OtherComponent>, res: Res) { /// assert_eq!( /// query.get_component::(res.entity), - /// Err(QueryComponentError::MissingReadAccess), + /// Err(QueryComponentError::MissingReadAccess( + /// QueryComponentErrorDetail { + /// requested_entity: res.entity, + /// requested_component: "RequestedComponent", + /// query_type: "bevy_ecs::system::Query<&OtherComponent>", + /// } + /// )), /// ); /// println!("query doesn't have read access to RequestedComponent because it does not appear in Query<&OtherComponent>"); /// } /// # bevy_ecs::system::assert_is_system(get_missing_read_access_error); /// ``` - MissingReadAccess(QueryComponentErrorDetails), + MissingReadAccess(QueryComponentErrorDetail), /// The [`Query`] does not have write access to the requested component. /// /// This error occurs when the requested component is not included in the original query, or the mutability of the requested component is mismatched with the original query. @@ -1562,23 +1516,33 @@ pub enum QueryComponentError { /// fn get_missing_write_access_error(mut query: Query<&RequestedComponent>, res: Res) { /// assert_eq!( /// query.get_component::(res.entity), - /// Err(QueryComponentError::MissingWriteAccess), + /// Err(QueryComponentError::MissingWriteAccess( + /// QueryComponentErrorDetail { + /// requested_entity: res.entity, + /// requested_component: "RequestedComponent", + /// query_type: "bevy_ecs::system::Query<&RequestedComponent>", + /// } + /// )), /// ); /// println!("query doesn't have write access to RequestedComponent because it doesn't have &mut in Query<&RequestedComponent>"); /// } /// # bevy_ecs::system::assert_is_system(get_missing_write_access_error); /// ``` - MissingWriteAccess(QueryComponentErrorDetails), + MissingWriteAccess(QueryComponentErrorDetail), /// The given [`Entity`] does not have the requested component. - MissingComponent(&'static str, QueryComponentErrorDetails), + MissingComponent(QueryComponentErrorDetail), /// The requested [`Entity`] does not exist. - NoSuchEntity(QueryComponentErrorDetails), + NoSuchEntity(QueryComponentErrorDetail), } +/// Additional information in the context of a [`QueryComponentError`]. #[derive(Debug, PartialEq, Eq)] -pub struct QueryComponentErrorDetails { +pub struct QueryComponentErrorDetail { + /// Specific entity which was requested when encountering the error. pub requested_entity: Entity, + /// Component which was requested when encountering the error. pub requested_component: &'static str, + /// Representation of the query's type. pub query_type: &'static str, } @@ -1587,31 +1551,33 @@ impl std::error::Error for QueryComponentError {} impl std::fmt::Display for QueryComponentError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - QueryComponentError::MissingReadAccess( - QueryComponentErrorDetails{requested_component, query_type, ..} - ) => { + QueryComponentError::MissingReadAccess(QueryComponentErrorDetail { + requested_component, + query_type, + .. + }) => { write!( f, "The query {} does not have read access to the requested component {}.", - requested_component, - query_type + requested_component, query_type ) } - QueryComponentError::MissingWriteAccess( - QueryComponentErrorDetails{requested_component, query_type, ..} - ) => { + QueryComponentError::MissingWriteAccess(QueryComponentErrorDetail { + requested_component, + query_type, + .. + }) => { write!( f, "The query {} does not have write access to the requested component {}.", - requested_component, - query_type + requested_component, query_type ) } - QueryComponentError::MissingComponent( - component, - QueryComponentErrorDetails{requested_entity, requested_component, ..} - ) => { - todo!(); + QueryComponentError::MissingComponent(QueryComponentErrorDetail { + requested_entity, + requested_component, + .. + }) => { write!( f, "The given entity {} does not have the requested component {}.", @@ -1619,9 +1585,10 @@ impl std::fmt::Display for QueryComponentError { requested_component ) } - QueryComponentError::NoSuchEntity( - QueryComponentErrorDetails{requested_entity, ..} - ) => { + QueryComponentError::NoSuchEntity(QueryComponentErrorDetail { + requested_entity, + .. + }) => { write!( f, "The requested entity {} does not exist.", @@ -1632,6 +1599,17 @@ impl std::fmt::Display for QueryComponentError { } } +impl QueryComponentErrorDetail { + /// Creates a [`QueryComponentErrorDetail`] given a [`Query`], a [`Component`] and an [`Entity`]. + pub fn from(entity: Entity) -> QueryComponentErrorDetail { + QueryComponentErrorDetail { + requested_entity: entity, + requested_component: std::any::type_name::(), + query_type: std::any::type_name::(), + } + } +} + impl<'w, 's, Q: ReadOnlyWorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> { /// Returns the query item for the given [`Entity`], with the actual "inner" world lifetime. /// From 40f5fe337fb5df38a51851c65c8c0e153d4b14e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9l=C3=A8ne=20Amanita?= Date: Tue, 4 Jul 2023 15:15:53 +0100 Subject: [PATCH 3/5] minor, fix QueryComponentErrorDetail rename --- crates/bevy_ecs/src/system/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 7432d85684593..22aa6174f41b5 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -511,7 +511,7 @@ mod tests { }, system::{ adapter::new, Commands, In, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, - QueryComponentError, QueryComponentErrorDetails, Res, ResMut, Resource, System, + QueryComponentError, QueryComponentErrorDetail, Res, ResMut, Resource, System, SystemState, }, world::{FromWorld, World}, @@ -1727,7 +1727,7 @@ mod tests { run_system(&mut world, move |q: Query<&mut W>| { let mut rq = q.to_readonly(); assert_eq!( - QueryComponentError::MissingWriteAccess(QueryComponentErrorDetails { + QueryComponentError::MissingWriteAccess(QueryComponentErrorDetail { requested_entity: entity, requested_component: "W", query_type: "Query<'_, '_, &W>", From 7e1edd18a930d0a59c61b9906b095256ca35cdd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9l=C3=A8ne=20Amanita?= Date: Tue, 4 Jul 2023 17:38:39 +0100 Subject: [PATCH 4/5] Fix tests, doc tests, QueryComponentErrorDetail::from, and CI --- crates/bevy_ecs/src/query/state.rs | 52 +++++++++++++++++++++++------ crates/bevy_ecs/src/system/mod.rs | 4 +-- crates/bevy_ecs/src/system/query.rs | 8 ++--- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 75d534cd5544e..a17761fede2ea 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -263,7 +263,7 @@ impl QueryState { /// /// ```rust /// use bevy_ecs::prelude::*; - /// use bevy_ecs::query::QueryEntityError; + /// use bevy_ecs::query::{QueryEntityError, QueryEntityErrorDetail}; /// /// #[derive(Component, PartialEq, Debug)] /// struct A(usize); @@ -282,7 +282,13 @@ impl QueryState { /// /// let wrong_entity = Entity::from_raw(365); /// - /// assert_eq!(query_state.get_many(&world, [wrong_entity]), Err(QueryEntityError::NoSuchEntity(wrong_entity))); + /// assert!(matches!( + /// query_state.get_many(&world, [wrong_entity]), + /// Err(QueryEntityError::NoSuchEntity(QueryEntityErrorDetail { + /// requested_entity: wrong_entity, + /// .. + /// })) + /// )); /// ``` #[inline] pub fn get_many<'w, const N: usize>( @@ -334,7 +340,11 @@ impl QueryState { /// /// ```rust /// use bevy_ecs::prelude::*; - /// use bevy_ecs::query::QueryEntityError; + /// use bevy_ecs::query::{ + /// QueryEntityError, + /// QueryEntityErrorDetail, + /// QueryEntityMismatchDetail, + /// }; /// /// #[derive(Component, PartialEq, Debug)] /// struct A(usize); @@ -361,9 +371,31 @@ impl QueryState { /// let wrong_entity = Entity::from_raw(57); /// let invalid_entity = world.spawn_empty().id(); /// - /// assert_eq!(query_state.get_many_mut(&mut world, [wrong_entity]).unwrap_err(), QueryEntityError::NoSuchEntity(wrong_entity)); - /// assert_eq!(query_state.get_many_mut(&mut world, [invalid_entity]).unwrap_err(), QueryEntityError::QueryDoesNotMatch(invalid_entity)); - /// assert_eq!(query_state.get_many_mut(&mut world, [entities[0], entities[0]]).unwrap_err(), QueryEntityError::AliasedMutability(entities[0])); + /// assert!(matches!( + /// query_state.get_many_mut(&mut world, [wrong_entity]).unwrap_err(), + /// QueryEntityError::NoSuchEntity(QueryEntityErrorDetail { + /// requested_entity: wrong_entity, + /// .. + /// }) + /// )); + /// assert!(matches!( + /// query_state.get_many_mut(&mut world, [invalid_entity]).unwrap_err(), + /// QueryEntityError::QueryDoesNotMatch( + /// QueryEntityMismatchDetail::ComponentMismatch(_), + /// QueryEntityErrorDetail { + /// requested_entity: invalid_entity, + /// .. + /// } + /// ) + /// )); + /// let first_entity = entities[0]; + /// assert!(matches!( + /// query_state.get_many_mut(&mut world, [entities[0], entities[0]]).unwrap_err(), + /// QueryEntityError::AliasedMutability(QueryEntityErrorDetail { + /// requested_entity: first_entity, + /// .. + /// }) + /// )); /// ``` #[inline] pub fn get_many_mut<'w, const N: usize>( @@ -1317,7 +1349,7 @@ impl QueryState { ) -> Vec { let mut mismatch_details = Vec::new(); - for components_access in self.component_access.filter_sets.iter() { + for components_access in &self.component_access.filter_sets { let query_with_components = components_access.with.clone(); let query_without_components = components_access.without.clone(); let mut query_unmatched_components = query_with_components.clone(); @@ -1500,7 +1532,7 @@ mod tests { }, QueryEntityError::AliasedMutability(QueryEntityErrorDetail { requested_entity: entities[0], - query_type: "QueryState", + query_type: "bevy_ecs::query::state::QueryState", }) ); @@ -1518,7 +1550,7 @@ mod tests { }, QueryEntityError::AliasedMutability(QueryEntityErrorDetail { requested_entity: entities[0], - query_type: "QueryState", + query_type: "bevy_ecs::query::state::QueryState", }) ); @@ -1536,7 +1568,7 @@ mod tests { }, QueryEntityError::AliasedMutability(QueryEntityErrorDetail { requested_entity: entities[9], - query_type: "QueryState", + query_type: "bevy_ecs::query::state::QueryState", }) ); } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 22aa6174f41b5..7ff8139e1a52d 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1729,8 +1729,8 @@ mod tests { assert_eq!( QueryComponentError::MissingWriteAccess(QueryComponentErrorDetail { requested_entity: entity, - requested_component: "W", - query_type: "Query<'_, '_, &W>", + requested_component: "bevy_ecs::system::tests::W", + query_type: "bevy_ecs::system::query::Query<&bevy_ecs::system::tests::W>", }), rq.get_component_mut::>(entity).unwrap_err(), ); diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index e0e784579728d..48f87750f24d2 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1467,7 +1467,7 @@ pub enum QueryComponentError { /// # Example /// /// ``` - /// # use bevy_ecs::{prelude::*, system::QueryComponentError}; + /// # use bevy_ecs::{prelude::*, system::{QueryComponentError, QueryComponentErrorDetail}}; /// # /// # #[derive(Component)] /// # struct OtherComponent; @@ -1503,7 +1503,7 @@ pub enum QueryComponentError { /// # Example /// /// ``` - /// # use bevy_ecs::{prelude::*, system::QueryComponentError}; + /// # use bevy_ecs::{prelude::*, system::{QueryComponentError, QueryComponentErrorDetail}}; /// # /// # #[derive(Component, PartialEq, Debug)] /// # struct RequestedComponent; @@ -1601,11 +1601,11 @@ impl std::fmt::Display for QueryComponentError { impl QueryComponentErrorDetail { /// Creates a [`QueryComponentErrorDetail`] given a [`Query`], a [`Component`] and an [`Entity`]. - pub fn from(entity: Entity) -> QueryComponentErrorDetail { + pub fn from(entity: Entity) -> QueryComponentErrorDetail { QueryComponentErrorDetail { requested_entity: entity, requested_component: std::any::type_name::(), - query_type: std::any::type_name::(), + query_type: std::any::type_name::(), } } } From 6d500350025831b7bc3a8ef65118a194f5e12dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9l=C3=A8ne=20Amanita?= Date: Tue, 4 Jul 2023 17:55:05 +0100 Subject: [PATCH 5/5] Fix broken link in doc --- crates/bevy_ecs/src/query/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index a17761fede2ea..621d8fdc6ebc2 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -1427,7 +1427,7 @@ pub enum QueryEntityMismatchDetail { /// Represents which [`Entity`]'s component do not match a [`Query`](crate::system::Query) or [`QueryState`]. /// /// Will be returned automatically with [`QueryEntityMismatchDetail::ComponentMismatch`], -/// or can be requested manually with [`QueryState::get_mismatch_details_unchecked`]. +/// or can be requested manually with [`QueryState::get_mismatches_detail`]. /// /// This is usually used in an array because of a query like `QueryState, With>` /// is separated into two queries `QueryState>` and `QueryState>`,