add owned_cow lint (#13948)
Closes #13697. --- changelog: add [`owned_cow`] lint
This commit is contained in:
commit
bbf65f008d
12 changed files with 315 additions and 96 deletions
|
|
@ -745,6 +745,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::types::BOX_COLLECTION_INFO,
|
||||
crate::types::LINKEDLIST_INFO,
|
||||
crate::types::OPTION_OPTION_INFO,
|
||||
crate::types::OWNED_COW_INFO,
|
||||
crate::types::RC_BUFFER_INFO,
|
||||
crate::types::RC_MUTEX_INFO,
|
||||
crate::types::REDUNDANT_ALLOCATION_INFO,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ mod borrowed_box;
|
|||
mod box_collection;
|
||||
mod linked_list;
|
||||
mod option_option;
|
||||
mod owned_cow;
|
||||
mod rc_buffer;
|
||||
mod rc_mutex;
|
||||
mod redundant_allocation;
|
||||
|
|
@ -355,13 +356,63 @@ declare_clippy_lint! {
|
|||
"usage of `Rc<Mutex<T>>`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects needlessly owned `Cow` types.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The borrowed types are usually more flexible, in that e.g. a
|
||||
/// `Cow<'_, str>` can accept both `&str` and `String` while
|
||||
/// `Cow<'_, String>` can only accept `&String` and `String`. In
|
||||
/// particular, `&str` is more general, because it allows for string
|
||||
/// literals while `&String` can only be borrowed from a heap-owned
|
||||
/// `String`).
|
||||
///
|
||||
/// ### Known Problems
|
||||
/// The lint does not check for usage of the type. There may be external
|
||||
/// interfaces that require the use of an owned type.
|
||||
///
|
||||
/// At least the `CString` type also has a different API than `CStr`: The
|
||||
/// former has an `as_bytes` method which the latter calls `to_bytes`.
|
||||
/// There is no guarantee that other types won't gain additional methods
|
||||
/// leading to a similar mismatch.
|
||||
///
|
||||
/// In addition, the lint only checks for the known problematic types
|
||||
/// `String`, `Vec<_>`, `CString`, `OsString` and `PathBuf`. Custom types
|
||||
/// that implement `ToOwned` will not be detected.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let wrogn: std::borrow::Cow<'_, Vec<u8>>;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let right: std::borrow::Cow<'_, [u8]>;
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub OWNED_COW,
|
||||
style,
|
||||
"needlessly owned Cow type"
|
||||
}
|
||||
|
||||
pub struct Types {
|
||||
vec_box_size_threshold: u64,
|
||||
type_complexity_threshold: u64,
|
||||
avoid_breaking_exported_api: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
|
||||
impl_lint_pass!(Types => [
|
||||
BOX_COLLECTION,
|
||||
VEC_BOX,
|
||||
OPTION_OPTION,
|
||||
LINKEDLIST,
|
||||
BORROWED_BOX,
|
||||
REDUNDANT_ALLOCATION,
|
||||
RC_BUFFER,
|
||||
RC_MUTEX,
|
||||
TYPE_COMPLEXITY,
|
||||
OWNED_COW
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Types {
|
||||
fn check_fn(
|
||||
|
|
@ -561,6 +612,7 @@ impl Types {
|
|||
triggered |= option_option::check(cx, hir_ty, qpath, def_id);
|
||||
triggered |= linked_list::check(cx, hir_ty, def_id);
|
||||
triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
|
||||
triggered |= owned_cow::check(cx, qpath, def_id);
|
||||
|
||||
if triggered {
|
||||
return;
|
||||
|
|
@ -612,6 +664,14 @@ impl Types {
|
|||
QPath::LangItem(..) => {},
|
||||
}
|
||||
},
|
||||
TyKind::Path(ref qpath) => {
|
||||
let res = cx.qpath_res(qpath, hir_ty.hir_id);
|
||||
if let Some(def_id) = res.opt_def_id()
|
||||
&& self.is_type_change_allowed(context)
|
||||
{
|
||||
owned_cow::check(cx, qpath, def_id);
|
||||
}
|
||||
},
|
||||
TyKind::Ref(lt, ref mut_ty) => {
|
||||
context.is_nested_call = true;
|
||||
if !borrowed_box::check(cx, hir_ty, lt, mut_ty) {
|
||||
|
|
|
|||
66
clippy_lints/src/types/owned_cow.rs
Normal file
66
clippy_lints/src/types/owned_cow.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, def_id: DefId) -> bool {
|
||||
if cx.tcx.is_diagnostic_item(sym::Cow, def_id)
|
||||
&& let hir::QPath::Resolved(_, path) = qpath
|
||||
&& let [.., last_seg] = path.segments
|
||||
&& let Some(args) = last_seg.args
|
||||
&& let [_lt, carg] = args.args
|
||||
&& let hir::GenericArg::Type(cty) = carg
|
||||
&& let Some((span, repl)) = replacement(cx, cty.as_unambig_ty())
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
super::OWNED_COW,
|
||||
span,
|
||||
"needlessly owned Cow type",
|
||||
"use",
|
||||
repl,
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn replacement(cx: &LateContext<'_>, cty: &hir::Ty<'_>) -> Option<(Span, String)> {
|
||||
if clippy_utils::is_path_lang_item(cx, cty, hir::LangItem::String) {
|
||||
return Some((cty.span, "str".into()));
|
||||
}
|
||||
if clippy_utils::is_path_diagnostic_item(cx, cty, sym::Vec) {
|
||||
return if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = cty.kind
|
||||
&& let [.., last_seg] = path.segments
|
||||
&& let Some(args) = last_seg.args
|
||||
&& let [t, ..] = args.args
|
||||
&& let Some(snip) = snippet_opt(cx, t.span())
|
||||
{
|
||||
Some((cty.span, format!("[{snip}]")))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
if clippy_utils::is_path_diagnostic_item(cx, cty, sym::cstring_type) {
|
||||
return Some((
|
||||
cty.span,
|
||||
(if clippy_utils::is_no_std_crate(cx) {
|
||||
"core::ffi::CStr"
|
||||
} else {
|
||||
"std::ffi::CStr"
|
||||
})
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
// Neither OsString nor PathBuf are available outside std
|
||||
for (diag, repl) in [(sym::OsString, "std::ffi::OsStr"), (sym::PathBuf, "std::path::Path")] {
|
||||
if clippy_utils::is_path_diagnostic_item(cx, cty, diag) {
|
||||
return Some((cty.span, repl.into()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue