Skip to content

Commit

Permalink
add owned_cow lint
Browse files Browse the repository at this point in the history
  • Loading branch information
llogiq committed Jan 5, 2025
1 parent b57d98b commit e442270
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5891,6 +5891,7 @@ Released 2018-09-13
[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
[`owned_cow`]: https://rust-lang.github.io/rust-clippy/master/index.html#owned_cow
[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO,
crate::too_owned::OWNED_COW_INFO,
crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ mod temporary_assignment;
mod tests_outside_test_module;
mod to_digit_is_some;
mod to_string_trait_impl;
mod too_owned;
mod trailing_empty_array;
mod trait_bounds;
mod transmute;
Expand Down Expand Up @@ -970,5 +971,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
store.register_late_pass(|_| Box::new(too_owned::TooOwned));
// add lints here, do not remove this comment, it's used in `new_lint`
}
86 changes: 86 additions & 0 deletions clippy_lints/src/too_owned.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym;
use rustc_span::Span;

declare_clippy_lint! {
/// ### What it does
/// Detects needlessly owned `Cow` types.
///
/// ### Why is this bad?
/// The borrowed types are usually more flexible (e.g. `&str` vs. `&String`).
///
/// ### Example
/// ```no_run
/// let wrogn: std::borrow::Cow<'_, String>;
/// ```
/// Use instead:
/// ```no_run
/// let right: std::borrow::Cow<'_, str>;
/// ```
#[clippy::version = "1.85.0"]
pub OWNED_COW,
style,
"needlessly owned Cow type"
}

declare_lint_pass!(TooOwned => [OWNED_COW]);

impl LateLintPass<'_> for TooOwned {
//TODO: Switch to splitting between in-body and out-of-body execution
//typeck_results() exists only in-body
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) {
if let hir::TyKind::Path(qpath) = ty.kind
&& let Some(cow_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id()
&& cx.tcx.is_diagnostic_item(sym::Cow, 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)
{
span_lint_and_sugg(
cx,
OWNED_COW,
span,
"needlessly owned Cow type",
"use",
repl,
Applicability::MachineApplicable,
);
}
}
}

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
};
}
for (diag, repl) in [
(sym::cstring_type, "std::ffi::CStr"),
(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
}
17 changes: 17 additions & 0 deletions tests/ui/too_owned.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#![warn(clippy::cmp_owned)]

use std::borrow::Cow;
use std::ffi::{CString, OsString};
use std::path::PathBuf;

fn main() {
let x: Cow<'static, str> = Cow::Owned(String::from("Hi!"));
let y: Cow<'_, [u8]> = Cow::Owned(vec![]);
let z: Cow<'_, [_]> = Cow::Owned(vec![2_i32]);
let o: Cow<'_, std::ffi::OsStr> = Cow::Owned(OsString::new());
let c: Cow<'_, std::ffi::CStr> = Cow::Owned(CString::new("").unwrap());
let p: Cow<'_, std::path::Path> = Cow::Owned(PathBuf::new());

// false positive: borrowed type
let b: Cow<'_, str> = Cow::Borrowed("Hi!");
}
17 changes: 17 additions & 0 deletions tests/ui/too_owned.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#![warn(clippy::too_owned)]

use std::borrow::Cow;
use std::ffi::{CString, OsString};
use std::path::PathBuf;

fn main() {
let x: Cow<'static, String> = Cow::Owned(String::from("Hi!"));
let y: Cow<'_, Vec<u8>> = Cow::Owned(vec![]);
let z: Cow<'_, Vec<_>> = Cow::Owned(vec![2_i32]);
let o: Cow<'_, OsString> = Cow::Owned(OsString::new());
let c: Cow<'_, CString> = Cow::Owned(CString::new("").unwrap());
let p: Cow<'_, PathBuf> = Cow::Owned(PathBuf::new());

// false positive: borrowed type
let b: Cow<'_, str> = Cow::Borrowed("Hi!");
}
50 changes: 50 additions & 0 deletions tests/ui/too_owned.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
error: unknown lint: `clippy::too_owned`
--> tests/ui/too_owned.rs:1:9
|
LL | #![warn(clippy::too_owned)]
| ^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_owned`
|
= note: `-D unknown-lints` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(unknown_lints)]`

error: needlessly owned Cow type
--> tests/ui/too_owned.rs:8:25
|
LL | let x: Cow<'static, String> = Cow::Owned(String::from("Hi!"));
| ^^^^^^ help: use: `str`
|
= note: `-D clippy::owned-cow` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::owned_cow)]`

error: needlessly owned Cow type
--> tests/ui/too_owned.rs:9:20
|
LL | let y: Cow<'_, Vec<u8>> = Cow::Owned(vec![]);
| ^^^^^^^ help: use: `[u8]`

error: needlessly owned Cow type
--> tests/ui/too_owned.rs:10:20
|
LL | let z: Cow<'_, Vec<_>> = Cow::Owned(vec![2_i32]);
| ^^^^^^ help: use: `[_]`

error: needlessly owned Cow type
--> tests/ui/too_owned.rs:11:20
|
LL | let o: Cow<'_, OsString> = Cow::Owned(OsString::new());
| ^^^^^^^^ help: use: `std::ffi::OsStr`

error: needlessly owned Cow type
--> tests/ui/too_owned.rs:12:20
|
LL | let c: Cow<'_, CString> = Cow::Owned(CString::new("").unwrap());
| ^^^^^^^ help: use: `std::ffi::CStr`

error: needlessly owned Cow type
--> tests/ui/too_owned.rs:13:20
|
LL | let p: Cow<'_, PathBuf> = Cow::Owned(PathBuf::new());
| ^^^^^^^ help: use: `std::path::Path`

error: aborting due to 7 previous errors

0 comments on commit e442270

Please sign in to comment.