summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/self_named_constructors.rs
blob: beca203c868d85c9fc470e1309a9cd10995ef6b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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_tool_lint};

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).subst_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_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,
                    SELF_NAMED_CONSTRUCTORS,
                    impl_item.span,
                    &format!("constructor `{}` has the same name as the type", impl_item.ident.name),
                );
            }
        }
    }
}