11use clippy_utils:: diagnostics:: span_lint_and_help;
22use rustc_hir:: { def:: Res , HirId , Path , PathSegment } ;
3- use rustc_lint:: { LateContext , LateLintPass , Lint } ;
4- use rustc_session:: { declare_lint_pass , declare_tool_lint } ;
5- use rustc_span:: { sym, symbol:: kw, Symbol } ;
3+ use rustc_lint:: { LateContext , LateLintPass } ;
4+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
5+ use rustc_span:: { sym, symbol:: kw, Span } ;
66
77declare_clippy_lint ! {
88 /// ### What it does
@@ -81,39 +81,54 @@ declare_clippy_lint! {
8181 "type is imported from alloc when available in core"
8282}
8383
84- declare_lint_pass ! ( StdReexports => [ STD_INSTEAD_OF_CORE , STD_INSTEAD_OF_ALLOC , ALLOC_INSTEAD_OF_CORE ] ) ;
84+ #[ derive( Default ) ]
85+ pub struct StdReexports {
86+ // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
87+ // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
88+ // when the path could be also be used to access the module.
89+ prev_span : Span ,
90+ }
91+ impl_lint_pass ! ( StdReexports => [ STD_INSTEAD_OF_CORE , STD_INSTEAD_OF_ALLOC , ALLOC_INSTEAD_OF_CORE ] ) ;
8592
8693impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
8794 fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
88- // std_instead_of_core
89- check_path ( cx, path, sym:: std, sym:: core, STD_INSTEAD_OF_CORE ) ;
90- // std_instead_of_alloc
91- check_path ( cx, path, sym:: std, sym:: alloc, STD_INSTEAD_OF_ALLOC ) ;
92- // alloc_instead_of_core
93- check_path ( cx, path, sym:: alloc, sym:: core, ALLOC_INSTEAD_OF_CORE ) ;
94- }
95- }
96-
97- fn check_path ( cx : & LateContext < ' _ > , path : & Path < ' _ > , krate : Symbol , suggested_crate : Symbol , lint : & ' static Lint ) {
98- if_chain ! {
99- // check if path resolves to the suggested crate.
100- if let Res :: Def ( _, def_id) = path. res;
101- if suggested_crate == cx. tcx. crate_name( def_id. krate) ;
102-
103- // check if the first segment of the path is the crate we want to identify
104- if let Some ( path_root_segment) = get_first_segment( path) ;
105-
106- // check if the path matches the crate we want to suggest the other path for.
107- if krate == path_root_segment. ident. name;
108- then {
109- span_lint_and_help(
110- cx,
111- lint,
112- path. span,
113- & format!( "used import from `{}` instead of `{}`" , krate, suggested_crate) ,
114- None ,
115- & format!( "consider importing the item from `{}`" , suggested_crate) ,
116- ) ;
95+ if let Res :: Def ( _, def_id) = path. res
96+ && let Some ( first_segment) = get_first_segment ( path)
97+ {
98+ let ( lint, msg, help) = match first_segment. ident . name {
99+ sym:: std => match cx. tcx . crate_name ( def_id. krate ) {
100+ sym:: core => (
101+ STD_INSTEAD_OF_CORE ,
102+ "used import from `std` instead of `core`" ,
103+ "consider importing the item from `core`" ,
104+ ) ,
105+ sym:: alloc => (
106+ STD_INSTEAD_OF_ALLOC ,
107+ "used import from `std` instead of `alloc`" ,
108+ "consider importing the item from `alloc`" ,
109+ ) ,
110+ _ => {
111+ self . prev_span = path. span ;
112+ return ;
113+ }
114+ } ,
115+ sym:: alloc => match cx. tcx . crate_name ( def_id. krate ) {
116+ sym:: core => (
117+ ALLOC_INSTEAD_OF_CORE ,
118+ "used import from `alloc` instead of `core`" ,
119+ "consider importing the item from `core`" ,
120+ ) ,
121+ _ => {
122+ self . prev_span = path. span ;
123+ return ;
124+ }
125+ }
126+ _ => return ,
127+ } ;
128+ if path. span != self . prev_span {
129+ span_lint_and_help ( cx, lint, path. span , msg, None , help) ;
130+ self . prev_span = path. span ;
131+ }
117132 }
118133 }
119134}
@@ -123,12 +138,10 @@ fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_cr
123138/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
124139/// is returned.
125140fn get_first_segment < ' tcx > ( path : & Path < ' tcx > ) -> Option < & ' tcx PathSegment < ' tcx > > {
126- let segment = path. segments . first ( ) ?;
127-
128- // A global path will have PathRoot as the first segment. In this case, return the segment after.
129- if segment. ident . name == kw:: PathRoot {
130- path. segments . get ( 1 )
131- } else {
132- Some ( segment)
141+ match path. segments {
142+ // A global path will have PathRoot as the first segment. In this case, return the segment after.
143+ [ x, y, ..] if x. ident . name == kw:: PathRoot => Some ( y) ,
144+ [ x, ..] => Some ( x) ,
145+ _ => None ,
133146 }
134147}
0 commit comments