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