-fn check_trait_method_impl_decl<'tcx>(
- cx: &LateContext<'tcx>,
- impl_item: &ImplItem<'_>,
- impl_decl: &'tcx FnDecl<'_>,
- impl_trait_ref: ty::TraitRef<'tcx>,
-) {
- let trait_method = cx
- .tcx
- .associated_items(impl_trait_ref.def_id)
- .find_by_name_and_kind(cx.tcx, impl_item.ident, ty::AssocKind::Fn, impl_trait_ref.def_id)
- .expect("impl method matches a trait method");
-
- let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
- let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
-
- let output_hir_ty = if let FnRetTy::Return(ty) = &impl_decl.output {
- Some(&**ty)
- } else {
- None
- };
-
- // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
- // `trait_ty` (of type `ty::Ty`) is the semantic type for the signature in the trait.
- // We use `impl_hir_ty` to see if the type was written as `Self`,
- // `hir_ty_to_ty(...)` to check semantic types of paths, and
- // `trait_ty` to determine which parts of the signature in the trait, mention
- // the type being implemented verbatim (as opposed to `Self`).
- for (impl_hir_ty, trait_ty) in impl_decl
- .inputs
- .iter()
- .chain(output_hir_ty)
- .zip(trait_method_sig.inputs_and_output)
- {
- // Check if the input/output type in the trait method specifies the implemented
- // type verbatim, and only suggest `Self` if that isn't the case.
- // This avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`,
- // in an `impl Trait for u8`, when the trait always uses `Vec<u8>`.
- // See also https://github.com/rust-lang/rust-clippy/issues/2894.
- let self_ty = impl_trait_ref.self_ty();
- if !trait_ty.walk().any(|inner| inner == self_ty.into()) {
- let mut visitor = SemanticUseSelfVisitor { cx, self_ty };
-
- visitor.visit_ty(&impl_hir_ty);
+ fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
+ // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
+ // declaration. The collection of those types is all this method implementation does.
+ if_chain! {
+ if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
+ if let Some(&mut StackItem::Check {
+ impl_trait_ref_def_id: Some(def_id),
+ ref mut types_to_skip,
+ ..
+ }) = self.stack.last_mut();
+ if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
+ then {
+ // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
+ // `Self`.
+ let self_ty = impl_trait_ref.self_ty();
+
+ // `trait_method_sig` is the signature of the function, how it is declared in the
+ // trait, not in the impl of the trait.
+ let trait_method = cx
+ .tcx
+ .associated_items(impl_trait_ref.def_id)
+ .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id)
+ .expect("impl method matches a trait method");
+ let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
+ let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
+
+ // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
+ // implementation of the trait.
+ let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
+ Some(&**ty)
+ } else {
+ None
+ };
+ let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
+
+ // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
+ //
+ // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
+ // trait declaration. This is used to check if `Self` was used in the trait
+ // declaration.
+ //
+ // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
+ // to `Self`), we want to skip linting that type and all subtypes of it. This
+ // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
+ // for u8`, when the trait always uses `Vec<u8>`.
+ //
+ // See also https://github.com/rust-lang/rust-clippy/issues/2894.
+ for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
+ if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
+ let mut visitor = SkipTyCollector::default();
+ visitor.visit_ty(&impl_hir_ty);
+ types_to_skip.extend(visitor.types_to_skip);
+ }
+ }
+ }