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,55 @@ 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 => {
116+ if cx. tcx . crate_name ( def_id. krate ) == sym:: core {
117+ (
118+ ALLOC_INSTEAD_OF_CORE ,
119+ "used import from `alloc` instead of `core`" ,
120+ "consider importing the item from `core`" ,
121+ )
122+ } else {
123+ self . prev_span = path. span ;
124+ return ;
125+ }
126+ } ,
127+ _ => return ,
128+ } ;
129+ if path. span != self . prev_span {
130+ span_lint_and_help ( cx, lint, path. span , msg, None , help) ;
131+ self . prev_span = path. span ;
132+ }
117133 }
118134 }
119135}
@@ -123,12 +139,10 @@ fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_cr
123139/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
124140/// is returned.
125141fn 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)
142+ match path. segments {
143+ // A global path will have PathRoot as the first segment. In this case, return the segment after.
144+ [ x, y, ..] if x. ident . name == kw:: PathRoot => Some ( y) ,
145+ [ x, ..] => Some ( x) ,
146+ _ => None ,
133147 }
134148}
0 commit comments