| 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 type_name = x.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), |
| ); |
| } |
| } |
| } |