| use clippy_utils::diagnostics::span_lint; | |
| use clippy_utils::return_ty; | |
| use clippy_utils::ty::contains_adt_constructor; | |
| use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node}; | |
| use rustc_lint::{LateContext, LateLintPass}; | |
| use rustc_session::declare_lint_pass; | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Warns when constructors have the same name as their types. | |
| /// | |
| /// ### Why is this bad? | |
| /// Repeating the name of the type is redundant. | |
| /// | |
| /// ### Example | |
| /// ```rust,ignore | |
| /// struct Foo {} | |
| /// | |
| /// impl Foo { | |
| /// pub fn foo() -> Foo { | |
| /// Foo {} | |
| /// } | |
| /// } | |
| /// ``` | |
| /// Use instead: | |
| /// ```rust,ignore | |
| /// struct Foo {} | |
| /// | |
| /// impl Foo { | |
| /// pub fn new() -> Foo { | |
| /// Foo {} | |
| /// } | |
| /// } | |
| /// ``` | |
| #[clippy::version = "1.55.0"] | |
| pub SELF_NAMED_CONSTRUCTORS, | |
| style, | |
| "method should not have the same name as the type it is implemented for" | |
| } | |
| declare_lint_pass!(SelfNamedConstructors => [SELF_NAMED_CONSTRUCTORS]); | |
| impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { | |
| fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { | |
| match impl_item.kind { | |
| ImplItemKind::Fn(ref sig, _) => { | |
| if sig.decl.implicit_self.has_implicit_self() { | |
| return; | |
| } | |
| }, | |
| _ => return, | |
| } | |
| let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id; | |
| let item = cx.tcx.hir_expect_item(parent); | |
| let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); | |
| let ret_ty = return_ty(cx, impl_item.owner_id); | |
| // Do not check trait impls | |
| if matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. })) { | |
| return; | |
| } | |
| // Ensure method is constructor-like | |
| if let Some(self_adt) = self_ty.ty_adt_def() { | |
| if !contains_adt_constructor(ret_ty, self_adt) { | |
| return; | |
| } | |
| } else if !ret_ty.contains(self_ty) { | |
| return; | |
| } | |
| if let Some(self_def) = self_ty.ty_adt_def() | |
| && let Some(self_local_did) = self_def.did().as_local() | |
| && let Node::Item(x) = cx.tcx.hir_node_by_def_id(self_local_did) | |
| && let Some(type_ident) = x.kind.ident() | |
| && let type_name = type_ident.name.as_str().to_lowercase() | |
| && (impl_item.ident.name.as_str() == type_name | |
| || impl_item.ident.name.as_str().replace('_', "") == type_name) | |
| { | |
| span_lint( | |
| cx, | |
| SELF_NAMED_CONSTRUCTORS, | |
| impl_item.span, | |
| format!("constructor `{}` has the same name as the type", impl_item.ident.name), | |
| ); | |
| } | |
| } | |
| } |