1+ use std:: hash:: { Hash , Hasher } ;
12use clippy_utils:: diagnostics:: span_lint_and_sugg;
23use clippy_utils:: is_from_proc_macro;
4+ use rustc_data_structures:: fx:: FxIndexSet ;
35use rustc_errors:: Applicability ;
46use rustc_hir:: def:: Res ;
57use rustc_hir:: def_id:: DefId ;
6- use rustc_hir:: { HirId , Path , PathSegment } ;
8+ use rustc_hir:: { HirId , Item , ItemKind , Path , PathSegment , UseKind } ;
79use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
810use rustc_middle:: lint:: in_external_macro;
911use rustc_session:: impl_lint_pass;
@@ -93,9 +95,47 @@ pub struct StdReexports {
9395 // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
9496 // when the path could be also be used to access the module.
9597 prev_span : Span ,
98+ open_use : Option < OpenUseSpan >
9699}
100+
97101impl_lint_pass ! ( StdReexports => [ STD_INSTEAD_OF_CORE , STD_INSTEAD_OF_ALLOC , ALLOC_INSTEAD_OF_CORE ] ) ;
98102
103+ #[ derive( Debug ) ]
104+ struct OpenUseSpan {
105+ container : Span ,
106+ members : FxIndexSet < UseSpanMember >
107+ }
108+
109+ #[ derive( Debug , Copy , Clone ) ]
110+ struct UseSpanMember {
111+ inner : Span ,
112+ lint_data : LintData ,
113+ }
114+
115+ impl PartialEq for UseSpanMember {
116+ fn eq ( & self , other : & Self ) -> bool {
117+ self . inner . eq ( & other. inner )
118+ }
119+ }
120+
121+ impl Eq for UseSpanMember { }
122+
123+ impl Hash for UseSpanMember {
124+ fn hash < H : Hasher > ( & self , state : & mut H ) {
125+ self . inner . hash ( state) ;
126+ }
127+ }
128+
129+ #[ derive( Debug , Copy , Clone ) ]
130+ enum LintData {
131+ CanReplace {
132+ lint : & ' static crate :: Lint ,
133+ used_mod : & ' static str ,
134+ replace_with : & ' static str ,
135+ } ,
136+ NoReplace ,
137+ }
138+
99139impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
100140 fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
101141 if let Res :: Def ( _, def_id) = path. res
@@ -104,37 +144,74 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
104144 && !in_external_macro ( cx. sess ( ) , path. span )
105145 && !is_from_proc_macro ( cx, & first_segment. ident )
106146 {
107- let ( lint , used_mod , replace_with ) = match first_segment. ident . name {
147+ let lint_data = match first_segment. ident . name {
108148 sym:: std => match cx. tcx . crate_name ( def_id. krate ) {
109- sym:: core => ( STD_INSTEAD_OF_CORE , "std" , "core" ) ,
110- sym:: alloc => ( STD_INSTEAD_OF_ALLOC , "std" , "alloc" ) ,
149+ sym:: core => LintData :: CanReplace {
150+ lint : STD_INSTEAD_OF_CORE ,
151+ used_mod : "std" ,
152+ replace_with : "core" ,
153+ } ,
154+ sym:: alloc => LintData :: CanReplace {
155+ lint : STD_INSTEAD_OF_ALLOC ,
156+ used_mod : "std" ,
157+ replace_with : "alloc" ,
158+ } ,
111159 _ => {
112160 self . prev_span = first_segment. ident . span ;
113- return ;
161+ LintData :: NoReplace
114162 } ,
115163 } ,
116164 sym:: alloc => {
117165 if cx. tcx . crate_name ( def_id. krate ) == sym:: core {
118- ( ALLOC_INSTEAD_OF_CORE , "alloc" , "core" )
166+ LintData :: CanReplace {
167+ lint : ALLOC_INSTEAD_OF_CORE ,
168+ used_mod : "alloc" ,
169+ replace_with : "core" ,
170+ }
119171 } else {
120172 self . prev_span = first_segment. ident . span ;
121- return ;
173+ LintData :: NoReplace
122174 }
123175 } ,
124176 _ => return ,
125177 } ;
126- if first_segment. ident . span != self . prev_span {
127- span_lint_and_sugg (
128- cx,
129- lint,
130- first_segment. ident . span ,
131- format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
132- format ! ( "consider importing the item from `{replace_with}`" ) ,
133- replace_with. to_string ( ) ,
134- Applicability :: MachineApplicable ,
135- ) ;
136- self . prev_span = first_segment. ident . span ;
178+ if let Some ( in_use) = self . open_use . as_mut ( ) {
179+ in_use. members . insert ( UseSpanMember {
180+ inner : path. span ,
181+ lint_data,
182+ } ) ;
183+ return ;
137184 }
185+ if let LintData :: CanReplace { lint, used_mod, replace_with } = lint_data {
186+ if first_segment. ident . span != self . prev_span {
187+ span_lint_and_sugg (
188+ cx,
189+ lint,
190+ first_segment. ident . span ,
191+ & format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
192+ & format ! ( "consider importing the item from `{replace_with}`" ) ,
193+ replace_with. to_string ( ) ,
194+ Applicability :: MachineApplicable ,
195+ ) ;
196+ self . prev_span = first_segment. ident . span ;
197+ }
198+ }
199+ }
200+ }
201+
202+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
203+ if matches ! ( item. kind, ItemKind :: Use ( _, UseKind :: ListStem ) ) {
204+ self . open_use = Some ( OpenUseSpan {
205+ container : item. span ,
206+ members : FxIndexSet :: default ( ) ,
207+ } )
208+ }
209+
210+ }
211+
212+ fn check_item_post ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
213+ if let Some ( collected_use) = self . open_use . take ( ) {
214+
138215 }
139216 }
140217}
0 commit comments