From ec9c4e81f3f6f120709f7e4aa9d1cb0453caf989 Mon Sep 17 00:00:00 2001 From: janis Date: Thu, 11 Sep 2025 21:23:48 +0200 Subject: [PATCH 1/5] warning when systemset contains no sets when building schedule graph --- crates/bevy_ecs/src/schedule/error.rs | 22 ++++++++++++++++++++++ crates/bevy_ecs/src/schedule/mod.rs | 21 +++++++++++++++++++++ crates/bevy_ecs/src/schedule/schedule.rs | 17 +++++++++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/error.rs b/crates/bevy_ecs/src/schedule/error.rs index ec008e9c3fa69..68b32b3571945 100644 --- a/crates/bevy_ecs/src/schedule/error.rs +++ b/crates/bevy_ecs/src/schedule/error.rs @@ -68,6 +68,17 @@ pub enum ScheduleBuildWarning { /// [`LogLevel::Error`]: crate::schedule::LogLevel::Error #[error("Systems with conflicting access have indeterminate run order: {0:?}")] Ambiguity(Vec<(SystemKey, SystemKey, Vec)>), + /// The system set contains no systems. + /// + /// This warning is **enabled** by default, but can be disabled by setting + /// [`ScheduleBuildSettings::hierarchy_detection`] to [`LogLevel::Ignore`] + /// or upgraded to a [`ScheduleBuildError`] by setting it to [`LogLevel::Error`]. + /// + /// [`ScheduleBuildSettings::hierarchy_detection`]: crate::schedule::ScheduleBuildSettings::hierarchy_detection + /// [`LogLevel::Ignore`]: crate::schedule::LogLevel::Ignore + /// [`LogLevel::Error`]: crate::schedule::LogLevel::Error + #[error("The system set contains no systems: {0:?}")] + EmptySet(NodeId), } impl ScheduleBuildError { @@ -156,6 +167,14 @@ impl ScheduleBuildError { message } + fn empty_set_to_string(node_id: &NodeId, graph: &ScheduleGraph) -> String { + format!( + "{} `{}` contains no systems", + node_id.kind(), + graph.get_node_name(node_id) + ) + } + fn dependency_loop_to_string(node_id: &NodeId, graph: &ScheduleGraph) -> String { format!( "{} `{}` has been told to run before itself", @@ -256,6 +275,9 @@ impl ScheduleBuildWarning { ScheduleBuildWarning::Ambiguity(ambiguities) => { ScheduleBuildError::ambiguity_to_string(ambiguities, graph, world.components()) } + ScheduleBuildWarning::EmptySet(node_id) => { + ScheduleBuildError::empty_set_to_string(node_id, graph) + } } } } diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index f5a5bb5bd9833..d45a938dd89e6 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -709,6 +709,27 @@ mod tests { )); } + #[test] + fn empty_set_check() { + let mut world = World::new(); + let mut schedule = Schedule::default(); + + schedule.set_build_settings(ScheduleBuildSettings { + empty_set_detection: true, + ..Default::default() + }); + + // Add `A`. + schedule.configure_sets(TestSystems::A); + + // schedule.configure_sets((TestSystems::B,TestSystems::C).chain()); + + _ = schedule.initialize(&mut world); + let warnings = schedule.warnings(); + + assert!(matches!(warnings[0], ScheduleBuildWarning::EmptySet(_))); + } + #[test] fn cross_dependency() { let mut world = World::new(); diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 7bf509f6fd8cb..a5902b9df60dc 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1000,8 +1000,11 @@ impl ScheduleGraph { // map all system sets to their systems // go in reverse topological order (bottom-up) for efficiency - let (set_systems, set_system_bitsets) = - self.map_sets_to_systems(&self.hierarchy.topsort, &self.hierarchy.graph); + let (set_systems, set_system_bitsets) = self.map_sets_to_systems( + &self.hierarchy.topsort, + &self.hierarchy.graph, + &mut warnings, + ); self.check_order_but_intersect(&dep_results.connected, &set_system_bitsets)?; // check that there are no edges to system-type sets that have multiple instances @@ -1058,6 +1061,7 @@ impl ScheduleGraph { &self, hierarchy_topsort: &[NodeId], hierarchy_graph: &DiGraph, + warnings: &mut Vec, ) -> ( HashMap>, HashMap>, @@ -1089,6 +1093,10 @@ impl ScheduleGraph { } } + if self.settings.empty_set_detection && systems.is_empty() { + warnings.push(ScheduleBuildWarning::EmptySet(id)); + } + set_systems.insert(set_key, systems); set_system_sets.insert(set_key, system_set); } @@ -1719,6 +1727,10 @@ pub struct ScheduleBuildSettings { /// /// Defaults to [`LogLevel::Warn`]. pub hierarchy_detection: LogLevel, + /// If set to `true`, warnings will be emitted if any system set contains no systems. + /// + /// Defaults to `true`. + pub empty_set_detection: bool, /// Auto insert [`ApplyDeferred`] systems into the schedule, /// when there are [`Deferred`](crate::prelude::Deferred) /// in one system and there are ordering dependencies on that system. [`Commands`](crate::system::Commands) is one @@ -1752,6 +1764,7 @@ impl ScheduleBuildSettings { Self { ambiguity_detection: LogLevel::Ignore, hierarchy_detection: LogLevel::Warn, + empty_set_detection: true, auto_insert_apply_deferred: true, use_shortnames: true, report_sets: true, From 9a0b86eb39dbd058e8ef9be0d082869d1057aa09 Mon Sep 17 00:00:00 2001 From: janis Date: Thu, 11 Sep 2025 22:03:12 +0200 Subject: [PATCH 2/5] few more tests :) --- crates/bevy_ecs/src/schedule/mod.rs | 44 +++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index d45a938dd89e6..63d50a6ffecce 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -710,7 +710,7 @@ mod tests { } #[test] - fn empty_set_check() { + fn empty_set() { let mut world = World::new(); let mut schedule = Schedule::default(); @@ -722,7 +722,47 @@ mod tests { // Add `A`. schedule.configure_sets(TestSystems::A); - // schedule.configure_sets((TestSystems::B,TestSystems::C).chain()); + _ = schedule.initialize(&mut world); + let warnings = schedule.warnings(); + + assert!(matches!(warnings[0], ScheduleBuildWarning::EmptySet(_))); + } + + #[test] + fn empty_set_with_edges() { + let mut world = World::new(); + let mut schedule = Schedule::default(); + + schedule.set_build_settings(ScheduleBuildSettings { + empty_set_detection: true, + ..Default::default() + }); + + // Add `A`. + schedule.configure_sets(TestSystems::A.after(TestSystems::B)); + + _ = schedule.initialize(&mut world); + let warnings = schedule.warnings(); + + assert!(matches!(warnings[0], ScheduleBuildWarning::EmptySet(_))); + } + + #[test] + fn empty_set_with_condition() { + let mut world = World::new(); + let mut schedule = Schedule::default(); + + schedule.set_build_settings(ScheduleBuildSettings { + empty_set_detection: true, + ..Default::default() + }); + + fn true_condition() -> bool { + true + } + + // Add `A`. + schedule.configure_sets(TestSystems::A.run_if(true_condition)); _ = schedule.initialize(&mut world); let warnings = schedule.warnings(); From a288849683fafae1506e65f565f76d26aedc68ad Mon Sep 17 00:00:00 2001 From: janis Date: Fri, 12 Sep 2025 18:43:51 +0200 Subject: [PATCH 3/5] fix EmptySet error docs --- crates/bevy_ecs/src/schedule/error.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/error.rs b/crates/bevy_ecs/src/schedule/error.rs index 68b32b3571945..728b6682cb4e8 100644 --- a/crates/bevy_ecs/src/schedule/error.rs +++ b/crates/bevy_ecs/src/schedule/error.rs @@ -71,12 +71,9 @@ pub enum ScheduleBuildWarning { /// The system set contains no systems. /// /// This warning is **enabled** by default, but can be disabled by setting - /// [`ScheduleBuildSettings::hierarchy_detection`] to [`LogLevel::Ignore`] - /// or upgraded to a [`ScheduleBuildError`] by setting it to [`LogLevel::Error`]. + /// [`ScheduleBuildSettings::empty_set_detection`] to `false`]. /// - /// [`ScheduleBuildSettings::hierarchy_detection`]: crate::schedule::ScheduleBuildSettings::hierarchy_detection - /// [`LogLevel::Ignore`]: crate::schedule::LogLevel::Ignore - /// [`LogLevel::Error`]: crate::schedule::LogLevel::Error + /// [`ScheduleBuildSettings::empty_set_detection`]: crate::schedule::ScheduleBuildSettings::empty_set_detection #[error("The system set contains no systems: {0:?}")] EmptySet(NodeId), } From 74e1baa5b4008ca4c019ce04fdfeff9f3dfb2904 Mon Sep 17 00:00:00 2001 From: janis Date: Fri, 12 Sep 2025 18:56:38 +0200 Subject: [PATCH 4/5] test for set with system --- crates/bevy_ecs/src/schedule/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 63d50a6ffecce..21aebb39fb408 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -770,6 +770,27 @@ mod tests { assert!(matches!(warnings[0], ScheduleBuildWarning::EmptySet(_))); } + #[test] + fn empty_set_with_system() { + let mut world = World::new(); + let mut schedule = Schedule::default(); + + schedule.set_build_settings(ScheduleBuildSettings { + empty_set_detection: true, + ..Default::default() + }); + + fn system() {} + + // Add `A`. + schedule.add_systems(system.in_set(TestSystems::A)); + + _ = schedule.initialize(&mut world); + let warnings = schedule.warnings(); + + assert!(warnings.is_empty()); + } + #[test] fn cross_dependency() { let mut world = World::new(); From 8ba32f8ac21790b0019c57446e0aeb158e3c90c2 Mon Sep 17 00:00:00 2001 From: janis Date: Fri, 12 Sep 2025 22:09:04 +0200 Subject: [PATCH 5/5] disable empty set detection by default --- crates/bevy_ecs/src/schedule/error.rs | 4 ++-- crates/bevy_ecs/src/schedule/schedule.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/error.rs b/crates/bevy_ecs/src/schedule/error.rs index 728b6682cb4e8..cd5e909dfeb9d 100644 --- a/crates/bevy_ecs/src/schedule/error.rs +++ b/crates/bevy_ecs/src/schedule/error.rs @@ -70,8 +70,8 @@ pub enum ScheduleBuildWarning { Ambiguity(Vec<(SystemKey, SystemKey, Vec)>), /// The system set contains no systems. /// - /// This warning is **enabled** by default, but can be disabled by setting - /// [`ScheduleBuildSettings::empty_set_detection`] to `false`]. + /// This warning is **disabled** by default, but can be enabled by setting + /// [`ScheduleBuildSettings::empty_set_detection`] to `true`]. /// /// [`ScheduleBuildSettings::empty_set_detection`]: crate::schedule::ScheduleBuildSettings::empty_set_detection #[error("The system set contains no systems: {0:?}")] diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index a5902b9df60dc..cc92deebaa540 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -1764,7 +1764,7 @@ impl ScheduleBuildSettings { Self { ambiguity_detection: LogLevel::Ignore, hierarchy_detection: LogLevel::Warn, - empty_set_detection: true, + empty_set_detection: false, auto_insert_apply_deferred: true, use_shortnames: true, report_sets: true,