Skip to content

Commit 7092848

Browse files
track callsites for observers & hooks
1 parent cc0f6a8 commit 7092848

File tree

18 files changed

+631
-112
lines changed

18 files changed

+631
-112
lines changed

crates/bevy_core_pipeline/src/oit/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,13 @@ impl Component for OrderIndependentTransparencySettings {
7171
type Mutability = Mutable;
7272

7373
fn register_component_hooks(hooks: &mut ComponentHooks) {
74-
hooks.on_add(|world, entity, _| {
74+
hooks.on_add(|world, entity, _, caller| {
7575
if let Some(value) = world.get::<OrderIndependentTransparencySettings>(entity) {
7676
if value.layer_count > 32 {
77-
warn!("OrderIndependentTransparencySettings layer_count set to {} might be too high.", value.layer_count);
77+
warn!("{}OrderIndependentTransparencySettings layer_count set to {} might be too high.",
78+
caller.map(|location|format!("{location}: ")).unwrap_or_default(),
79+
value.layer_count
80+
);
7881
}
7982
}
8083
});

crates/bevy_ecs/src/bundle.rs

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,12 +1037,16 @@ impl<'w> BundleInserter<'w> {
10371037
ON_REPLACE,
10381038
entity,
10391039
archetype_after_insert.iter_existing(),
1040+
#[cfg(feature = "track_change_detection")]
1041+
caller,
10401042
);
10411043
}
10421044
deferred_world.trigger_on_replace(
10431045
archetype,
10441046
entity,
10451047
archetype_after_insert.iter_existing(),
1048+
#[cfg(feature = "track_change_detection")]
1049+
caller,
10461050
);
10471051
}
10481052
}
@@ -1213,12 +1217,16 @@ impl<'w> BundleInserter<'w> {
12131217
new_archetype,
12141218
entity,
12151219
archetype_after_insert.iter_added(),
1220+
#[cfg(feature = "track_change_detection")]
1221+
caller,
12161222
);
12171223
if new_archetype.has_add_observer() {
12181224
deferred_world.trigger_observers(
12191225
ON_ADD,
12201226
entity,
12211227
archetype_after_insert.iter_added(),
1228+
#[cfg(feature = "track_change_detection")]
1229+
caller,
12221230
);
12231231
}
12241232
match insert_mode {
@@ -1228,12 +1236,16 @@ impl<'w> BundleInserter<'w> {
12281236
new_archetype,
12291237
entity,
12301238
archetype_after_insert.iter_inserted(),
1239+
#[cfg(feature = "track_change_detection")]
1240+
caller,
12311241
);
12321242
if new_archetype.has_insert_observer() {
12331243
deferred_world.trigger_observers(
12341244
ON_INSERT,
12351245
entity,
12361246
archetype_after_insert.iter_inserted(),
1247+
#[cfg(feature = "track_change_detection")]
1248+
caller,
12371249
);
12381250
}
12391251
}
@@ -1244,12 +1256,16 @@ impl<'w> BundleInserter<'w> {
12441256
new_archetype,
12451257
entity,
12461258
archetype_after_insert.iter_added(),
1259+
#[cfg(feature = "track_change_detection")]
1260+
caller,
12471261
);
12481262
if new_archetype.has_insert_observer() {
12491263
deferred_world.trigger_observers(
12501264
ON_INSERT,
12511265
entity,
12521266
archetype_after_insert.iter_added(),
1267+
#[cfg(feature = "track_change_detection")]
1268+
caller,
12531269
);
12541270
}
12551271
}
@@ -1325,6 +1341,7 @@ impl<'w> BundleSpawner<'w> {
13251341
/// # Safety
13261342
/// `entity` must be allocated (but non-existent), `T` must match this [`BundleInfo`]'s type
13271343
#[inline]
1344+
#[track_caller]
13281345
pub unsafe fn spawn_non_existent<T: DynamicBundle>(
13291346
&mut self,
13301347
entity: Entity,
@@ -1372,24 +1389,32 @@ impl<'w> BundleSpawner<'w> {
13721389
archetype,
13731390
entity,
13741391
bundle_info.iter_contributed_components(),
1392+
#[cfg(feature = "track_change_detection")]
1393+
caller,
13751394
);
13761395
if archetype.has_add_observer() {
13771396
deferred_world.trigger_observers(
13781397
ON_ADD,
13791398
entity,
13801399
bundle_info.iter_contributed_components(),
1400+
#[cfg(feature = "track_change_detection")]
1401+
caller,
13811402
);
13821403
}
13831404
deferred_world.trigger_on_insert(
13841405
archetype,
13851406
entity,
13861407
bundle_info.iter_contributed_components(),
1408+
#[cfg(feature = "track_change_detection")]
1409+
caller,
13871410
);
13881411
if archetype.has_insert_observer() {
13891412
deferred_world.trigger_observers(
13901413
ON_INSERT,
13911414
entity,
13921415
bundle_info.iter_contributed_components(),
1416+
#[cfg(feature = "track_change_detection")]
1417+
caller,
13931418
);
13941419
}
13951420
};
@@ -1642,6 +1667,7 @@ fn sorted_remove<T: Eq + Ord + Copy>(source: &mut Vec<T>, remove: &[T]) {
16421667
mod tests {
16431668
use crate as bevy_ecs;
16441669
use crate::{component::ComponentId, prelude::*, world::DeferredWorld};
1670+
use core::panic::Location;
16451671

16461672
#[derive(Component)]
16471673
struct A;
@@ -1650,19 +1676,39 @@ mod tests {
16501676
#[component(on_add = a_on_add, on_insert = a_on_insert, on_replace = a_on_replace, on_remove = a_on_remove)]
16511677
struct AMacroHooks;
16521678

1653-
fn a_on_add(mut world: DeferredWorld, _: Entity, _: ComponentId) {
1679+
fn a_on_add(
1680+
mut world: DeferredWorld,
1681+
_: Entity,
1682+
_: ComponentId,
1683+
_: Option<&'static Location<'static>>,
1684+
) {
16541685
world.resource_mut::<R>().assert_order(0);
16551686
}
16561687

1657-
fn a_on_insert<T1, T2>(mut world: DeferredWorld, _: T1, _: T2) {
1688+
fn a_on_insert<T1, T2>(
1689+
mut world: DeferredWorld,
1690+
_: T1,
1691+
_: T2,
1692+
_: Option<&'static Location<'static>>,
1693+
) {
16581694
world.resource_mut::<R>().assert_order(1);
16591695
}
16601696

1661-
fn a_on_replace<T1, T2>(mut world: DeferredWorld, _: T1, _: T2) {
1697+
fn a_on_replace<T1, T2>(
1698+
mut world: DeferredWorld,
1699+
_: T1,
1700+
_: T2,
1701+
_: Option<&'static Location<'static>>,
1702+
) {
16621703
world.resource_mut::<R>().assert_order(2);
16631704
}
16641705

1665-
fn a_on_remove<T1, T2>(mut world: DeferredWorld, _: T1, _: T2) {
1706+
fn a_on_remove<T1, T2>(
1707+
mut world: DeferredWorld,
1708+
_: T1,
1709+
_: T2,
1710+
_: Option<&'static Location<'static>>,
1711+
) {
16661712
world.resource_mut::<R>().assert_order(3);
16671713
}
16681714

@@ -1695,10 +1741,10 @@ mod tests {
16951741
world.init_resource::<R>();
16961742
world
16971743
.register_component_hooks::<A>()
1698-
.on_add(|mut world, _, _| world.resource_mut::<R>().assert_order(0))
1699-
.on_insert(|mut world, _, _| world.resource_mut::<R>().assert_order(1))
1700-
.on_replace(|mut world, _, _| world.resource_mut::<R>().assert_order(2))
1701-
.on_remove(|mut world, _, _| world.resource_mut::<R>().assert_order(3));
1744+
.on_add(|mut world, _, _, _| world.resource_mut::<R>().assert_order(0))
1745+
.on_insert(|mut world, _, _, _| world.resource_mut::<R>().assert_order(1))
1746+
.on_replace(|mut world, _, _, _| world.resource_mut::<R>().assert_order(2))
1747+
.on_remove(|mut world, _, _, _| world.resource_mut::<R>().assert_order(3));
17021748

17031749
let entity = world.spawn(A).id();
17041750
world.despawn(entity);
@@ -1722,10 +1768,10 @@ mod tests {
17221768
world.init_resource::<R>();
17231769
world
17241770
.register_component_hooks::<A>()
1725-
.on_add(|mut world, _, _| world.resource_mut::<R>().assert_order(0))
1726-
.on_insert(|mut world, _, _| world.resource_mut::<R>().assert_order(1))
1727-
.on_replace(|mut world, _, _| world.resource_mut::<R>().assert_order(2))
1728-
.on_remove(|mut world, _, _| world.resource_mut::<R>().assert_order(3));
1771+
.on_add(|mut world, _, _, _| world.resource_mut::<R>().assert_order(0))
1772+
.on_insert(|mut world, _, _, _| world.resource_mut::<R>().assert_order(1))
1773+
.on_replace(|mut world, _, _, _| world.resource_mut::<R>().assert_order(2))
1774+
.on_remove(|mut world, _, _, _| world.resource_mut::<R>().assert_order(3));
17291775

17301776
let mut entity = world.spawn_empty();
17311777
entity.insert(A);
@@ -1739,8 +1785,8 @@ mod tests {
17391785
let mut world = World::new();
17401786
world
17411787
.register_component_hooks::<A>()
1742-
.on_replace(|mut world, _, _| world.resource_mut::<R>().assert_order(0))
1743-
.on_insert(|mut world, _, _| {
1788+
.on_replace(|mut world, _, _, _| world.resource_mut::<R>().assert_order(0))
1789+
.on_insert(|mut world, _, _, _| {
17441790
if let Some(mut r) = world.get_resource_mut::<R>() {
17451791
r.assert_order(1);
17461792
}
@@ -1761,22 +1807,22 @@ mod tests {
17611807
world.init_resource::<R>();
17621808
world
17631809
.register_component_hooks::<A>()
1764-
.on_add(|mut world, entity, _| {
1810+
.on_add(|mut world, entity, _, _| {
17651811
world.resource_mut::<R>().assert_order(0);
17661812
world.commands().entity(entity).insert(B);
17671813
})
1768-
.on_remove(|mut world, entity, _| {
1814+
.on_remove(|mut world, entity, _, _| {
17691815
world.resource_mut::<R>().assert_order(2);
17701816
world.commands().entity(entity).remove::<B>();
17711817
});
17721818

17731819
world
17741820
.register_component_hooks::<B>()
1775-
.on_add(|mut world, entity, _| {
1821+
.on_add(|mut world, entity, _, _| {
17761822
world.resource_mut::<R>().assert_order(1);
17771823
world.commands().entity(entity).remove::<A>();
17781824
})
1779-
.on_remove(|mut world, _, _| {
1825+
.on_remove(|mut world, _, _, _| {
17801826
world.resource_mut::<R>().assert_order(3);
17811827
});
17821828

@@ -1793,27 +1839,27 @@ mod tests {
17931839
world.init_resource::<R>();
17941840
world
17951841
.register_component_hooks::<A>()
1796-
.on_add(|mut world, entity, _| {
1842+
.on_add(|mut world, entity, _, _| {
17971843
world.resource_mut::<R>().assert_order(0);
17981844
world.commands().entity(entity).insert(B).insert(C);
17991845
});
18001846

18011847
world
18021848
.register_component_hooks::<B>()
1803-
.on_add(|mut world, entity, _| {
1849+
.on_add(|mut world, entity, _, _| {
18041850
world.resource_mut::<R>().assert_order(1);
18051851
world.commands().entity(entity).insert(D);
18061852
});
18071853

18081854
world
18091855
.register_component_hooks::<C>()
1810-
.on_add(|mut world, _, _| {
1856+
.on_add(|mut world, _, _, _| {
18111857
world.resource_mut::<R>().assert_order(3);
18121858
});
18131859

18141860
world
18151861
.register_component_hooks::<D>()
1816-
.on_add(|mut world, _, _| {
1862+
.on_add(|mut world, _, _, _| {
18171863
world.resource_mut::<R>().assert_order(2);
18181864
});
18191865

crates/bevy_ecs/src/component.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@ use bevy_ptr::{OwningPtr, UnsafeCellDeref};
1717
#[cfg(feature = "bevy_reflect")]
1818
use bevy_reflect::Reflect;
1919
use bevy_utils::{HashMap, HashSet, TypeIdMap};
20-
#[cfg(feature = "track_change_detection")]
21-
use core::panic::Location;
2220
use core::{
2321
alloc::Layout,
2422
any::{Any, TypeId},
2523
cell::UnsafeCell,
2624
fmt::Debug,
2725
marker::PhantomData,
2826
mem::needs_drop,
27+
panic::Location,
2928
};
3029
use disqualified::ShortName;
3130
use thiserror::Error;
@@ -300,6 +299,7 @@ pub use bevy_ecs_macros::require;
300299
/// # use bevy_ecs::world::DeferredWorld;
301300
/// # use bevy_ecs::entity::Entity;
302301
/// # use bevy_ecs::component::ComponentId;
302+
/// # use core::panic::Location;
303303
/// #
304304
/// #[derive(Component)]
305305
/// #[component(on_add = my_on_add_hook)]
@@ -311,12 +311,12 @@ pub use bevy_ecs_macros::require;
311311
/// // #[component(on_replace = my_on_replace_hook, on_remove = my_on_remove_hook)]
312312
/// struct ComponentA;
313313
///
314-
/// fn my_on_add_hook(world: DeferredWorld, entity: Entity, id: ComponentId) {
314+
/// fn my_on_add_hook(world: DeferredWorld, entity: Entity, id: ComponentId, caller: Option<&'static Location<'static>>) {
315315
/// // ...
316316
/// }
317317
///
318318
/// // You can also omit writing some types using generics.
319-
/// fn my_on_insert_hook<T1, T2>(world: DeferredWorld, _: T1, _: T2) {
319+
/// fn my_on_insert_hook<T1, T2>(world: DeferredWorld, _: T1, _: T2, caller: Option<&'static Location<'static>>) {
320320
/// // ...
321321
/// }
322322
/// ```
@@ -493,8 +493,10 @@ pub enum StorageType {
493493
SparseSet,
494494
}
495495

496-
/// The type used for [`Component`] lifecycle hooks such as `on_add`, `on_insert` or `on_remove`
497-
pub type ComponentHook = for<'w> fn(DeferredWorld<'w>, Entity, ComponentId);
496+
/// The type used for [`Component`] lifecycle hooks such as `on_add`, `on_insert` or `on_remove`.
497+
/// The caller location is `Some` if the `track_change_detection` feature is enabled.
498+
pub type ComponentHook =
499+
for<'w> fn(DeferredWorld<'w>, Entity, ComponentId, Option<&'static Location<'static>>);
498500

499501
/// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`].
500502
///
@@ -531,12 +533,12 @@ pub type ComponentHook = for<'w> fn(DeferredWorld<'w>, Entity, ComponentId);
531533
/// let mut tracked_component_query = world.query::<&MyTrackedComponent>();
532534
/// assert!(tracked_component_query.iter(&world).next().is_none());
533535
///
534-
/// world.register_component_hooks::<MyTrackedComponent>().on_add(|mut world, entity, _component_id| {
536+
/// world.register_component_hooks::<MyTrackedComponent>().on_add(|mut world, entity, _component_id, _caller| {
535537
/// let mut tracked_entities = world.resource_mut::<TrackedEntities>();
536538
/// tracked_entities.0.insert(entity);
537539
/// });
538540
///
539-
/// world.register_component_hooks::<MyTrackedComponent>().on_remove(|mut world, entity, _component_id| {
541+
/// world.register_component_hooks::<MyTrackedComponent>().on_remove(|mut world, entity, _component_id, _caller| {
540542
/// let mut tracked_entities = world.resource_mut::<TrackedEntities>();
541543
/// tracked_entities.0.remove(&entity);
542544
/// });

crates/bevy_ecs/src/entity/clone_entities.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub struct EntityCloner {
2323

2424
impl EntityCloner {
2525
/// Clones and inserts components from the `source` entity into `target` entity using the stored configuration.
26+
#[track_caller]
2627
pub fn clone_entity(&mut self, world: &mut World) {
2728
let source_entity = world
2829
.get_entity(self.source)

crates/bevy_ecs/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,8 +1983,8 @@ mod tests {
19831983
world.insert_resource(I(0));
19841984
world
19851985
.register_component_hooks::<Y>()
1986-
.on_add(|mut world, _, _| world.resource_mut::<A>().0 += 1)
1987-
.on_insert(|mut world, _, _| world.resource_mut::<I>().0 += 1);
1986+
.on_add(|mut world, _, _, _| world.resource_mut::<A>().0 += 1)
1987+
.on_insert(|mut world, _, _, _| world.resource_mut::<I>().0 += 1);
19881988

19891989
// Spawn entity and ensure Y was added
19901990
assert!(world.spawn(X).contains::<Y>());
@@ -2013,8 +2013,8 @@ mod tests {
20132013
world.insert_resource(I(0));
20142014
world
20152015
.register_component_hooks::<Y>()
2016-
.on_add(|mut world, _, _| world.resource_mut::<A>().0 += 1)
2017-
.on_insert(|mut world, _, _| world.resource_mut::<I>().0 += 1);
2016+
.on_add(|mut world, _, _, _| world.resource_mut::<A>().0 += 1)
2017+
.on_insert(|mut world, _, _, _| world.resource_mut::<I>().0 += 1);
20182018

20192019
// Spawn entity and ensure Y was added
20202020
assert!(world.spawn_empty().insert(X).contains::<Y>());

crates/bevy_ecs/src/observer/entity_observer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ impl Component for ObservedBy {
1414
type Mutability = Mutable;
1515

1616
fn register_component_hooks(hooks: &mut ComponentHooks) {
17-
hooks.on_remove(|mut world, entity, _| {
17+
hooks.on_remove(|mut world, entity, _, _| {
1818
let observed_by = {
1919
let mut component = world.get_mut::<ObservedBy>(entity).unwrap();
2020
core::mem::take(&mut component.0)

0 commit comments

Comments
 (0)