]>
Commit | Line | Data |
---|---|---|
f20569fa | 1 | use crate::methods::SelfKind; |
cdc7bbd5 XL |
2 | use clippy_utils::diagnostics::span_lint_and_help; |
3 | use clippy_utils::ty::is_copy; | |
f20569fa | 4 | use rustc_lint::LateContext; |
5099ac24 | 5 | use rustc_middle::ty::Ty; |
f20569fa XL |
6 | use rustc_span::source_map::Span; |
7 | use std::fmt; | |
8 | ||
f20569fa XL |
9 | use super::WRONG_SELF_CONVENTION; |
10 | ||
11 | #[rustfmt::skip] | |
cdc7bbd5 XL |
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]), | |
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 |
29 | enum 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 | ||
39 | impl 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 | ||
61 | impl 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 |
84 | pub(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 | } |