| use clippy_config::Conf; | |
| use clippy_utils::diagnostics::span_lint_and_sugg; | |
| use clippy_utils::is_from_proc_macro; | |
| use clippy_utils::msrvs::{self, Msrv}; | |
| use clippy_utils::ty::{same_type_modulo_regions, ty_from_hir_ty}; | |
| use rustc_data_structures::fx::FxHashSet; | |
| use rustc_errors::Applicability; | |
| use rustc_hir::def::{CtorOf, DefKind, Res}; | |
| use rustc_hir::def_id::LocalDefId; | |
| use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; | |
| use rustc_hir::{ | |
| self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl, | |
| ImplItemImplKind, ImplItemKind, Item, ItemKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, | |
| }; | |
| use rustc_lint::{LateContext, LateLintPass}; | |
| use rustc_middle::ty::Ty as MiddleTy; | |
| use rustc_session::impl_lint_pass; | |
| use rustc_span::Span; | |
| use std::iter; | |
| use std::ops::ControlFlow; | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Checks for unnecessary repetition of structure name when a | |
| /// replacement with `Self` is applicable. | |
| /// | |
| /// ### Why is this bad? | |
| /// Unnecessary repetition. Mixed use of `Self` and struct | |
| /// name | |
| /// feels inconsistent. | |
| /// | |
| /// ### Known problems | |
| /// - Unaddressed false negative in fn bodies of trait implementations | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// struct Foo; | |
| /// impl Foo { | |
| /// fn new() -> Foo { | |
| /// Foo {} | |
| /// } | |
| /// } | |
| /// ``` | |
| /// could be | |
| /// ```no_run | |
| /// struct Foo; | |
| /// impl Foo { | |
| /// fn new() -> Self { | |
| /// Self {} | |
| /// } | |
| /// } | |
| /// ``` | |
| #[clippy::version = "pre 1.29.0"] | |
| pub USE_SELF, | |
| nursery, | |
| "unnecessary structure name repetition whereas `Self` is applicable" | |
| } | |
| impl_lint_pass!(UseSelf => [USE_SELF]); | |
| pub struct UseSelf { | |
| msrv: Msrv, | |
| stack: Vec<StackItem>, | |
| recursive_self_in_type_definitions: bool, | |
| } | |
| impl UseSelf { | |
| pub fn new(conf: &'static Conf) -> Self { | |
| Self { | |
| msrv: conf.msrv, | |
| stack: Vec::new(), | |
| recursive_self_in_type_definitions: conf.recursive_self_in_type_definitions, | |
| } | |
| } | |
| } | |
| #[derive(Debug)] | |
| enum StackItem { | |
| Check { | |
| impl_id: LocalDefId, | |
| types_to_skip: FxHashSet<HirId>, | |
| }, | |
| NoCheck, | |
| } | |
| const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; | |
| impl<'tcx> LateLintPass<'tcx> for UseSelf { | |
| fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { | |
| // We push the self types of items on a stack here. Only the top type on the stack is | |
| // relevant for linting, since this is the self type of the item we're currently in. To | |
| // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal that | |
| // we're in an item or nested item that we don't want to lint | |
| let stack_item = if let ItemKind::Impl(Impl { self_ty, generics, .. }) = item.kind | |
| && let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind | |
| && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args | |
| && parameters | |
| .as_ref() | |
| .is_none_or(|params| params.parenthesized == GenericArgsParentheses::No) | |
| && !item.span.from_expansion() | |
| // expensive, should be last check | |
| && !is_from_proc_macro(cx, item) | |
| { | |
| // Self cannot be used inside const generic parameters | |
| let types_to_skip = generics | |
| .params | |
| .iter() | |
| .filter_map(|param| match param.kind { | |
| GenericParamKind::Const { ty, .. } => Some(ty.hir_id), | |
| _ => None, | |
| }) | |
| .chain([self_ty.hir_id]) | |
| .collect(); | |
| StackItem::Check { | |
| impl_id: item.owner_id.def_id, | |
| types_to_skip, | |
| } | |
| } else if let ItemKind::Struct(..) | ItemKind::Enum(..) = item.kind | |
| && self.recursive_self_in_type_definitions | |
| && !item.span.from_expansion() | |
| && !is_from_proc_macro(cx, item) | |
| { | |
| StackItem::Check { | |
| impl_id: item.owner_id.def_id, | |
| types_to_skip: FxHashSet::default(), | |
| } | |
| } else { | |
| StackItem::NoCheck | |
| }; | |
| self.stack.push(stack_item); | |
| } | |
| fn check_item_post(&mut self, _: &LateContext<'_>, _: &Item<'_>) { | |
| self.stack.pop(); | |
| } | |
| fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { | |
| // Checking items of `impl Self` blocks in which macro expands into. | |
| if impl_item.span.from_expansion() { | |
| self.stack.push(StackItem::NoCheck); | |
| return; | |
| } | |
| // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait | |
| // declaration. The collection of those types is all this method implementation does. | |
| if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind | |
| && let ImplItemImplKind::Trait { .. } = impl_item.impl_kind | |
| && let Some(&mut StackItem::Check { | |
| impl_id, | |
| ref mut types_to_skip, | |
| .. | |
| }) = self.stack.last_mut() | |
| { | |
| let impl_trait_ref = cx.tcx.impl_trait_ref(impl_id); | |
| // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be | |
| // `Self`. | |
| let self_ty = impl_trait_ref.instantiate_identity().self_ty(); | |
| // `trait_method_sig` is the signature of the function, how it is declared in the | |
| // trait, not in the impl of the trait. | |
| let trait_method = cx | |
| .tcx | |
| .trait_item_of(impl_item.owner_id) | |
| .expect("impl method matches a trait method"); | |
| let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); | |
| let trait_method_sig = cx.tcx.instantiate_bound_regions_with_erased(trait_method_sig); | |
| // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the | |
| // implementation of the trait. | |
| let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output { | |
| Some(&**ty) | |
| } else { | |
| None | |
| }; | |
| let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty); | |
| // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature. | |
| // | |
| // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the | |
| // trait declaration. This is used to check if `Self` was used in the trait | |
| // declaration. | |
| // | |
| // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed | |
| // to `Self`), we want to skip linting that type and all subtypes of it. This | |
| // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait | |
| // for u8`, when the trait always uses `Vec<u8>`. | |
| // | |
| // See also https://github.com/rust-lang/rust-clippy/issues/2894. | |
| for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) { | |
| if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) { | |
| let mut visitor = SkipTyCollector::default(); | |
| visitor.visit_ty_unambig(impl_hir_ty); | |
| types_to_skip.extend(visitor.types_to_skip); | |
| } | |
| } | |
| } | |
| } | |
| fn check_impl_item_post(&mut self, _: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { | |
| if impl_item.span.from_expansion() | |
| && let Some(StackItem::NoCheck) = self.stack.last() | |
| { | |
| self.stack.pop(); | |
| } | |
| } | |
| fn check_ty(&mut self, cx: &LateContext<'tcx>, hir_ty: &Ty<'tcx, AmbigArg>) { | |
| if !hir_ty.span.from_expansion() | |
| && let Some(&StackItem::Check { | |
| impl_id, | |
| ref types_to_skip, | |
| }) = self.stack.last() | |
| && let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind | |
| && !matches!( | |
| path.res, | |
| Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } | Res::Def(DefKind::TyParam, _) | |
| ) | |
| && !ty_is_in_generic_args(cx, hir_ty) | |
| && !types_to_skip.contains(&hir_ty.hir_id) | |
| && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) | |
| && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() | |
| && same_type_modulo_regions(ty, impl_ty) | |
| // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that | |
| // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in | |
| // which case we must still trigger the lint. | |
| && same_lifetimes(ty, impl_ty) | |
| && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) | |
| { | |
| span_lint(cx, hir_ty.span); | |
| } | |
| } | |
| fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { | |
| if !expr.span.from_expansion() | |
| && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() | |
| && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity() | |
| && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) | |
| { | |
| match expr.kind { | |
| ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), | |
| ExprKind::Call(fun, _) => { | |
| if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { | |
| check_path(cx, path); | |
| } | |
| }, | |
| ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), | |
| _ => (), | |
| } | |
| } | |
| } | |
| fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { | |
| if !pat.span.from_expansion() | |
| && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() | |
| // get the path from the pattern | |
| && let PatKind::Expr(&PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) | |
| | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) | |
| | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind | |
| && cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity() | |
| && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) | |
| { | |
| check_path(cx, path); | |
| } | |
| } | |
| } | |
| #[derive(Default)] | |
| struct SkipTyCollector { | |
| types_to_skip: Vec<HirId>, | |
| } | |
| impl Visitor<'_> for SkipTyCollector { | |
| fn visit_infer(&mut self, inf_id: HirId, _inf_span: Span, kind: InferKind<'_>) -> Self::Result { | |
| // Conservatively assume ambiguously kinded inferred arguments are type arguments | |
| if let InferKind::Ambig(_) | InferKind::Ty(_) = kind { | |
| self.types_to_skip.push(inf_id); | |
| } | |
| self.visit_id(inf_id); | |
| } | |
| fn visit_ty(&mut self, hir_ty: &Ty<'_, AmbigArg>) { | |
| self.types_to_skip.push(hir_ty.hir_id); | |
| walk_ty(self, hir_ty); | |
| } | |
| } | |
| fn span_lint(cx: &LateContext<'_>, span: Span) { | |
| span_lint_and_sugg( | |
| cx, | |
| USE_SELF, | |
| span, | |
| "unnecessary structure name repetition", | |
| "use the applicable keyword", | |
| "Self".to_owned(), | |
| Applicability::MachineApplicable, | |
| ); | |
| } | |
| fn check_path(cx: &LateContext<'_>, path: &Path<'_>) { | |
| match path.res { | |
| Res::Def(DefKind::Ctor(CtorOf::Variant, _) | DefKind::Variant, ..) => { | |
| lint_path_to_variant(cx, path); | |
| }, | |
| Res::Def(DefKind::Ctor(CtorOf::Struct, _) | DefKind::Struct, ..) => span_lint(cx, path.span), | |
| _ => (), | |
| } | |
| } | |
| fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { | |
| if let [.., self_seg, _variant] = path.segments { | |
| let span = path | |
| .span | |
| .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi()); | |
| span_lint(cx, span); | |
| } | |
| } | |
| fn ty_is_in_generic_args<'tcx>(cx: &LateContext<'tcx>, hir_ty: &Ty<'tcx, AmbigArg>) -> bool { | |
| cx.tcx.hir_parent_iter(hir_ty.hir_id).any(|(_, parent)| { | |
| matches!(parent, Node::ImplItem(impl_item) if impl_item.generics.params.iter().any(|param| { | |
| let GenericParamKind::Const { ty: const_ty, .. } = ¶m.kind else { | |
| return false; | |
| }; | |
| ty_contains_ty(const_ty, hir_ty) | |
| })) | |
| }) | |
| } | |
| fn ty_contains_ty<'tcx>(outer: &Ty<'tcx>, inner: &Ty<'tcx, AmbigArg>) -> bool { | |
| struct ContainsVisitor<'tcx> { | |
| inner: &'tcx Ty<'tcx, AmbigArg>, | |
| } | |
| impl<'tcx> Visitor<'tcx> for ContainsVisitor<'tcx> { | |
| type Result = ControlFlow<()>; | |
| fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) -> Self::Result { | |
| if t.hir_id == self.inner.hir_id { | |
| return ControlFlow::Break(()); | |
| } | |
| walk_ty(self, t) | |
| } | |
| } | |
| let mut visitor = ContainsVisitor { inner }; | |
| visitor.visit_ty_unambig(outer).is_break() | |
| } | |
| /// Checks whether types `a` and `b` have the same lifetime parameters. | |
| /// | |
| /// This function does not check that types `a` and `b` are the same types. | |
| fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { | |
| use rustc_middle::ty::{Adt, GenericArgKind}; | |
| match (a.kind(), b.kind()) { | |
| (Adt(_, args_a), Adt(_, args_b)) => { | |
| iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { | |
| // TODO: Handle inferred lifetimes | |
| (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, | |
| (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), | |
| _ => true, | |
| }) | |
| }, | |
| _ => a == b, | |
| } | |
| } |