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..eebdea5f --- /dev/null +++ b/bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs @@ -0,0 +1,213 @@ +//! 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, +//! - DefaultHasher, +//! - HashMap, +//! - HashSet, +//! - Instant, +//! - LazyLock, +//! - LockResult, +//! - Mutex, +//! - MutexGuard, +//! - Once, +//! - OnceLock, +//! - OnceState, +//! - PoisonError, +//! - RandomState, +//! - 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::{ + HirId, Path, PathSegment, + def::{DefKind, Res}, +}; +use rustc_lint::{LateContext, LateLintPass}; + +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, + 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_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 + // 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 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 +/// +/// ```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 $(($mod_path:expr))? => $path:ident + ) + ,+$(,)? + ) => { + #[derive(Copy, Clone)] + pub enum BevyPlatformType { + $( + $variant, + )+ + } + + impl BevyPlatformType{ + /// 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::*; + $( + if $path.matches_ty(cx, ty) { + Some(Self::$variant) + } else + )+ + { + 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! { + Arc("sync") => ARC, + Barrier("sync") => BARRIER, + BarrierWaitResult("sync") => BARRIERWAITRESULT, + DefaultHasher("hash") => DEFAULTHASHER, + HashMap("collections") => HASHMAP, + HashSet("collections") => 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, + RandomState("hash") => RANDOMSTATE, + 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/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/src/paths.rs b/bevy_lint/src/paths.rs index 036851e2..0d4e119b 100644 --- a/bevy_lint/src/paths.rs +++ b/bevy_lint/src/paths.rs @@ -86,3 +86,35 @@ 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 :) in neovim, use `:sort/\vpub static \w+/ ri` +pub mod bevy_platform_types { + use clippy_utils::paths::{PathLookup, PathNS}; + + 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 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); + 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 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); + 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); +} diff --git a/bevy_lint/src/sym.rs b/bevy_lint/src/sym.rs index d4cdb280..4eb7207d 100644 --- a/bevy_lint/src/sym.rs +++ b/bevy_lint/src/sym.rs @@ -55,9 +55,12 @@ )] 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. +/// These are symbols that we use but are already interned by either the compiler or Clippy. pub use clippy_utils::sym::{app, filter}; -pub use rustc_span::sym::{bevy_ecs, bundle, message, plugin, reflect}; +pub use rustc_span::sym::{ + Arc, HashMap, HashSet, Instant, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, + SyncUnsafeCell, bevy_ecs, bundle, hash, message, plugin, reflect, std, sync, +}; use rustc_span::{Symbol, symbol::PREDEFINED_SYMBOLS_COUNT}; /// The starting offset used for the first Bevy-specific symbol. @@ -114,36 +117,49 @@ 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` in nvim) declare_bevy_symbols! { App, + Barrier, + BarrierWaitResult, Bundle, Camera, Commands, Component, + DefaultHasher, Deferred, DeferredWorld, EntityCommands, EntityMut, Event, Events, + Exclusive, FilteredEntityMut, FixedUpdate, + LazyLock, + LockResult, Message, Messages, Mut, MutUntyped, NonSendMut, + Once, + OnceLock, + OnceState, PartialReflect, Plugin, + PoisonError, PtrMut, Query, + RandomState, Reflect, RelatedSpawner, RelatedSpawnerCommands, ResMut, Resource, SystemSet, + TryLockError, + TryLockResult, Update, With, World, @@ -154,7 +170,9 @@ declare_bevy_symbols! { bevy_ptr, bevy_reflect, camera, + cell, change_detection, + collections, commands, component, deferred_world, @@ -174,6 +192,7 @@ declare_bevy_symbols! { spawn, system, system_param, + time, world, } 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..e5b7d617 --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.fixed @@ -0,0 +1,55 @@ +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::bevy_platform_alternative_exists)] +#![allow(dead_code)] + +fn main() { + 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 + hash_map.insert("foo", "bar"); + + // 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::sync::Barrier` can be replaced with the `no_std` + // compatible type bevy::platform::sync::Barrier + + 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 new file mode 100644 index 00000000..31565aa3 --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.rs @@ -0,0 +1,55 @@ +#![feature(register_tool)] +#![register_tool(bevy)] +#![deny(bevy::bevy_platform_alternative_exists)] +#![allow(dead_code)] + +fn main() { + 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 + hash_map.insert("foo", "bar"); + + // 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::sync::Barrier` can be replaced with the `no_std` + // compatible type bevy::platform::sync::Barrier + + 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 new file mode 100644 index 00000000..cd50587b --- /dev/null +++ b/bevy_lint/tests/ui/bevy_platform_alternative_exists/main.stderr @@ -0,0 +1,127 @@ +error: Used type from the `std` that has an existing alternative from `bevy_platform` + --> 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); + | ^^^^^^^^^^^^^^^^^^ + | +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(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: the type `std::collections::HashSet` can be replaced with the `no_std` compatible type bevy::platform::collections::HashSet + | +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:: + | +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:51:17 + | +51 | let _lazy = std::sync::LazyLock::new(|| "lazy"); + | ^^^^^^^^^^^^^^^^^^^ + | +help: the type `std::sync::LazyLock` can be replaced with the `no_std` compatible type bevy::platform::sync::LazyLock + | +51 - let _lazy = std::sync::LazyLock::new(|| "lazy"); +51 + let _lazy = bevy::platform::sync::LazyLock::new(|| "lazy"); + | + +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(); +}