]>
Commit | Line | Data |
---|---|---|
136023e0 XL |
1 | use clippy_utils::diagnostics::span_lint; |
2 | use clippy_utils::return_ty; | |
f2b60f7d | 3 | use clippy_utils::ty::contains_adt_constructor; |
136023e0 XL |
4 | use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node}; |
5 | use rustc_lint::{LateContext, LateLintPass}; | |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
7 | ||
8 | declare_clippy_lint! { | |
94222f64 XL |
9 | /// ### What it does |
10 | /// Warns when constructors have the same name as their types. | |
136023e0 | 11 | /// |
94222f64 XL |
12 | /// ### Why is this bad? |
13 | /// Repeating the name of the type is redundant. | |
136023e0 | 14 | /// |
94222f64 | 15 | /// ### Example |
136023e0 XL |
16 | /// ```rust,ignore |
17 | /// struct Foo {} | |
18 | /// | |
19 | /// impl Foo { | |
20 | /// pub fn foo() -> Foo { | |
21 | /// Foo {} | |
22 | /// } | |
23 | /// } | |
24 | /// ``` | |
25 | /// Use instead: | |
26 | /// ```rust,ignore | |
27 | /// struct Foo {} | |
28 | /// | |
29 | /// impl Foo { | |
30 | /// pub fn new() -> Foo { | |
31 | /// Foo {} | |
32 | /// } | |
33 | /// } | |
34 | /// ``` | |
a2a8927a | 35 | #[clippy::version = "1.55.0"] |
136023e0 XL |
36 | pub SELF_NAMED_CONSTRUCTORS, |
37 | style, | |
38 | "method should not have the same name as the type it is implemented for" | |
39 | } | |
40 | ||
41 | declare_lint_pass!(SelfNamedConstructors => [SELF_NAMED_CONSTRUCTORS]); | |
42 | ||
43 | impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { | |
44 | fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { | |
45 | match impl_item.kind { | |
46 | ImplItemKind::Fn(ref sig, _) => { | |
47 | if sig.decl.implicit_self.has_implicit_self() { | |
48 | return; | |
49 | } | |
50 | }, | |
51 | _ => return, | |
52 | } | |
53 | ||
5099ac24 | 54 | let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()); |
136023e0 XL |
55 | let item = cx.tcx.hir().expect_item(parent); |
56 | let self_ty = cx.tcx.type_of(item.def_id); | |
57 | let ret_ty = return_ty(cx, impl_item.hir_id()); | |
58 | ||
59 | // Do not check trait impls | |
60 | if matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. })) { | |
61 | return; | |
62 | } | |
63 | ||
64 | // Ensure method is constructor-like | |
65 | if let Some(self_adt) = self_ty.ty_adt_def() { | |
5099ac24 | 66 | if !contains_adt_constructor(ret_ty, self_adt) { |
136023e0 XL |
67 | return; |
68 | } | |
f2b60f7d | 69 | } else if !ret_ty.contains(self_ty) { |
136023e0 XL |
70 | return; |
71 | } | |
72 | ||
73 | if_chain! { | |
74 | if let Some(self_def) = self_ty.ty_adt_def(); | |
5e7ed085 | 75 | if let Some(self_local_did) = self_def.did().as_local(); |
136023e0 XL |
76 | let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did); |
77 | if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id); | |
78 | let type_name = x.ident.name.as_str().to_lowercase(); | |
a2a8927a | 79 | if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name; |
136023e0 XL |
80 | |
81 | then { | |
82 | span_lint( | |
83 | cx, | |
84 | SELF_NAMED_CONSTRUCTORS, | |
85 | impl_item.span, | |
86 | &format!("constructor `{}` has the same name as the type", impl_item.ident.name), | |
87 | ); | |
88 | } | |
89 | } | |
90 | } | |
91 | } |