11use std:: hash:: { Hash , Hasher } ;
2- use clippy_utils:: diagnostics:: span_lint_and_sugg;
2+ use clippy_utils:: diagnostics:: { span_lint_and_sugg, span_lint_hir_and_then } ;
33use clippy_utils:: is_from_proc_macro;
4+ use rustc_ast:: Attribute ;
45use rustc_data_structures:: fx:: FxIndexSet ;
56use rustc_errors:: Applicability ;
67use rustc_hir:: def:: Res ;
78use rustc_hir:: def_id:: DefId ;
8- use rustc_hir:: { HirId , Item , ItemKind , Path , PathSegment , UseKind } ;
9+ use rustc_hir:: { Arm , Block , Body , Expr , FieldDef , FnDecl , ForeignItem , GenericParam , Generics , HirId , ImplItem , Item , ItemKind , Local , Mod , Pat , Path , PathSegment , PolyTraitRef , Stmt , TraitItem , Ty , UseKind , Variant , VariantData } ;
10+ use rustc_hir:: intravisit:: FnKind ;
911use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1012use rustc_middle:: lint:: in_external_macro;
1113use rustc_session:: impl_lint_pass;
1214use rustc_span:: symbol:: kw;
1315use rustc_span:: { sym, Span } ;
16+ use rustc_span:: def_id:: LocalDefId ;
1417
1518declare_clippy_lint ! {
1619 /// ### What it does
@@ -109,6 +112,8 @@ struct OpenUseSpan {
109112#[ derive( Debug , Copy , Clone ) ]
110113struct UseSpanMember {
111114 inner : Span ,
115+ hir_id : HirId ,
116+ first_seg_ident_span : Span ,
112117 lint_data : LintData ,
113118}
114119
@@ -128,16 +133,79 @@ impl Hash for UseSpanMember {
128133
129134#[ derive( Debug , Copy , Clone ) ]
130135enum LintData {
131- CanReplace {
132- lint : & ' static crate :: Lint ,
133- used_mod : & ' static str ,
134- replace_with : & ' static str ,
135- } ,
136+ CanReplace ( ReplaceLintData ) ,
136137 NoReplace ,
137138}
138139
140+ #[ derive( Debug , Copy , Clone ) ]
141+ struct ReplaceLintData {
142+ lint : & ' static crate :: Lint ,
143+ used_mod : & ' static str ,
144+ replace_with : & ' static str ,
145+ }
146+
147+ impl StdReexports {
148+ fn suggest_for_open_use_item_if_after < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , span : Span ) {
149+ if let Some ( collected_use) = self . open_use . take ( ) {
150+ // Still contains other span, throw it back
151+ if collected_use. container . contains ( span) {
152+ self . open_use = Some ( collected_use) ;
153+ return ;
154+ }
155+ let mut place_holder_unique_check: Option < ( Span , ReplaceLintData ) > = None ;
156+ let mut can_chunk = true ;
157+ for member in collected_use. members . iter ( ) {
158+ match & member. lint_data {
159+ LintData :: CanReplace ( lint_data) => {
160+ if let Some ( ( _span, prev_lint_data) ) = place_holder_unique_check. take ( ) {
161+ if prev_lint_data. lint . name == lint_data. lint . name && prev_lint_data. used_mod == lint_data. used_mod && prev_lint_data. replace_with == lint_data. replace_with {
162+ place_holder_unique_check = Some ( ( member. first_seg_ident_span , * lint_data) ) ;
163+ } else {
164+ // Will have to warn for individual entries
165+ can_chunk = false ;
166+ break ;
167+ }
168+ } else {
169+ place_holder_unique_check = Some ( ( member. first_seg_ident_span , * lint_data) ) ;
170+ }
171+ }
172+ LintData :: NoReplace => {
173+ // Will have to warn for individual entries
174+ can_chunk = false ;
175+ break ;
176+ }
177+ }
178+ }
179+ // If they can all be replaced with the same thing, just lint and suggest, then
180+ // clippy-fix works as well
181+ if can_chunk {
182+ if let Some ( ( first_segment_ident_span, ReplaceLintData { lint, used_mod, replace_with } ) ) = place_holder_unique_check {
183+ span_lint_and_sugg (
184+ cx,
185+ lint,
186+ first_segment_ident_span,
187+ & format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) ,
188+ & format ! ( "consider importing the item from `{replace_with}`" ) ,
189+ replace_with. to_string ( ) ,
190+ Applicability :: MachineApplicable ,
191+ ) ;
192+ }
193+ } else {
194+ for member in collected_use. members {
195+ if let LintData :: CanReplace ( ReplaceLintData { lint, used_mod, replace_with } ) = member. lint_data {
196+ span_lint_hir_and_then ( cx, lint, member. hir_id , member. inner , & format ! ( "used import from `{used_mod}` instead of `{replace_with}`" ) , |diag| {
197+ diag. help ( format ! ( "consider importing the item from `{replace_with}`" ) ) ;
198+ } )
199+ }
200+ }
201+ }
202+ }
203+ }
204+ }
205+
139206impl < ' tcx > LateLintPass < ' tcx > for StdReexports {
140- fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , _: HirId ) {
207+ fn check_path ( & mut self , cx : & LateContext < ' tcx > , path : & Path < ' tcx > , hir_id : HirId ) {
208+ self . suggest_for_open_use_item_if_after ( cx, path. span ) ;
141209 if let Res :: Def ( _, def_id) = path. res
142210 && let Some ( first_segment) = get_first_segment ( path)
143211 && is_stable ( cx, def_id)
@@ -146,28 +214,28 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
146214 {
147215 let lint_data = match first_segment. ident . name {
148216 sym:: std => match cx. tcx . crate_name ( def_id. krate ) {
149- sym:: core => LintData :: CanReplace {
217+ sym:: core => LintData :: CanReplace ( ReplaceLintData {
150218 lint : STD_INSTEAD_OF_CORE ,
151219 used_mod : "std" ,
152220 replace_with : "core" ,
153- } ,
154- sym:: alloc => LintData :: CanReplace {
221+ } ) ,
222+ sym:: alloc => LintData :: CanReplace ( ReplaceLintData {
155223 lint : STD_INSTEAD_OF_ALLOC ,
156224 used_mod : "std" ,
157225 replace_with : "alloc" ,
158- } ,
226+ } ) ,
159227 _ => {
160228 self . prev_span = first_segment. ident . span ;
161229 LintData :: NoReplace
162230 } ,
163231 } ,
164232 sym:: alloc => {
165233 if cx. tcx . crate_name ( def_id. krate ) == sym:: core {
166- LintData :: CanReplace {
234+ LintData :: CanReplace ( ReplaceLintData {
167235 lint : ALLOC_INSTEAD_OF_CORE ,
168236 used_mod : "alloc" ,
169237 replace_with : "core" ,
170- }
238+ } )
171239 } else {
172240 self . prev_span = first_segment. ident . span ;
173241 LintData :: NoReplace
@@ -178,11 +246,13 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
178246 if let Some ( in_use) = self . open_use . as_mut ( ) {
179247 in_use. members . insert ( UseSpanMember {
180248 inner : path. span ,
249+ hir_id,
250+ first_seg_ident_span : first_segment. ident . span ,
181251 lint_data,
182252 } ) ;
183253 return ;
184254 }
185- if let LintData :: CanReplace { lint, used_mod, replace_with } = lint_data {
255+ if let LintData :: CanReplace ( ReplaceLintData { lint, used_mod, replace_with } ) = lint_data {
186256 if first_segment. ident . span != self . prev_span {
187257 span_lint_and_sugg (
188258 cx,
@@ -200,6 +270,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
200270 }
201271
202272 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
273+ self . suggest_for_open_use_item_if_after ( cx, item. span ) ;
203274 if matches ! ( item. kind, ItemKind :: Use ( _, UseKind :: ListStem ) ) {
204275 self . open_use = Some ( OpenUseSpan {
205276 container : item. span ,
@@ -209,10 +280,114 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
209280
210281 }
211282
283+ #[ inline]
212284 fn check_item_post ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
213- if let Some ( collected_use) = self . open_use . take ( ) {
285+ self . suggest_for_open_use_item_if_after ( cx, item. span ) ;
286+ }
214287
215- }
288+ #[ inline]
289+ fn check_body ( & mut self , cx : & LateContext < ' tcx > , body : & ' tcx Body < ' tcx > ) {
290+ self . suggest_for_open_use_item_if_after ( cx, body. value . span ) ;
291+ }
292+
293+ #[ inline]
294+ fn check_crate_post ( & mut self , cx : & LateContext < ' tcx > ) {
295+ self . suggest_for_open_use_item_if_after ( cx, Span :: default ( ) ) ;
296+ }
297+
298+ #[ inline]
299+ fn check_mod ( & mut self , cx : & LateContext < ' tcx > , _: & ' tcx Mod < ' tcx > , _: HirId ) {
300+ self . suggest_for_open_use_item_if_after ( cx, Span :: default ( ) ) ;
301+ }
302+
303+ #[ inline]
304+ fn check_foreign_item ( & mut self , cx : & LateContext < ' tcx > , f_item : & ' tcx ForeignItem < ' tcx > ) {
305+ self . suggest_for_open_use_item_if_after ( cx, f_item. span ) ;
306+ }
307+
308+ #[ inline]
309+ fn check_local ( & mut self , cx : & LateContext < ' tcx > , local : & ' tcx Local < ' tcx > ) {
310+ self . suggest_for_open_use_item_if_after ( cx, local. span ) ;
311+ }
312+
313+ #[ inline]
314+ fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx Block < ' tcx > ) {
315+ self . suggest_for_open_use_item_if_after ( cx, block. span ) ;
316+ }
317+
318+ #[ inline]
319+ fn check_stmt ( & mut self , cx : & LateContext < ' tcx > , stmt : & ' tcx Stmt < ' tcx > ) {
320+ self . suggest_for_open_use_item_if_after ( cx, stmt. span ) ;
321+ }
322+
323+ #[ inline]
324+ fn check_arm ( & mut self , cx : & LateContext < ' tcx > , arm : & ' tcx Arm < ' tcx > ) {
325+ self . suggest_for_open_use_item_if_after ( cx, arm. span ) ;
326+ }
327+
328+ #[ inline]
329+ fn check_pat ( & mut self , cx : & LateContext < ' tcx > , pat : & ' tcx Pat < ' tcx > ) {
330+ self . suggest_for_open_use_item_if_after ( cx, pat. span ) ;
331+ }
332+
333+ #[ inline]
334+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
335+ self . suggest_for_open_use_item_if_after ( cx, expr. span ) ;
336+ }
337+
338+ #[ inline]
339+ fn check_ty ( & mut self , cx : & LateContext < ' tcx > , ty : & ' tcx Ty < ' tcx > ) {
340+ self . suggest_for_open_use_item_if_after ( cx, ty. span ) ;
341+ }
342+
343+ #[ inline]
344+ fn check_generic_param ( & mut self , cx : & LateContext < ' tcx > , gp : & ' tcx GenericParam < ' tcx > ) {
345+ self . suggest_for_open_use_item_if_after ( cx, gp. span ) ;
346+ }
347+
348+ #[ inline]
349+ fn check_generics ( & mut self , cx : & LateContext < ' tcx > , g : & ' tcx Generics < ' tcx > ) {
350+ self . suggest_for_open_use_item_if_after ( cx, g. span ) ;
351+ }
352+
353+ #[ inline]
354+ fn check_poly_trait_ref ( & mut self , cx : & LateContext < ' tcx > , ptr : & ' tcx PolyTraitRef < ' tcx > ) {
355+ self . suggest_for_open_use_item_if_after ( cx, ptr. span ) ;
356+ }
357+
358+ #[ inline]
359+ fn check_fn ( & mut self , cx : & LateContext < ' tcx > , _: FnKind < ' tcx > , _: & ' tcx FnDecl < ' tcx > , _: & ' tcx Body < ' tcx > , s : Span , _: LocalDefId ) {
360+ self . suggest_for_open_use_item_if_after ( cx, s) ;
361+ }
362+
363+ #[ inline]
364+ fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , ti : & ' tcx TraitItem < ' tcx > ) {
365+ self . suggest_for_open_use_item_if_after ( cx, ti. span ) ;
366+ }
367+
368+ #[ inline]
369+ fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , imp : & ' tcx ImplItem < ' tcx > ) {
370+ self . suggest_for_open_use_item_if_after ( cx, imp. span ) ;
371+ }
372+
373+ #[ inline]
374+ fn check_struct_def ( & mut self , cx : & LateContext < ' tcx > , _: & ' tcx VariantData < ' tcx > ) {
375+ self . suggest_for_open_use_item_if_after ( cx, Span :: default ( ) ) ;
376+ }
377+
378+ #[ inline]
379+ fn check_field_def ( & mut self , cx : & LateContext < ' tcx > , fd : & ' tcx FieldDef < ' tcx > ) {
380+ self . suggest_for_open_use_item_if_after ( cx, fd. span ) ;
381+ }
382+
383+ #[ inline]
384+ fn check_variant ( & mut self , cx : & LateContext < ' tcx > , v : & ' tcx Variant < ' tcx > ) {
385+ self . suggest_for_open_use_item_if_after ( cx, v. span ) ;
386+ }
387+
388+ #[ inline]
389+ fn check_attribute ( & mut self , cx : & LateContext < ' tcx > , attr : & ' tcx Attribute ) {
390+ self . suggest_for_open_use_item_if_after ( cx, attr. span ) ;
216391 }
217392}
218393
0 commit comments