2021-07-19 09:52:05 +00:00
|
|
|
use clippy_utils::diagnostics::span_lint;
|
|
|
|
use clippy_utils::return_ty;
|
|
|
|
use clippy_utils::ty::{contains_adt_constructor, contains_ty};
|
|
|
|
use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node};
|
|
|
|
use rustc_lint::{LateContext, LateLintPass};
|
|
|
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|
|
|
|
|
|
|
declare_clippy_lint! {
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### What it does
|
|
|
|
/// Warns when constructors have the same name as their types.
|
2021-07-19 09:52:05 +00:00
|
|
|
///
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### Why is this bad?
|
|
|
|
/// Repeating the name of the type is redundant.
|
2021-07-19 09:52:05 +00:00
|
|
|
///
|
2021-07-29 10:16:06 +00:00
|
|
|
/// ### Example
|
2021-07-19 09:52:05 +00:00
|
|
|
/// ```rust,ignore
|
|
|
|
/// struct Foo {}
|
|
|
|
///
|
|
|
|
/// impl Foo {
|
|
|
|
/// pub fn foo() -> Foo {
|
|
|
|
/// Foo {}
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
/// Use instead:
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// struct Foo {}
|
|
|
|
///
|
|
|
|
/// impl Foo {
|
|
|
|
/// pub fn new() -> Foo {
|
|
|
|
/// Foo {}
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2021-07-29 10:16:06 +00:00
|
|
|
pub SELF_NAMED_CONSTRUCTORS,
|
2021-07-19 09:52:05 +00:00
|
|
|
style,
|
|
|
|
"method should not have the same name as the type it is implemented for"
|
|
|
|
}
|
|
|
|
|
2021-07-29 10:16:06 +00:00
|
|
|
declare_lint_pass!(SelfNamedConstructors => [SELF_NAMED_CONSTRUCTORS]);
|
2021-07-19 09:52:05 +00:00
|
|
|
|
2021-07-29 10:16:06 +00:00
|
|
|
impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
|
2021-07-19 09:52:05 +00:00
|
|
|
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());
|
|
|
|
let item = cx.tcx.hir().expect_item(parent);
|
|
|
|
let self_ty = cx.tcx.type_of(item.def_id);
|
|
|
|
let ret_ty = return_ty(cx, impl_item.hir_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 !contains_ty(ret_ty, self_ty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if_chain! {
|
|
|
|
if let Some(self_def) = self_ty.ty_adt_def();
|
|
|
|
if let Some(self_local_did) = self_def.did.as_local();
|
|
|
|
let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
|
|
|
|
if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id);
|
|
|
|
let type_name = x.ident.name.as_str().to_lowercase();
|
|
|
|
if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace("_", "") == type_name;
|
|
|
|
|
|
|
|
then {
|
|
|
|
span_lint(
|
|
|
|
cx,
|
2021-07-29 10:16:06 +00:00
|
|
|
SELF_NAMED_CONSTRUCTORS,
|
2021-07-19 09:52:05 +00:00
|
|
|
impl_item.span,
|
|
|
|
&format!("constructor `{}` has the same name as the type", impl_item.ident.name),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|