blob: 84038283bcf8fb2e2ddd913b0258b4327e1482d0 [file] [log] [blame] [edit]
use clippy_utils::sym;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_hir::Mutability;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(super) enum SelfKind {
Value,
Ref,
RefMut,
No, // When we want the first argument type to be different than `Self`
}
impl SelfKind {
pub(super) fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
if ty == parent_ty {
true
} else if let Some(boxed_ty) = ty.boxed_ty() {
boxed_ty == parent_ty
} else if let ty::Adt(adt_def, args) = ty.kind()
&& matches!(cx.tcx.get_diagnostic_name(adt_def.did()), Some(sym::Rc | sym::Arc))
{
args.types().next() == Some(parent_ty)
} else {
false
}
}
fn matches_ref<'a>(cx: &LateContext<'a>, mutability: Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
if let ty::Ref(_, t, m) = *ty.kind() {
return m == mutability && t == parent_ty;
}
let trait_sym = match mutability {
Mutability::Not => sym::AsRef,
Mutability::Mut => sym::AsMut,
};
let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else {
return false;
};
implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
}
fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
!matches_value(cx, parent_ty, ty)
&& !matches_ref(cx, Mutability::Not, parent_ty, ty)
&& !matches_ref(cx, Mutability::Mut, parent_ty, ty)
}
match self {
Self::Value => matches_value(cx, parent_ty, ty),
Self::Ref => matches_ref(cx, Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
Self::RefMut => matches_ref(cx, Mutability::Mut, parent_ty, ty),
Self::No => matches_none(cx, parent_ty, ty),
}
}
#[must_use]
pub(super) fn description(self) -> &'static str {
match self {
Self::Value => "`self` by value",
Self::Ref => "`self` by reference",
Self::RefMut => "`self` by mutable reference",
Self::No => "no `self`",
}
}
}