1 use crate::methods::SelfKind;
2 use clippy_utils::diagnostics::span_lint_and_help;
3 use clippy_utils::ty::is_copy;
4 use rustc_lint::LateContext;
5 use rustc_middle::ty::TyS;
6 use rustc_span::source_map::Span;
9 use super::WRONG_PUB_SELF_CONVENTION;
10 use super::WRONG_SELF_CONVENTION;
13 const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
14 (&[Convention::Eq("new")], &[SelfKind::No]),
15 (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
16 (&[Convention::StartsWith("from_")], &[SelfKind::No]),
17 (&[Convention::StartsWith("into_")], &[SelfKind::Value]),
18 (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
19 (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
20 (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
22 // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
23 // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
24 (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false),
25 Convention::IsTraitItem(false)], &[SelfKind::Ref]),
26 (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true),
27 Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
32 StartsWith(&'static str),
33 EndsWith(&'static str),
34 NotEndsWith(&'static str),
36 ImplementsTrait(bool),
44 cx: &LateContext<'tcx>,
45 self_ty: &'tcx TyS<'tcx>,
47 implements_trait: bool,
51 Self::Eq(this) => this == other,
52 Self::StartsWith(this) => other.starts_with(this) && this != other,
53 Self::EndsWith(this) => other.ends_with(this) && this != other,
54 Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item),
55 Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty),
56 Self::ImplementsTrait(is_true) => is_true == implements_trait,
57 Self::IsTraitItem(is_true) => is_true == is_trait_item,
62 impl fmt::Display for Convention {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
65 Self::Eq(this) => format!("`{}`", this).fmt(f),
66 Self::StartsWith(this) => format!("`{}*`", this).fmt(f),
67 Self::EndsWith(this) => format!("`*{}`", this).fmt(f),
68 Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f),
69 Self::IsSelfTypeCopy(is_true) => {
70 format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f)
72 Self::ImplementsTrait(is_true) => {
73 let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") };
74 format!("method{} implement{} a trait", negation, s_suffix).fmt(f)
76 Self::IsTraitItem(is_true) => {
77 let suffix = if is_true { " is" } else { " is not" };
78 format!("method{} a trait item", suffix).fmt(f)
84 #[allow(clippy::too_many_arguments)]
85 pub(super) fn check<'tcx>(
86 cx: &LateContext<'tcx>,
89 self_ty: &'tcx TyS<'tcx>,
90 first_arg_ty: &'tcx TyS<'tcx>,
92 implements_trait: bool,
95 let lint = if is_pub {
96 WRONG_PUB_SELF_CONVENTION
100 if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
103 .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
105 // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
109 .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
113 if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
115 if conventions.len() > 1 {
116 // Don't mention `NotEndsWith` when there is also `StartsWith` convention present
117 let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_)))
120 .any(|conv| matches!(conv, Convention::NotEndsWith(_)));
125 if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
126 || matches!(conv, Convention::ImplementsTrait(_))
127 || matches!(conv, Convention::IsTraitItem(_))
131 Some(conv.to_string())
137 format!("methods with the following characteristics: ({})", &s)
139 format!("methods called {}", &conventions[0])
148 "{} usually take {}",
152 .map(|k| k.description())
157 "consider choosing a less ambiguous name",