1 //! lint on inherent implementations
3 use clippy_utils
::diagnostics
::span_lint_and_note
;
4 use clippy_utils
::is_lint_allowed
;
5 use rustc_data_structures
::fx
::FxHashMap
;
6 use rustc_hir
::def_id
::LocalDefId
;
7 use rustc_hir
::{Item, ItemKind, Node}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
9 use rustc_session
::declare_lint_pass
;
11 use std
::collections
::hash_map
::Entry
;
13 declare_clippy_lint
! {
15 /// Checks for multiple inherent implementations of a struct
17 /// ### Why is this bad?
18 /// Splitting the implementation of a type makes the code harder to navigate.
40 #[clippy::version = "pre 1.29.0"]
41 pub MULTIPLE_INHERENT_IMPL
,
43 "Multiple inherent impl that could be grouped"
46 declare_lint_pass
!(MultipleInherentImpl
=> [MULTIPLE_INHERENT_IMPL
]);
48 impl<'tcx
> LateLintPass
<'tcx
> for MultipleInherentImpl
{
49 fn check_crate_post(&mut self, cx
: &LateContext
<'tcx
>) {
50 // Map from a type to it's first impl block. Needed to distinguish generic arguments.
51 // e.g. `Foo<Bar>` and `Foo<Baz>`
52 let mut type_map
= FxHashMap
::default();
53 // List of spans to lint. (lint_span, first_span)
54 let mut lint_spans
= Vec
::new();
56 let Ok(impls
) = cx
.tcx
.crate_inherent_impls(()) else {
59 let inherent_impls
= cx
61 .with_stable_hashing_context(|hcx
| impls
.inherent_impls
.to_sorted(&hcx
, true));
63 for (_
, impl_ids
) in inherent_impls
.into_iter().filter(|(&id
, impls
)| {
65 // Check for `#[allow]` on the type definition
68 MULTIPLE_INHERENT_IMPL
,
69 cx
.tcx
.local_def_id_to_hir_id(id
),
72 for impl_id
in impl_ids
.iter().map(|id
| id
.expect_local()) {
73 let impl_ty
= cx
.tcx
.type_of(impl_id
).instantiate_identity();
74 match type_map
.entry(impl_ty
) {
76 // Store the id for the first impl block of this type. The span is retrieved lazily.
77 e
.insert(IdOrSpan
::Id(impl_id
));
79 Entry
::Occupied(mut e
) => {
80 if let Some(span
) = get_impl_span(cx
, impl_id
) {
81 let first_span
= match *e
.get() {
82 IdOrSpan
::Span(s
) => s
,
84 if let Some(s
) = get_impl_span(cx
, id
) {
85 // Remember the span of the first block.
86 *e
.get_mut() = IdOrSpan
::Span(s
);
89 // The first impl block isn't considered by the lint. Replace it with the
91 *e
.get_mut() = IdOrSpan
::Span(span
);
96 lint_spans
.push((span
, first_span
));
102 // Switching to the next type definition, no need to keep the current entries around.
106 // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first.
107 lint_spans
.sort_by_key(|x
| x
.0.lo());
108 for (span
, first_span
) in lint_spans
{
111 MULTIPLE_INHERENT_IMPL
,
113 "multiple implementations of this structure",
115 "first implementation here",
121 /// Gets the span for the given impl block unless it's not being considered by the lint.
122 fn get_impl_span(cx
: &LateContext
<'_
>, id
: LocalDefId
) -> Option
<Span
> {
123 let id
= cx
.tcx
.local_def_id_to_hir_id(id
);
124 if let Node
::Item(&Item
{
125 kind
: ItemKind
::Impl(impl_item
),
128 }) = cx
.tcx
.hir_node(id
)
130 (!span
.from_expansion()
131 && impl_item
.generics
.params
.is_empty()
132 && !is_lint_allowed(cx
, MULTIPLE_INHERENT_IMPL
, id
))