]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
f20569fa | 2 | use if_chain::if_chain; |
f2b60f7d | 3 | use rustc_ast::ast::{BindingAnnotation, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; |
f20569fa XL |
4 | use rustc_errors::Applicability; |
5 | use rustc_lint::{EarlyContext, EarlyLintPass}; | |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
7 | use rustc_span::symbol::kw; | |
8 | use rustc_span::Span; | |
9 | ||
10 | declare_clippy_lint! { | |
94222f64 XL |
11 | /// ### What it does |
12 | /// The lint checks for `self` in fn parameters that | |
f20569fa | 13 | /// specify the `Self`-type explicitly |
94222f64 XL |
14 | /// ### Why is this bad? |
15 | /// Increases the amount and decreases the readability of code | |
f20569fa | 16 | /// |
94222f64 | 17 | /// ### Example |
f20569fa XL |
18 | /// ```rust |
19 | /// enum ValType { | |
20 | /// I32, | |
21 | /// I64, | |
22 | /// F32, | |
23 | /// F64, | |
24 | /// } | |
25 | /// | |
26 | /// impl ValType { | |
27 | /// pub fn bytes(self: Self) -> usize { | |
28 | /// match self { | |
29 | /// Self::I32 | Self::F32 => 4, | |
30 | /// Self::I64 | Self::F64 => 8, | |
31 | /// } | |
32 | /// } | |
33 | /// } | |
34 | /// ``` | |
35 | /// | |
36 | /// Could be rewritten as | |
37 | /// | |
38 | /// ```rust | |
39 | /// enum ValType { | |
40 | /// I32, | |
41 | /// I64, | |
42 | /// F32, | |
43 | /// F64, | |
44 | /// } | |
45 | /// | |
46 | /// impl ValType { | |
47 | /// pub fn bytes(self) -> usize { | |
48 | /// match self { | |
49 | /// Self::I32 | Self::F32 => 4, | |
50 | /// Self::I64 | Self::F64 => 8, | |
51 | /// } | |
52 | /// } | |
53 | /// } | |
54 | /// ``` | |
a2a8927a | 55 | #[clippy::version = "1.47.0"] |
f20569fa XL |
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) => { | |
a2a8927a | 81 | if lifetime.ident.span.from_expansion() { |
f20569fa XL |
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) => { | |
a2a8927a | 90 | if lifetime.ident.span.from_expansion() { |
f20569fa XL |
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 | |
a2a8927a | 117 | if !p.is_self() || p.span.from_expansion() { |
f20569fa XL |
118 | return; |
119 | } | |
120 | ||
121 | match &p.ty.kind { | |
122 | TyKind::Path(None, path) => { | |
f2b60f7d | 123 | if let PatKind::Ident(BindingAnnotation(ByRef::No, mutbl), _, _) = p.pat.kind { |
17df50a5 | 124 | check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl); |
f20569fa XL |
125 | } |
126 | }, | |
f25598a0 | 127 | TyKind::Ref(lifetime, mut_ty) => { |
f20569fa XL |
128 | if_chain! { |
129 | if let TyKind::Path(None, path) = &mut_ty.ty.kind; | |
f2b60f7d | 130 | if let PatKind::Ident(BindingAnnotation::NONE, _, _) = p.pat.kind; |
f20569fa | 131 | then { |
17df50a5 | 132 | check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl); |
f20569fa XL |
133 | } |
134 | } | |
135 | }, | |
136 | _ => {}, | |
137 | } | |
138 | } | |
139 | } |