5fa961b951
Specifically, change `Ty` from this: ``` pub type Ty<'tcx> = &'tcx TyS<'tcx>; ``` to this ``` pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>); ``` There are two benefits to this. - It's now a first class type, so we can define methods on it. This means we can move a lot of methods away from `TyS`, leaving `TyS` as a barely-used type, which is appropriate given that it's not meant to be used directly. - The uniqueness requirement is now explicit, via the `Interned` type. E.g. the pointer-based `Eq` and `Hash` comes from `Interned`, rather than via `TyS`, which wasn't obvious at all. Much of this commit is boring churn. The interesting changes are in these files: - compiler/rustc_middle/src/arena.rs - compiler/rustc_middle/src/mir/visit.rs - compiler/rustc_middle/src/ty/context.rs - compiler/rustc_middle/src/ty/mod.rs Specifically: - Most mentions of `TyS` are removed. It's very much a dumb struct now; `Ty` has all the smarts. - `TyS` now has `crate` visibility instead of `pub`. - `TyS::make_for_test` is removed in favour of the static `BOOL_TY`, which just works better with the new structure. - The `Eq`/`Ord`/`Hash` impls are removed from `TyS`. `Interned`s impls of `Eq`/`Hash` now suffice. `Ord` is now partly on `Interned` (pointer-based, for the `Equal` case) and partly on `TyS` (contents-based, for the other cases). - There are many tedious sigil adjustments, i.e. adding or removing `*` or `&`. They seem to be unavoidable.
155 lines
6.0 KiB
Rust
155 lines
6.0 KiB
Rust
use crate::methods::SelfKind;
|
|
use clippy_utils::diagnostics::span_lint_and_help;
|
|
use clippy_utils::ty::is_copy;
|
|
use rustc_lint::LateContext;
|
|
use rustc_middle::ty::Ty;
|
|
use rustc_span::source_map::Span;
|
|
use std::fmt;
|
|
|
|
use super::WRONG_SELF_CONVENTION;
|
|
|
|
#[rustfmt::skip]
|
|
const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
|
|
(&[Convention::Eq("new")], &[SelfKind::No]),
|
|
(&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
|
|
(&[Convention::StartsWith("from_")], &[SelfKind::No]),
|
|
(&[Convention::StartsWith("into_")], &[SelfKind::Value]),
|
|
(&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
|
|
(&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
|
|
(&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
|
|
|
|
// Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
|
|
// Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
|
|
(&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false),
|
|
Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]),
|
|
(&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true),
|
|
Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
|
|
];
|
|
|
|
enum Convention {
|
|
Eq(&'static str),
|
|
StartsWith(&'static str),
|
|
EndsWith(&'static str),
|
|
NotEndsWith(&'static str),
|
|
IsSelfTypeCopy(bool),
|
|
ImplementsTrait(bool),
|
|
IsTraitItem(bool),
|
|
}
|
|
|
|
impl Convention {
|
|
#[must_use]
|
|
fn check<'tcx>(
|
|
&self,
|
|
cx: &LateContext<'tcx>,
|
|
self_ty: Ty<'tcx>,
|
|
other: &str,
|
|
implements_trait: bool,
|
|
is_trait_item: bool,
|
|
) -> bool {
|
|
match *self {
|
|
Self::Eq(this) => this == other,
|
|
Self::StartsWith(this) => other.starts_with(this) && this != other,
|
|
Self::EndsWith(this) => other.ends_with(this) && this != other,
|
|
Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item),
|
|
Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty),
|
|
Self::ImplementsTrait(is_true) => is_true == implements_trait,
|
|
Self::IsTraitItem(is_true) => is_true == is_trait_item,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Convention {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
match *self {
|
|
Self::Eq(this) => format!("`{}`", this).fmt(f),
|
|
Self::StartsWith(this) => format!("`{}*`", this).fmt(f),
|
|
Self::EndsWith(this) => format!("`*{}`", this).fmt(f),
|
|
Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f),
|
|
Self::IsSelfTypeCopy(is_true) => {
|
|
format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f)
|
|
},
|
|
Self::ImplementsTrait(is_true) => {
|
|
let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") };
|
|
format!("method{} implement{} a trait", negation, s_suffix).fmt(f)
|
|
},
|
|
Self::IsTraitItem(is_true) => {
|
|
let suffix = if is_true { " is" } else { " is not" };
|
|
format!("method{} a trait item", suffix).fmt(f)
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub(super) fn check<'tcx>(
|
|
cx: &LateContext<'tcx>,
|
|
item_name: &str,
|
|
self_ty: Ty<'tcx>,
|
|
first_arg_ty: Ty<'tcx>,
|
|
first_arg_span: Span,
|
|
implements_trait: bool,
|
|
is_trait_item: bool,
|
|
) {
|
|
if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
|
|
convs
|
|
.iter()
|
|
.all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
|
|
}) {
|
|
// don't lint if it implements a trait but not willing to check `Copy` types conventions (see #7032)
|
|
if implements_trait
|
|
&& !conventions
|
|
.iter()
|
|
.any(|conv| matches!(conv, Convention::IsSelfTypeCopy(_)))
|
|
{
|
|
return;
|
|
}
|
|
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
|
|
let suggestion = {
|
|
if conventions.len() > 1 {
|
|
// Don't mention `NotEndsWith` when there is also `StartsWith` convention present
|
|
let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_)))
|
|
&& conventions
|
|
.iter()
|
|
.any(|conv| matches!(conv, Convention::NotEndsWith(_)));
|
|
|
|
let s = conventions
|
|
.iter()
|
|
.filter_map(|conv| {
|
|
if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
|
|
|| matches!(conv, Convention::ImplementsTrait(_))
|
|
|| matches!(conv, Convention::IsTraitItem(_))
|
|
{
|
|
None
|
|
} else {
|
|
Some(conv.to_string())
|
|
}
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.join(" and ");
|
|
|
|
format!("methods with the following characteristics: ({})", &s)
|
|
} else {
|
|
format!("methods called {}", &conventions[0])
|
|
}
|
|
};
|
|
|
|
span_lint_and_help(
|
|
cx,
|
|
WRONG_SELF_CONVENTION,
|
|
first_arg_span,
|
|
&format!(
|
|
"{} usually take {}",
|
|
suggestion,
|
|
&self_kinds
|
|
.iter()
|
|
.map(|k| k.description())
|
|
.collect::<Vec<_>>()
|
|
.join(" or ")
|
|
),
|
|
None,
|
|
"consider choosing a less ambiguous name",
|
|
);
|
|
}
|
|
}
|
|
}
|