blob: 85a2b1a6735253196ddeee8f9f71ea5e78db60fe [file] [log] [blame]
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),
);
}
}
}