1
1
use 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 } ;
3
3
use clippy_utils:: is_from_proc_macro;
4
+ use rustc_ast:: Attribute ;
4
5
use rustc_data_structures:: fx:: FxIndexSet ;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: def:: Res ;
7
8
use 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 ;
9
11
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
12
use rustc_middle:: lint:: in_external_macro;
11
13
use rustc_session:: impl_lint_pass;
12
14
use rustc_span:: symbol:: kw;
13
15
use rustc_span:: { sym, Span } ;
16
+ use rustc_span:: def_id:: LocalDefId ;
14
17
15
18
declare_clippy_lint ! {
16
19
/// ### What it does
@@ -109,6 +112,8 @@ struct OpenUseSpan {
109
112
#[ derive( Debug , Copy , Clone ) ]
110
113
struct UseSpanMember {
111
114
inner : Span ,
115
+ hir_id : HirId ,
116
+ first_seg_ident_span : Span ,
112
117
lint_data : LintData ,
113
118
}
114
119
@@ -128,16 +133,79 @@ impl Hash for UseSpanMember {
128
133
129
134
#[ derive( Debug , Copy , Clone ) ]
130
135
enum LintData {
131
- CanReplace {
132
- lint : & ' static crate :: Lint ,
133
- used_mod : & ' static str ,
134
- replace_with : & ' static str ,
135
- } ,
136
+ CanReplace ( ReplaceLintData ) ,
136
137
NoReplace ,
137
138
}
138
139
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
+
139
206
impl < ' 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 ) ;
141
209
if let Res :: Def ( _, def_id) = path. res
142
210
&& let Some ( first_segment) = get_first_segment ( path)
143
211
&& is_stable ( cx, def_id)
@@ -146,28 +214,28 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
146
214
{
147
215
let lint_data = match first_segment. ident . name {
148
216
sym:: std => match cx. tcx . crate_name ( def_id. krate ) {
149
- sym:: core => LintData :: CanReplace {
217
+ sym:: core => LintData :: CanReplace ( ReplaceLintData {
150
218
lint : STD_INSTEAD_OF_CORE ,
151
219
used_mod : "std" ,
152
220
replace_with : "core" ,
153
- } ,
154
- sym:: alloc => LintData :: CanReplace {
221
+ } ) ,
222
+ sym:: alloc => LintData :: CanReplace ( ReplaceLintData {
155
223
lint : STD_INSTEAD_OF_ALLOC ,
156
224
used_mod : "std" ,
157
225
replace_with : "alloc" ,
158
- } ,
226
+ } ) ,
159
227
_ => {
160
228
self . prev_span = first_segment. ident . span ;
161
229
LintData :: NoReplace
162
230
} ,
163
231
} ,
164
232
sym:: alloc => {
165
233
if cx. tcx . crate_name ( def_id. krate ) == sym:: core {
166
- LintData :: CanReplace {
234
+ LintData :: CanReplace ( ReplaceLintData {
167
235
lint : ALLOC_INSTEAD_OF_CORE ,
168
236
used_mod : "alloc" ,
169
237
replace_with : "core" ,
170
- }
238
+ } )
171
239
} else {
172
240
self . prev_span = first_segment. ident . span ;
173
241
LintData :: NoReplace
@@ -178,11 +246,13 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
178
246
if let Some ( in_use) = self . open_use . as_mut ( ) {
179
247
in_use. members . insert ( UseSpanMember {
180
248
inner : path. span ,
249
+ hir_id,
250
+ first_seg_ident_span : first_segment. ident . span ,
181
251
lint_data,
182
252
} ) ;
183
253
return ;
184
254
}
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 {
186
256
if first_segment. ident . span != self . prev_span {
187
257
span_lint_and_sugg (
188
258
cx,
@@ -200,6 +270,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
200
270
}
201
271
202
272
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' tcx > ) {
273
+ self . suggest_for_open_use_item_if_after ( cx, item. span ) ;
203
274
if matches ! ( item. kind, ItemKind :: Use ( _, UseKind :: ListStem ) ) {
204
275
self . open_use = Some ( OpenUseSpan {
205
276
container : item. span ,
@@ -209,10 +280,114 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
209
280
210
281
}
211
282
283
+ #[ inline]
212
284
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
+ }
214
287
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 ) ;
216
391
}
217
392
}
218
393
0 commit comments