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_SELF_CONVENTION;
12 const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
13 (&[Convention::Eq("new")], &[SelfKind::No]),
14 (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
15 (&[Convention::StartsWith("from_")], &[SelfKind::No]),
16 (&[Convention::StartsWith("into_")], &[SelfKind::Value]),
17 (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
18 (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
19 (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
21 // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
22 // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
23 (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false),
24 Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]),
25 (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true),
26 Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
31 StartsWith(&'static str),
32 EndsWith(&'static str),
33 NotEndsWith(&'static str),
35 ImplementsTrait(bool),
43 cx: &LateContext<'tcx>,
44 self_ty: &'tcx TyS<'tcx>,
46 implements_trait: bool,
50 Self::Eq(this) => this == other,
51 Self::StartsWith(this) => other.starts_with(this) && this != other,
52 Self::EndsWith(this) => other.ends_with(this) && this != other,
53 Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item),
54 Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty),
55 Self::ImplementsTrait(is_true) => is_true == implements_trait,
56 Self::IsTraitItem(is_true) => is_true == is_trait_item,
61 impl fmt::Display for Convention {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
64 Self::Eq(this) => format!("`{}`", this).fmt(f),
65 Self::StartsWith(this) => format!("`{}*`", this).fmt(f),
66 Self::EndsWith(this) => format!("`*{}`", this).fmt(f),
67 Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f),
68 Self::IsSelfTypeCopy(is_true) => {
69 format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f)
71 Self::ImplementsTrait(is_true) => {
72 let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") };
73 format!("method{} implement{} a trait", negation, s_suffix).fmt(f)
75 Self::IsTraitItem(is_true) => {
76 let suffix = if is_true { " is" } else { " is not" };
77 format!("method{} a trait item", suffix).fmt(f)
83 #[allow(clippy::too_many_arguments)]
84 pub(super) fn check<'tcx>(
85 cx: &LateContext<'tcx>,
87 self_ty: &'tcx TyS<'tcx>,
88 first_arg_ty: &'tcx TyS<'tcx>,
90 implements_trait: bool,
93 if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
96 .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
98 // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
102 .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
106 if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
108 if conventions.len() > 1 {
109 // Don't mention `NotEndsWith` when there is also `StartsWith` convention present
110 let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_)))
113 .any(|conv| matches!(conv, Convention::NotEndsWith(_)));
118 if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
119 || matches!(conv, Convention::ImplementsTrait(_))
120 || matches!(conv, Convention::IsTraitItem(_))
124 Some(conv.to_string())
130 format!("methods with the following characteristics: ({})", &s)
132 format!("methods called {}", &conventions[0])
138 WRONG_SELF_CONVENTION,
141 "{} usually take {}",
145 .map(|k| k.description())
150 "consider choosing a less ambiguous name",