1
1
use clippy_utils:: diagnostics:: span_lint_and_help;
2
2
use 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 } ;
6
6
7
7
declare_clippy_lint ! {
8
8
/// ### What it does
@@ -81,39 +81,55 @@ declare_clippy_lint! {
81
81
"type is imported from alloc when available in core"
82
82
}
83
83
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 ] ) ;
85
92
86
93
impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
87
94
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
+ }
117
133
}
118
134
}
119
135
}
@@ -123,12 +139,10 @@ fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_cr
123
139
/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
124
140
/// is returned.
125
141
fn 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 ,
133
147
}
134
148
}
0 commit comments