From bf99294cffd67f32554c980e6976925a549e3c23 Mon Sep 17 00:00:00 2001 From: Gino Valente Date: Sun, 27 Oct 2024 14:14:08 -0700 Subject: [PATCH] Add support for `ResMut` --- bevy_lint/src/lints/borrow_of_reborrowable.rs | 14 ++++- bevy_lint/src/lints/mod.rs | 1 + bevy_lint/src/paths.rs | 1 + .../ui/borrow_of_reborrowable/resource.rs | 51 +++++++++++++++++++ .../ui/borrow_of_reborrowable/resource.stderr | 20 ++++++++ 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 bevy_lint/tests/ui/borrow_of_reborrowable/resource.rs create mode 100644 bevy_lint/tests/ui/borrow_of_reborrowable/resource.stderr diff --git a/bevy_lint/src/lints/borrow_of_reborrowable.rs b/bevy_lint/src/lints/borrow_of_reborrowable.rs index 8db75161..3534bbeb 100644 --- a/bevy_lint/src/lints/borrow_of_reborrowable.rs +++ b/bevy_lint/src/lints/borrow_of_reborrowable.rs @@ -56,6 +56,12 @@ declare_bevy_lint! { "parameter takes `&mut Commands` instead of a re-borrowed `Commands`", } +declare_bevy_lint! { + pub BORROW_OF_RESOURCE, + PEDANTIC, + "parameter takes `&mut ResMut` instead of a re-borrowed `ResMut`", +} + declare_bevy_lint! { pub BORROW_OF_QUERY, PEDANTIC, @@ -63,7 +69,7 @@ declare_bevy_lint! { } declare_lint_pass! { - BorrowOfReborrowable => [BORROW_OF_COMMANDS.lint, BORROW_OF_QUERY.lint] + BorrowOfReborrowable => [BORROW_OF_COMMANDS.lint, BORROW_OF_RESOURCE.lint, BORROW_OF_QUERY.lint] } impl<'tcx> LateLintPass<'tcx> for BorrowOfReborrowable { @@ -147,7 +153,7 @@ enum Reborrowable { // PtrMut, Query, // QueryIterationCursor, - // ResMut, + ResMut, } impl Reborrowable { @@ -158,6 +164,8 @@ impl Reborrowable { Some(Self::EntityCommands) } else if match_type(cx, ty, &crate::paths::QUERY) { Some(Self::Query) + } else if match_type(cx, ty, &crate::paths::RES_MUT) { + Some(Self::ResMut) } else { None } @@ -168,6 +176,7 @@ impl Reborrowable { Self::Commands => BORROW_OF_COMMANDS.lint, Self::EntityCommands => BORROW_OF_COMMANDS.lint, Self::Query => BORROW_OF_QUERY.lint, + Self::ResMut => BORROW_OF_RESOURCE.lint, } } @@ -181,6 +190,7 @@ impl Reborrowable { Self::Commands => "Commands", Self::EntityCommands => "EntityCommands", Self::Query => "Query", + Self::ResMut => "ResMut", } } diff --git a/bevy_lint/src/lints/mod.rs b/bevy_lint/src/lints/mod.rs index 1de64cd4..73db6e52 100644 --- a/bevy_lint/src/lints/mod.rs +++ b/bevy_lint/src/lints/mod.rs @@ -14,6 +14,7 @@ pub mod plugin_not_ending_in_plugin; pub(crate) static LINTS: &[&BevyLint] = &[ borrow_of_reborrowable::BORROW_OF_COMMANDS, + borrow_of_reborrowable::BORROW_OF_RESOURCE, borrow_of_reborrowable::BORROW_OF_QUERY, insert_event_resource::INSERT_EVENT_RESOURCE, main_return_without_appexit::MAIN_RETURN_WITHOUT_APPEXIT, diff --git a/bevy_lint/src/paths.rs b/bevy_lint/src/paths.rs index 9462e115..9ec93616 100644 --- a/bevy_lint/src/paths.rs +++ b/bevy_lint/src/paths.rs @@ -17,5 +17,6 @@ pub const PLUGIN: [&str; 3] = ["bevy_app", "plugin", "Plugin"]; pub const QUERY: [&str; 4] = ["bevy_ecs", "system", "query", "Query"]; pub const QUERY_STATE: [&str; 4] = ["bevy_ecs", "query", "state", "QueryState"]; pub const REFLECT: [&str; 3] = ["bevy_reflect", "reflect", "Reflect"]; +pub const RES_MUT: [&str; 3] = ["bevy_ecs", "change_detection", "ResMut"]; pub const RESOURCE: [&str; 4] = ["bevy_ecs", "system", "system_param", "Resource"]; pub const WORLD: [&str; 3] = ["bevy_ecs", "world", "World"]; diff --git a/bevy_lint/tests/ui/borrow_of_reborrowable/resource.rs b/bevy_lint/tests/ui/borrow_of_reborrowable/resource.rs new file mode 100644 index 00000000..835ca00d --- /dev/null +++ b/bevy_lint/tests/ui/borrow_of_reborrowable/resource.rs @@ -0,0 +1,51 @@ +//! This tests the `borrow_of_reborrowable` lint, specifically when triggered on the `ResMut` type. + +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::borrow_of_resource)] + +use bevy::prelude::*; + +#[derive(Resource)] +struct Data(String); + +// OK: Lint does not apply to immutable references +fn immutable_reference(_res: &ResMut) { + // ... +} + +//~| HELP: use `ResMut` instead +//~v ERROR: parameter takes `&mut ResMut` instead of a re-borrowed `ResMut` +fn mutable_reference(_res: &mut ResMut) { + // ... +} + +//~| HELP: use `ResMut` instead +//~v ERROR: parameter takes `&mut ResMut` instead of a re-borrowed `ResMut` +fn mutable_reference_return<'a>(_res: &'a mut ResMut) -> usize { + 123 +} + +// OK: Lint does not apply when return type relies on reference lifetime +fn mutable_reference_bounded_return<'a>(res: &'a mut ResMut) -> &'a mut String { + &mut res.0 +} + +// OK: Lint does not apply when return type relies on reference lifetime +fn mutable_reference_bounded_return_complex<'a>( + res: &'a mut ResMut, +) -> Vec<(usize, &'a mut String)> { + vec![(1, &mut res.0)] +} + +fn main() { + fn some_system(mut res: ResMut) { + immutable_reference(&res); + mutable_reference(&mut res); + _ = mutable_reference_return(&mut res); + _ = mutable_reference_bounded_return(&mut res); + _ = mutable_reference_bounded_return_complex(&mut res); + } + + App::new().add_systems(Update, some_system).run(); +} diff --git a/bevy_lint/tests/ui/borrow_of_reborrowable/resource.stderr b/bevy_lint/tests/ui/borrow_of_reborrowable/resource.stderr new file mode 100644 index 00000000..2c588224 --- /dev/null +++ b/bevy_lint/tests/ui/borrow_of_reborrowable/resource.stderr @@ -0,0 +1,20 @@ +error: parameter takes `&mut ResMut` instead of a re-borrowed `ResMut` + --> tests/ui/borrow_of_reborrowable/resource.rs:19:22 + | +19 | fn mutable_reference(_res: &mut ResMut) { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `ResMut` instead: `mut _res: bevy::prelude::ResMut<'_, Data>` + | +note: the lint level is defined here + --> tests/ui/borrow_of_reborrowable/resource.rs:5:9 + | +5 | #![deny(bevy::borrow_of_resource)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: parameter takes `&mut ResMut` instead of a re-borrowed `ResMut` + --> tests/ui/borrow_of_reborrowable/resource.rs:25:33 + | +25 | fn mutable_reference_return<'a>(_res: &'a mut ResMut) -> usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `ResMut` instead: `mut _res: bevy::prelude::ResMut<'_, Data>` + +error: aborting due to 2 previous errors +