From 2dd52f8dd5bc947259b9ace72be61bdee24012c4 Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 6 Feb 2025 13:00:49 +0000 Subject: [PATCH 1/2] Add lint: Res> --- bevy_lint/src/lints/mod.rs | 4 + bevy_lint/src/lints/sus_resource.rs | 75 +++++++++++++++++++ bevy_lint/src/paths.rs | 1 + bevy_lint/tests/ui/sus_resource/my_test.rs | 13 ++++ .../tests/ui/sus_resource/my_test.stderr | 15 ++++ 5 files changed, 108 insertions(+) create mode 100644 bevy_lint/src/lints/sus_resource.rs create mode 100644 bevy_lint/tests/ui/sus_resource/my_test.rs create mode 100644 bevy_lint/tests/ui/sus_resource/my_test.stderr diff --git a/bevy_lint/src/lints/mod.rs b/bevy_lint/src/lints/mod.rs index 17b40606..99c3328b 100644 --- a/bevy_lint/src/lints/mod.rs +++ b/bevy_lint/src/lints/mod.rs @@ -14,6 +14,7 @@ pub mod main_return_without_appexit; pub mod missing_reflect; pub mod panicking_methods; pub mod plugin_not_ending_in_plugin; +pub mod sus_resource; pub mod zst_query; pub(crate) static LINTS: &[&BevyLint] = &[ @@ -26,6 +27,7 @@ pub(crate) static LINTS: &[&BevyLint] = &[ panicking_methods::PANICKING_WORLD_METHODS, plugin_not_ending_in_plugin::PLUGIN_NOT_ENDING_IN_PLUGIN, zst_query::ZST_QUERY, + sus_resource::EVENTS_RESOURCE_SYSTEM_PARAM, ]; pub(crate) fn register_lints(store: &mut LintStore) { @@ -46,4 +48,6 @@ pub(crate) fn register_passes(store: &mut LintStore) { }); store.register_late_pass(|_| Box::new(zst_query::ZstQuery::default())); store.register_late_pass(|_| Box::new(insert_unit_bundle::InsertUnitBundle::default())); + + store.register_late_pass(|_| Box::new(sus_resource::EventsResourceSystemParam::default())); } diff --git a/bevy_lint/src/lints/sus_resource.rs b/bevy_lint/src/lints/sus_resource.rs new file mode 100644 index 00000000..a6f907b6 --- /dev/null +++ b/bevy_lint/src/lints/sus_resource.rs @@ -0,0 +1,75 @@ +use crate::{ + declare_bevy_lint, declare_bevy_lint_pass, + utils::hir_parse::{detuple, generic_type_at}, +}; +use clippy_utils::{ + diagnostics::span_lint_and_help, + source::{snippet, snippet_opt}, + ty::match_type, +}; +use rustc_hir::{intravisit::FnKind, Expr, ExprKind, FnDecl, GenericArgs}; +use rustc_hir_analysis::collect::ItemCtxt; +use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_middle::ty::Ty; +use rustc_span::{Span, Symbol}; + +declare_bevy_lint! { + pub EVENTS_RESOURCE_SYSTEM_PARAM, + RESTRICTION, + // FIXME: Better note. + "use of bad system parameter", +} + +declare_bevy_lint_pass! { + pub EventsResourceSystemParam => [EVENTS_RESOURCE_SYSTEM_PARAM.lint], +} + +impl<'tcx> LateLintPass<'tcx> for EventsResourceSystemParam { + fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &'tcx rustc_hir::Ty<'tcx>) { + let item_cx = ItemCtxt::new(cx.tcx, hir_ty.hir_id.owner.def_id); + let ty = item_cx.lower_ty(hir_ty); + + let Some(res_kind) = ResKind::try_from_ty(cx, ty) else { + return; + }; + + let Some(res_data_ty) = generic_type_at(cx, hir_ty, 1) else { + return; + }; + + // FIXME: Is detuple necessary ? Or will `T` in `Res` always be a single type ? + detuple(*res_data_ty) + .iter() + .filter(|&hir_ty| match_type(cx, item_cx.lower_ty(hir_ty), &crate::paths::EVENTS)) + .for_each(|hir_ty| { + span_lint_and_help( + cx, + EVENTS_RESOURCE_SYSTEM_PARAM.lint, + hir_ty.span, + EVENTS_RESOURCE_SYSTEM_PARAM.lint.desc, + None, + res_kind.help(None), + ) + }); + } +} + +enum ResKind { + Res, +} + +impl ResKind { + fn try_from_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option { + if match_type(cx, ty, &crate::paths::RES) { + Some(Self::Res) + } else { + None + } + } + + fn help(&self, o_ty: Option<&Ty<'_>>) -> String { + match self { + Self::Res => format!("events Resource footgun"), + } + } +} diff --git a/bevy_lint/src/paths.rs b/bevy_lint/src/paths.rs index 54b0e6ef..903c0307 100644 --- a/bevy_lint/src/paths.rs +++ b/bevy_lint/src/paths.rs @@ -24,6 +24,7 @@ pub const PTR_MUT: [&str; 2] = ["bevy_ptr", "PtrMut"]; 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: [&str; 3] = ["bevy_ecs", "change_detection", "Res"]; 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/sus_resource/my_test.rs b/bevy_lint/tests/ui/sus_resource/my_test.rs new file mode 100644 index 00000000..d82d1b44 --- /dev/null +++ b/bevy_lint/tests/ui/sus_resource/my_test.rs @@ -0,0 +1,13 @@ +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::events_resource_system_param)] + +use bevy::prelude::*; + +fn main() { + App::new().add_systems(Startup, (bad_system,)).run(); +} + +//~| HELP: events Resource footgun +//~v ERROR: use of bad system parameter +fn bad_system(_resource_events: Res>) {} diff --git a/bevy_lint/tests/ui/sus_resource/my_test.stderr b/bevy_lint/tests/ui/sus_resource/my_test.stderr new file mode 100644 index 00000000..8f4b1dde --- /dev/null +++ b/bevy_lint/tests/ui/sus_resource/my_test.stderr @@ -0,0 +1,15 @@ +error: use of bad system parameter + --> tests/ui/sus_resource/my_test.rs:13:37 + | +13 | fn bad_system(_resource_events: Res>) {} + | ^^^^^^^^^^^^^^^ + | + = help: events Resource footgun +note: the lint level is defined here + --> tests/ui/sus_resource/my_test.rs:3:9 + | +3 | #![deny(bevy::events_resource_system_param)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 4f78d6aa87dfb2e2d779897d5891a0083c0398fc Mon Sep 17 00:00:00 2001 From: chris Date: Thu, 6 Feb 2025 13:03:12 +0000 Subject: [PATCH 2/2] cargo fix --- bevy_lint/src/lints/sus_resource.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bevy_lint/src/lints/sus_resource.rs b/bevy_lint/src/lints/sus_resource.rs index a6f907b6..d229594b 100644 --- a/bevy_lint/src/lints/sus_resource.rs +++ b/bevy_lint/src/lints/sus_resource.rs @@ -4,14 +4,11 @@ use crate::{ }; use clippy_utils::{ diagnostics::span_lint_and_help, - source::{snippet, snippet_opt}, ty::match_type, }; -use rustc_hir::{intravisit::FnKind, Expr, ExprKind, FnDecl, GenericArgs}; use rustc_hir_analysis::collect::ItemCtxt; -use rustc_lint::{LateContext, LateLintPass, Lint}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -use rustc_span::{Span, Symbol}; declare_bevy_lint! { pub EVENTS_RESOURCE_SYSTEM_PARAM,