]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/use_self.rs
Make ItemKind check dry
[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         if !is_item_interesting(item) {
91             // This does two things:
92             //  1) Reduce needless churn on `self.stack`
93             //  2) Don't push `StackItem::NoCheck` when entering `ItemKind::OpaqueTy`,
94             //     in order to lint `foo() -> impl <..>`
95             return;
96         }
97         // We push the self types of `impl`s on a stack here. Only the top type on the stack is
98         // relevant for linting, since this is the self type of the `impl` we're currently in. To
99         // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
100         // we're in an `impl` or nested item, that we don't want to lint
101         let stack_item = if_chain! {
102             if let ItemKind::Impl(Impl { self_ty, ref of_trait, .. }) = item.kind;
103             if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
104             let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
105             if parameters.as_ref().map_or(true, |params| {
106                 !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
107             });
108             then {
109                 let impl_trait_ref_def_id = of_trait.as_ref().map(|_| cx.tcx.hir().local_def_id(item.hir_id()));
110                 StackItem::Check {
111                     hir_id: self_ty.hir_id,
112                     impl_trait_ref_def_id,
113                     types_to_lint: Vec::new(),
114                     types_to_skip: Vec::new(),
115                 }
116             } else {
117                 StackItem::NoCheck
118             }
119         };
120         self.stack.push(stack_item);
121     }
122
123     fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
124         if is_item_interesting(item) {
125             self.stack.pop();
126         }
127     }
128
129     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
130         // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
131         // declaration. The collection of those types is all this method implementation does.
132         if_chain! {
133             if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
134             if let Some(&mut StackItem::Check {
135                 impl_trait_ref_def_id: Some(def_id),
136                 ref mut types_to_skip,
137                 ..
138             }) = self.stack.last_mut();
139             if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(def_id);
140             then {
141                 // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
142                 // `Self`.
143                 let self_ty = impl_trait_ref.self_ty();
144
145                 // `trait_method_sig` is the signature of the function, how it is declared in the
146                 // trait, not in the impl of the trait.
147                 let trait_method = cx
148                     .tcx
149                     .associated_items(impl_trait_ref.def_id)
150                     .find_by_name_and_kind(cx.tcx, impl_item.ident, AssocKind::Fn, impl_trait_ref.def_id)
151                     .expect("impl method matches a trait method");
152                 let trait_method_sig = cx.tcx.fn_sig(trait_method.def_id);
153                 let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
154
155                 // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
156                 // implementation of the trait.
157                 let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
158                     Some(&**ty)
159                 } else {
160                     None
161                 };
162                 let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
163
164                 // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
165                 //
166                 // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
167                 // trait declaration. This is used to check if `Self` was used in the trait
168                 // declaration.
169                 //
170                 // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
171                 // to `Self`), we want to skip linting that type and all subtypes of it. This
172                 // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
173                 // for u8`, when the trait always uses `Vec<u8>`.
174                 //
175                 // See also https://github.com/rust-lang/rust-clippy/issues/2894.
176                 for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
177                     if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
178                         let mut visitor = SkipTyCollector::default();
179                         visitor.visit_ty(impl_hir_ty);
180                         types_to_skip.extend(visitor.types_to_skip);
181                     }
182                 }
183             }
184         }
185     }
186
187     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) {
188         // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
189         // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
190         // However the `node_type()` method can *only* be called in bodies.
191         //
192         // This method implementation determines which types should get linted in a `Body` and
193         // which shouldn't, with a visitor. We could directly lint in the visitor, but then we
194         // could only allow this lint on item scope. And we would have to check if those types are
195         // already dealt with in `check_ty` anyway.
196         if let Some(StackItem::Check {
197             hir_id,
198             types_to_lint,
199             types_to_skip,
200             ..
201         }) = self.stack.last_mut()
202         {
203             let self_ty = ty_from_hir_id(cx, *hir_id);
204
205             let mut visitor = LintTyCollector {
206                 cx,
207                 self_ty,
208                 types_to_lint: vec![],
209                 types_to_skip: vec![],
210             };
211             visitor.visit_expr(&body.value);
212             types_to_lint.extend(visitor.types_to_lint);
213             types_to_skip.extend(visitor.types_to_skip);
214         }
215     }
216
217     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
218         if_chain! {
219             if !in_macro(hir_ty.span) && !in_impl(cx, hir_ty);
220             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
221             if let Some(StackItem::Check {
222                 hir_id,
223                 types_to_lint,
224                 types_to_skip,
225                 ..
226             }) = self.stack.last();
227             if !types_to_skip.contains(&hir_ty.hir_id);
228             if types_to_lint.contains(&hir_ty.hir_id)
229                 || {
230                     let self_ty = ty_from_hir_id(cx, *hir_id);
231                     should_lint_ty(hir_ty, hir_ty_to_ty(cx.tcx, hir_ty), self_ty)
232                 };
233             let hir = cx.tcx.hir();
234             let id = hir.get_parent_node(hir_ty.hir_id);
235             if !hir.opt_span(id).map_or(false, in_macro);
236             then {
237                 span_lint(cx, hir_ty.span);
238             }
239         }
240     }
241
242     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
243         if_chain! {
244             if !in_macro(expr.span);
245             if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
246             if let Some(StackItem::Check { hir_id, .. }) = self.stack.last();
247             if cx.typeck_results().expr_ty(expr) == ty_from_hir_id(cx, *hir_id);
248             then {} else { return; }
249         }
250         match expr.kind {
251             ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
252                 Res::SelfTy(..) => (),
253                 Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
254                 _ => span_lint(cx, path.span),
255             },
256             // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
257             ExprKind::Call(fun, _) => {
258                 if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
259                     if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
260                         match ctor_of {
261                             CtorOf::Variant => lint_path_to_variant(cx, path),
262                             CtorOf::Struct => span_lint(cx, path.span),
263                         }
264                     }
265                 }
266             },
267             // unit enum variants (`Enum::A`)
268             ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
269             _ => (),
270         }
271     }
272
273     extract_msrv_attr!(LateContext);
274 }
275
276 #[derive(Default)]
277 struct SkipTyCollector {
278     types_to_skip: Vec<HirId>,
279 }
280
281 impl<'tcx> Visitor<'tcx> for SkipTyCollector {
282     type Map = Map<'tcx>;
283
284     fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
285         self.types_to_skip.push(hir_ty.hir_id);
286
287         walk_ty(self, hir_ty);
288     }
289
290     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
291         NestedVisitorMap::None
292     }
293 }
294
295 struct LintTyCollector<'a, 'tcx> {
296     cx: &'a LateContext<'tcx>,
297     self_ty: Ty<'tcx>,
298     types_to_lint: Vec<HirId>,
299     types_to_skip: Vec<HirId>,
300 }
301
302 impl<'a, 'tcx> Visitor<'tcx> for LintTyCollector<'a, 'tcx> {
303     type Map = Map<'tcx>;
304
305     fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'_>) {
306         if_chain! {
307             if let Some(ty) = self.cx.typeck_results().node_type_opt(hir_ty.hir_id);
308             if should_lint_ty(hir_ty, ty, self.self_ty);
309             then {
310                 self.types_to_lint.push(hir_ty.hir_id);
311             } else {
312                 self.types_to_skip.push(hir_ty.hir_id);
313             }
314         }
315
316         walk_ty(self, hir_ty);
317     }
318
319     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
320         NestedVisitorMap::None
321     }
322 }
323
324 fn span_lint(cx: &LateContext<'_>, span: Span) {
325     span_lint_and_sugg(
326         cx,
327         USE_SELF,
328         span,
329         "unnecessary structure name repetition",
330         "use the applicable keyword",
331         "Self".to_owned(),
332         Applicability::MachineApplicable,
333     );
334 }
335
336 fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
337     if let [.., self_seg, _variant] = path.segments {
338         let span = path
339             .span
340             .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
341         span_lint(cx, span);
342     }
343 }
344
345 fn is_item_interesting(item: &Item<'_>) -> bool {
346     use rustc_hir::ItemKind::{Const, Enum, Fn, Impl, Static, Struct, Trait, Union};
347     matches!(
348         item.kind,
349         Impl { .. } | Static(..) | Const(..) | Fn(..) | Enum(..) | Struct(..) | Union(..) | Trait(..)
350     )
351 }
352
353 fn ty_from_hir_id<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Ty<'tcx> {
354     if let Some(Node::Ty(hir_ty)) = cx.tcx.hir().find(hir_id) {
355         hir_ty_to_ty(cx.tcx, hir_ty)
356     } else {
357         unreachable!("This function should only be called with `HirId`s that are for sure `Node::Ty`")
358     }
359 }
360
361 fn in_impl(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> bool {
362     let map = cx.tcx.hir();
363     let parent = map.get_parent_node(hir_ty.hir_id);
364     if_chain! {
365         if let Some(Node::Item(item)) = map.find(parent);
366         if let ItemKind::Impl { .. } = item.kind;
367         then {
368             true
369         } else {
370             false
371         }
372     }
373 }
374
375 fn should_lint_ty(hir_ty: &hir::Ty<'_>, ty: Ty<'_>, self_ty: Ty<'_>) -> bool {
376     if_chain! {
377         if same_type_and_consts(ty, self_ty);
378         if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
379         then {
380             !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _))
381         } else {
382             false
383         }
384     }
385 }