Skip to content

Commit e7a7bf5

Browse files
committed
fixup! new lint: source_item_ordering
1 parent 69bb5b7 commit e7a7bf5

File tree

5 files changed

+565
-198
lines changed

5 files changed

+565
-198
lines changed

clippy_lints/src/arbitrary_source_item_ordering.rs

Lines changed: 91 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use clippy_config::types::{
55
};
66
use clippy_utils::diagnostics::span_lint_and_note;
77
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,
1010
};
1111
use rustc_lint::{LateContext, LateLintPass};
1212
use 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+
}

tests/ui/arbitrary_source_item_ordering.stderr

Lines changed: 0 additions & 184 deletions
This file was deleted.

0 commit comments

Comments
 (0)