--- /dev/null
+use crate::utils::{in_macro, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::kw;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** The lint checks for `self` in fn parameters that
+ /// specify the `Self`-type explicitly
+ /// **Why is this bad?** Increases the amount and decreases the readability of code
+ ///
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// ```rust
+ /// enum ValType {
+ /// I32,
+ /// I64,
+ /// F32,
+ /// F64,
+ /// }
+ ///
+ /// impl ValType {
+ /// pub fn bytes(self: Self) -> usize {
+ /// match self {
+ /// Self::I32 | Self::F32 => 4,
+ /// Self::I64 | Self::F64 => 8,
+ /// }
+ /// }
+ /// }
+ /// ```
+ ///
+ /// Could be rewritten as
+ ///
+ /// ```rust
+ /// enum ValType {
+ /// I32,
+ /// I64,
+ /// F32,
+ /// F64,
+ /// }
+ ///
+ /// impl ValType {
+ /// pub fn bytes(self) -> usize {
+ /// match self {
+ /// Self::I32 | Self::F32 => 4,
+ /// Self::I64 | Self::F64 => 8,
+ /// }
+ /// }
+ /// }
+ /// ```
+ pub NEEDLESS_ARBITRARY_SELF_TYPE,
+ complexity,
+ "type of `self` parameter is already by default `Self`"
+}
+
+declare_lint_pass!(NeedlessArbitrarySelfType => [NEEDLESS_ARBITRARY_SELF_TYPE]);
+
+enum Mode {
+ Ref(Option<Lifetime>),
+ Value,
+}
+
+fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) {
+ if_chain! {
+ if let [segment] = &path.segments[..];
+ if segment.ident.name == kw::SelfUpper;
+ then {
+ // In case we have a named lifetime, we check if the name comes from expansion.
+ // If it does, at this point we know the rest of the parameter was written by the user,
+ // so let them decide what the name of the lifetime should be.
+ // See #6089 for more details.
+ let mut applicability = Applicability::MachineApplicable;
+ let self_param = match (binding_mode, mutbl) {
+ (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
+ (Mode::Ref(Some(lifetime)), Mutability::Mut) => {
+ if in_macro(lifetime.ident.span) {
+ applicability = Applicability::HasPlaceholders;
+ "&'_ mut self".to_string()
+ } else {
+ format!("&{} mut self", &lifetime.ident.name)
+ }
+ },
+ (Mode::Ref(None), Mutability::Not) => "&self".to_string(),
+ (Mode::Ref(Some(lifetime)), Mutability::Not) => {
+ if in_macro(lifetime.ident.span) {
+ applicability = Applicability::HasPlaceholders;
+ "&'_ self".to_string()
+ } else {
+ format!("&{} self", &lifetime.ident.name)
+ }
+ },
+ (Mode::Value, Mutability::Mut) => "mut self".to_string(),
+ (Mode::Value, Mutability::Not) => "self".to_string(),
+ };
+
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_ARBITRARY_SELF_TYPE,
+ span,
+ "the type of the `self` parameter does not need to be arbitrary",
+ "consider to change this parameter to",
+ self_param,
+ applicability,
+ )
+ }
+ }
+}
+
+impl EarlyLintPass for NeedlessArbitrarySelfType {
+ fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
+ // Bail out if the parameter it's not a receiver or was not written by the user
+ if !p.is_self() || in_macro(p.span) {
+ return;
+ }
+
+ match &p.ty.kind {
+ TyKind::Path(None, path) => {
+ if let PatKind::Ident(BindingMode::ByValue(mutbl), _, _) = p.pat.kind {
+ check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl)
+ }
+ },
+ TyKind::Rptr(lifetime, mut_ty) => {
+ if_chain! {
+ if let TyKind::Path(None, path) = &mut_ty.ty.kind;
+ if let PatKind::Ident(BindingMode::ByValue(Mutability::Not), _, _) = p.pat.kind;
+ then {
+ check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl)
+ }
+ }
+ },
+ _ => {},
+ }
+ }
+}