]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/use_self.rs
Merge remote-tracking branch 'upstream/master' into rustup
[rust.git] / clippy_lints / src / use_self.rs
1 use clippy_utils::diagnostics::span_lint_and_sugg;
2 use clippy_utils::ty::same_type_and_consts;
3 use clippy_utils::{in_macro, meets_msrv, msrvs};
4 use if_chain::if_chain;
5 use rustc_errors::Applicability;
6 use rustc_hir::{
7     self as hir,
8     def::{CtorOf, DefKind, Res},
9     def_id::LocalDefId,
10     intravisit::{walk_ty, NestedVisitorMap, Visitor},
11     Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Node, Path, QPath, TyKind,
12 };
13 use rustc_lint::{LateContext, LateLintPass, LintContext};
14 use rustc_middle::hir::map::Map;
15 use rustc_middle::ty::{AssocKind, Ty};
16 use rustc_semver::RustcVersion;
17 use rustc_session::{declare_tool_lint, impl_lint_pass};
18 use rustc_span::Span;
19 use rustc_typeck::hir_ty_to_ty;
20
21 declare_clippy_lint! {
22     /// **What it does:** Checks for unnecessary repetition of structure name when a
23     /// replacement with `Self` is applicable.
24     ///
25     /// **Why is this bad?** Unnecessary repetition. Mixed use of `Self` and struct
26     /// name
27     /// feels inconsistent.
28     ///
29     /// **Known problems:**
30     /// - Unaddressed false negative in fn bodies of trait implementations
31     /// - False positive with assotiated types in traits (#4140)
32     ///
33     /// **Example:**
34     ///
35     /// ```rust
36     /// struct Foo {}
37     /// impl Foo {
38     ///     fn new() -> Foo {
39     ///         Foo {}
40     ///     }
41     /// }
42     /// ```
43     /// could be
44     /// ```rust
45     /// struct Foo {}
46     /// impl Foo {
47     ///     fn new() -> Self {
48     ///         Self {}
49     ///     }
50     /// }
51     /// ```
52     pub USE_SELF,
53     nursery,
54     "unnecessary structure name repetition whereas `Self` is applicable"
55 }
56
57 #[derive(Default)]
58 pub struct UseSelf {
59     msrv: Option<RustcVersion>,
60     stack: Vec<StackItem>,
61 }
62
63 impl UseSelf {
64     #[must_use]
65     pub fn new(msrv: Option<RustcVersion>) -> Self {
66         Self {
67             msrv,
68             ..Self::default()
69         }
70     }
71 }
72
73 #[derive(Debug)]
74 enum StackItem {
75     Check {
76         hir_id: HirId,
77         impl_trait_ref_def_id: Option<LocalDefId>,
78         types_to_skip: Vec<HirId>,
79         types_to_lint: Vec<HirId>,
80     },
81     NoCheck,
82 }
83
84 impl_lint_pass!(UseSelf => [USE_SELF]);
85
86 const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
87
88 impl<'tcx> LateLintPass<'tcx> for UseSelf {
89     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
90         // We push the self types of `impl`s on a stack here. Only the top type on the stack is
91         // relevant for linting, since this is the self type of the `impl` we're currently in. To
92         // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
93         // we're in an `impl` or nested item, that we don't want to lint
94         //
95         // NB: If you push something on the stack in this method, remember to also pop it in the
96         // `check_item_post` method.
97         match &item.kind {
98             ItemKind::Impl(Impl {
99                 self_ty: hir_self_ty,
100                 of_trait,
101                 ..
102             }) => {
103                 let should_check = if let TyKind::Path(QPath::Resolved(_, item_path)) = hir_self_ty.kind {
104                     let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
105                     parameters.as_ref().map_or(true, |params| {
106                         !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
107                     })
108                 } else {
109                     false
110                 };
111                 let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id()));
112                 if should_check {
113                     self.stack.push(StackItem::Check {
114                         hir_id: hir_self_ty.hir_id,
115                         impl_trait_ref_def_id,
116                         types_to_lint: Vec::new(),
117                         types_to_skip: Vec::new(),
118                     });
119                 } else {
120                     self.stack.push(StackItem::NoCheck);
121                 }
122             },
123             ItemKind::Static(..)
124             | ItemKind::Const(..)
125             | ItemKind::Fn(..)
126             | ItemKind::Enum(..)
127             | ItemKind::Struct(..)
128             | ItemKind::Union(..)
129             | ItemKind::Trait(..) => {
130                 self.stack.push(StackItem::NoCheck);
131             },
132             _ => (),
133         }
134     }
135
136     fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
137         use ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
138         match item.kind {
139             Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..) => {
140                 self.stack.pop();
141             },
142             _ => (),
143         }
144     }
145
146     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
147         // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
148         // declaration. The collection of those types is all this method implementation does.
149         if_chain! {
150             if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
151             if let Some(&mut StackItem::Check {
152                 impl_trait_ref_def_id: Some(def_id),
153                 ref mut types_to_skip,
154                 ..
155             }) = self.stack.last_mut();
156             if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
157             then {
158                 // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
159                 // `Self`.
160                 let self_ty = impl_trait_ref.self_ty();
161
162                 // `trait_method_sig` is the signature of the function, how it is declared in the
163                 // trait, not in the impl of the trait.
164                 let trait_method = cx
165                     .tcx
166                     .associated_items(impl_trait_ref.def_id)
167                     .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id)
168                     .expect("impl method matches a trait method");
169                 let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
170                 let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
171
172                 // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
173                 // implementation of the trait.
174                 let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
175                     Some(&**ty)
176                 } else {
177                     None
178                 };
179                 let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
180
181                 // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
182                 //
183                 // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
184                 // trait declaration. This is used to check if `Self` was used in the trait
185                 // declaration.
186                 //
187                 // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
188                 // to `Self`), we want to skip linting that type and all subtypes of it. This
189                 // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
190                 // for u8`, when the trait always uses `Vec<u8>`.
191                 //
192                 // See also https://github.com/rust-lang/rust-clippy/issues/2894.
193                 for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
194                     if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
195                         let mut visitor = SkipTyCollector::default();
196                         visitor.visit_ty(impl_hir_ty);
197                         types_to_skip.extend(visitor.types_to_skip);
198                     }
199                 }
200             }
201         }
202     }
203
204     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
205         // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
206         // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
207         // However the `node_type()` method can *only* be called in bodies.
208         //
209         // This method implementation determines which types should get linted in a `Body` and
210         // which shouldn't, with a visitor. We could directly lint in the visitor, but then we
211         // could only allow this lint on item scope. And we would have to check if those types are
212         // already dealt with in `check_ty` anyway.
213         if let Some(StackItem::Check {
214             hir_id,
215             types_to_lint,
216             types_to_skip,
217             ..
218         }) = self.stack.last_mut()
219         {
220             let self_ty = ty_from_hir_id(cx, *hir_id);
221
222             let mut visitor = LintTyCollector {
223                 cx,
224                 self_ty,
225                 types_to_lint: vec![],
226                 types_to_skip: vec![],
227             };
228             visitor.visit_expr(&body.value);
229             types_to_lint.extend(visitor.types_to_lint);
230             types_to_skip.extend(visitor.types_to_skip);
231         }
232     }
233
234     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
235         if_chain! {
236             if !in_macro(hir_ty.span) && !in_impl(cx, hir_ty);
237             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
238             if let Some(StackItem::Check {
239                 hir_id,
240                 types_to_lint,
241                 types_to_skip,
242                 ..
243             }) = self.stack.last();
244             if !types_to_skip.contains(&hir_ty.hir_id);
245             if types_to_lint.contains(&hir_ty.hir_id)
246                 || {
247                     let self_ty = ty_from_hir_id(cx, *hir_id);
248                     should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
249                 };
250             let hir = cx.tcx.hir();
251             let id = hir.get_parent_node(hir_ty.hir_id);
252             if !hir.opt_span(id).map_or(false, in_macro);
253             then {
254                 span_lint(cx, hir_ty.span);
255             }
256         }
257     }
258
259     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
260         if_chain! {
261             if !in_macro(expr.span);
262             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
263             if let Some(StackItem::Check { hir_id, .. }) = self.stack.last();
264             if cx.typeck_results().expr_ty(expr) == ty_from_hir_id(cx, *hir_id);
265             then {} else { return; }
266         }
267         match expr.kind {
268             ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
269                 Res::SelfTy(..) => (),
270                 Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
271                 _ => span_lint(cx, path.span),
272             },
273             // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
274             ExprKind::Call(fun, _) => {
275                 if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
276                     if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
277                         match ctor_of {
278                             CtorOf::Variant => lint_path_to_variant(cx, path),
279                             CtorOf::Struct => span_lint(cx, path.span),
280                         }
281                     }
282                 }
283             },
284             // unit enum variants (`Enum::A`)
285             ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
286             _ => (),
287         }
288     }
289
290     extract_msrv_attr!(LateContext);
291 }
292
293 #[derive(Default)]
294 struct SkipTyCollector {
295     types_to_skip: Vec<HirId>,
296 }
297
298 impl<'tcx> Visitor<'tcx> for SkipTyCollector {
299     type Map = Map<'tcx>;
300
301     fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
302         self.types_to_skip.push(hir_ty.hir_id);
303
304         walk_ty(self, hir_ty);
305     }
306
307     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
308         NestedVisitorMap::None
309     }
310 }
311
312 struct LintTyCollector<'a, 'tcx> {
313     cx: &'a LateContext<'tcx>,
314     self_ty: Ty<'tcx>,
315     types_to_lint: Vec<HirId>,
316     types_to_skip: Vec<HirId>,
317 }
318
319 impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> {
320     type Map = Map<'tcx>;
321
322     fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
323         if_chain! {
324             if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id);
325             if should_lint_ty(hir_ty, ty, self.self_ty);
326             then {
327                 self.types_to_lint.push(hir_ty.hir_id);
328             } else {
329                 self.types_to_skip.push(hir_ty.hir_id);
330             }
331         }
332
333         walk_ty(self, hir_ty);
334     }
335
336     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
337         NestedVisitorMap::None
338     }
339 }
340
341 fn span_lint(cx: &LateContext<'_>, span: Span) {
342     span_lint_and_sugg(
343         cx,
344         USE_SELF,
345         span,
346         "unnecessary structure name repetition",
347         "use the applicable keyword",
348         "Self".to_owned(),
349         Applicability::MachineApplicable,
350     );
351 }
352
353 fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
354     if let [.., self_seg, _variant] = path.segments {
355         let span = path
356             .span
357             .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
358         span_lint(cx, span);
359     }
360 }
361
362 fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> {
363     if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) {
364         hir_ty_to_ty(cx.tcx, hir_ty)
365     } else {
366         unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
367     }
368 }
369
370 fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
371     let map = cx.tcx.hir();
372     let parent = map.get_parent_node(hir_ty.hir_id);
373     if_chain! {
374         if let Some(Node::Item(item)) = map.find(parent);
375         if let ItemKind::Impl { .. } = item.kind;
376         then {
377             true
378         } else {
379             false
380         }
381     }
382 }
383
384 fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
385     if_chain! {
386         if same_type_and_consts(ty, self_ty);
387         if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
388         then {
389             !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _))
390         } else {
391             false
392         }
393     }
394 }