]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs
Merge commit '27afd6ade4bb1123a8bf82001629b69d23d62aff' into clippyup
[rust.git] / src / tools / clippy / clippy_lints / src / needless_arbitrary_self_type.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::in_macro;
3 use if_chain::if_chain;
4 use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
5 use rustc_errors::Applicability;
6 use rustc_lint::{EarlyContext, EarlyLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8 use rustc_span::symbol::kw;
9 use rustc_span::Span;
10
11 declare_clippy_lint! {
12     /// ### What it does
13     /// The lint checks for `self` in fn parameters that
14     /// specify the `Self`-type explicitly
15     /// ### Why is this bad?
16     /// Increases the amount and decreases the readability of code
17     ///
18     /// ### Example
19     /// ```rust
20     /// enum ValType {
21     ///     I32,
22     ///     I64,
23     ///     F32,
24     ///     F64,
25     /// }
26     ///
27     /// impl ValType {
28     ///     pub fn bytes(self: Self) -> usize {
29     ///         match self {
30     ///             Self::I32 | Self::F32 => 4,
31     ///             Self::I64 | Self::F64 => 8,
32     ///         }
33     ///     }
34     /// }
35     /// ```
36     ///
37     /// Could be rewritten as
38     ///
39     /// ```rust
40     /// enum ValType {
41     ///     I32,
42     ///     I64,
43     ///     F32,
44     ///     F64,
45     /// }
46     ///
47     /// impl ValType {
48     ///     pub fn bytes(self) -> usize {
49     ///         match self {
50     ///             Self::I32 | Self::F32 => 4,
51     ///             Self::I64 | Self::F64 => 8,
52     ///         }
53     ///     }
54     /// }
55     /// ```
56     pub NEEDLESS_ARBITRARY_SELF_TYPE,
57     complexity,
58     "type of `self` parameter is already by default `Self`"
59 }
60
61 declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]);
62
63 enum Mode {
64     Ref(Option<Lifetime>),
65     Value,
66 }
67
68 fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) {
69     if_chain! {
70         if let [segment] = &path.segments[..];
71         if segment.ident.name == kw::SelfUpper;
72         then {
73             // In case we have a named lifetime, we check if the name comes from expansion.
74             // If it does, at this point we know the rest of the parameter was written by the user,
75             // so let them decide what the name of the lifetime should be.
76             // See #6089 for more details.
77             let mut applicability = Applicability::MachineApplicable;
78             let self_param = match (binding_mode, mutbl) {
79                 (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
80                 (Mode::Ref(Some(lifetime)), Mutability::Mut) => {
81                     if in_macro(lifetime.ident.span) {
82                         applicability = Applicability::HasPlaceholders;
83                         "&'_ mut self".to_string()
84                     } else {
85                         format!("&{} mut self", &lifetime.ident.name)
86                     }
87                 },
88                 (Mode::Ref(None), Mutability::Not) => "&self".to_string(),
89                 (Mode::Ref(Some(lifetime)), Mutability::Not) => {
90                     if in_macro(lifetime.ident.span) {
91                         applicability = Applicability::HasPlaceholders;
92                         "&'_ self".to_string()
93                     } else {
94                         format!("&{} self", &lifetime.ident.name)
95                     }
96                 },
97                 (Mode::Value, Mutability::Mut) => "mut self".to_string(),
98                 (Mode::Value, Mutability::Not) => "self".to_string(),
99             };
100
101             span_lint_and_sugg(
102                 cx,
103                 NEEDLESS_ARBITRARY_SELF_TYPE,
104                 span,
105                 "the type of the `self` parameter does not need to be arbitrary",
106                 "consider to change this parameter to",
107                 self_param,
108                 applicability,
109             )
110         }
111     }
112 }
113
114 impl EarlyLintPass for NeedlessArbitrarySelfType {
115     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
116         // Bail out if the parameter it's not a receiver or was not written by the user
117         if !p.is_self() || in_macro(p.span) {
118             return;
119         }
120
121         match &p.ty.kind {
122             TyKind::Path(None, path) => {
123                 if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind {
124                     check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl);
125                 }
126             },
127             TyKind::Rptr(lifetime, mut_ty) => {
128                 if_chain! {
129                 if let TyKind::Path(None, path) = &mut_ty.ty.kind;
130                 if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind;
131                     then {
132                         check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl);
133                     }
134                 }
135             },
136             _ => {},
137         }
138     }
139 }