#![allow( clippy::needless_borrow, clippy::needless_borrows_for_generic_args, clippy::ptr_arg, clippy::manual_async_fn, clippy::needless_lifetimes )] #![warn(clippy::unnecessary_to_owned, clippy::redundant_clone)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; use std::ops::Deref; #[derive(Clone)] struct X(String); impl Deref for X { type Target = [u8]; fn deref(&self) -> &[u8] { self.0.as_bytes() } } impl AsRef for X { fn as_ref(&self) -> &str { self.0.as_str() } } #[allow(clippy::to_string_trait_impl)] impl ToString for X { fn to_string(&self) -> String { self.0.to_string() } } impl X { fn join(&self, other: impl AsRef) -> Self { let mut s = self.0.clone(); s.push_str(other.as_ref()); Self(s) } } #[allow(dead_code)] #[derive(Clone)] enum FileType { Account, PrivateKey, Certificate, } fn main() { let c_str = CStr::from_bytes_with_nul(&[0]).unwrap(); let os_str = OsStr::new("x"); let path = std::path::Path::new("x"); let s = "x"; let array = ["x"]; let array_ref = &["x"]; let slice = &["x"][..]; let x = X(String::from("x")); let x_ref = &x; require_c_str(&Cow::from(c_str)); require_c_str(c_str); require_os_str(os_str); require_os_str(&Cow::from(os_str)); require_os_str(os_str); require_path(path); require_path(&Cow::from(path)); require_path(path); require_str(s); require_str(&Cow::from(s)); require_str(s); require_str(x_ref.as_ref()); require_slice(slice); require_slice(&Cow::from(slice)); require_slice(array.as_ref()); require_slice(array_ref.as_ref()); require_slice(slice); require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. require_x(&Cow::::Owned(x.clone())); require_x(&x_ref.to_owned()); // No longer flagged because of #8759. require_deref_c_str(c_str); require_deref_os_str(os_str); require_deref_path(path); require_deref_str(s); require_deref_slice(slice); require_impl_deref_c_str(c_str); require_impl_deref_os_str(os_str); require_impl_deref_path(path); require_impl_deref_str(s); require_impl_deref_slice(slice); require_deref_str_slice(s, slice); require_deref_slice_str(slice, s); require_as_ref_c_str(c_str); require_as_ref_os_str(os_str); require_as_ref_path(path); require_as_ref_str(s); require_as_ref_str(&x); require_as_ref_slice(array); require_as_ref_slice(array_ref); require_as_ref_slice(slice); require_impl_as_ref_c_str(c_str); require_impl_as_ref_os_str(os_str); require_impl_as_ref_path(path); require_impl_as_ref_str(s); require_impl_as_ref_str(&x); require_impl_as_ref_slice(array); require_impl_as_ref_slice(array_ref); require_impl_as_ref_slice(slice); require_as_ref_str_slice(s, array); require_as_ref_str_slice(s, array_ref); require_as_ref_str_slice(s, slice); require_as_ref_slice_str(array, s); require_as_ref_slice_str(array_ref, s); require_as_ref_slice_str(slice, s); let _ = x.join(x_ref); let _ = slice.iter().copied(); let _ = slice.iter().copied(); let _ = [std::path::PathBuf::new()][..].iter().cloned(); let _ = [std::path::PathBuf::new()][..].iter().cloned(); let _ = slice.iter().copied(); let _ = slice.iter().copied(); let _ = [std::path::PathBuf::new()][..].iter().cloned(); let _ = [std::path::PathBuf::new()][..].iter().cloned(); let _ = check_files(&[FileType::Account]); // negative tests require_string(&s.to_string()); require_string(&Cow::from(s).into_owned()); require_string(&s.to_owned()); require_string(&x_ref.to_string()); // `X` isn't copy. require_slice(&x.to_owned()); require_deref_slice(x.to_owned()); // The following should be flagged by `redundant_clone`, but not by this lint. require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap()); require_os_str(&OsString::from("x")); require_path(&std::path::PathBuf::from("x")); require_str(&String::from("x")); require_slice(&[String::from("x")]); } fn require_c_str(_: &CStr) {} fn require_os_str(_: &OsStr) {} fn require_path(_: &std::path::Path) {} fn require_str(_: &str) {} fn require_slice(_: &[T]) {} fn require_x(_: &X) {} fn require_deref_c_str>(_: T) {} fn require_deref_os_str>(_: T) {} fn require_deref_path>(_: T) {} fn require_deref_str>(_: T) {} fn require_deref_slice>(_: U) {} fn require_impl_deref_c_str(_: impl Deref) {} fn require_impl_deref_os_str(_: impl Deref) {} fn require_impl_deref_path(_: impl Deref) {} fn require_impl_deref_str(_: impl Deref) {} fn require_impl_deref_slice(_: impl Deref) {} fn require_deref_str_slice, U, V: Deref>(_: T, _: V) {} fn require_deref_slice_str, V: Deref>(_: U, _: V) {} fn require_as_ref_c_str>(_: T) {} fn require_as_ref_os_str>(_: T) {} fn require_as_ref_path>(_: T) {} fn require_as_ref_str>(_: T) {} fn require_as_ref_slice>(_: U) {} fn require_impl_as_ref_c_str(_: impl AsRef) {} fn require_impl_as_ref_os_str(_: impl AsRef) {} fn require_impl_as_ref_path(_: impl AsRef) {} fn require_impl_as_ref_str(_: impl AsRef) {} fn require_impl_as_ref_slice(_: impl AsRef<[T]>) {} fn require_as_ref_str_slice, U, V: AsRef<[U]>>(_: T, _: V) {} fn require_as_ref_slice_str, V: AsRef>(_: U, _: V) {} // `check_files` is based on: // https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262 fn check_files(file_types: &[FileType]) -> bool { for t in file_types { let path = match get_file_path(t) { Ok(p) => p, Err(_) => { return false; }, }; if !path.is_file() { return false; } } true } fn get_file_path(_file_type: &FileType) -> Result { Ok(std::path::PathBuf::new()) } fn require_string(_: &String) {} #[clippy::msrv = "1.35"] fn _msrv_1_35() { // `copied` was stabilized in 1.36, so clippy should use `cloned`. let _ = &["x"][..].iter().cloned(); } #[clippy::msrv = "1.36"] fn _msrv_1_36() { let _ = &["x"][..].iter().copied(); } // https://github.com/rust-lang/rust-clippy/issues/8507 mod issue_8507 { #![allow(dead_code)] struct Opaque

(P); pub trait Abstracted {} impl

Abstracted for Opaque

{} fn build

(p: P) -> Opaque

where P: AsRef, { Opaque(p) } // Should not lint. fn test_str(s: &str) -> Box { Box::new(build(s.to_string())) } // Should not lint. fn test_x(x: super::X) -> Box { Box::new(build(x)) } #[derive(Clone, Copy)] struct Y(&'static str); impl AsRef for Y { fn as_ref(&self) -> &str { self.0 } } #[allow(clippy::to_string_trait_impl)] impl ToString for Y { fn to_string(&self) -> String { self.0.to_string() } } // Should lint because Y is copy. fn test_y(y: Y) -> Box { Box::new(build(y)) } } // https://github.com/rust-lang/rust-clippy/issues/8759 mod issue_8759 { #![allow(dead_code)] #[derive(Default)] struct View {} impl std::borrow::ToOwned for View { type Owned = View; fn to_owned(&self) -> Self::Owned { View {} } } #[derive(Default)] struct RenderWindow { default_view: View, } impl RenderWindow { fn default_view(&self) -> &View { &self.default_view } fn set_view(&mut self, _view: &View) {} } fn main() { let mut rw = RenderWindow::default(); rw.set_view(&rw.default_view().to_owned()); } } mod issue_8759_variant { #![allow(dead_code)] #[derive(Clone, Default)] struct View {} #[derive(Default)] struct RenderWindow { default_view: View, } impl RenderWindow { fn default_view(&self) -> &View { &self.default_view } fn set_view(&mut self, _view: &View) {} } fn main() { let mut rw = RenderWindow::default(); rw.set_view(&rw.default_view().to_owned()); } } mod issue_9317 { #![allow(dead_code)] struct Bytes {} #[allow(clippy::to_string_trait_impl)] impl ToString for Bytes { fn to_string(&self) -> String { "123".to_string() } } impl AsRef<[u8]> for Bytes { fn as_ref(&self) -> &[u8] { &[1, 2, 3] } } fn consume>(c: C) { let _ = c; } pub fn main() { let b = Bytes {}; // Should not lint. consume(b.to_string()); } } mod issue_9351 { #![allow(dead_code)] use std::ops::Deref; use std::path::{Path, PathBuf}; fn require_deref_path>(x: T) -> T { x } fn generic_arg_used_elsewhere>(_x: T, _y: T) {} fn id>(x: T) -> T { x } fn predicates_are_satisfied(_x: impl std::fmt::Write) {} // Should lint fn single_return() -> impl AsRef { id("abc") } // Should not lint fn multiple_returns(b: bool) -> impl AsRef { if b { return String::new(); } id("abc".to_string()) } struct S1(String); // Should not lint fn fields1() -> S1 { S1(id("abc".to_string())) } struct S2 { s: String, } // Should not lint fn fields2() { let mut s = S2 { s: "abc".into() }; s.s = id("abc".to_string()); } pub fn main() { let path = std::path::Path::new("x"); let path_buf = path.to_owned(); // Should not lint. let _x: PathBuf = require_deref_path(path.to_owned()); generic_arg_used_elsewhere(path.to_owned(), path_buf); predicates_are_satisfied(id("abc".to_string())); } } mod issue_9504 { #![allow(dead_code)] async fn foo>(_: S) {} async fn bar() { foo(std::path::PathBuf::new().to_string_lossy().to_string()).await; } } mod issue_9771a { #![allow(dead_code)] use std::marker::PhantomData; pub struct Key, V: ?Sized>(K, PhantomData); impl, V: ?Sized> Key { pub fn new(key: K) -> Key { Key(key, PhantomData) } } pub fn pkh(pkh: &[u8]) -> Key, String> { Key::new([b"pkh-", pkh].concat().to_vec()) } } mod issue_9771b { #![allow(dead_code)] pub struct Key>(K); pub fn from(c: &[u8]) -> Key> { let v = [c].concat(); Key(v.to_vec()) } } // This is a watered down version of the code in: https://github.com/oxigraph/rio // The ICE is triggered by the call to `to_owned` on this line: // https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116 mod issue_10021 { #![allow(unused)] pub struct Iri(T); impl> Iri { pub fn parse(iri: T) -> Result { unimplemented!() } } pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> { let base_iri = Iri::parse(url.to_owned())?; Ok(()) } } mod issue_10033 { #![allow(dead_code)] use std::fmt::Display; use std::ops::Deref; fn _main() { let f = Foo; // Not actually unnecessary - this calls `Foo`'s `Display` impl, not `str`'s (even though `Foo` does // deref to `str`) foo(&f.to_string()); } fn foo(s: &str) { println!("{}", s); } struct Foo; impl Deref for Foo { type Target = str; fn deref(&self) -> &Self::Target { "str" } } impl Display for Foo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Foo") } } } mod issue_11952 { use core::future::{Future, IntoFuture}; fn foo<'a, T: AsRef<[u8]>>(x: T, y: &'a i32) -> impl 'a + Future> { async move { let _y = y; Ok(()) } } fn bar() { IntoFuture::into_future(foo([], &0)); } } fn borrow_checks() { use std::borrow::Borrow; use std::collections::HashSet; fn inner(a: &[&str]) { let mut s = HashSet::from([vec!["a"]]); s.remove(a); //~ ERROR: unnecessary use of `to_vec` } let mut s = HashSet::from(["a".to_string()]); s.remove("b"); //~ ERROR: unnecessary use of `to_owned` s.remove("b"); //~ ERROR: unnecessary use of `to_string` // Should not warn. s.remove("b"); let mut s = HashSet::from([vec!["a"]]); s.remove(["b"].as_slice()); //~ ERROR: unnecessary use of `to_vec` s.remove((&["b"]).as_slice()); //~ ERROR: unnecessary use of `to_vec` // Should not warn. s.remove(&["b"].to_vec().clone()); s.remove(["a"].as_slice()); trait SetExt { fn foo>(&self, _: &String); } impl SetExt for HashSet { fn foo>(&self, _: &String) {} } // Should not lint! HashSet::::new().foo::<&str>(&"".to_owned()); HashSet::::new().get(&1.to_string()); }