From 6d2c6f9c5a526a956416d9f61aabe4c0d31472c2 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Wed, 8 Oct 2025 23:23:23 +0200 Subject: [PATCH 01/13] feat: setup new lint --- .../style/bevy_platform_alternative_exists.rs | 67 +++++++++++++++++++ bevy_lint/src/lints/style/mod.rs | 9 ++- .../bevy_platform_alternative_exists/main.rs | 7 ++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs create mode 100644 bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs new file mode 100644 index 00000000..2846a256 --- /dev/null +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -0,0 +1,67 @@ +use clippy_utils::ty::ty_from_hir_ty; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; + +use crate::{declare_bevy_lint, declare_bevy_lint_pass}; + +declare_bevy_lint! { + pub(crate) BEVY_PLATFORM_ALTERNATIVE_EXISTS, + super::Style, + "Used type from the `std` that has an existing alternative from `bevy_platform`", +} + +declare_bevy_lint_pass! { + pub(crate) BevyPlatformAlternativeExists => [BEVY_PLATFORM_ALTERNATIVE_EXISTS], +} + +impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { + fn check_ty( + &mut self, + cx: &LateContext<'tcx>, + hir_ty: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>, + ) { + if hir_ty.span.in_external_macro(cx.tcx.sess.source_map()) { + return; + } + let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()); + + if let Some(platform_type) = BevyPlatformTypes::try_from_ty(cx, ty) { + // TODO: lint + } + dbg!(ty); + } +} + +enum BevyPlatformTypes { + Instant, + RwLock, + RwLockReadGuard, + RwLockWriteGuard, + LockResult, + TryLockError, + TryLockResult, + Once, + OnceState, + OnceLock, + Mutex, + MutexGuard, + LazyLock, + Barrier, + BarrierWaitResult, + // TODO: atomics + HashTable, + HashSet, + HashMap, + // cell + SyncUnsafeCell, + Exclusive, +} + +impl BevyPlatformTypes { + fn try_from_ty(cx: &LateContext, ty: Ty) -> Option { + if crate::paths::bevy_platform_types::STD_INSTANT.matches_ty(cx, ty) { + dbg!("found"); + } + None + } +} diff --git a/bevy_lint/src/lints/style/mod.rs b/bevy_lint/src/lints/style/mod.rs index f7a9fc6b..904522ce 100644 --- a/bevy_lint/src/lints/style/mod.rs +++ b/bevy_lint/src/lints/style/mod.rs @@ -8,6 +8,7 @@ use rustc_lint::{Level, Lint, LintStore}; use crate::lint::LintGroup; +pub mod bevy_platform_alternative_exists; pub mod unconventional_naming; pub(crate) struct Style; @@ -15,9 +16,15 @@ pub(crate) struct Style; impl LintGroup for Style { const NAME: &str = "bevy::style"; const LEVEL: Level = Level::Warn; - const LINTS: &[&Lint] = &[unconventional_naming::UNCONVENTIONAL_NAMING]; + const LINTS: &[&Lint] = &[ + bevy_platform_alternative_exists::BEVY_PLATFORM_ALTERNATIVE_EXISTS, + unconventional_naming::UNCONVENTIONAL_NAMING, + ]; fn register_passes(store: &mut LintStore) { + store.register_late_pass(|_| { + Box::new(bevy_platform_alternative_exists::BevyPlatformAlternativeExists) + }); store.register_late_pass(|_| Box::new(unconventional_naming::UnconventionalNaming)); } } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs new file mode 100644 index 00000000..21f8c9dc --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs @@ -0,0 +1,7 @@ +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::bevy_platform_alternative_exists)] + +fn main() { + let foo: std::time::Instant = std::time::Instant::now(); +} From 196eff186b69165bb29932519246fe3d2b865843 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Thu, 9 Oct 2025 23:22:20 +0200 Subject: [PATCH 02/13] feat: add all symbols and paths --- .../style/bevy_platform_alternative_exists.rs | 144 ++++++++++++++---- bevy_lint/src/paths.rs | 29 ++++ bevy_lint/src/sym.rs | 27 +++- .../bevy_platform_alternative_exists/main.rs | 2 +- 4 files changed, 170 insertions(+), 32 deletions(-) diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 2846a256..17ab2fbb 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -1,4 +1,4 @@ -use clippy_utils::ty::ty_from_hir_ty; +use clippy_utils::{diagnostics::span_lint_hir_and_then, source::snippet_opt, ty::ty_from_hir_ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; @@ -23,45 +23,137 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { if hir_ty.span.in_external_macro(cx.tcx.sess.source_map()) { return; } + + // lower the [`hir::Ty`] to a [`rustc_middle::ty::Ty`] let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()); - if let Some(platform_type) = BevyPlatformTypes::try_from_ty(cx, ty) { - // TODO: lint + // Check if for the given `ty` an alternative from `bevy_platform` exists. + if let Some(bevy_platform_alternative) = BevyPlatformTypes::try_from_ty(cx, ty) { + span_lint_hir_and_then( + cx, + BEVY_PLATFORM_ALTERNATIVE_EXISTS, + hir_ty.hir_id, + hir_ty.span, + BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc, + |diag| { + diag.note(format!( + "the type `{}` can be replaced with the no_std compatible type \"bevy_platform::{}\"", + snippet_opt(cx.tcx.sess, hir_ty.span), + conventional_name_impl.suffix() + )); + + diag.span_suggestion( + struct_span, + format!("use `{}` instead", struct_name.as_str()), + conventional_name_impl.name_suggestion(struct_name.as_str()), + Applicability::MaybeIncorrect, + ); + }, + ); } - dbg!(ty); } } +/// Represents all the types in the `bevy_platform` crate that +/// are drop in replacements for the equivalently named std types. enum BevyPlatformTypes { - Instant, - RwLock, - RwLockReadGuard, - RwLockWriteGuard, + // cell: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/cell/mod.rs + SyncCell, + SyncUnsafeCell, + // collections: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/collections/mod.rs + HashMap, + HashSet, + // sync: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/sync/mod.rs + Barrier, + BarrierWaitResult, + LazyLock, + Mutex, + MutexGuard, + Once, + OnceLock, + OnceState, LockResult, + PoisonError, TryLockError, TryLockResult, - Once, - OnceState, - OnceLock, - Mutex, - MutexGuard, - LazyLock, - Barrier, - BarrierWaitResult, - // TODO: atomics - HashTable, - HashSet, - HashMap, - // cell - SyncUnsafeCell, - Exclusive, + RwLock, + RwLockReadGuard, + RwLockWriteGuard, + // time: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/time/mod.rs + Instant, } impl BevyPlatformTypes { fn try_from_ty(cx: &LateContext, ty: Ty) -> Option { - if crate::paths::bevy_platform_types::STD_INSTANT.matches_ty(cx, ty) { - dbg!("found"); + use crate::paths::bevy_platform_types::*; + + if SYNCCELL.matches_ty(cx, ty) { + Some(BevyPlatformTypes::SyncCell) + } else if SYNCUNSAFECELL.matches_ty(cx, ty) { + Some(BevyPlatformTypes::SyncUnsafeCell) + } else if HASHMAP.matches_ty(cx, ty) { + Some(BevyPlatformTypes::HashMap) + } else if HASHSET.matches_ty(cx, ty) { + Some(BevyPlatformTypes::HashSet) + } else if INSTANT.matches_ty(cx, ty) { + Some(BevyPlatformTypes::Instant) + } else if BARRIER.matches_ty(cx, ty) { + Some(BevyPlatformTypes::Barrier) + } else if BARRIERWAITRESULT.matches_ty(cx, ty) { + Some(BevyPlatformTypes::BarrierWaitResult) + } else if LAZYLOCK.matches_ty(cx, ty) { + Some(BevyPlatformTypes::LazyLock) + } else if MUTEX.matches_ty(cx, ty) { + Some(BevyPlatformTypes::Mutex) + } else if MUTEXGUARD.matches_ty(cx, ty) { + Some(BevyPlatformTypes::MutexGuard) + } else if ONCE.matches_ty(cx, ty) { + Some(BevyPlatformTypes::Once) + } else if ONCELOCK.matches_ty(cx, ty) { + Some(BevyPlatformTypes::OnceLock) + } else if ONCESTATE.matches_ty(cx, ty) { + Some(BevyPlatformTypes::OnceState) + } else if LOCKRESULT.matches_ty(cx, ty) { + Some(BevyPlatformTypes::LockResult) + } else if POISONERROR.matches_ty(cx, ty) { + Some(BevyPlatformTypes::PoisonError) + } else if TRYLOCKERROR.matches_ty(cx, ty) { + Some(BevyPlatformTypes::TryLockError) + } else if TRYLOCKRESULT.matches_ty(cx, ty) { + Some(BevyPlatformTypes::TryLockResult) + } else if RWLOCK.matches_ty(cx, ty) { + Some(BevyPlatformTypes::RwLock) + } else if RWLOCKREADGUARD.matches_ty(cx, ty) { + Some(BevyPlatformTypes::RwLockReadGuard) + } else if RWLOCKWRITEGUARD.matches_ty(cx, ty) { + Some(BevyPlatformTypes::RwLockWriteGuard) + } else { + None + } + } + + fn name(&self) -> &'static str { + match self { + BevyPlatformTypes::SyncCell => "SyncCell", + BevyPlatformTypes::SyncUnsafeCell => "SyncUnsafeCell", + BevyPlatformTypes::HashMap => "HashMap", + BevyPlatformTypes::HashSet => "HashSet", + BevyPlatformTypes::Barrier => "Barrier", + BevyPlatformTypes::BarrierWaitResult => "BarrierWaitResult", + BevyPlatformTypes::LazyLock => "LazyLock", + BevyPlatformTypes::Mutex => "Mutex", + BevyPlatformTypes::MutexGuard => "MutexGuard", + BevyPlatformTypes::Once => "Once", + BevyPlatformTypes::OnceLock => "OnceLock", + BevyPlatformTypes::OnceState => "OnceState", + BevyPlatformTypes::LockResult => "LockResult", + BevyPlatformTypes::PoisonError => "PoisonError", + BevyPlatformTypes::TryLockError => "TryLockError", + BevyPlatformTypes::TryLockResult => "TryLockResult", + BevyPlatformTypes::RwLock => "RwLock", + BevyPlatformTypes::RwLockReadGuard => "RwLockReadGuard", + BevyPlatformTypes::RwLockWriteGuard => "RwLockWriteGuard", + BevyPlatformTypes::Instant => "Instant", } - None } } diff --git a/bevy_lint/src/paths.rs b/bevy_lint/src/paths.rs index 9ed21c38..2c227867 100644 --- a/bevy_lint/src/paths.rs +++ b/bevy_lint/src/paths.rs @@ -86,3 +86,32 @@ pub static UPDATE: PathLookup = type_path!(bevy_app::main_schedule::Update); pub static WITH: PathLookup = type_path!(bevy_ecs::query::filter::With); /// pub static WORLD: PathLookup = type_path!(bevy_ecs::world::World); + +// All the paths that represent the `bevy_platform` types. +// Keep the following list alphabetically sorted :) +pub mod bevy_platform_types { + use clippy_utils::paths::{PathLookup, PathNS}; + + use crate::sym; + + pub static SYNCCELL: PathLookup = type_path!(std::sync::Exclusive); + pub static SYNCUNSAFECELL: PathLookup = type_path!(std::cell::SyncUnsafeCell); + pub static INSTANT: PathLookup = type_path!(std::time::Instant); + pub static HASHMAP: PathLookup = type_path!(std::collections::HashMap); + pub static HASHSET: PathLookup = type_path!(std::collections::HashSet); + pub static BARRIER: PathLookup = type_path!(std::sync::Barrier); + pub static BARRIERWAITRESULT: PathLookup = type_path!(std::sync::BarrierWaitResult); + pub static LAZYLOCK: PathLookup = type_path!(std::sync::LazyLock); + pub static MUTEX: PathLookup = type_path!(std::sync::Mutex); + pub static MUTEXGUARD: PathLookup = type_path!(std::sync::MutexGuard); + pub static ONCE: PathLookup = type_path!(std::sync::Once); + pub static ONCELOCK: PathLookup = type_path!(std::sync::OnceLock); + pub static ONCESTATE: PathLookup = type_path!(std::sync::OnceState); + pub static LOCKRESULT: PathLookup = type_path!(std::sync::LockResult); + pub static POISONERROR: PathLookup = type_path!(std::sync::PoisonError); + pub static TRYLOCKERROR: PathLookup = type_path!(std::sync::TryLockError); + pub static TRYLOCKRESULT: PathLookup = type_path!(std::sync::TryLockResult); + pub static RWLOCK: PathLookup = type_path!(std::sync::RwLock); + pub static RWLOCKREADGUARD: PathLookup = type_path!(std::sync::RwLockReadGuard); + pub static RWLOCKWRITEGUARD: PathLookup = type_path!(std::sync::RwLockWriteGuard); +} diff --git a/bevy_lint/src/sym.rs b/bevy_lint/src/sym.rs index bd88efb5..79552e1e 100644 --- a/bevy_lint/src/sym.rs +++ b/bevy_lint/src/sym.rs @@ -57,7 +57,10 @@ use clippy_utils::sym::EXTRA_SYMBOLS as CLIPPY_SYMBOLS; /// These are symbols that we use but are already interned by either the compiler or Clippy. pub use clippy_utils::sym::filter; -pub use rustc_span::sym::{bevy_ecs, bundle, message, plugin, reflect}; +pub use rustc_span::sym::{ + HashMap, HashSet, Instant, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, + SyncUnsafeCell, bevy_ecs, bundle, message, plugin, reflect, std, sync, +}; use rustc_span::{Symbol, symbol::PREDEFINED_SYMBOLS_COUNT}; /// The starting offset used for the first Bevy-specific symbol. @@ -114,26 +117,30 @@ macro_rules! declare_bevy_symbols { // Before adding a new symbol here, check that it doesn't exist yet in `rustc_span::sym` or // `clippy_utils::sym`. Having duplicate symbols will cause the compiler to ICE! Also please keep -// this list alphabetically sorted :) +// this list alphabetically sorted :) (use `:sort i` in nvim) declare_bevy_symbols! { add_systems, app, App, + Barrier, + BarrierWaitResult, + bevy, bevy_app, bevy_camera, bevy_ptr, bevy_reflect, - bevy, Bundle, camera, Camera, + cell, change_detection, + collections, commands, Commands, component, Component, - deferred_world, Deferred, + deferred_world, DeferredWorld, entity_ref, EntityCommands, @@ -141,11 +148,13 @@ declare_bevy_symbols! { event, Event, Events, + Exclusive, FilteredEntityMut, FixedUpdate, init_resource, insert_resource, iter_current_update_messages, + LazyLock, main_schedule, Message, Messages, @@ -169,13 +178,21 @@ declare_bevy_symbols! { schedule, set, spawn, - system_param, system, + system_param, SystemSet, + time, Update, With, world, World, + Once, + OnceLock, + OnceState, + LockResult, + PoisonError, + TryLockError, + TryLockResult, } /// Returns a list of strings that should be supplied to diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs index 21f8c9dc..648d10c5 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs @@ -3,5 +3,5 @@ #![deny(bevy::bevy_platform_alternative_exists)] fn main() { - let foo: std::time::Instant = std::time::Instant::now(); + let _ = std::time::Instant::now(); } From 557d8c87525225d92ba349668228b97aa57fda57 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Sun, 12 Oct 2025 12:40:46 +0200 Subject: [PATCH 03/13] feat: switch to marco, only lint and help --- .../style/bevy_platform_alternative_exists.rs | 180 ++++++------------ 1 file changed, 63 insertions(+), 117 deletions(-) diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 17ab2fbb..19eae5c2 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -1,6 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_hir_and_then, source::snippet_opt, ty::ty_from_hir_ty}; +use clippy_utils::{diagnostics::span_lint_and_help, source::snippet, ty::ty_from_hir_ty}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::Ty; use crate::{declare_bevy_lint, declare_bevy_lint_pass}; @@ -28,132 +27,79 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()); // Check if for the given `ty` an alternative from `bevy_platform` exists. - if let Some(bevy_platform_alternative) = BevyPlatformTypes::try_from_ty(cx, ty) { - span_lint_hir_and_then( + if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) { + span_lint_and_help( cx, BEVY_PLATFORM_ALTERNATIVE_EXISTS, - hir_ty.hir_id, hir_ty.span, BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc, - |diag| { - diag.note(format!( - "the type `{}` can be replaced with the no_std compatible type \"bevy_platform::{}\"", - snippet_opt(cx.tcx.sess, hir_ty.span), - conventional_name_impl.suffix() - )); - - diag.span_suggestion( - struct_span, - format!("use `{}` instead", struct_name.as_str()), - conventional_name_impl.name_suggestion(struct_name.as_str()), - Applicability::MaybeIncorrect, - ); - }, + None, + format!( + "the type `{}` can be replaced with the `no_std` compatible type + from `bevy_platform` {}", + snippet(cx.tcx.sess, hir_ty.span, ""), + bevy_platform_alternative.name(), + ), ); } } } -/// Represents all the types in the `bevy_platform` crate that -/// are drop in replacements for the equivalently named std types. -enum BevyPlatformTypes { - // cell: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/cell/mod.rs - SyncCell, - SyncUnsafeCell, - // collections: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/collections/mod.rs - HashMap, - HashSet, - // sync: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/sync/mod.rs - Barrier, - BarrierWaitResult, - LazyLock, - Mutex, - MutexGuard, - Once, - OnceLock, - OnceState, - LockResult, - PoisonError, - TryLockError, - TryLockResult, - RwLock, - RwLockReadGuard, - RwLockWriteGuard, - // time: https://github.com/bevyengine/bevy/blob/v0.17.2/crates/bevy_platform/src/time/mod.rs - Instant, -} +macro_rules! declare_bevy_platform_types { + ( + $( + $variant:ident => $path:ident + ) + ,+$(,)? + ) => { + #[derive(Copy, Clone)] + pub enum BevyPlatformType { + $( + $variant, + )+ + } -impl BevyPlatformTypes { - fn try_from_ty(cx: &LateContext, ty: Ty) -> Option { - use crate::paths::bevy_platform_types::*; + impl BevyPlatformType{ + pub const fn name(&self) -> &'static str { + match self { + $(Self::$variant => stringify!($variant),)+ + } + } - if SYNCCELL.matches_ty(cx, ty) { - Some(BevyPlatformTypes::SyncCell) - } else if SYNCUNSAFECELL.matches_ty(cx, ty) { - Some(BevyPlatformTypes::SyncUnsafeCell) - } else if HASHMAP.matches_ty(cx, ty) { - Some(BevyPlatformTypes::HashMap) - } else if HASHSET.matches_ty(cx, ty) { - Some(BevyPlatformTypes::HashSet) - } else if INSTANT.matches_ty(cx, ty) { - Some(BevyPlatformTypes::Instant) - } else if BARRIER.matches_ty(cx, ty) { - Some(BevyPlatformTypes::Barrier) - } else if BARRIERWAITRESULT.matches_ty(cx, ty) { - Some(BevyPlatformTypes::BarrierWaitResult) - } else if LAZYLOCK.matches_ty(cx, ty) { - Some(BevyPlatformTypes::LazyLock) - } else if MUTEX.matches_ty(cx, ty) { - Some(BevyPlatformTypes::Mutex) - } else if MUTEXGUARD.matches_ty(cx, ty) { - Some(BevyPlatformTypes::MutexGuard) - } else if ONCE.matches_ty(cx, ty) { - Some(BevyPlatformTypes::Once) - } else if ONCELOCK.matches_ty(cx, ty) { - Some(BevyPlatformTypes::OnceLock) - } else if ONCESTATE.matches_ty(cx, ty) { - Some(BevyPlatformTypes::OnceState) - } else if LOCKRESULT.matches_ty(cx, ty) { - Some(BevyPlatformTypes::LockResult) - } else if POISONERROR.matches_ty(cx, ty) { - Some(BevyPlatformTypes::PoisonError) - } else if TRYLOCKERROR.matches_ty(cx, ty) { - Some(BevyPlatformTypes::TryLockError) - } else if TRYLOCKRESULT.matches_ty(cx, ty) { - Some(BevyPlatformTypes::TryLockResult) - } else if RWLOCK.matches_ty(cx, ty) { - Some(BevyPlatformTypes::RwLock) - } else if RWLOCKREADGUARD.matches_ty(cx, ty) { - Some(BevyPlatformTypes::RwLockReadGuard) - } else if RWLOCKWRITEGUARD.matches_ty(cx, ty) { - Some(BevyPlatformTypes::RwLockWriteGuard) - } else { - None + pub fn try_from_ty(cx: &LateContext<'_>, ty: rustc_middle::ty::Ty<'_>) -> Option { + use crate::paths::bevy_platform_types::*; + $( + if $path.matches_ty(cx, ty) { + Some(Self::$variant) + } else + )+ + { + None + } + } } - } + }; +} - fn name(&self) -> &'static str { - match self { - BevyPlatformTypes::SyncCell => "SyncCell", - BevyPlatformTypes::SyncUnsafeCell => "SyncUnsafeCell", - BevyPlatformTypes::HashMap => "HashMap", - BevyPlatformTypes::HashSet => "HashSet", - BevyPlatformTypes::Barrier => "Barrier", - BevyPlatformTypes::BarrierWaitResult => "BarrierWaitResult", - BevyPlatformTypes::LazyLock => "LazyLock", - BevyPlatformTypes::Mutex => "Mutex", - BevyPlatformTypes::MutexGuard => "MutexGuard", - BevyPlatformTypes::Once => "Once", - BevyPlatformTypes::OnceLock => "OnceLock", - BevyPlatformTypes::OnceState => "OnceState", - BevyPlatformTypes::LockResult => "LockResult", - BevyPlatformTypes::PoisonError => "PoisonError", - BevyPlatformTypes::TryLockError => "TryLockError", - BevyPlatformTypes::TryLockResult => "TryLockResult", - BevyPlatformTypes::RwLock => "RwLock", - BevyPlatformTypes::RwLockReadGuard => "RwLockReadGuard", - BevyPlatformTypes::RwLockWriteGuard => "RwLockWriteGuard", - BevyPlatformTypes::Instant => "Instant", - } - } +declare_bevy_platform_types! { + Barrier => BARRIER, + BarrierWaitResult => BARRIERWAITRESULT, + HashMap => HASHMAP, + HashSet => HASHSET, + Instant => INSTANT, + LazyLock => LAZYLOCK, + LockResult => LOCKRESULT, + Mutex => MUTEX, + MutexGuard => MUTEXGUARD, + Once => ONCE, + OnceLock => ONCELOCK, + OnceState => ONCESTATE, + PoisonError => POISONERROR, + RwLock => RWLOCK, + RwLockReadGuard => RWLOCKREADGUARD, + RwLockWriteGuard => RWLOCKWRITEGUARD, + SyncCell => SYNCCELL, + SyncUnsafeCell => SYNCUNSAFECELL, + TryLockError => TRYLOCKERROR, + TryLockResult => TRYLOCKRESULT, } From a9f26d43c10aac455499edff663d7b41e4cf23b0 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Sun, 12 Oct 2025 13:04:55 +0200 Subject: [PATCH 04/13] feat: sort paths of `bevy_platform_types` --- bevy_lint/src/paths.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bevy_lint/src/paths.rs b/bevy_lint/src/paths.rs index 2c227867..b816f42c 100644 --- a/bevy_lint/src/paths.rs +++ b/bevy_lint/src/paths.rs @@ -88,30 +88,30 @@ pub static WITH: PathLookup = type_path!(bevy_ecs::query::filter::With); pub static WORLD: PathLookup = type_path!(bevy_ecs::world::World); // All the paths that represent the `bevy_platform` types. -// Keep the following list alphabetically sorted :) +// Keep the following list alphabetically sorted :) in neovim, use `:sort/\vpub static \w+/ ri` pub mod bevy_platform_types { use clippy_utils::paths::{PathLookup, PathNS}; use crate::sym; - pub static SYNCCELL: PathLookup = type_path!(std::sync::Exclusive); - pub static SYNCUNSAFECELL: PathLookup = type_path!(std::cell::SyncUnsafeCell); - pub static INSTANT: PathLookup = type_path!(std::time::Instant); - pub static HASHMAP: PathLookup = type_path!(std::collections::HashMap); - pub static HASHSET: PathLookup = type_path!(std::collections::HashSet); pub static BARRIER: PathLookup = type_path!(std::sync::Barrier); pub static BARRIERWAITRESULT: PathLookup = type_path!(std::sync::BarrierWaitResult); + pub static HASHMAP: PathLookup = type_path!(std::collections::HashMap); + pub static HASHSET: PathLookup = type_path!(std::collections::HashSet); + pub static INSTANT: PathLookup = type_path!(std::time::Instant); pub static LAZYLOCK: PathLookup = type_path!(std::sync::LazyLock); + pub static LOCKRESULT: PathLookup = type_path!(std::sync::LockResult); pub static MUTEX: PathLookup = type_path!(std::sync::Mutex); pub static MUTEXGUARD: PathLookup = type_path!(std::sync::MutexGuard); pub static ONCE: PathLookup = type_path!(std::sync::Once); pub static ONCELOCK: PathLookup = type_path!(std::sync::OnceLock); pub static ONCESTATE: PathLookup = type_path!(std::sync::OnceState); - pub static LOCKRESULT: PathLookup = type_path!(std::sync::LockResult); pub static POISONERROR: PathLookup = type_path!(std::sync::PoisonError); - pub static TRYLOCKERROR: PathLookup = type_path!(std::sync::TryLockError); - pub static TRYLOCKRESULT: PathLookup = type_path!(std::sync::TryLockResult); pub static RWLOCK: PathLookup = type_path!(std::sync::RwLock); pub static RWLOCKREADGUARD: PathLookup = type_path!(std::sync::RwLockReadGuard); pub static RWLOCKWRITEGUARD: PathLookup = type_path!(std::sync::RwLockWriteGuard); + pub static SYNCCELL: PathLookup = type_path!(std::sync::Exclusive); + pub static SYNCUNSAFECELL: PathLookup = type_path!(std::cell::SyncUnsafeCell); + pub static TRYLOCKERROR: PathLookup = type_path!(std::sync::TryLockError); + pub static TRYLOCKRESULT: PathLookup = type_path!(std::sync::TryLockResult); } From d183ebe6d024ff98b9a22f6a5a19a32eb2980038 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Sun, 12 Oct 2025 15:43:55 +0200 Subject: [PATCH 05/13] feat: extend macro to include full path to the declared type --- .../style/bevy_platform_alternative_exists.rs | 73 +++++++++++-------- .../bevy_platform_alternative_exists/main.rs | 3 + .../main.stderr | 15 ++++ 3 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 19eae5c2..ff1558a3 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -35,20 +35,31 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc, None, format!( - "the type `{}` can be replaced with the `no_std` compatible type - from `bevy_platform` {}", + "the type `{}` can be replaced with the `no_std` compatible type {}", snippet(cx.tcx.sess, hir_ty.span, ""), - bevy_platform_alternative.name(), + bevy_platform_alternative.full_path() ), ); } } } +/// Creates an enum containing all the types form `bevy_platform` as variants. +/// +/// # Example +/// +/// ```ignore +/// declare_bevy_platform_types! { +/// // The variant name => [`PathLookup`] that matches the equivalent type in the std. +/// CustomType => CUSTOMTYPE, +/// // Optional the module path can be passed too, default is `bevy_platform::`. +/// // If an additional module path is added, it will result in: `bevy_platform::custom::thread::CustomType`. +/// CustomType("custom::thread") => BARRIER, +/// ``` macro_rules! declare_bevy_platform_types { ( $( - $variant:ident => $path:ident + $variant:ident $(($mod_path:expr))? => $path:ident ) ,+$(,)? ) => { @@ -60,12 +71,7 @@ macro_rules! declare_bevy_platform_types { } impl BevyPlatformType{ - pub const fn name(&self) -> &'static str { - match self { - $(Self::$variant => stringify!($variant),)+ - } - } - + /// Try to create a [`BevyPlatformType`] from the given [`Ty`]. pub fn try_from_ty(cx: &LateContext<'_>, ty: rustc_middle::ty::Ty<'_>) -> Option { use crate::paths::bevy_platform_types::*; $( @@ -77,29 +83,36 @@ macro_rules! declare_bevy_platform_types { None } } + + ///Returns a string identifying this [`BevyPlatformType`]. This string is suitable for user output. + pub const fn full_path(&self) -> &'static str { + match self { + $(Self::$variant => concat!("bevy_platform", $("::", $mod_path,)? "::" , stringify!($variant)),)+ + } + } } }; } declare_bevy_platform_types! { - Barrier => BARRIER, - BarrierWaitResult => BARRIERWAITRESULT, - HashMap => HASHMAP, - HashSet => HASHSET, - Instant => INSTANT, - LazyLock => LAZYLOCK, - LockResult => LOCKRESULT, - Mutex => MUTEX, - MutexGuard => MUTEXGUARD, - Once => ONCE, - OnceLock => ONCELOCK, - OnceState => ONCESTATE, - PoisonError => POISONERROR, - RwLock => RWLOCK, - RwLockReadGuard => RWLOCKREADGUARD, - RwLockWriteGuard => RWLOCKWRITEGUARD, - SyncCell => SYNCCELL, - SyncUnsafeCell => SYNCUNSAFECELL, - TryLockError => TRYLOCKERROR, - TryLockResult => TRYLOCKRESULT, + Barrier("sync") => BARRIER, + BarrierWaitResult("sync") => BARRIERWAITRESULT, + HashMap("collection") => HASHMAP, + HashSet("collection") => HASHSET, + Instant("time") => INSTANT, + LazyLock("sync") => LAZYLOCK, + LockResult("sync") => LOCKRESULT, + Mutex("sync") => MUTEX, + MutexGuard("sync") => MUTEXGUARD, + Once("sync")=> ONCE, + OnceLock("sync") => ONCELOCK, + OnceState("sync") => ONCESTATE, + PoisonError("sync") => POISONERROR, + RwLock("sync") => RWLOCK, + RwLockReadGuard("sync") => RWLOCKREADGUARD, + RwLockWriteGuard("sync") => RWLOCKWRITEGUARD, + SyncCell("sync") => SYNCCELL, + SyncUnsafeCell("cell") => SYNCUNSAFECELL, + TryLockError("sync") => TRYLOCKERROR, + TryLockResult("sync") => TRYLOCKRESULT, } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs index 648d10c5..a3bd2c07 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs @@ -4,4 +4,7 @@ fn main() { let _ = std::time::Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible type + // bevy_platform::time::Instant } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr new file mode 100644 index 00000000..15c0ac97 --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr @@ -0,0 +1,15 @@ +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:6:13 + | +6 | let _ = std::time::Instant::now(); + | ^^^^^^^^^^^^^^^^^^ + | + = help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy_platform::time::Instant +note: the lint level is defined here + --> tests/ui/bevy_platform_alternative_exists/main.rs:3:9 + | +3 | #![deny(bevy::bevy_platform_alternative_exists)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 6156c1e95ad6b81e66227d4e0ba48147b099280f Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Sun, 12 Oct 2025 16:44:29 +0200 Subject: [PATCH 06/13] fix: wrong platform paths --- .../style/bevy_platform_alternative_exists.rs | 18 ++++++++++-------- .../main.fixed | 11 +++++++++++ .../bevy_platform_alternative_exists/main.rs | 7 ++++--- .../main.stderr | 12 ++++++++---- 4 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index ff1558a3..8529e442 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_help, source::snippet, ty::ty_from_hir_ty}; +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet, ty::ty_from_hir_ty}; +use rustc_errors::Applicability; use rustc_lint::{LateContext, LateLintPass}; use crate::{declare_bevy_lint, declare_bevy_lint_pass}; @@ -28,17 +29,18 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { // Check if for the given `ty` an alternative from `bevy_platform` exists. if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) { - span_lint_and_help( + span_lint_and_sugg( cx, BEVY_PLATFORM_ALTERNATIVE_EXISTS, hir_ty.span, BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc, - None, format!( "the type `{}` can be replaced with the `no_std` compatible type {}", snippet(cx.tcx.sess, hir_ty.span, ""), bevy_platform_alternative.full_path() ), + bevy_platform_alternative.full_path().to_string(), + Applicability::MachineApplicable, ); } } @@ -52,8 +54,8 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { /// declare_bevy_platform_types! { /// // The variant name => [`PathLookup`] that matches the equivalent type in the std. /// CustomType => CUSTOMTYPE, -/// // Optional the module path can be passed too, default is `bevy_platform::`. -/// // If an additional module path is added, it will result in: `bevy_platform::custom::thread::CustomType`. +/// // Optional the module path can be passed too, default is `bevy::platform::`. +/// // If an additional module path is added, it will result in: `bevy::platform::custom::thread::CustomType`. /// CustomType("custom::thread") => BARRIER, /// ``` macro_rules! declare_bevy_platform_types { @@ -87,7 +89,7 @@ macro_rules! declare_bevy_platform_types { ///Returns a string identifying this [`BevyPlatformType`]. This string is suitable for user output. pub const fn full_path(&self) -> &'static str { match self { - $(Self::$variant => concat!("bevy_platform", $("::", $mod_path,)? "::" , stringify!($variant)),)+ + $(Self::$variant => concat!("bevy::platform", $("::", $mod_path,)? "::" , stringify!($variant)),)+ } } } @@ -97,8 +99,8 @@ macro_rules! declare_bevy_platform_types { declare_bevy_platform_types! { Barrier("sync") => BARRIER, BarrierWaitResult("sync") => BARRIERWAITRESULT, - HashMap("collection") => HASHMAP, - HashSet("collection") => HASHSET, + HashMap("collections") => HASHMAP, + HashSet("collections") => HASHSET, Instant("time") => INSTANT, LazyLock("sync") => LAZYLOCK, LockResult("sync") => LOCKRESULT, diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed new file mode 100644 index 00000000..4ea6939b --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed @@ -0,0 +1,11 @@ +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::bevy_platform_alternative_exists)] + +fn main() { + let mut bar = bevy::platform::collections::HashMap::new(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` compatible + // type bevy::platform::collections::HashMap + bar.insert("foo", "bar"); +} diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs index a3bd2c07..14e60afd 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs @@ -3,8 +3,9 @@ #![deny(bevy::bevy_platform_alternative_exists)] fn main() { - let _ = std::time::Instant::now(); + let mut bar = std::collections::HashMap::new(); //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` - //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible type - // bevy_platform::time::Instant + //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` compatible + // type bevy::platform::collections::HashMap + bar.insert("foo", "bar"); } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr index 15c0ac97..38aeef5a 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr @@ -1,15 +1,19 @@ error: Used type from the `std` that has an existing alternative from `bevy_platform` - --> tests/ui/bevy_platform_alternative_exists/main.rs:6:13 + --> tests/ui/bevy_platform_alternative_exists/main.rs:6:19 | -6 | let _ = std::time::Instant::now(); - | ^^^^^^^^^^^^^^^^^^ +6 | let mut bar = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy_platform::time::Instant note: the lint level is defined here --> tests/ui/bevy_platform_alternative_exists/main.rs:3:9 | 3 | #![deny(bevy::bevy_platform_alternative_exists)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the type `std::collections::HashMap` can be replaced with the `no_std` compatible type bevy::platform::collections::HashMap + | +6 - let mut bar = std::collections::HashMap::new(); +6 + let mut bar = bevy::platform::collections::HashMap::new(); + | error: aborting due to 1 previous error From eeb34dc6bf69d5351797ba0210b4f1cc81470769 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Mon, 13 Oct 2025 22:45:59 +0200 Subject: [PATCH 07/13] feat: try to narrow down the type definition --- .../style/bevy_platform_alternative_exists.rs | 18 +++++++-- .../main.fixed | 21 +++++++++- .../bevy_platform_alternative_exists/main.rs | 23 ++++++++++- .../main.stderr | 40 ++++++++++++------- 4 files changed, 81 insertions(+), 21 deletions(-) diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 8529e442..4bc2d74c 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -1,5 +1,6 @@ use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet, ty::ty_from_hir_ty}; use rustc_errors::Applicability; +use rustc_hir::{QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use crate::{declare_bevy_lint, declare_bevy_lint_pass}; @@ -24,11 +25,22 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { return; } + let as_unambig_ty = hir_ty.as_unambig_ty(); + // lower the [`hir::Ty`] to a [`rustc_middle::ty::Ty`] - let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()); + let ty = ty_from_hir_ty(cx, as_unambig_ty); + + // Get the path to the type definition. + let TyKind::Path(QPath::Resolved(_, path)) = &as_unambig_ty.kind else { + return; + }; - // Check if for the given `ty` an alternative from `bevy_platform` exists. - if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) { + // if for the given `ty` an alternative from `bevy_platform` exists. + if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) + // Only emit a lint if the first segment of this path is `std` thus the type originates + // from the standart library. This prevents linting for `bevy::platform` types that are just a reexport of the `std`. + && path.segments.first().is_some_and(|segment| segment.ident.name.as_str().starts_with("std")) + { span_lint_and_sugg( cx, BEVY_PLATFORM_ALTERNATIVE_EXISTS, diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed index 4ea6939b..a21c8430 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed @@ -2,10 +2,27 @@ #![register_tool(bevy)] #![deny(bevy::bevy_platform_alternative_exists)] +#[allow(dead_code)] +struct Player { + attack_cd: bevy::platform::time::Instant, + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant +} + fn main() { - let mut bar = bevy::platform::collections::HashMap::new(); + let mut hash_map = bevy::platform::collections::HashMap::new(); //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` compatible // type bevy::platform::collections::HashMap - bar.insert("foo", "bar"); + hash_map.insert("foo", "bar"); + + let _time = bevy::platform::time::Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + // This should be fine even tho it will result in a `std::time::Instant` after full path + // resolution. + let _bevy_time = bevy::platform::time::Instant::now(); } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs index 14e60afd..fa7948b0 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs @@ -2,10 +2,29 @@ #![register_tool(bevy)] #![deny(bevy::bevy_platform_alternative_exists)] +use std::time; + +#[allow(dead_code)] +struct Player { + attack_cd: time::Instant, + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant +} + fn main() { - let mut bar = std::collections::HashMap::new(); + let mut hash_map = std::collections::HashMap::new(); //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` compatible // type bevy::platform::collections::HashMap - bar.insert("foo", "bar"); + hash_map.insert("foo", "bar"); + + let _time = std::time::Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + // This should be fine even tho it will result in a `std::time::Instant` after full path + // resolution. + let _bevy_time = bevy::platform::time::Instant::now(); } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr index 38aeef5a..6555bc81 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr @@ -1,19 +1,31 @@ error: Used type from the `std` that has an existing alternative from `bevy_platform` - --> tests/ui/bevy_platform_alternative_exists/main.rs:6:19 - | -6 | let mut bar = std::collections::HashMap::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | + --> tests/ui/bevy_platform_alternative_exists/main.rs:16:24 + | +16 | let mut hash_map = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | note: the lint level is defined here - --> tests/ui/bevy_platform_alternative_exists/main.rs:3:9 - | -3 | #![deny(bevy::bevy_platform_alternative_exists)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> tests/ui/bevy_platform_alternative_exists/main.rs:3:9 + | + 3 | #![deny(bevy::bevy_platform_alternative_exists)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: the type `std::collections::HashMap` can be replaced with the `no_std` compatible type bevy::platform::collections::HashMap - | -6 - let mut bar = std::collections::HashMap::new(); -6 + let mut bar = bevy::platform::collections::HashMap::new(); - | + | +16 - let mut hash_map = std::collections::HashMap::new(); +16 + let mut hash_map = bevy::platform::collections::HashMap::new(); + | -error: aborting due to 1 previous error +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:22:17 + | +22 | let _time = std::time::Instant::now(); + | ^^^^^^^^^^^^^^^^^^ + | +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +22 - let _time = std::time::Instant::now(); +22 + let _time = bevy::platform::time::Instant::now(); + | + +error: aborting due to 2 previous errors From e8fa3d8bc9a528fe9de79206c209b8a6c1f72df4 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Sun, 19 Oct 2025 21:46:55 +0200 Subject: [PATCH 08/13] feat: switch to check_path based on zulip feedback --- .../style/bevy_platform_alternative_exists.rs | 104 ++++++++------ bevy_lint/src/paths.rs | 1 + bevy_lint/src/sym.rs | 2 +- bevy_lint/tests/ui.rs | 1 + .../main.fixed | 55 ++++++-- .../bevy_platform_alternative_exists/main.rs | 57 +++++--- .../main.stderr | 128 +++++++++++++++--- .../time.fixed | 63 +++++++++ .../bevy_platform_alternative_exists/time.rs | 63 +++++++++ .../time.stderr | 91 +++++++++++++ .../time_import_issue.rs | 14 ++ 11 files changed, 493 insertions(+), 86 deletions(-) create mode 100644 bevy_lint/tests/ui/bevy_platform_alternative_exists/time.fixed create mode 100644 bevy_lint/tests/ui/bevy_platform_alternative_exists/time.rs create mode 100644 bevy_lint/tests/ui/bevy_platform_alternative_exists/time.stderr create mode 100644 bevy_lint/tests/ui/bevy_platform_alternative_exists/time_import_issue.rs diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 4bc2d74c..0c219e15 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -1,9 +1,12 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet, ty::ty_from_hir_ty}; +use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, source::snippet}; use rustc_errors::Applicability; -use rustc_hir::{QPath, TyKind}; +use rustc_hir::{ + HirId, Path, PathSegment, + def::{DefKind, Res}, +}; use rustc_lint::{LateContext, LateLintPass}; -use crate::{declare_bevy_lint, declare_bevy_lint_pass}; +use crate::{declare_bevy_lint, declare_bevy_lint_pass, sym, utils::hir_parse::generic_args_snippet}; declare_bevy_lint! { pub(crate) BEVY_PLATFORM_ALTERNATIVE_EXISTS, @@ -16,48 +19,70 @@ declare_bevy_lint_pass! { } impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { - fn check_ty( - &mut self, - cx: &LateContext<'tcx>, - hir_ty: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>, - ) { - if hir_ty.span.in_external_macro(cx.tcx.sess.source_map()) { - return; - } - - let as_unambig_ty = hir_ty.as_unambig_ty(); - - // lower the [`hir::Ty`] to a [`rustc_middle::ty::Ty`] - let ty = ty_from_hir_ty(cx, as_unambig_ty); + fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { + if let Res::Def(def_kind, def_id) = path.res + // Retrieve the first path segment, this could look like: `bevy`, `std`, `serde`. + && let Some(first_segment) = get_first_segment(path) + // Skip if this span originates from an external macro. + // Or likely originates from a proc_macro, note this should be called after + // `in_external_macro`. + && !path.span.in_external_macro(cx.tcx.sess.source_map()) + && !is_from_proc_macro(cx, &first_segment.ident) + // Skip if this Definition is not originating from `std`. + && first_segment.ident.name == sym::std + // Get the def_id of the crate from first segment. + && let Res::Def(DefKind::Mod,crate_def_id) = first_segment.res + // If the first segment is not the crate root, then this type was checked when + // importing. + && crate_def_id.is_crate_root() + // Get potential generic arguments. + && let Some(generic_args) = path.segments.last().map(|s| generic_args_snippet(cx, s)) + { - // Get the path to the type definition. - let TyKind::Path(QPath::Resolved(_, path)) = &as_unambig_ty.kind else { - return; - }; + // Skip Resolutions that are modules for example: `use std::time`. + // This lint checks if a given Type from the `std` exists in `bevy_platform` and does not + // compare entire modules and getting the ty from a module DefId will result in a + // panic. + if DefKind::Mod == def_kind{ + return; + } - // if for the given `ty` an alternative from `bevy_platform` exists. - if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) - // Only emit a lint if the first segment of this path is `std` thus the type originates - // from the standart library. This prevents linting for `bevy::platform` types that are just a reexport of the `std`. - && path.segments.first().is_some_and(|segment| segment.ident.name.as_str().starts_with("std")) - { - span_lint_and_sugg( - cx, - BEVY_PLATFORM_ALTERNATIVE_EXISTS, - hir_ty.span, - BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc, - format!( - "the type `{}` can be replaced with the `no_std` compatible type {}", - snippet(cx.tcx.sess, hir_ty.span, ""), - bevy_platform_alternative.full_path() - ), - bevy_platform_alternative.full_path().to_string(), - Applicability::MachineApplicable, - ); + // Get the Ty of this Definition. + let ty = cx.tcx.type_of(def_id).skip_binder(); + //Check if an alternative exists in `bevy_platform`. + if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) { + span_lint_and_sugg( + cx, + BEVY_PLATFORM_ALTERNATIVE_EXISTS, + path.span, + BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc, + format!( + "the type `{}` can be replaced with the `no_std` compatible type {}{}", + snippet(cx.tcx.sess, path.span, ""), + bevy_platform_alternative.full_path(),generic_args, + ), + format!("{}{}",bevy_platform_alternative.full_path(),generic_args), + Applicability::MachineApplicable, + ); + } } } } +/// Returns the first named segment of a [`Path`]. +/// +/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`] +/// is returned. +fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> { + match path.segments { + // A global path will have PathRoot as the first segment. In this case, return the segment + // after. + [x, y, ..] if x.ident.name == rustc_span::symbol::kw::PathRoot => Some(y), + [x, ..] => Some(x), + _ => None, + } +} + /// Creates an enum containing all the types form `bevy_platform` as variants. /// /// # Example @@ -109,6 +134,7 @@ macro_rules! declare_bevy_platform_types { } declare_bevy_platform_types! { + Arc("sync") => ARC, Barrier("sync") => BARRIER, BarrierWaitResult("sync") => BARRIERWAITRESULT, HashMap("collections") => HASHMAP, diff --git a/bevy_lint/src/paths.rs b/bevy_lint/src/paths.rs index b816f42c..7b534059 100644 --- a/bevy_lint/src/paths.rs +++ b/bevy_lint/src/paths.rs @@ -94,6 +94,7 @@ pub mod bevy_platform_types { use crate::sym; + pub static ARC: PathLookup = type_path!(std::sync::Arc); pub static BARRIER: PathLookup = type_path!(std::sync::Barrier); pub static BARRIERWAITRESULT: PathLookup = type_path!(std::sync::BarrierWaitResult); pub static HASHMAP: PathLookup = type_path!(std::collections::HashMap); diff --git a/bevy_lint/src/sym.rs b/bevy_lint/src/sym.rs index 79552e1e..fb040d2d 100644 --- a/bevy_lint/src/sym.rs +++ b/bevy_lint/src/sym.rs @@ -58,7 +58,7 @@ use clippy_utils::sym::EXTRA_SYMBOLS as CLIPPY_SYMBOLS; /// These are symbols that we use but are already interned by either the compiler or Clippy. pub use clippy_utils::sym::filter; pub use rustc_span::sym::{ - HashMap, HashSet, Instant, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, + Arc, HashMap, HashSet, Instant, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, SyncUnsafeCell, bevy_ecs, bundle, message, plugin, reflect, std, sync, }; use rustc_span::{Symbol, symbol::PREDEFINED_SYMBOLS_COUNT}; diff --git a/bevy_lint/tests/ui.rs b/bevy_lint/tests/ui.rs index 5bf5a25f..4150b226 100644 --- a/bevy_lint/tests/ui.rs +++ b/bevy_lint/tests/ui.rs @@ -9,5 +9,6 @@ mod test_utils; fn main() { let config = base_config("ui").unwrap(); + run_tests(config).unwrap(); } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed index a21c8430..e5b7d617 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed @@ -1,14 +1,7 @@ #![feature(register_tool)] #![register_tool(bevy)] #![deny(bevy::bevy_platform_alternative_exists)] - -#[allow(dead_code)] -struct Player { - attack_cd: bevy::platform::time::Instant, - //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` - //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible - // type bevy::platform::time::Instant -} +#![allow(dead_code)] fn main() { let mut hash_map = bevy::platform::collections::HashMap::new(); @@ -17,12 +10,46 @@ fn main() { // type bevy::platform::collections::HashMap hash_map.insert("foo", "bar"); - let _time = bevy::platform::time::Instant::now(); + // compatible type bevy::platform::collections::HashMap + //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` + //~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + let _declared_hash_map: bevy::platform::collections::HashMap:: = bevy::platform::collections::HashMap::new(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` + // compatible type bevy::platform::collections::HashMap + + let _arc = bevy::platform::sync::Arc::new(10); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::sync::Arc` can be replaced with the `no_std` + // compatible type bevy::platform::sync::Arc + + let barrier = bevy::platform::sync::Barrier::new(10); //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` - //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible - // type bevy::platform::time::Instant + //~| HELP: the type `std::sync::Barrier` can be replaced with the `no_std` + // compatible type bevy::platform::sync::Barrier - // This should be fine even tho it will result in a `std::time::Instant` after full path - // resolution. - let _bevy_time = bevy::platform::time::Instant::now(); + let _barrier_wait_result: bevy::platform::sync::BarrierWaitResult = barrier.wait(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::sync::BarrierWaitResult` can be replaced with the `no_std` + // compatible type bevy::platform::sync::BarrierWaitResult + + let mut hash_set = bevy::platform::collections::HashSet::new(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std` + // compatible type bevy::platform::collections::HashSet + + hash_set.insert(1); + + // compatible type bevy::platform::collections::HashSet + //~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std` + //~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + let _declared_hash_map: bevy::platform::collections::HashSet:: = bevy::platform::collections::HashSet::new(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std` + // compatible type bevy::platform::collections::HashSet + + let _lazy = bevy::platform::sync::LazyLock::new(|| "lazy"); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::sync::LazyLock` can be replaced with the `no_std` + // compatible type bevy::platform::sync::LazyLock } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs index fa7948b0..31565aa3 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs @@ -1,16 +1,7 @@ #![feature(register_tool)] #![register_tool(bevy)] #![deny(bevy::bevy_platform_alternative_exists)] - -use std::time; - -#[allow(dead_code)] -struct Player { - attack_cd: time::Instant, - //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` - //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible - // type bevy::platform::time::Instant -} +#![allow(dead_code)] fn main() { let mut hash_map = std::collections::HashMap::new(); @@ -19,12 +10,46 @@ fn main() { // type bevy::platform::collections::HashMap hash_map.insert("foo", "bar"); - let _time = std::time::Instant::now(); + // compatible type bevy::platform::collections::HashMap + //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` + //~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + let _declared_hash_map: std::collections::HashMap = std::collections::HashMap::new(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std` + // compatible type bevy::platform::collections::HashMap + + let _arc = std::sync::Arc::new(10); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::sync::Arc` can be replaced with the `no_std` + // compatible type bevy::platform::sync::Arc + + let barrier = std::sync::Barrier::new(10); //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` - //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible - // type bevy::platform::time::Instant + //~| HELP: the type `std::sync::Barrier` can be replaced with the `no_std` + // compatible type bevy::platform::sync::Barrier - // This should be fine even tho it will result in a `std::time::Instant` after full path - // resolution. - let _bevy_time = bevy::platform::time::Instant::now(); + let _barrier_wait_result: std::sync::BarrierWaitResult = barrier.wait(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::sync::BarrierWaitResult` can be replaced with the `no_std` + // compatible type bevy::platform::sync::BarrierWaitResult + + let mut hash_set = std::collections::HashSet::new(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std` + // compatible type bevy::platform::collections::HashSet + + hash_set.insert(1); + + // compatible type bevy::platform::collections::HashSet + //~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std` + //~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + let _declared_hash_map: std::collections::HashSet = std::collections::HashSet::new(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std` + // compatible type bevy::platform::collections::HashSet + + let _lazy = std::sync::LazyLock::new(|| "lazy"); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::sync::LazyLock` can be replaced with the `no_std` + // compatible type bevy::platform::sync::LazyLock } diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr index 6555bc81..cd50587b 100644 --- a/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr @@ -1,31 +1,127 @@ error: Used type from the `std` that has an existing alternative from `bevy_platform` - --> tests/ui/bevy_platform_alternative_exists/main.rs:16:24 + --> tests/ui/bevy_platform_alternative_exists/main.rs:7:24 + | +7 | let mut hash_map = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/bevy_platform_alternative_exists/main.rs:3:9 + | +3 | #![deny(bevy::bevy_platform_alternative_exists)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the type `std::collections::HashMap` can be replaced with the `no_std` compatible type bevy::platform::collections::HashMap + | +7 - let mut hash_map = std::collections::HashMap::new(); +7 + let mut hash_map = bevy::platform::collections::HashMap::new(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:16:67 + | +16 | let _declared_hash_map: std::collections::HashMap = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the type `std::collections::HashMap` can be replaced with the `no_std` compatible type bevy::platform::collections::HashMap + | +16 - let _declared_hash_map: std::collections::HashMap = std::collections::HashMap::new(); +16 + let _declared_hash_map: std::collections::HashMap = bevy::platform::collections::HashMap::new(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:16:29 + | +16 | let _declared_hash_map: std::collections::HashMap = std::collections::HashMap::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the type `std::collections::HashMap` can be replaced with the `no_std` compatible type bevy::platform::collections::HashMap:: + | +16 - let _declared_hash_map: std::collections::HashMap = std::collections::HashMap::new(); +16 + let _declared_hash_map: bevy::platform::collections::HashMap:: = std::collections::HashMap::new(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:21:16 + | +21 | let _arc = std::sync::Arc::new(10); + | ^^^^^^^^^^^^^^ + | +help: the type `std::sync::Arc` can be replaced with the `no_std` compatible type bevy::platform::sync::Arc + | +21 - let _arc = std::sync::Arc::new(10); +21 + let _arc = bevy::platform::sync::Arc::new(10); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:26:19 + | +26 | let barrier = std::sync::Barrier::new(10); + | ^^^^^^^^^^^^^^^^^^ | -16 | let mut hash_map = std::collections::HashMap::new(); +help: the type `std::sync::Barrier` can be replaced with the `no_std` compatible type bevy::platform::sync::Barrier + | +26 - let barrier = std::sync::Barrier::new(10); +26 + let barrier = bevy::platform::sync::Barrier::new(10); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:31:31 + | +31 | let _barrier_wait_result: std::sync::BarrierWaitResult = barrier.wait(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the type `std::sync::BarrierWaitResult` can be replaced with the `no_std` compatible type bevy::platform::sync::BarrierWaitResult + | +31 - let _barrier_wait_result: std::sync::BarrierWaitResult = barrier.wait(); +31 + let _barrier_wait_result: bevy::platform::sync::BarrierWaitResult = barrier.wait(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:36:24 + | +36 | let mut hash_set = std::collections::HashSet::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the lint level is defined here - --> tests/ui/bevy_platform_alternative_exists/main.rs:3:9 +help: the type `std::collections::HashSet` can be replaced with the `no_std` compatible type bevy::platform::collections::HashSet | - 3 | #![deny(bevy::bevy_platform_alternative_exists)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: the type `std::collections::HashMap` can be replaced with the `no_std` compatible type bevy::platform::collections::HashMap +36 - let mut hash_set = std::collections::HashSet::new(); +36 + let mut hash_set = bevy::platform::collections::HashSet::new(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:46:62 + | +46 | let _declared_hash_map: std::collections::HashSet = std::collections::HashSet::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the type `std::collections::HashSet` can be replaced with the `no_std` compatible type bevy::platform::collections::HashSet + | +46 - let _declared_hash_map: std::collections::HashSet = std::collections::HashSet::new(); +46 + let _declared_hash_map: std::collections::HashSet = bevy::platform::collections::HashSet::new(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/main.rs:46:29 + | +46 | let _declared_hash_map: std::collections::HashSet = std::collections::HashSet::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the type `std::collections::HashSet` can be replaced with the `no_std` compatible type bevy::platform::collections::HashSet:: | -16 - let mut hash_map = std::collections::HashMap::new(); -16 + let mut hash_map = bevy::platform::collections::HashMap::new(); +46 - let _declared_hash_map: std::collections::HashSet = std::collections::HashSet::new(); +46 + let _declared_hash_map: bevy::platform::collections::HashSet:: = std::collections::HashSet::new(); | error: Used type from the `std` that has an existing alternative from `bevy_platform` - --> tests/ui/bevy_platform_alternative_exists/main.rs:22:17 + --> tests/ui/bevy_platform_alternative_exists/main.rs:51:17 | -22 | let _time = std::time::Instant::now(); - | ^^^^^^^^^^^^^^^^^^ +51 | let _lazy = std::sync::LazyLock::new(|| "lazy"); + | ^^^^^^^^^^^^^^^^^^^ | -help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant +help: the type `std::sync::LazyLock` can be replaced with the `no_std` compatible type bevy::platform::sync::LazyLock | -22 - let _time = std::time::Instant::now(); -22 + let _time = bevy::platform::time::Instant::now(); +51 - let _lazy = std::sync::LazyLock::new(|| "lazy"); +51 + let _lazy = bevy::platform::sync::LazyLock::new(|| "lazy"); | -error: aborting due to 2 previous errors +error: aborting due to 10 previous errors diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.fixed b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.fixed new file mode 100644 index 00000000..414f5a0d --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.fixed @@ -0,0 +1,63 @@ +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::bevy_platform_alternative_exists)] +#![allow(dead_code)] + +use bevy::platform::time::Instant; +//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` +//~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible +// type bevy::platform::time::Instant + +// Ensure the lint can be muted on imports. +#[allow(bevy::bevy_platform_alternative_exists, unused_imports)] +use std::time::Instant as muted; + +struct Player { + attack_cd: bevy::platform::time::Instant, + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant +} + +fn main() { + let _time = bevy::platform::time::Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + // This should be fine even tho it will result in a `std::time::Instant` after full path + // resolution. + let _bevy_time = bevy::platform::time::Instant::now(); + + // Ensure the lint can be muted. + #[allow(bevy::bevy_platform_alternative_exists)] + let _time = std::time::Instant::now(); + + let _player_bevy = Player { + attack_cd: bevy::platform::time::Instant::now(), + }; + + let _player = Player { + attack_cd: bevy::platform::time::Instant::now(), + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + }; + + let _time = Instant::now(); + + let _time: bevy::platform::time::Instant = Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + // type bevy::platform::time::Instant + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + //~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + let _time: bevy::platform::time::Instant = bevy::platform::time::Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + let _bevy_time: bevy::platform::time::Instant = bevy::platform::time::Instant::now(); +} diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.rs new file mode 100644 index 00000000..245817b7 --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.rs @@ -0,0 +1,63 @@ +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::bevy_platform_alternative_exists)] +#![allow(dead_code)] + +use std::time::Instant; +//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` +//~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible +// type bevy::platform::time::Instant + +// Ensure the lint can be muted on imports. +#[allow(bevy::bevy_platform_alternative_exists, unused_imports)] +use std::time::Instant as muted; + +struct Player { + attack_cd: std::time::Instant, + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant +} + +fn main() { + let _time = std::time::Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + // This should be fine even tho it will result in a `std::time::Instant` after full path + // resolution. + let _bevy_time = bevy::platform::time::Instant::now(); + + // Ensure the lint can be muted. + #[allow(bevy::bevy_platform_alternative_exists)] + let _time = std::time::Instant::now(); + + let _player_bevy = Player { + attack_cd: bevy::platform::time::Instant::now(), + }; + + let _player = Player { + attack_cd: std::time::Instant::now(), + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + }; + + let _time = Instant::now(); + + let _time: std::time::Instant = Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + // type bevy::platform::time::Instant + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + //~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + let _time: std::time::Instant = std::time::Instant::now(); + //~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform` + //~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible + // type bevy::platform::time::Instant + + let _bevy_time: bevy::platform::time::Instant = bevy::platform::time::Instant::now(); +} diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.stderr b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.stderr new file mode 100644 index 00000000..62e9219c --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time.stderr @@ -0,0 +1,91 @@ +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/time.rs:6:5 + | +6 | use std::time::Instant; + | ^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> tests/ui/bevy_platform_alternative_exists/time.rs:3:9 + | +3 | #![deny(bevy::bevy_platform_alternative_exists)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +6 - use std::time::Instant; +6 + use bevy::platform::time::Instant; + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/time.rs:16:16 + | +16 | attack_cd: std::time::Instant, + | ^^^^^^^^^^^^^^^^^^ + | +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +16 - attack_cd: std::time::Instant, +16 + attack_cd: bevy::platform::time::Instant, + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/time.rs:23:17 + | +23 | let _time = std::time::Instant::now(); + | ^^^^^^^^^^^^^^^^^^ + | +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +23 - let _time = std::time::Instant::now(); +23 + let _time = bevy::platform::time::Instant::now(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/time.rs:41:20 + | +41 | attack_cd: std::time::Instant::now(), + | ^^^^^^^^^^^^^^^^^^ + | +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +41 - attack_cd: std::time::Instant::now(), +41 + attack_cd: bevy::platform::time::Instant::now(), + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/time.rs:49:16 + | +49 | let _time: std::time::Instant = Instant::now(); + | ^^^^^^^^^^^^^^^^^^ + | +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +49 - let _time: std::time::Instant = Instant::now(); +49 + let _time: bevy::platform::time::Instant = Instant::now(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/time.rs:57:37 + | +57 | let _time: std::time::Instant = std::time::Instant::now(); + | ^^^^^^^^^^^^^^^^^^ + | +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +57 - let _time: std::time::Instant = std::time::Instant::now(); +57 + let _time: std::time::Instant = bevy::platform::time::Instant::now(); + | + +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> tests/ui/bevy_platform_alternative_exists/time.rs:57:16 + | +57 | let _time: std::time::Instant = std::time::Instant::now(); + | ^^^^^^^^^^^^^^^^^^ + | +help: the type `std::time::Instant` can be replaced with the `no_std` compatible type bevy::platform::time::Instant + | +57 - let _time: std::time::Instant = std::time::Instant::now(); +57 + let _time: bevy::platform::time::Instant = std::time::Instant::now(); + | + +error: aborting due to 7 previous errors + diff --git a/bevy_lint/tests/ui/bevy_platform_alternative_exists/time_import_issue.rs b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time_import_issue.rs new file mode 100644 index 00000000..d6d3b3d7 --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/time_import_issue.rs @@ -0,0 +1,14 @@ +// This tracks the issue that we cannot lint if only `std::time` is imported, if this tests starts +// failing, this known issue has been solved. +//@check-pass + +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::bevy_platform_alternative_exists)] +#![allow(dead_code)] + +use std::time; + +fn main() { + let _use_time = time::Instant::now(); +} From 031d74489b61bb8ed9b53e5fd5f08d926ac6a8ed Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Tue, 21 Oct 2025 14:52:02 +0200 Subject: [PATCH 09/13] feat: only check structs for now --- .../lints/style/bevy_platform_alternative_exists.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 0c219e15..0ee2ff7b 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -20,7 +20,8 @@ declare_bevy_lint_pass! { impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { - if let Res::Def(def_kind, def_id) = path.res + // Skip Resolutions that are not Structs for example: `use std::time`. + if let Res::Def(DefKind::Struct, def_id) = path.res // Retrieve the first path segment, this could look like: `bevy`, `std`, `serde`. && let Some(first_segment) = get_first_segment(path) // Skip if this span originates from an external macro. @@ -38,15 +39,6 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { // Get potential generic arguments. && let Some(generic_args) = path.segments.last().map(|s| generic_args_snippet(cx, s)) { - - // Skip Resolutions that are modules for example: `use std::time`. - // This lint checks if a given Type from the `std` exists in `bevy_platform` and does not - // compare entire modules and getting the ty from a module DefId will result in a - // panic. - if DefKind::Mod == def_kind{ - return; - } - // Get the Ty of this Definition. let ty = cx.tcx.type_of(def_id).skip_binder(); //Check if an alternative exists in `bevy_platform`. From 0044776d8b3074090d9b170ba4795c11de0d585c Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Tue, 21 Oct 2025 14:53:37 +0200 Subject: [PATCH 10/13] cargo fmt --- .../lints/style/bevy_platform_alternative_exists.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 0ee2ff7b..406c4116 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -6,7 +6,9 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; -use crate::{declare_bevy_lint, declare_bevy_lint_pass, sym, utils::hir_parse::generic_args_snippet}; +use crate::{ + declare_bevy_lint, declare_bevy_lint_pass, sym, utils::hir_parse::generic_args_snippet, +}; declare_bevy_lint! { pub(crate) BEVY_PLATFORM_ALTERNATIVE_EXISTS, @@ -21,7 +23,7 @@ declare_bevy_lint_pass! { impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) { // Skip Resolutions that are not Structs for example: `use std::time`. - if let Res::Def(DefKind::Struct, def_id) = path.res + if let Res::Def(DefKind::Struct, def_id) = path.res // Retrieve the first path segment, this could look like: `bevy`, `std`, `serde`. && let Some(first_segment) = get_first_segment(path) // Skip if this span originates from an external macro. @@ -41,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { { // Get the Ty of this Definition. let ty = cx.tcx.type_of(def_id).skip_binder(); - //Check if an alternative exists in `bevy_platform`. + // Check if an alternative exists in `bevy_platform`. if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) { span_lint_and_sugg( cx, @@ -51,9 +53,10 @@ impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists { format!( "the type `{}` can be replaced with the `no_std` compatible type {}{}", snippet(cx.tcx.sess, path.span, ""), - bevy_platform_alternative.full_path(),generic_args, + bevy_platform_alternative.full_path(), + generic_args, ), - format!("{}{}",bevy_platform_alternative.full_path(),generic_args), + format!("{}{}", bevy_platform_alternative.full_path(), generic_args), Applicability::MachineApplicable, ); } From d754d81043133d9d9415c4cf46fe25a3833354f9 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Tue, 21 Oct 2025 15:31:45 +0200 Subject: [PATCH 11/13] doc: add lint description --- .../style/bevy_platform_alternative_exists.rs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 406c4116..92292b5b 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -1,3 +1,59 @@ +//! Checks for types from `std` that have an equivalent in `bevy_platform`. +//! +//! # Motivation +//! +//! `bevy_platform` helps with platform compatibility support by providing drop in replacements for +//! the following types: +//! +//! - Arc, +//! - Barrier, +//! - BarrierWaitResult, +//! - HashMap, +//! - HashSet, +//! - Instant, +//! - LazyLock, +//! - LockResult, +//! - Mutex, +//! - MutexGuard, +//! - Once, +//! - OnceLock, +//! - OnceState, +//! - PoisonError, +//! - RwLock, +//! - RwLockReadGuard, +//! - RwLockWriteGuard, +//! - SyncCell, +//! - SyncUnsafeCell, +//! - TryLockError, +//! - TryLockResult, +//! +//! +//! # Known Issues +//! +//! This lint does not currently support checking partial imported definitions. For example: +//! +//! ``` +//! use std::time; +//! +//! let now = time::Instant::now(); +//! ``` +//! +//! Will not emit a lint. +//! +//! # Example +//! +//! ``` +//! use std::time::Instant; +//! let now = Instant::now(); +//! ``` +//! +//! Use instead: +//! +//! ``` +//! use bevy::platform::time::Instant; +//! let now = Instant::now(); +//! ``` + use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, source::snippet}; use rustc_errors::Applicability; use rustc_hir::{ From efef1ba858a498395ed7c3f77d72381924f038ec Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Wed, 22 Oct 2025 22:16:31 +0200 Subject: [PATCH 12/13] feat: add structs from hash module that also exist in the std --- .../style/bevy_platform_alternative_exists.rs | 6 +++++- bevy_lint/src/paths.rs | 2 ++ bevy_lint/src/sym.rs | 18 ++++++++++-------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs index 92292b5b..eebdea5f 100644 --- a/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -8,6 +8,7 @@ //! - Arc, //! - Barrier, //! - BarrierWaitResult, +//! - DefaultHasher, //! - HashMap, //! - HashSet, //! - Instant, @@ -19,13 +20,14 @@ //! - OnceLock, //! - OnceState, //! - PoisonError, +//! - RandomState, //! - RwLock, //! - RwLockReadGuard, //! - RwLockWriteGuard, //! - SyncCell, //! - SyncUnsafeCell, //! - TryLockError, -//! - TryLockResult, +//! - TryLockResult //! //! //! # Known Issues @@ -188,6 +190,7 @@ declare_bevy_platform_types! { Arc("sync") => ARC, Barrier("sync") => BARRIER, BarrierWaitResult("sync") => BARRIERWAITRESULT, + DefaultHasher("hash") => DEFAULTHASHER, HashMap("collections") => HASHMAP, HashSet("collections") => HASHSET, Instant("time") => INSTANT, @@ -199,6 +202,7 @@ declare_bevy_platform_types! { OnceLock("sync") => ONCELOCK, OnceState("sync") => ONCESTATE, PoisonError("sync") => POISONERROR, + RandomState("hash") => RANDOMSTATE, RwLock("sync") => RWLOCK, RwLockReadGuard("sync") => RWLOCKREADGUARD, RwLockWriteGuard("sync") => RWLOCKWRITEGUARD, diff --git a/bevy_lint/src/paths.rs b/bevy_lint/src/paths.rs index 7b534059..7c740659 100644 --- a/bevy_lint/src/paths.rs +++ b/bevy_lint/src/paths.rs @@ -97,6 +97,7 @@ pub mod bevy_platform_types { pub static ARC: PathLookup = type_path!(std::sync::Arc); pub static BARRIER: PathLookup = type_path!(std::sync::Barrier); pub static BARRIERWAITRESULT: PathLookup = type_path!(std::sync::BarrierWaitResult); + pub static DEFAULTHASHER: PathLookup = type_path!(std::hash::DefaultHasher); pub static HASHMAP: PathLookup = type_path!(std::collections::HashMap); pub static HASHSET: PathLookup = type_path!(std::collections::HashSet); pub static INSTANT: PathLookup = type_path!(std::time::Instant); @@ -108,6 +109,7 @@ pub mod bevy_platform_types { pub static ONCELOCK: PathLookup = type_path!(std::sync::OnceLock); pub static ONCESTATE: PathLookup = type_path!(std::sync::OnceState); pub static POISONERROR: PathLookup = type_path!(std::sync::PoisonError); + pub static RANDOMSTATE: PathLookup = type_path!(std::hash::RandomState); pub static RWLOCK: PathLookup = type_path!(std::sync::RwLock); pub static RWLOCKREADGUARD: PathLookup = type_path!(std::sync::RwLockReadGuard); pub static RWLOCKWRITEGUARD: PathLookup = type_path!(std::sync::RwLockWriteGuard); diff --git a/bevy_lint/src/sym.rs b/bevy_lint/src/sym.rs index fb040d2d..5e881893 100644 --- a/bevy_lint/src/sym.rs +++ b/bevy_lint/src/sym.rs @@ -59,7 +59,7 @@ use clippy_utils::sym::EXTRA_SYMBOLS as CLIPPY_SYMBOLS; pub use clippy_utils::sym::filter; pub use rustc_span::sym::{ Arc, HashMap, HashSet, Instant, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, - SyncUnsafeCell, bevy_ecs, bundle, message, plugin, reflect, std, sync, + SyncUnsafeCell, bevy_ecs, bundle, hash, message, plugin, reflect, std, sync, }; use rustc_span::{Symbol, symbol::PREDEFINED_SYMBOLS_COUNT}; @@ -139,6 +139,7 @@ declare_bevy_symbols! { Commands, component, Component, + DefaultHasher, Deferred, deferred_world, DeferredWorld, @@ -155,17 +156,23 @@ declare_bevy_symbols! { insert_resource, iter_current_update_messages, LazyLock, + LockResult, main_schedule, Message, Messages, Mut, MutUntyped, NonSendMut, + Once, + OnceLock, + OnceState, PartialReflect, Plugin, + PoisonError, PtrMut, query, Query, + RandomState, Reflect, related_methods, RelatedSpawner, @@ -182,17 +189,12 @@ declare_bevy_symbols! { system_param, SystemSet, time, + TryLockError, + TryLockResult, Update, With, world, World, - Once, - OnceLock, - OnceState, - LockResult, - PoisonError, - TryLockError, - TryLockResult, } /// Returns a list of strings that should be supplied to From d7489f7edcb7b2c842429cab0c069d314b625db8 Mon Sep 17 00:00:00 2001 From: DaAlbrecht Date: Wed, 22 Oct 2025 22:39:02 +0200 Subject: [PATCH 13/13] chore: undo leftover empty line from trying to implement custom flags. See: https://github.com/oli-obk/ui_test/issues/341 i realized i can just mark the test as passing and dont have to look into this --- bevy_lint/tests/ui.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bevy_lint/tests/ui.rs b/bevy_lint/tests/ui.rs index 4150b226..5bf5a25f 100644 --- a/bevy_lint/tests/ui.rs +++ b/bevy_lint/tests/ui.rs @@ -9,6 +9,5 @@ mod test_utils; fn main() { let config = base_config("ui").unwrap(); - run_tests(config).unwrap(); }