@@ -5,8 +5,8 @@ use clippy_config::types::{
5
5
} ;
6
6
use clippy_utils:: diagnostics:: span_lint_and_note;
7
7
use rustc_hir:: {
8
- AssocItemKind , FieldDef , HirId , ImplItemRef , IsAuto , Item , ItemKind , Mod , TraitItemRef , UseKind , Variant ,
9
- VariantData ,
8
+ AssocItemKind , FieldDef , HirId , ImplItemRef , IsAuto , Item , ItemKind , Mod , QPath , TraitItemRef , TyKind , UseKind ,
9
+ Variant , VariantData ,
10
10
} ;
11
11
use rustc_lint:: { LateContext , LateLintPass } ;
12
12
use rustc_session:: impl_lint_pass;
@@ -72,7 +72,7 @@ declare_clippy_lint! {
72
72
/// where it matters. Other solutions can be to use profile guided
73
73
/// optimization (PGO), post-link optimization (e.g. using BOLT for LLVM),
74
74
/// or other advanced optimization methods. A good starting point to dig
75
- /// into optimization is [cargo-pgo].
75
+ /// into optimization is [cargo-pgo][cargo-pgo] .
76
76
///
77
77
/// #### Lints on a Contains basis
78
78
///
@@ -191,6 +191,35 @@ impl ArbitrarySourceItemOrdering {
191
191
) ;
192
192
}
193
193
194
+ fn lint_member_item < T : rustc_lint:: LintContext > ( cx : & T , item : & Item < ' _ > , before_item : & Item < ' _ > ) {
195
+ let span = if item. ident . as_str ( ) . is_empty ( ) {
196
+ & item. span
197
+ } else {
198
+ & item. ident . span
199
+ } ;
200
+
201
+ let ( before_span, note) = if before_item. ident . as_str ( ) . is_empty ( ) {
202
+ (
203
+ & before_item. span ,
204
+ "should be placed before the following item" . to_owned ( ) ,
205
+ )
206
+ } else {
207
+ (
208
+ & before_item. ident . span ,
209
+ format ! ( "should be placed before `{}`" , before_item. ident. as_str( ) , ) ,
210
+ )
211
+ } ;
212
+
213
+ span_lint_and_note (
214
+ cx,
215
+ ARBITRARY_SOURCE_ITEM_ORDERING ,
216
+ * span,
217
+ "incorrect ordering of items (must be alphabetically ordered)" ,
218
+ Some ( * before_span) ,
219
+ note,
220
+ ) ;
221
+ }
222
+
194
223
/// Produces a linting warning for incorrectly ordered trait items.
195
224
fn lint_trait_item < T : rustc_lint:: LintContext > ( & self , cx : & T , item : & TraitItemRef , before_item : & TraitItemRef ) {
196
225
span_lint_and_note (
@@ -247,7 +276,6 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
247
276
if cur_t_kind == item_kind && cur_t. ident . name . as_str ( ) > item. ident . name . as_str ( ) {
248
277
Self :: lint_member_name ( cx, & item. ident , & cur_t. ident ) ;
249
278
} else if cur_t_kind_index > item_kind_index {
250
- // let type1 = item
251
279
self . lint_trait_item ( cx, item, cur_t) ;
252
280
}
253
281
}
@@ -267,7 +295,6 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
267
295
if cur_t_kind == item_kind && cur_t. ident . name . as_str ( ) > item. ident . name . as_str ( ) {
268
296
Self :: lint_member_name ( cx, & item. ident , & cur_t. ident ) ;
269
297
} else if cur_t_kind_index > item_kind_index {
270
- // let type1 = item
271
298
self . lint_impl_item ( cx, item, cur_t) ;
272
299
}
273
300
}
@@ -304,9 +331,10 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
304
331
// appears in the existing code base.
305
332
if item. ident . name == rustc_span:: symbol:: kw:: Empty {
306
333
if let ItemKind :: Impl ( _) = item. kind {
307
- // Filters attribute parameters.
308
- // TODO: This could be of relevance to order.
309
- continue ;
334
+ // Sorting trait impls for unnamed types makes no sense.
335
+ if get_item_name ( item) . is_empty ( ) {
336
+ continue ;
337
+ }
310
338
} else if let ItemKind :: ForeignMod { .. } = item. kind {
311
339
continue ;
312
340
} else if let ItemKind :: GlobalAsm ( _) = item. kind {
@@ -358,13 +386,13 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
358
386
use std:: cmp:: Ordering ; // Better legibility.
359
387
match module_level_order. cmp ( & cur_t. order ) {
360
388
Ordering :: Less => {
361
- Self :: lint_member_name ( cx, & item. ident , & cur_t. item . ident ) ;
389
+ Self :: lint_member_item ( cx, item, cur_t. item ) ;
362
390
} ,
363
391
Ordering :: Equal if item_kind == SourceItemOrderingModuleItemKind :: Use => {
364
392
// Skip ordering use statements, as these should be ordered by rustfmt.
365
393
} ,
366
- Ordering :: Equal if cur_t. name . as_str ( ) > item . ident . name . as_str ( ) => {
367
- Self :: lint_member_name ( cx, & item. ident , & cur_t. item . ident ) ;
394
+ Ordering :: Equal if cur_t. name > get_item_name ( item ) => {
395
+ Self :: lint_member_item ( cx, item, cur_t. item ) ;
368
396
} ,
369
397
Ordering :: Equal | Ordering :: Greater => {
370
398
// Nothing to do in this case, they're already in the right order.
@@ -376,7 +404,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
376
404
cur_t = Some ( CurItem {
377
405
order : module_level_order,
378
406
item,
379
- name : item . ident . name . as_str ( ) . to_owned ( ) ,
407
+ name : get_item_name ( item ) ,
380
408
} ) ;
381
409
}
382
410
}
@@ -425,3 +453,54 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte
425
453
ItemKind :: Impl ( ..) => Impl ,
426
454
}
427
455
}
456
+
457
+ /// Gets the item name for sorting purposes, which in the general case is
458
+ /// `item.ident.name`.
459
+ ///
460
+ /// For trait impls, the name used for sorting will be the written path of
461
+ /// `item.self_ty` plus the written path of `item.of_trait`, joined with
462
+ /// exclamation marks. Exclamation marks are used because they are the first
463
+ /// printable ASCII character.
464
+ ///
465
+ /// Trait impls generated using a derive-macro will have their path rewritten,
466
+ /// such that for example `Default` is `$crate::default::Default`, and
467
+ /// `std::clone::Clone` is `$crate::clone::Clone`. This behaviour is described
468
+ /// further in the [Rust Reference, Paths Chapter][rust_ref].
469
+ ///
470
+ /// [rust_ref]: https://doc.rust-lang.org/reference/paths.html#crate-1
471
+ fn get_item_name ( item : & Item < ' _ > ) -> String {
472
+ match item. kind {
473
+ ItemKind :: Impl ( im) => {
474
+ if let TyKind :: Path ( path) = im. self_ty . kind {
475
+ match path {
476
+ QPath :: Resolved ( _, path) => {
477
+ let segs = path. segments . iter ( ) ;
478
+ let mut segs: Vec < String > = segs. map ( |s| s. ident . name . as_str ( ) . to_owned ( ) ) . collect ( ) ;
479
+
480
+ if let Some ( of_trait) = im. of_trait {
481
+ let mut trait_segs: Vec < String > = of_trait
482
+ . path
483
+ . segments
484
+ . iter ( )
485
+ . map ( |s| s. ident . name . as_str ( ) . to_owned ( ) )
486
+ . collect ( ) ;
487
+ segs. append ( & mut trait_segs) ;
488
+ }
489
+
490
+ segs. push ( String :: new ( ) ) ;
491
+ segs. join ( "!!" )
492
+ } ,
493
+ QPath :: TypeRelative ( _, _path_seg) => {
494
+ // This case doesn't exist in the clippy tests codebase.
495
+ String :: new ( )
496
+ } ,
497
+ QPath :: LangItem ( _, _) => String :: new ( ) ,
498
+ }
499
+ } else {
500
+ // Impls for anything that isn't a named type can be skipped.
501
+ String :: new ( )
502
+ }
503
+ } ,
504
+ _ => item. ident . name . as_str ( ) . to_owned ( ) ,
505
+ }
506
+ }
0 commit comments