diff --git a/Cargo.toml b/Cargo.toml index 6386d902d0..7dc0d8d6e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" repository = "https://github.com/bevyengine/bevy" [workspace] -exclude = ["benches"] +exclude = ["benches", "crates/bevy_ecs_compile_fail_tests"] members = ["crates/*", "examples/ios", "tools/ci", "errors"] [features] @@ -47,7 +47,7 @@ bevy_audio = ["bevy_internal/bevy_audio"] bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"] bevy_gilrs = ["bevy_internal/bevy_gilrs"] bevy_gltf = ["bevy_internal/bevy_gltf"] -bevy_wgpu = ["bevy_internal/bevy_wgpu"] +bevy_wgpu = ["bevy_internal/bevy_wgpu"] bevy_winit = ["bevy_internal/bevy_winit"] trace_chrome = ["bevy_internal/trace_chrome"] diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index a123d871b3..dab9815d6d 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -786,172 +786,3 @@ mod tests { } } } - -/// ```compile_fail E0499 -/// use bevy_ecs::prelude::*; -/// #[derive(Component)] -/// struct A(usize); -/// fn system(mut query: Query<&mut A>, e: Res) { -/// let mut iter = query.iter_mut(); -/// let a = &mut *iter.next().unwrap(); -/// -/// let mut iter2 = query.iter_mut(); -/// let b = &mut *iter2.next().unwrap(); -/// -/// // this should fail to compile -/// println!("{}", a.0); -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_query_iter_lifetime_safety_test() {} - -/// ```compile_fail E0499 -/// use bevy_ecs::prelude::*; -/// #[derive(Component)] -/// struct A(usize); -/// fn system(mut query: Query<&mut A>, e: Res) { -/// let mut a1 = query.get_mut(*e).unwrap(); -/// let mut a2 = query.get_mut(*e).unwrap(); -/// // this should fail to compile -/// println!("{} {}", a1.0, a2.0); -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_query_get_lifetime_safety_test() {} - -/// ```compile_fail E0499 -/// use bevy_ecs::prelude::*; -/// #[derive(Component)] -/// struct A(usize); -/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { -/// let mut q2 = queries.q0(); -/// let mut iter2 = q2.iter_mut(); -/// let mut b = iter2.next().unwrap(); -/// -/// let q1 = queries.q1(); -/// let mut iter = q1.iter(); -/// let a = &*iter.next().unwrap(); -/// -/// // this should fail to compile -/// b.0 = a.0 -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_query_set_iter_lifetime_safety_test() {} - -/// ```compile_fail E0499 -/// use bevy_ecs::prelude::*; -/// #[derive(Component)] -/// struct A(usize); -/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { -/// let q1 = queries.q1(); -/// let mut iter = q1.iter(); -/// let a = &*iter.next().unwrap(); -/// -/// let mut q2 = queries.q0(); -/// let mut iter2 = q2.iter_mut(); -/// let mut b = iter2.next().unwrap(); -/// -/// // this should fail to compile -/// b.0 = a.0; -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_query_set_iter_flip_lifetime_safety_test() {} - -/// ```compile_fail E0499 -/// use bevy_ecs::prelude::*; -/// #[derive(Component)] -/// struct A(usize); -/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { -/// let mut q2 = queries.q0(); -/// let mut b = q2.get_mut(*e).unwrap(); -/// -/// let q1 = queries.q1(); -/// let a = q1.get(*e).unwrap(); -/// -/// // this should fail to compile -/// b.0 = a.0 -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_query_set_get_lifetime_safety_test() {} - -/// ```compile_fail E0499 -/// use bevy_ecs::prelude::*; -/// #[derive(Component)] -/// struct A(usize); -/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { -/// let q1 = queries.q1(); -/// let a = q1.get(*e).unwrap(); -/// -/// let mut q2 = queries.q0(); -/// let mut b = q2.get_mut(*e).unwrap(); -/// // this should fail to compile -/// b.0 = a.0 -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_query_set_get_flip_lifetime_safety_test() {} - -/// ```compile_fail E0502 -/// use bevy_ecs::prelude::*; -/// use bevy_ecs::system::SystemState; -/// #[derive(Component)] -/// struct A(usize); -/// #[derive(Component)] -/// struct B(usize); -/// struct State { -/// state_r: SystemState>, -/// state_w: SystemState>, -/// } -/// -/// impl State { -/// fn get_component<'w>(&mut self, world: &'w mut World, entity: Entity) { -/// let q1 = self.state_r.get(&world); -/// let a1 = q1.get(entity).unwrap(); -/// -/// let mut q2 = self.state_w.get_mut(world); -/// let a2 = q2.get_mut(entity).unwrap(); -/// -/// // this should fail to compile -/// println!("{}", a1.0); -/// } -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_state_get_lifetime_safety_test() {} - -/// ```compile_fail E0502 -/// use bevy_ecs::prelude::*; -/// use bevy_ecs::system::SystemState; -/// #[derive(Component)] -/// struct A(usize); -/// #[derive(Component)] -/// struct B(usize); -/// struct State { -/// state_r: SystemState>, -/// state_w: SystemState>, -/// } -/// -/// impl State { -/// fn get_components<'w>(&mut self, world: &'w mut World) { -/// let q1 = self.state_r.get(&world); -/// let a1 = q1.iter().next().unwrap(); -/// let mut q2 = self.state_w.get_mut(world); -/// let a2 = q2.iter_mut().next().unwrap(); -/// // this should fail to compile -/// println!("{}", a1.0); -/// } -/// } -/// ``` -#[allow(unused)] -#[cfg(doctest)] -fn system_state_iter_lifetime_safety_test() {} diff --git a/crates/bevy_ecs_compile_fail_tests/Cargo.toml b/crates/bevy_ecs_compile_fail_tests/Cargo.toml new file mode 100644 index 0000000000..eed9fca6c8 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "bevy_ecs_compile_fail_tests" +version = "0.5.0" +edition = "2021" +description = "Compile fail tests for Bevy Engine's entity component system" +homepage = "https://bevyengine.org" +repository = "https://github.com/bevyengine/bevy" +license = "MIT OR Apache-2.0" +publish = false + +[dev-dependencies] +bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" } +trybuild = "1.0" diff --git a/crates/bevy_ecs_compile_fail_tests/README.md b/crates/bevy_ecs_compile_fail_tests/README.md new file mode 100644 index 0000000000..97b10fa3e8 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/README.md @@ -0,0 +1,5 @@ +# Compile fail tests for bevy_ecs + +This crate is separate from `bevy_ecs` and not part of the Bevy workspace in order to not fail `crater` tests for Bevy. The tests assert on the exact compiler errors and can easily fail for new Rust versions due to updated compiler errors (e.g. changes in spans). + +The `CI` workflow executes these tests on the stable rust toolchain (see [tools/ci](../../tools/ci/src/main.rs)). diff --git a/crates/bevy_ecs_compile_fail_tests/src/lib.rs b/crates/bevy_ecs_compile_fail_tests/src/lib.rs new file mode 100644 index 0000000000..d0d1683dd6 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/src/lib.rs @@ -0,0 +1 @@ +// Nothing here, check out the integration tests diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui.rs new file mode 100644 index 0000000000..01b6a62ddb --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui.rs @@ -0,0 +1,5 @@ +#[test] +fn test() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); +} diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.rs new file mode 100644 index 0000000000..cc778bff7b --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.rs @@ -0,0 +1,13 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(usize); + +fn system(mut query: Query<&mut A>, e: Res) { + let a1 = query.get_mut(*e).unwrap(); + let a2 = query.get_mut(*e).unwrap(); + // this should fail to compile + println!("{} {}", a1.0, a2.0); +} + +fn main() {} diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.stderr new file mode 100644 index 0000000000..dfafdb7bc8 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_get_lifetime_safety.stderr @@ -0,0 +1,10 @@ +error[E0499]: cannot borrow `query` as mutable more than once at a time + --> tests/ui/system_query_get_lifetime_safety.rs:8:14 + | +7 | let a1 = query.get_mut(*e).unwrap(); + | ----- first mutable borrow occurs here +8 | let a2 = query.get_mut(*e).unwrap(); + | ^^^^^ second mutable borrow occurs here +9 | // this should fail to compile +10 | println!("{} {}", a1.0, a2.0); + | -- first borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_lifetime_safety.rs new file mode 100644 index 0000000000..6796fab5fc --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_lifetime_safety.rs @@ -0,0 +1,17 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(usize); + +fn system(mut query: Query<&mut A>) { + let mut iter = query.iter_mut(); + let a = &mut *iter.next().unwrap(); + + let mut iter2 = query.iter_mut(); + let _ = &mut *iter2.next().unwrap(); + + // this should fail to compile + println!("{}", a.0); +} + +fn main() {} diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_lifetime_safety.stderr new file mode 100644 index 0000000000..85b480ddd5 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_iter_lifetime_safety.stderr @@ -0,0 +1,11 @@ +error[E0499]: cannot borrow `query` as mutable more than once at a time + --> tests/ui/system_query_iter_lifetime_safety.rs:10:21 + | +7 | let mut iter = query.iter_mut(); + | ----- first mutable borrow occurs here +... +10 | let mut iter2 = query.iter_mut(); + | ^^^^^ second mutable borrow occurs here +... +14 | println!("{}", a.0); + | --- first borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.rs new file mode 100644 index 0000000000..0fff26700d --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.rs @@ -0,0 +1,28 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(usize); + +fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { + let mut q2 = queries.q0(); + let mut b = q2.get_mut(*e).unwrap(); + + let q1 = queries.q1(); + let a = q1.get(*e).unwrap(); + + // this should fail to compile + b.0 = a.0 +} + +fn query_set_flip(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { + let q1 = queries.q1(); + let a = q1.get(*e).unwrap(); + + let mut q2 = queries.q0(); + let mut b = q2.get_mut(*e).unwrap(); + + // this should fail to compile + b.0 = a.0 +} + +fn main() {} diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.stderr new file mode 100644 index 0000000000..2be23fa1aa --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_get_lifetime_safety.stderr @@ -0,0 +1,23 @@ +error[E0499]: cannot borrow `queries` as mutable more than once at a time + --> tests/ui/system_query_set_get_lifetime_safety.rs:10:14 + | +7 | let mut q2 = queries.q0(); + | ------- first mutable borrow occurs here +... +10 | let q1 = queries.q1(); + | ^^^^^^^ second mutable borrow occurs here +... +14 | b.0 = a.0 + | - first borrow later used here + +error[E0499]: cannot borrow `queries` as mutable more than once at a time + --> tests/ui/system_query_set_get_lifetime_safety.rs:21:18 + | +18 | let q1 = queries.q1(); + | ------- first mutable borrow occurs here +... +21 | let mut q2 = queries.q0(); + | ^^^^^^^ second mutable borrow occurs here +... +25 | b.0 = a.0 + | --- first borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_iter_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_iter_lifetime_safety.rs new file mode 100644 index 0000000000..4207b8ccd5 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_iter_lifetime_safety.rs @@ -0,0 +1,32 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(usize); + +fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>) { + let mut q2 = queries.q0(); + let mut iter2 = q2.iter_mut(); + let mut b = iter2.next().unwrap(); + + let q1 = queries.q1(); + let mut iter = q1.iter(); + let a = &*iter.next().unwrap(); + + // this should fail to compile + b.0 = a.0 +} + +fn query_set_flip(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>) { + let q1 = queries.q1(); + let mut iter = q1.iter(); + let a = &*iter.next().unwrap(); + + let mut q2 = queries.q0(); + let mut iter2 = q2.iter_mut(); + let mut b = iter2.next().unwrap(); + + // this should fail to compile + b.0 = a.0; +} + +fn main() {} diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_iter_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_iter_lifetime_safety.stderr new file mode 100644 index 0000000000..7e5f211d74 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_query_set_iter_lifetime_safety.stderr @@ -0,0 +1,23 @@ +error[E0499]: cannot borrow `queries` as mutable more than once at a time + --> tests/ui/system_query_set_iter_lifetime_safety.rs:11:14 + | +7 | let mut q2 = queries.q0(); + | ------- first mutable borrow occurs here +... +11 | let q1 = queries.q1(); + | ^^^^^^^ second mutable borrow occurs here +... +16 | b.0 = a.0 + | - first borrow later used here + +error[E0499]: cannot borrow `queries` as mutable more than once at a time + --> tests/ui/system_query_set_iter_lifetime_safety.rs:24:18 + | +20 | let q1 = queries.q1(); + | ------- first mutable borrow occurs here +... +24 | let mut q2 = queries.q0(); + | ^^^^^^^ second mutable borrow occurs here +... +29 | b.0 = a.0; + | --- first borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_get_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_get_lifetime_safety.rs new file mode 100644 index 0000000000..ec3b3d79c1 --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_get_lifetime_safety.rs @@ -0,0 +1,28 @@ +use bevy_ecs::prelude::*; +use bevy_ecs::system::SystemState; + +#[derive(Component)] +struct A(usize); + +#[derive(Component)] +struct B(usize); + +struct State { + state_r: SystemState>, + state_w: SystemState>, +} + +impl State { + fn get_component(&mut self, world: &mut World, entity: Entity) { + let q1 = self.state_r.get(&world); + let a1 = q1.get(entity).unwrap(); + + let mut q2 = self.state_w.get_mut(world); + let _ = q2.get_mut(entity).unwrap(); + + // this should fail to compile + println!("{}", a1.0); + } +} + +fn main() {} diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_get_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_get_lifetime_safety.stderr new file mode 100644 index 0000000000..fcad3cff1b --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_get_lifetime_safety.stderr @@ -0,0 +1,11 @@ +error[E0502]: cannot borrow `*world` as mutable because it is also borrowed as immutable + --> tests/ui/system_state_get_lifetime_safety.rs:20:22 + | +17 | let q1 = self.state_r.get(&world); + | ------ immutable borrow occurs here +... +20 | let mut q2 = self.state_w.get_mut(world); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +... +24 | println!("{}", a1.0); + | ---- immutable borrow later used here diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_iter_lifetime_safety.rs b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_iter_lifetime_safety.rs new file mode 100644 index 0000000000..bcd9c827cc --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_iter_lifetime_safety.rs @@ -0,0 +1,28 @@ +use bevy_ecs::prelude::*; +use bevy_ecs::system::SystemState; + +#[derive(Component)] +struct A(usize); + +#[derive(Component)] +struct B(usize); + +struct State { + state_r: SystemState>, + state_w: SystemState>, +} + +impl State { + fn get_components(&mut self, world: &mut World) { + let q1 = self.state_r.get(&world); + let a1 = q1.iter().next().unwrap(); + + let mut q2 = self.state_w.get_mut(world); + let _ = q2.iter_mut().next().unwrap(); + + // this should fail to compile + println!("{}", a1.0); + } +} + +fn main() {} diff --git a/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_iter_lifetime_safety.stderr b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_iter_lifetime_safety.stderr new file mode 100644 index 0000000000..e1ca01e68c --- /dev/null +++ b/crates/bevy_ecs_compile_fail_tests/tests/ui/system_state_iter_lifetime_safety.stderr @@ -0,0 +1,11 @@ +error[E0502]: cannot borrow `*world` as mutable because it is also borrowed as immutable + --> tests/ui/system_state_iter_lifetime_safety.rs:20:22 + | +17 | let q1 = self.state_r.get(&world); + | ------ immutable borrow occurs here +... +20 | let mut q2 = self.state_w.get_mut(world); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +... +24 | println!("{}", a1.0); + | ---- immutable borrow later used here diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 35a4f876a0..76bc476c11 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,4 +1,4 @@ -use xshell::cmd; +use xshell::{cmd, pushd}; fn main() { // When run locally, results may differ from actual CI runs triggered by @@ -14,6 +14,16 @@ fn main() { // See if clippy has any complaints. // - Type complexity must be ignored because we use huge templates for queries cmd!("cargo clippy --workspace --all-targets --all-features -- -D warnings -A clippy::type_complexity") - .run() - .expect("Please fix clippy errors in output above."); + .run() + .expect("Please fix clippy errors in output above."); + + // Run UI tests (they do not get executed with the workspace tests) + // - See crates/bevy_ecs_compile_fail_tests/README.md + { + let _bevy_ecs_compile_fail_tests = pushd("crates/bevy_ecs_compile_fail_tests") + .expect("Failed to navigate to the 'bevy_ecs_compile_fail_tests' crate"); + cmd!("cargo test") + .run() + .expect("Compiler errors of the ECS compile fail tests seem to be different than expected! Check locally and compare rust versions."); + } }