]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/unused_self.rs
Auto merge of #74060 - kpp:remove_length_at_most_32, r=dtolnay
[rust.git] / src / tools / clippy / clippy_lints / src / unused_self.rs
1 use if_chain::if_chain;
2 use rustc_hir::def::Res;
3 use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
4 use rustc_hir::{HirId, ImplItem, ImplItemKind, ItemKind, Path};
5 use rustc_lint::{LateContext, LateLintPass};
6 use rustc_middle::hir::map::Map;
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8
9 use crate::utils::span_lint_and_help;
10
11 declare_clippy_lint! {
12     /// **What it does:** Checks methods that contain a `self` argument but don't use it
13     ///
14     /// **Why is this bad?** It may be clearer to define the method as an associated function instead
15     /// of an instance method if it doesn't require `self`.
16     ///
17     /// **Known problems:** None.
18     ///
19     /// **Example:**
20     /// ```rust,ignore
21     /// struct A;
22     /// impl A {
23     ///     fn method(&self) {}
24     /// }
25     /// ```
26     ///
27     /// Could be written:
28     ///
29     /// ```rust,ignore
30     /// struct A;
31     /// impl A {
32     ///     fn method() {}
33     /// }
34     /// ```
35     pub UNUSED_SELF,
36     pedantic,
37     "methods that contain a `self` argument but don't use it"
38 }
39
40 declare_lint_pass!(UnusedSelf => [UNUSED_SELF]);
41
42 impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
43     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &ImplItem<'_>) {
44         if impl_item.span.from_expansion() {
45             return;
46         }
47         let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id);
48         let parent_item = cx.tcx.hir().expect_item(parent);
49         let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
50         let assoc_item = cx.tcx.associated_item(def_id);
51         if_chain! {
52             if let ItemKind::Impl { of_trait: None, .. } = parent_item.kind;
53             if assoc_item.fn_has_self_parameter;
54             if let ImplItemKind::Fn(.., body_id) = &impl_item.kind;
55             let body = cx.tcx.hir().body(*body_id);
56             if !body.params.is_empty();
57             then {
58                 let self_param = &body.params[0];
59                 let self_hir_id = self_param.pat.hir_id;
60                 let mut visitor = UnusedSelfVisitor {
61                     cx,
62                     uses_self: false,
63                     self_hir_id: &self_hir_id,
64                 };
65                 visitor.visit_body(body);
66                 if !visitor.uses_self {
67                     span_lint_and_help(
68                         cx,
69                         UNUSED_SELF,
70                         self_param.span,
71                         "unused `self` argument",
72                         None,
73                         "consider refactoring to a associated function",
74                     );
75                     return;
76                 }
77             }
78         }
79     }
80 }
81
82 struct UnusedSelfVisitor<'a, 'tcx> {
83     cx: &'a LateContext<'tcx>,
84     uses_self: bool,
85     self_hir_id: &'a HirId,
86 }
87
88 impl<'a, 'tcx> Visitor<'tcx> for UnusedSelfVisitor<'a, 'tcx> {
89     type Map = Map<'tcx>;
90
91     fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
92         if self.uses_self {
93             // This function already uses `self`
94             return;
95         }
96         if let Res::Local(hir_id) = &path.res {
97             self.uses_self = self.self_hir_id == hir_id
98         }
99         walk_path(self, path);
100     }
101
102     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
103         NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
104     }
105 }