Skip to content

Commit e8fa3d8

Browse files
committed
feat: switch to check_path based on zulip feedback
1 parent eeb34dc commit e8fa3d8

File tree

11 files changed

+493
-86
lines changed

11 files changed

+493
-86
lines changed

bevy_lint/src/lints/style/bevy_platform_alternative_exists.rs

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet, ty::ty_from_hir_ty};
1+
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, source::snippet};
22
use rustc_errors::Applicability;
3-
use rustc_hir::{QPath, TyKind};
3+
use rustc_hir::{
4+
HirId, Path, PathSegment,
5+
def::{DefKind, Res},
6+
};
47
use rustc_lint::{LateContext, LateLintPass};
58

6-
use crate::{declare_bevy_lint, declare_bevy_lint_pass};
9+
use crate::{declare_bevy_lint, declare_bevy_lint_pass, sym, utils::hir_parse::generic_args_snippet};
710

811
declare_bevy_lint! {
912
pub(crate) BEVY_PLATFORM_ALTERNATIVE_EXISTS,
@@ -16,48 +19,70 @@ declare_bevy_lint_pass! {
1619
}
1720

1821
impl<'tcx> LateLintPass<'tcx> for BevyPlatformAlternativeExists {
19-
fn check_ty(
20-
&mut self,
21-
cx: &LateContext<'tcx>,
22-
hir_ty: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>,
23-
) {
24-
if hir_ty.span.in_external_macro(cx.tcx.sess.source_map()) {
25-
return;
26-
}
27-
28-
let as_unambig_ty = hir_ty.as_unambig_ty();
29-
30-
// lower the [`hir::Ty`] to a [`rustc_middle::ty::Ty`]
31-
let ty = ty_from_hir_ty(cx, as_unambig_ty);
22+
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
23+
if let Res::Def(def_kind, def_id) = path.res
24+
// Retrieve the first path segment, this could look like: `bevy`, `std`, `serde`.
25+
&& let Some(first_segment) = get_first_segment(path)
26+
// Skip if this span originates from an external macro.
27+
// Or likely originates from a proc_macro, note this should be called after
28+
// `in_external_macro`.
29+
&& !path.span.in_external_macro(cx.tcx.sess.source_map())
30+
&& !is_from_proc_macro(cx, &first_segment.ident)
31+
// Skip if this Definition is not originating from `std`.
32+
&& first_segment.ident.name == sym::std
33+
// Get the def_id of the crate from first segment.
34+
&& let Res::Def(DefKind::Mod,crate_def_id) = first_segment.res
35+
// If the first segment is not the crate root, then this type was checked when
36+
// importing.
37+
&& crate_def_id.is_crate_root()
38+
// Get potential generic arguments.
39+
&& let Some(generic_args) = path.segments.last().map(|s| generic_args_snippet(cx, s))
40+
{
3241

33-
// Get the path to the type definition.
34-
let TyKind::Path(QPath::Resolved(_, path)) = &as_unambig_ty.kind else {
35-
return;
36-
};
42+
// Skip Resolutions that are modules for example: `use std::time`.
43+
// This lint checks if a given Type from the `std` exists in `bevy_platform` and does not
44+
// compare entire modules and getting the ty from a module DefId will result in a
45+
// panic.
46+
if DefKind::Mod == def_kind{
47+
return;
48+
}
3749

38-
// if for the given `ty` an alternative from `bevy_platform` exists.
39-
if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty)
40-
// Only emit a lint if the first segment of this path is `std` thus the type originates
41-
// from the standart library. This prevents linting for `bevy::platform` types that are just a reexport of the `std`.
42-
&& path.segments.first().is_some_and(|segment| segment.ident.name.as_str().starts_with("std"))
43-
{
44-
span_lint_and_sugg(
45-
cx,
46-
BEVY_PLATFORM_ALTERNATIVE_EXISTS,
47-
hir_ty.span,
48-
BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc,
49-
format!(
50-
"the type `{}` can be replaced with the `no_std` compatible type {}",
51-
snippet(cx.tcx.sess, hir_ty.span, ""),
52-
bevy_platform_alternative.full_path()
53-
),
54-
bevy_platform_alternative.full_path().to_string(),
55-
Applicability::MachineApplicable,
56-
);
50+
// Get the Ty of this Definition.
51+
let ty = cx.tcx.type_of(def_id).skip_binder();
52+
//Check if an alternative exists in `bevy_platform`.
53+
if let Some(bevy_platform_alternative) = BevyPlatformType::try_from_ty(cx, ty) {
54+
span_lint_and_sugg(
55+
cx,
56+
BEVY_PLATFORM_ALTERNATIVE_EXISTS,
57+
path.span,
58+
BEVY_PLATFORM_ALTERNATIVE_EXISTS.desc,
59+
format!(
60+
"the type `{}` can be replaced with the `no_std` compatible type {}{}",
61+
snippet(cx.tcx.sess, path.span, ""),
62+
bevy_platform_alternative.full_path(),generic_args,
63+
),
64+
format!("{}{}",bevy_platform_alternative.full_path(),generic_args),
65+
Applicability::MachineApplicable,
66+
);
67+
}
5768
}
5869
}
5970
}
6071

72+
/// Returns the first named segment of a [`Path`].
73+
///
74+
/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
75+
/// is returned.
76+
fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
77+
match path.segments {
78+
// A global path will have PathRoot as the first segment. In this case, return the segment
79+
// after.
80+
[x, y, ..] if x.ident.name == rustc_span::symbol::kw::PathRoot => Some(y),
81+
[x, ..] => Some(x),
82+
_ => None,
83+
}
84+
}
85+
6186
/// Creates an enum containing all the types form `bevy_platform` as variants.
6287
///
6388
/// # Example
@@ -109,6 +134,7 @@ macro_rules! declare_bevy_platform_types {
109134
}
110135

111136
declare_bevy_platform_types! {
137+
Arc("sync") => ARC,
112138
Barrier("sync") => BARRIER,
113139
BarrierWaitResult("sync") => BARRIERWAITRESULT,
114140
HashMap("collections") => HASHMAP,

bevy_lint/src/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub mod bevy_platform_types {
9494

9595
use crate::sym;
9696

97+
pub static ARC: PathLookup = type_path!(std::sync::Arc);
9798
pub static BARRIER: PathLookup = type_path!(std::sync::Barrier);
9899
pub static BARRIERWAITRESULT: PathLookup = type_path!(std::sync::BarrierWaitResult);
99100
pub static HASHMAP: PathLookup = type_path!(std::collections::HashMap);

bevy_lint/src/sym.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ use clippy_utils::sym::EXTRA_SYMBOLS as CLIPPY_SYMBOLS;
5858
/// These are symbols that we use but are already interned by either the compiler or Clippy.
5959
pub use clippy_utils::sym::filter;
6060
pub use rustc_span::sym::{
61-
HashMap, HashSet, Instant, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
61+
Arc, HashMap, HashSet, Instant, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
6262
SyncUnsafeCell, bevy_ecs, bundle, message, plugin, reflect, std, sync,
6363
};
6464
use rustc_span::{Symbol, symbol::PREDEFINED_SYMBOLS_COUNT};

bevy_lint/tests/ui.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ mod test_utils;
99

1010
fn main() {
1111
let config = base_config("ui").unwrap();
12+
1213
run_tests(config).unwrap();
1314
}
Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
#![feature(register_tool)]
22
#![register_tool(bevy)]
33
#![deny(bevy::bevy_platform_alternative_exists)]
4-
5-
#[allow(dead_code)]
6-
struct Player {
7-
attack_cd: bevy::platform::time::Instant,
8-
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
9-
//~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible
10-
// type bevy::platform::time::Instant
11-
}
4+
#![allow(dead_code)]
125

136
fn main() {
147
let mut hash_map = bevy::platform::collections::HashMap::new();
@@ -17,12 +10,46 @@ fn main() {
1710
// type bevy::platform::collections::HashMap
1811
hash_map.insert("foo", "bar");
1912

20-
let _time = bevy::platform::time::Instant::now();
13+
// compatible type bevy::platform::collections::HashMap
14+
//~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std`
15+
//~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
16+
let _declared_hash_map: bevy::platform::collections::HashMap::<u32, u32> = bevy::platform::collections::HashMap::new();
17+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
18+
//~| HELP: the type `std::collections::HashMap<u32, u32>` can be replaced with the `no_std`
19+
// compatible type bevy::platform::collections::HashMap<u32, u32>
20+
21+
let _arc = bevy::platform::sync::Arc::new(10);
22+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
23+
//~| HELP: the type `std::sync::Arc` can be replaced with the `no_std`
24+
// compatible type bevy::platform::sync::Arc
25+
26+
let barrier = bevy::platform::sync::Barrier::new(10);
2127
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
22-
//~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible
23-
// type bevy::platform::time::Instant
28+
//~| HELP: the type `std::sync::Barrier` can be replaced with the `no_std`
29+
// compatible type bevy::platform::sync::Barrier
2430

25-
// This should be fine even tho it will result in a `std::time::Instant` after full path
26-
// resolution.
27-
let _bevy_time = bevy::platform::time::Instant::now();
31+
let _barrier_wait_result: bevy::platform::sync::BarrierWaitResult = barrier.wait();
32+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
33+
//~| HELP: the type `std::sync::BarrierWaitResult` can be replaced with the `no_std`
34+
// compatible type bevy::platform::sync::BarrierWaitResult
35+
36+
let mut hash_set = bevy::platform::collections::HashSet::new();
37+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
38+
//~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std`
39+
// compatible type bevy::platform::collections::HashSet
40+
41+
hash_set.insert(1);
42+
43+
// compatible type bevy::platform::collections::HashSet
44+
//~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std`
45+
//~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
46+
let _declared_hash_map: bevy::platform::collections::HashSet::<u32> = bevy::platform::collections::HashSet::new();
47+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
48+
//~| HELP: the type `std::collections::HashSet<u32>` can be replaced with the `no_std`
49+
// compatible type bevy::platform::collections::HashSet<u32>
50+
51+
let _lazy = bevy::platform::sync::LazyLock::new(|| "lazy");
52+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
53+
//~| HELP: the type `std::sync::LazyLock` can be replaced with the `no_std`
54+
// compatible type bevy::platform::sync::LazyLock
2855
}
Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
#![feature(register_tool)]
22
#![register_tool(bevy)]
33
#![deny(bevy::bevy_platform_alternative_exists)]
4-
5-
use std::time;
6-
7-
#[allow(dead_code)]
8-
struct Player {
9-
attack_cd: time::Instant,
10-
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
11-
//~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible
12-
// type bevy::platform::time::Instant
13-
}
4+
#![allow(dead_code)]
145

156
fn main() {
167
let mut hash_map = std::collections::HashMap::new();
@@ -19,12 +10,46 @@ fn main() {
1910
// type bevy::platform::collections::HashMap
2011
hash_map.insert("foo", "bar");
2112

22-
let _time = std::time::Instant::now();
13+
// compatible type bevy::platform::collections::HashMap
14+
//~| HELP: the type `std::collections::HashMap` can be replaced with the `no_std`
15+
//~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
16+
let _declared_hash_map: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
17+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
18+
//~| HELP: the type `std::collections::HashMap<u32, u32>` can be replaced with the `no_std`
19+
// compatible type bevy::platform::collections::HashMap<u32, u32>
20+
21+
let _arc = std::sync::Arc::new(10);
22+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
23+
//~| HELP: the type `std::sync::Arc` can be replaced with the `no_std`
24+
// compatible type bevy::platform::sync::Arc
25+
26+
let barrier = std::sync::Barrier::new(10);
2327
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
24-
//~| HELP: the type `std::time::Instant` can be replaced with the `no_std` compatible
25-
// type bevy::platform::time::Instant
28+
//~| HELP: the type `std::sync::Barrier` can be replaced with the `no_std`
29+
// compatible type bevy::platform::sync::Barrier
2630

27-
// This should be fine even tho it will result in a `std::time::Instant` after full path
28-
// resolution.
29-
let _bevy_time = bevy::platform::time::Instant::now();
31+
let _barrier_wait_result: std::sync::BarrierWaitResult = barrier.wait();
32+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
33+
//~| HELP: the type `std::sync::BarrierWaitResult` can be replaced with the `no_std`
34+
// compatible type bevy::platform::sync::BarrierWaitResult
35+
36+
let mut hash_set = std::collections::HashSet::new();
37+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
38+
//~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std`
39+
// compatible type bevy::platform::collections::HashSet
40+
41+
hash_set.insert(1);
42+
43+
// compatible type bevy::platform::collections::HashSet
44+
//~| HELP: the type `std::collections::HashSet` can be replaced with the `no_std`
45+
//~v ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
46+
let _declared_hash_map: std::collections::HashSet<u32> = std::collections::HashSet::new();
47+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
48+
//~| HELP: the type `std::collections::HashSet<u32>` can be replaced with the `no_std`
49+
// compatible type bevy::platform::collections::HashSet<u32>
50+
51+
let _lazy = std::sync::LazyLock::new(|| "lazy");
52+
//~^ ERROR: Used type from the `std` that has an existing alternative from `bevy_platform`
53+
//~| HELP: the type `std::sync::LazyLock` can be replaced with the `no_std`
54+
// compatible type bevy::platform::sync::LazyLock
3055
}

0 commit comments

Comments
 (0)