B0003: Print caller (#14556)

# Objective

B0003 indicates that you tried to act upon a nonexistant entity, but
does not mention where the error occured:
```
2024-07-31T15:46:25.954840Z  WARN bevy_ecs::world: error[B0003]: Could not despawn entity Entity { index: 4294967295, generation: 1 } because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003
```

## Solution

Include caller location:

```
2024-07-31T15:46:25.954840Z  WARN bevy_ecs::world: error[B0003]: src/main.rs:18:11: Could not despawn entity Entity { index: 4294967295, generation: 1 } because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003
```

Open question: What should the exact message format be?

## Testing

None, this doesn't change any logic.
This commit is contained in:
SpecificProtagonist 2024-08-01 02:14:48 +02:00 committed by GitHub
parent d5ccb096b5
commit 0685d2da4d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 24 additions and 9 deletions

View file

@ -1,6 +1,5 @@
mod parallel_scope; mod parallel_scope;
#[cfg(feature = "track_change_detection")]
use core::panic::Location; use core::panic::Location;
use super::{Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource}; use super::{Deferred, IntoObserverSystem, IntoSystem, RegisterSystem, Resource};
@ -963,14 +962,16 @@ impl EntityCommands<'_> {
/// ///
/// - [`ComponentId`] must be from the same world as `self`. /// - [`ComponentId`] must be from the same world as `self`.
/// - `T` must have the same layout as the one passed during `component_id` creation. /// - `T` must have the same layout as the one passed during `component_id` creation.
#[track_caller]
pub unsafe fn insert_by_id<T: Send + 'static>( pub unsafe fn insert_by_id<T: Send + 'static>(
&mut self, &mut self,
component_id: ComponentId, component_id: ComponentId,
value: T, value: T,
) -> &mut Self { ) -> &mut Self {
let caller = Location::caller();
// SAFETY: same invariants as parent call // SAFETY: same invariants as parent call
self.add(unsafe {insert_by_id(component_id, value, move |entity| { self.add(unsafe {insert_by_id(component_id, value, move |entity| {
panic!("error[B0003]: Could not insert a component {component_id:?} (with type {}) for entity {entity:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/#b0003", std::any::type_name::<T>()); panic!("error[B0003]: {caller}: Could not insert a component {component_id:?} (with type {}) for entity {entity:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/#b0003", std::any::type_name::<T>());
})}); })});
self self
} }
@ -1125,8 +1126,9 @@ impl EntityCommands<'_> {
/// } /// }
/// # bevy_ecs::system::assert_is_system(remove_character_system); /// # bevy_ecs::system::assert_is_system(remove_character_system);
/// ``` /// ```
#[track_caller]
pub fn despawn(&mut self) { pub fn despawn(&mut self) {
self.add(despawn); self.add(despawn());
} }
/// Pushes an [`EntityCommand`] to the queue, which will get executed for the current [`Entity`]. /// Pushes an [`EntityCommand`] to the queue, which will get executed for the current [`Entity`].
@ -1300,14 +1302,17 @@ where
/// ///
/// This won't clean up external references to the entity (such as parent-child relationships /// This won't clean up external references to the entity (such as parent-child relationships
/// if you're using `bevy_hierarchy`), which may leave the world in an invalid state. /// if you're using `bevy_hierarchy`), which may leave the world in an invalid state.
fn despawn(entity: Entity, world: &mut World) { #[track_caller]
world.despawn(entity); fn despawn() -> impl EntityCommand {
let caller = Location::caller();
move |entity: Entity, world: &mut World| {
world.despawn_with_caller(entity, caller);
}
} }
/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity. /// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.
#[track_caller] #[track_caller]
fn insert<T: Bundle>(bundle: T) -> impl EntityCommand { fn insert<T: Bundle>(bundle: T) -> impl EntityCommand {
#[cfg(feature = "track_change_detection")]
let caller = core::panic::Location::caller(); let caller = core::panic::Location::caller();
move |entity: Entity, world: &mut World| { move |entity: Entity, world: &mut World| {
if let Some(mut entity) = world.get_entity_mut(entity) { if let Some(mut entity) = world.get_entity_mut(entity) {
@ -1317,7 +1322,7 @@ fn insert<T: Bundle>(bundle: T) -> impl EntityCommand {
caller, caller,
); );
} else { } else {
panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003", std::any::type_name::<T>(), entity); panic!("error[B0003]: {caller}: Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003", std::any::type_name::<T>(), entity);
} }
} }
} }

View file

@ -55,7 +55,7 @@ use std::{
#[cfg(feature = "track_change_detection")] #[cfg(feature = "track_change_detection")]
use bevy_ptr::UnsafeCellDeref; use bevy_ptr::UnsafeCellDeref;
#[cfg(feature = "track_change_detection")]
use core::panic::Location; use core::panic::Location;
use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}; use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell};
@ -1121,14 +1121,24 @@ impl World {
/// assert!(world.get_entity(entity).is_none()); /// assert!(world.get_entity(entity).is_none());
/// assert!(world.get::<Position>(entity).is_none()); /// assert!(world.get::<Position>(entity).is_none());
/// ``` /// ```
#[track_caller]
#[inline] #[inline]
pub fn despawn(&mut self, entity: Entity) -> bool { pub fn despawn(&mut self, entity: Entity) -> bool {
self.despawn_with_caller(entity, Location::caller())
}
#[inline]
pub(crate) fn despawn_with_caller(
&mut self,
entity: Entity,
caller: &'static Location,
) -> bool {
self.flush(); self.flush();
if let Some(entity) = self.get_entity_mut(entity) { if let Some(entity) = self.get_entity_mut(entity) {
entity.despawn(); entity.despawn();
true true
} else { } else {
warn!("error[B0003]: Could not despawn entity {:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003", entity); warn!("error[B0003]: {caller}: Could not despawn entity {:?} because it doesn't exist in this World. See: https://bevyengine.org/learn/errors/b0003", entity);
false false
} }
} }