@@ -5,8 +5,8 @@ use clippy_config::types::{
55} ;
66use clippy_utils:: diagnostics:: span_lint_and_note;
77use 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 ,
1010} ;
1111use rustc_lint:: { LateContext , LateLintPass } ;
1212use rustc_session:: impl_lint_pass;
@@ -72,7 +72,7 @@ declare_clippy_lint! {
7272 /// where it matters. Other solutions can be to use profile guided
7373 /// optimization (PGO), post-link optimization (e.g. using BOLT for LLVM),
7474 /// 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] .
7676 ///
7777 /// #### Lints on a Contains basis
7878 ///
@@ -191,6 +191,35 @@ impl ArbitrarySourceItemOrdering {
191191 ) ;
192192 }
193193
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+
194223 /// Produces a linting warning for incorrectly ordered trait items.
195224 fn lint_trait_item < T : rustc_lint:: LintContext > ( & self , cx : & T , item : & TraitItemRef , before_item : & TraitItemRef ) {
196225 span_lint_and_note (
@@ -247,7 +276,6 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
247276 if cur_t_kind == item_kind && cur_t. ident . name . as_str ( ) > item. ident . name . as_str ( ) {
248277 Self :: lint_member_name ( cx, & item. ident , & cur_t. ident ) ;
249278 } else if cur_t_kind_index > item_kind_index {
250- // let type1 = item
251279 self . lint_trait_item ( cx, item, cur_t) ;
252280 }
253281 }
@@ -267,7 +295,6 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
267295 if cur_t_kind == item_kind && cur_t. ident . name . as_str ( ) > item. ident . name . as_str ( ) {
268296 Self :: lint_member_name ( cx, & item. ident , & cur_t. ident ) ;
269297 } else if cur_t_kind_index > item_kind_index {
270- // let type1 = item
271298 self . lint_impl_item ( cx, item, cur_t) ;
272299 }
273300 }
@@ -304,9 +331,10 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
304331 // appears in the existing code base.
305332 if item. ident . name == rustc_span:: symbol:: kw:: Empty {
306333 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+ }
310338 } else if let ItemKind :: ForeignMod { .. } = item. kind {
311339 continue ;
312340 } else if let ItemKind :: GlobalAsm ( _) = item. kind {
@@ -358,13 +386,13 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
358386 use std:: cmp:: Ordering ; // Better legibility.
359387 match module_level_order. cmp ( & cur_t. order ) {
360388 Ordering :: Less => {
361- Self :: lint_member_name ( cx, & item. ident , & cur_t. item . ident ) ;
389+ Self :: lint_member_item ( cx, item, cur_t. item ) ;
362390 } ,
363391 Ordering :: Equal if item_kind == SourceItemOrderingModuleItemKind :: Use => {
364392 // Skip ordering use statements, as these should be ordered by rustfmt.
365393 } ,
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 ) ;
368396 } ,
369397 Ordering :: Equal | Ordering :: Greater => {
370398 // Nothing to do in this case, they're already in the right order.
@@ -376,7 +404,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
376404 cur_t = Some ( CurItem {
377405 order : module_level_order,
378406 item,
379- name : item . ident . name . as_str ( ) . to_owned ( ) ,
407+ name : get_item_name ( item ) ,
380408 } ) ;
381409 }
382410 }
@@ -425,3 +453,54 @@ fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleIte
425453 ItemKind :: Impl ( ..) => Impl ,
426454 }
427455}
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