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 } ;
22use rustc_errors:: Applicability ;
3- use rustc_hir:: { QPath , TyKind } ;
3+ use rustc_hir:: {
4+ HirId , Path , PathSegment ,
5+ def:: { DefKind , Res } ,
6+ } ;
47use 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
811declare_bevy_lint ! {
912 pub ( crate ) BEVY_PLATFORM_ALTERNATIVE_EXISTS ,
@@ -16,48 +19,70 @@ declare_bevy_lint_pass! {
1619}
1720
1821impl < ' 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
111136declare_bevy_platform_types ! {
137+ Arc ( "sync" ) => ARC ,
112138 Barrier ( "sync" ) => BARRIER ,
113139 BarrierWaitResult ( "sync" ) => BARRIERWAITRESULT ,
114140 HashMap ( "collections" ) => HASHMAP ,
0 commit comments