]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/methods/wrong_self_convention.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / methods / wrong_self_convention.rs
CommitLineData
f20569fa 1use crate::methods::SelfKind;
cdc7bbd5
XL
2use clippy_utils::diagnostics::span_lint_and_help;
3use clippy_utils::ty::is_copy;
f20569fa 4use rustc_lint::LateContext;
5099ac24 5use rustc_middle::ty::Ty;
f20569fa
XL
6use rustc_span::source_map::Span;
7use std::fmt;
8
f20569fa
XL
9use super::WRONG_SELF_CONVENTION;
10
11#[rustfmt::skip]
cdc7bbd5
XL
12const 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]),
04454e1e 17 (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]),
cdc7bbd5
XL
18 (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
19 (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
20
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
17df50a5
XL
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),
cdc7bbd5 26 Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
f20569fa 27];
cdc7bbd5 28
f20569fa
XL
29enum Convention {
30 Eq(&'static str),
31 StartsWith(&'static str),
cdc7bbd5
XL
32 EndsWith(&'static str),
33 NotEndsWith(&'static str),
34 IsSelfTypeCopy(bool),
35 ImplementsTrait(bool),
36 IsTraitItem(bool),
f20569fa
XL
37}
38
39impl Convention {
40 #[must_use]
cdc7bbd5
XL
41 fn check<'tcx>(
42 &self,
43 cx: &LateContext<'tcx>,
5099ac24 44 self_ty: Ty<'tcx>,
cdc7bbd5
XL
45 other: &str,
46 implements_trait: bool,
47 is_trait_item: bool,
48 ) -> bool {
f20569fa
XL
49 match *self {
50 Self::Eq(this) => this == other,
51 Self::StartsWith(this) => other.starts_with(this) && this != other,
cdc7bbd5
XL
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,
f20569fa
XL
57 }
58 }
59}
60
61impl fmt::Display for Convention {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
63 match *self {
cdc7bbd5
XL
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)
70 },
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)
74 },
75 Self::IsTraitItem(is_true) => {
76 let suffix = if is_true { " is" } else { " is not" };
77 format!("method{} a trait item", suffix).fmt(f)
78 },
f20569fa
XL
79 }
80 }
81}
82
cdc7bbd5 83#[allow(clippy::too_many_arguments)]
f20569fa
XL
84pub(super) fn check<'tcx>(
85 cx: &LateContext<'tcx>,
86 item_name: &str,
5099ac24
FG
87 self_ty: Ty<'tcx>,
88 first_arg_ty: Ty<'tcx>,
f20569fa 89 first_arg_span: Span,
cdc7bbd5
XL
90 implements_trait: bool,
91 is_trait_item: bool,
f20569fa 92) {
cdc7bbd5
XL
93 if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
94 convs
95 .iter()
96 .all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
97 }) {
98 // don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
99 if implements_trait
100 && !conventions
101 .iter()
102 .any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
103 {
104 return;
105 }
f20569fa 106 if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
cdc7bbd5
XL
107 let suggestion = {
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(_)))
111 && conventions
112 .iter()
113 .any(|conv| matches!(conv, Convention::NotEndsWith(_)));
114
115 let s = conventions
116 .iter()
117 .filter_map(|conv| {
118 if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
119 || matches!(conv, Convention::ImplementsTrait(_))
120 || matches!(conv, Convention::IsTraitItem(_))
121 {
122 None
123 } else {
124 Some(conv.to_string())
125 }
126 })
127 .collect::<Vec<_>>()
128 .join(" and ");
129
130 format!("methods with the following characteristics: ({})", &s)
131 } else {
132 format!("methods called {}", &conventions[0])
133 }
134 };
135
136 span_lint_and_help(
f20569fa 137 cx,
17df50a5 138 WRONG_SELF_CONVENTION,
f20569fa
XL
139 first_arg_span,
140 &format!(
cdc7bbd5
XL
141 "{} usually take {}",
142 suggestion,
f20569fa
XL
143 &self_kinds
144 .iter()
145 .map(|k| k.description())
146 .collect::<Vec<_>>()
147 .join(" or ")
148 ),
cdc7bbd5
XL
149 None,
150 "consider choosing a less ambiguous name",
f20569fa
XL
151 );
152 }
153 }
154}