Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 40fd785

Browse files
committedDec 15, 2021
Auto merge of #7978 - smoelius:master, r=llogiq
Add `unnecessary_to_owned` lint This PR adds a lint to check for unnecessary calls to `ToOwned::to_owned` and other similar functions (e.g., `Cow::into_owned`, `ToString::to_string`, etc.). The lint checks for expressions of the form `&receiver.to_owned_like()` used in a position requiring type `&T` where one of the following is true: * `receiver`'s type is `T` exactly * `receiver`'s type implements `Deref<Target = T>` * `receiver`'s type implements `AsRef<T>` The lint additionally checks for expressions of the form `receiver.to_owned_like()` used as arguments of type `impl AsRef<T>`. It would be nice if the lint could also check for expressions used as arguments to functions like the following: ``` fn foo<T: AsRef<str>>(x: T) { ... } ``` However, I couldn't figure out how to determine whether a function input type was instantiated from a parameter with a trait bound. If someone could offer me some guidance, I would be happy to add such functionality. Closes #7933 changelog: Add [`unnecessary_to_owned`] lint
2 parents aa3648a + b891389 commit 40fd785

17 files changed

+1887
-20
lines changed
 

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,6 +3210,7 @@ Released 2018-09-13
32103210
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
32113211
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
32123212
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
3213+
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
32133214
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
32143215
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
32153216
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern

‎clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
181181
LintId::of(methods::UNNECESSARY_FILTER_MAP),
182182
LintId::of(methods::UNNECESSARY_FOLD),
183183
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
184+
LintId::of(methods::UNNECESSARY_TO_OWNED),
184185
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
185186
LintId::of(methods::USELESS_ASREF),
186187
LintId::of(methods::WRONG_SELF_CONVENTION),

‎clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ store.register_lints(&[
315315
methods::UNNECESSARY_FILTER_MAP,
316316
methods::UNNECESSARY_FOLD,
317317
methods::UNNECESSARY_LAZY_EVALUATIONS,
318+
methods::UNNECESSARY_TO_OWNED,
318319
methods::UNWRAP_OR_ELSE_DEFAULT,
319320
methods::UNWRAP_USED,
320321
methods::USELESS_ASREF,

‎clippy_lints/src/lib.register_perf.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
1717
LintId::of(methods::MANUAL_STR_REPEAT),
1818
LintId::of(methods::OR_FUN_CALL),
1919
LintId::of(methods::SINGLE_CHAR_PATTERN),
20+
LintId::of(methods::UNNECESSARY_TO_OWNED),
2021
LintId::of(misc::CMP_OWNED),
2122
LintId::of(mutex_atomic::MUTEX_ATOMIC),
2223
LintId::of(redundant_clone::REDUNDANT_CLONE),

‎clippy_lints/src/methods/implicit_clone.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,7 @@ use super::IMPLICIT_CLONE;
1212
pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, span: Span) {
1313
if_chain! {
1414
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
15-
if match method_name {
16-
"to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
17-
"to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
18-
"to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
19-
"to_vec" => cx.tcx.impl_of_method(method_def_id)
20-
.map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
21-
== Some(true),
22-
_ => false,
23-
};
15+
if is_clone_like(cx, method_name, method_def_id);
2416
let return_type = cx.typeck_results().expr_ty(expr);
2517
let input_type = cx.typeck_results().expr_ty(recv).peel_refs();
2618
if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
@@ -38,3 +30,22 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv
3830
}
3931
}
4032
}
33+
34+
/// Returns true if the named method can be used to clone the receiver.
35+
/// Note that `to_string` is not flagged by `implicit_clone`. So other lints that call
36+
/// `is_clone_like` and that do flag `to_string` must handle it separately. See, e.g.,
37+
/// `is_to_owned_like` in `unnecessary_to_owned.rs`.
38+
pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir::def_id::DefId) -> bool {
39+
match method_name {
40+
"to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
41+
"to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
42+
"to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
43+
"to_vec" => {
44+
cx.tcx
45+
.impl_of_method(method_def_id)
46+
.map(|impl_did| Some(impl_did) == cx.tcx.lang_items().slice_alloc_impl())
47+
== Some(true)
48+
},
49+
_ => false,
50+
}
51+
}

‎clippy_lints/src/methods/mod.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ mod suspicious_splitn;
5656
mod uninit_assumed_init;
5757
mod unnecessary_filter_map;
5858
mod unnecessary_fold;
59+
mod unnecessary_iter_cloned;
5960
mod unnecessary_lazy_eval;
61+
mod unnecessary_to_owned;
6062
mod unwrap_or_else_default;
6163
mod unwrap_used;
6264
mod useless_asref;
@@ -1885,6 +1887,32 @@ declare_clippy_lint! {
18851887
"usages of `str::splitn` that can be replaced with `str::split`"
18861888
}
18871889

1890+
declare_clippy_lint! {
1891+
/// ### What it does
1892+
/// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
1893+
/// and other `to_owned`-like functions.
1894+
///
1895+
/// ### Why is this bad?
1896+
/// The unnecessary calls result in useless allocations.
1897+
///
1898+
/// ### Example
1899+
/// ```rust
1900+
/// let path = std::path::Path::new("x");
1901+
/// foo(&path.to_string_lossy().to_string());
1902+
/// fn foo(s: &str) {}
1903+
/// ```
1904+
/// Use instead:
1905+
/// ```rust
1906+
/// let path = std::path::Path::new("x");
1907+
/// foo(&path.to_string_lossy());
1908+
/// fn foo(s: &str) {}
1909+
/// ```
1910+
#[clippy::version = "1.58.0"]
1911+
pub UNNECESSARY_TO_OWNED,
1912+
perf,
1913+
"unnecessary calls to `to_owned`-like functions"
1914+
}
1915+
18881916
pub struct Methods {
18891917
avoid_breaking_exported_api: bool,
18901918
msrv: Option<RustcVersion>,
@@ -1964,7 +1992,8 @@ impl_lint_pass!(Methods => [
19641992
MANUAL_STR_REPEAT,
19651993
EXTEND_WITH_DRAIN,
19661994
MANUAL_SPLIT_ONCE,
1967-
NEEDLESS_SPLITN
1995+
NEEDLESS_SPLITN,
1996+
UNNECESSARY_TO_OWNED,
19681997
]);
19691998

19701999
/// Extracts a method call name, args, and `Span` of the method name.
@@ -2007,6 +2036,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
20072036
single_char_add_str::check(cx, expr, args);
20082037
into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
20092038
single_char_pattern::check(cx, expr, method_call.ident.name, args);
2039+
unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
20102040
},
20112041
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
20122042
let mut info = BinaryExprInfo {
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::higher::ForLoop;
3+
use clippy_utils::source::snippet_opt;
4+
use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait};
5+
use clippy_utils::{fn_def_id, get_parent_expr, path_to_local_id, usage};
6+
use rustc_errors::Applicability;
7+
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
8+
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, HirId, LangItem, Mutability, Pat};
9+
use rustc_lint::LateContext;
10+
use rustc_middle::{hir::map::Map, ty};
11+
use rustc_span::{sym, Symbol};
12+
13+
use super::UNNECESSARY_TO_OWNED;
14+
15+
pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool {
16+
if_chain! {
17+
if let Some(parent) = get_parent_expr(cx, expr);
18+
if let Some(callee_def_id) = fn_def_id(cx, parent);
19+
if is_into_iter(cx, callee_def_id);
20+
then {
21+
check_for_loop_iter(cx, parent, method_name, receiver)
22+
} else {
23+
false
24+
}
25+
}
26+
}
27+
28+
/// Checks whether `expr` is an iterator in a `for` loop and, if so, determines whether the
29+
/// iterated-over items could be iterated over by reference. The reason why `check` above does not
30+
/// include this code directly is so that it can be called from
31+
/// `unnecessary_into_owned::check_into_iter_call_arg`.
32+
pub fn check_for_loop_iter(
33+
cx: &LateContext<'tcx>,
34+
expr: &'tcx Expr<'tcx>,
35+
method_name: Symbol,
36+
receiver: &'tcx Expr<'tcx>,
37+
) -> bool {
38+
if_chain! {
39+
if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent));
40+
if let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent);
41+
let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body);
42+
if !clone_or_copy_needed;
43+
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
44+
then {
45+
let snippet = if_chain! {
46+
if let ExprKind::MethodCall(maybe_iter_method_name, _, [collection], _) = receiver.kind;
47+
if maybe_iter_method_name.ident.name == sym::iter;
48+
49+
if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
50+
let receiver_ty = cx.typeck_results().expr_ty(receiver);
51+
if implements_trait(cx, receiver_ty, iterator_trait_id, &[]);
52+
if let Some(iter_item_ty) = get_iterator_item_ty(cx, receiver_ty);
53+
54+
if let Some(into_iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator);
55+
let collection_ty = cx.typeck_results().expr_ty(collection);
56+
if implements_trait(cx, collection_ty, into_iterator_trait_id, &[]);
57+
if let Some(into_iter_item_ty) = get_associated_type(cx, collection_ty, into_iterator_trait_id, "Item");
58+
59+
if iter_item_ty == into_iter_item_ty;
60+
if let Some(collection_snippet) = snippet_opt(cx, collection.span);
61+
then {
62+
collection_snippet
63+
} else {
64+
receiver_snippet
65+
}
66+
};
67+
span_lint_and_then(
68+
cx,
69+
UNNECESSARY_TO_OWNED,
70+
expr.span,
71+
&format!("unnecessary use of `{}`", method_name),
72+
|diag| {
73+
diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable);
74+
for addr_of_expr in addr_of_exprs {
75+
match addr_of_expr.kind {
76+
ExprKind::AddrOf(_, _, referent) => {
77+
let span = addr_of_expr.span.with_hi(referent.span.lo());
78+
diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable);
79+
}
80+
_ => unreachable!(),
81+
}
82+
}
83+
}
84+
);
85+
return true;
86+
}
87+
}
88+
false
89+
}
90+
91+
/// The core logic of `check_for_loop_iter` above, this function wraps a use of
92+
/// `CloneOrCopyVisitor`.
93+
fn clone_or_copy_needed(
94+
cx: &LateContext<'tcx>,
95+
pat: &Pat<'tcx>,
96+
body: &'tcx Expr<'tcx>,
97+
) -> (bool, Vec<&'tcx Expr<'tcx>>) {
98+
let mut visitor = CloneOrCopyVisitor {
99+
cx,
100+
binding_hir_ids: pat_bindings(pat),
101+
clone_or_copy_needed: false,
102+
addr_of_exprs: Vec::new(),
103+
};
104+
visitor.visit_expr(body);
105+
(visitor.clone_or_copy_needed, visitor.addr_of_exprs)
106+
}
107+
108+
/// Returns a vector of all `HirId`s bound by the pattern.
109+
fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
110+
let mut collector = usage::ParamBindingIdCollector {
111+
binding_hir_ids: Vec::new(),
112+
};
113+
collector.visit_pat(pat);
114+
collector.binding_hir_ids
115+
}
116+
117+
/// `clone_or_copy_needed` will be false when `CloneOrCopyVisitor` is done visiting if the only
118+
/// operations performed on `binding_hir_ids` are:
119+
/// * to take non-mutable references to them
120+
/// * to use them as non-mutable `&self` in method calls
121+
/// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
122+
/// when `CloneOrCopyVisitor` is done visiting.
123+
struct CloneOrCopyVisitor<'cx, 'tcx> {
124+
cx: &'cx LateContext<'tcx>,
125+
binding_hir_ids: Vec<HirId>,
126+
clone_or_copy_needed: bool,
127+
addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
128+
}
129+
130+
impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
131+
type Map = Map<'tcx>;
132+
133+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
134+
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
135+
}
136+
137+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
138+
walk_expr(self, expr);
139+
if self.is_binding(expr) {
140+
if let Some(parent) = get_parent_expr(self.cx, expr) {
141+
match parent.kind {
142+
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
143+
self.addr_of_exprs.push(parent);
144+
return;
145+
},
146+
ExprKind::MethodCall(_, _, args, _) => {
147+
if_chain! {
148+
if args.iter().skip(1).all(|arg| !self.is_binding(arg));
149+
if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
150+
let method_ty = self.cx.tcx.type_of(method_def_id);
151+
let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
152+
if matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Not));
153+
then {
154+
return;
155+
}
156+
}
157+
},
158+
_ => {},
159+
}
160+
}
161+
self.clone_or_copy_needed = true;
162+
}
163+
}
164+
}
165+
166+
impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
167+
fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
168+
self.binding_hir_ids
169+
.iter()
170+
.any(|hir_id| path_to_local_id(expr, *hir_id))
171+
}
172+
}
173+
174+
/// Returns true if the named method is `IntoIterator::into_iter`.
175+
pub fn is_into_iter(cx: &LateContext<'_>, callee_def_id: DefId) -> bool {
176+
cx.tcx.lang_items().require(LangItem::IntoIterIntoIter) == Ok(callee_def_id)
177+
}

‎clippy_lints/src/methods/unnecessary_to_owned.rs

Lines changed: 397 additions & 0 deletions
Large diffs are not rendered by default.

‎clippy_lints/src/unit_return_expecting_ord.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
169169
trait_name
170170
),
171171
Some(last_semi),
172-
&"probably caused by this trailing semicolon".to_string(),
172+
"probably caused by this trailing semicolon",
173173
);
174174
},
175175
None => {},

‎clippy_utils/src/ty.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,20 @@ pub fn contains_adt_constructor<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, adt: &'tc
5858
pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
5959
cx.tcx
6060
.get_diagnostic_item(sym::Iterator)
61-
.and_then(|iter_did| {
62-
cx.tcx.associated_items(iter_did).find_by_name_and_kind(
63-
cx.tcx,
64-
Ident::from_str("Item"),
65-
ty::AssocKind::Type,
66-
iter_did,
67-
)
68-
})
61+
.and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item"))
62+
}
63+
64+
/// Returns the associated type `name` for `ty` as an implementation of `trait_id`.
65+
/// Do not invoke without first verifying that the type implements the trait.
66+
pub fn get_associated_type<'tcx>(
67+
cx: &LateContext<'tcx>,
68+
ty: Ty<'tcx>,
69+
trait_id: DefId,
70+
name: &str,
71+
) -> Option<Ty<'tcx>> {
72+
cx.tcx
73+
.associated_items(trait_id)
74+
.find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
6975
.map(|assoc| {
7076
let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
7177
cx.tcx.normalize_erasing_regions(cx.param_env, proj)

‎clippy_utils/src/usage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
7878
}
7979

8080
pub struct ParamBindingIdCollector {
81-
binding_hir_ids: Vec<hir::HirId>,
81+
pub binding_hir_ids: Vec<hir::HirId>,
8282
}
8383
impl<'tcx> ParamBindingIdCollector {
8484
fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// run-rustfix
2+
3+
#![allow(unused_assignments)]
4+
#![warn(clippy::unnecessary_to_owned)]
5+
6+
#[allow(dead_code)]
7+
#[derive(Clone, Copy)]
8+
enum FileType {
9+
Account,
10+
PrivateKey,
11+
Certificate,
12+
}
13+
14+
fn main() {
15+
let path = std::path::Path::new("x");
16+
17+
let _ = check_files(&[(FileType::Account, path)]);
18+
let _ = check_files_vec(vec![(FileType::Account, path)]);
19+
20+
// negative tests
21+
let _ = check_files_ref(&[(FileType::Account, path)]);
22+
let _ = check_files_mut(&[(FileType::Account, path)]);
23+
let _ = check_files_ref_mut(&[(FileType::Account, path)]);
24+
let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
25+
let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
26+
}
27+
28+
// `check_files` and its variants are based on:
29+
// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
30+
fn check_files(files: &[(FileType, &std::path::Path)]) -> bool {
31+
for (t, path) in files {
32+
let other = match get_file_path(t) {
33+
Ok(p) => p,
34+
Err(_) => {
35+
return false;
36+
},
37+
};
38+
if !path.is_file() || !other.is_file() {
39+
return false;
40+
}
41+
}
42+
true
43+
}
44+
45+
fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool {
46+
for (t, path) in files.iter() {
47+
let other = match get_file_path(t) {
48+
Ok(p) => p,
49+
Err(_) => {
50+
return false;
51+
},
52+
};
53+
if !path.is_file() || !other.is_file() {
54+
return false;
55+
}
56+
}
57+
true
58+
}
59+
60+
fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool {
61+
for (ref t, path) in files.iter().copied() {
62+
let other = match get_file_path(t) {
63+
Ok(p) => p,
64+
Err(_) => {
65+
return false;
66+
},
67+
};
68+
if !path.is_file() || !other.is_file() {
69+
return false;
70+
}
71+
}
72+
true
73+
}
74+
75+
#[allow(unused_assignments)]
76+
fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool {
77+
for (mut t, path) in files.iter().copied() {
78+
t = FileType::PrivateKey;
79+
let other = match get_file_path(&t) {
80+
Ok(p) => p,
81+
Err(_) => {
82+
return false;
83+
},
84+
};
85+
if !path.is_file() || !other.is_file() {
86+
return false;
87+
}
88+
}
89+
true
90+
}
91+
92+
fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool {
93+
for (ref mut t, path) in files.iter().copied() {
94+
*t = FileType::PrivateKey;
95+
let other = match get_file_path(t) {
96+
Ok(p) => p,
97+
Err(_) => {
98+
return false;
99+
},
100+
};
101+
if !path.is_file() || !other.is_file() {
102+
return false;
103+
}
104+
}
105+
true
106+
}
107+
108+
fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool {
109+
for (t, path) in files.iter().copied() {
110+
let other = match get_file_path(&t) {
111+
Ok(p) => p,
112+
Err(_) => {
113+
return false;
114+
},
115+
};
116+
if !path.join(path).is_file() || !other.is_file() {
117+
return false;
118+
}
119+
}
120+
true
121+
}
122+
123+
#[allow(unused_assignments)]
124+
fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
125+
for (mut t, path) in files.iter().cloned() {
126+
t = FileType::PrivateKey;
127+
let other = match get_file_path(&t) {
128+
Ok(p) => p,
129+
Err(_) => {
130+
return false;
131+
},
132+
};
133+
if !path.is_file() || !other.is_file() {
134+
return false;
135+
}
136+
}
137+
true
138+
}
139+
140+
fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
141+
Ok(std::path::PathBuf::new())
142+
}

‎tests/ui/unnecessary_iter_cloned.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// run-rustfix
2+
3+
#![allow(unused_assignments)]
4+
#![warn(clippy::unnecessary_to_owned)]
5+
6+
#[allow(dead_code)]
7+
#[derive(Clone, Copy)]
8+
enum FileType {
9+
Account,
10+
PrivateKey,
11+
Certificate,
12+
}
13+
14+
fn main() {
15+
let path = std::path::Path::new("x");
16+
17+
let _ = check_files(&[(FileType::Account, path)]);
18+
let _ = check_files_vec(vec![(FileType::Account, path)]);
19+
20+
// negative tests
21+
let _ = check_files_ref(&[(FileType::Account, path)]);
22+
let _ = check_files_mut(&[(FileType::Account, path)]);
23+
let _ = check_files_ref_mut(&[(FileType::Account, path)]);
24+
let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
25+
let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
26+
}
27+
28+
// `check_files` and its variants are based on:
29+
// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
30+
fn check_files(files: &[(FileType, &std::path::Path)]) -> bool {
31+
for (t, path) in files.iter().copied() {
32+
let other = match get_file_path(&t) {
33+
Ok(p) => p,
34+
Err(_) => {
35+
return false;
36+
},
37+
};
38+
if !path.is_file() || !other.is_file() {
39+
return false;
40+
}
41+
}
42+
true
43+
}
44+
45+
fn check_files_vec(files: Vec<(FileType, &std::path::Path)>) -> bool {
46+
for (t, path) in files.iter().copied() {
47+
let other = match get_file_path(&t) {
48+
Ok(p) => p,
49+
Err(_) => {
50+
return false;
51+
},
52+
};
53+
if !path.is_file() || !other.is_file() {
54+
return false;
55+
}
56+
}
57+
true
58+
}
59+
60+
fn check_files_ref(files: &[(FileType, &std::path::Path)]) -> bool {
61+
for (ref t, path) in files.iter().copied() {
62+
let other = match get_file_path(t) {
63+
Ok(p) => p,
64+
Err(_) => {
65+
return false;
66+
},
67+
};
68+
if !path.is_file() || !other.is_file() {
69+
return false;
70+
}
71+
}
72+
true
73+
}
74+
75+
#[allow(unused_assignments)]
76+
fn check_files_mut(files: &[(FileType, &std::path::Path)]) -> bool {
77+
for (mut t, path) in files.iter().copied() {
78+
t = FileType::PrivateKey;
79+
let other = match get_file_path(&t) {
80+
Ok(p) => p,
81+
Err(_) => {
82+
return false;
83+
},
84+
};
85+
if !path.is_file() || !other.is_file() {
86+
return false;
87+
}
88+
}
89+
true
90+
}
91+
92+
fn check_files_ref_mut(files: &[(FileType, &std::path::Path)]) -> bool {
93+
for (ref mut t, path) in files.iter().copied() {
94+
*t = FileType::PrivateKey;
95+
let other = match get_file_path(t) {
96+
Ok(p) => p,
97+
Err(_) => {
98+
return false;
99+
},
100+
};
101+
if !path.is_file() || !other.is_file() {
102+
return false;
103+
}
104+
}
105+
true
106+
}
107+
108+
fn check_files_self_and_arg(files: &[(FileType, &std::path::Path)]) -> bool {
109+
for (t, path) in files.iter().copied() {
110+
let other = match get_file_path(&t) {
111+
Ok(p) => p,
112+
Err(_) => {
113+
return false;
114+
},
115+
};
116+
if !path.join(path).is_file() || !other.is_file() {
117+
return false;
118+
}
119+
}
120+
true
121+
}
122+
123+
#[allow(unused_assignments)]
124+
fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
125+
for (mut t, path) in files.iter().cloned() {
126+
t = FileType::PrivateKey;
127+
let other = match get_file_path(&t) {
128+
Ok(p) => p,
129+
Err(_) => {
130+
return false;
131+
},
132+
};
133+
if !path.is_file() || !other.is_file() {
134+
return false;
135+
}
136+
}
137+
true
138+
}
139+
140+
fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
141+
Ok(std::path::PathBuf::new())
142+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: unnecessary use of `copied`
2+
--> $DIR/unnecessary_iter_cloned.rs:31:22
3+
|
4+
LL | for (t, path) in files.iter().copied() {
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
8+
help: use
9+
|
10+
LL | for (t, path) in files {
11+
| ~~~~~
12+
help: remove this `&`
13+
|
14+
LL - let other = match get_file_path(&t) {
15+
LL + let other = match get_file_path(t) {
16+
|
17+
18+
error: unnecessary use of `copied`
19+
--> $DIR/unnecessary_iter_cloned.rs:46:22
20+
|
21+
LL | for (t, path) in files.iter().copied() {
22+
| ^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
help: use
25+
|
26+
LL | for (t, path) in files.iter() {
27+
| ~~~~~~~~~~~~
28+
help: remove this `&`
29+
|
30+
LL - let other = match get_file_path(&t) {
31+
LL + let other = match get_file_path(t) {
32+
|
33+
34+
error: aborting due to 2 previous errors
35+

‎tests/ui/unnecessary_to_owned.fixed

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// run-rustfix
2+
3+
#![allow(clippy::ptr_arg)]
4+
#![warn(clippy::unnecessary_to_owned)]
5+
6+
use std::borrow::Cow;
7+
use std::ffi::{CStr, CString, OsStr, OsString};
8+
use std::ops::Deref;
9+
10+
#[derive(Clone)]
11+
struct X(String);
12+
13+
impl Deref for X {
14+
type Target = [u8];
15+
fn deref(&self) -> &[u8] {
16+
self.0.as_bytes()
17+
}
18+
}
19+
20+
impl AsRef<str> for X {
21+
fn as_ref(&self) -> &str {
22+
self.0.as_str()
23+
}
24+
}
25+
26+
impl ToString for X {
27+
fn to_string(&self) -> String {
28+
self.0.to_string()
29+
}
30+
}
31+
32+
impl X {
33+
fn join(&self, other: impl AsRef<str>) -> Self {
34+
let mut s = self.0.clone();
35+
s.push_str(other.as_ref());
36+
Self(s)
37+
}
38+
}
39+
40+
#[allow(dead_code)]
41+
#[derive(Clone)]
42+
enum FileType {
43+
Account,
44+
PrivateKey,
45+
Certificate,
46+
}
47+
48+
fn main() {
49+
let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
50+
let os_str = OsStr::new("x");
51+
let path = std::path::Path::new("x");
52+
let s = "x";
53+
let array = ["x"];
54+
let array_ref = &["x"];
55+
let slice = &["x"][..];
56+
let x = X(String::from("x"));
57+
let x_ref = &x;
58+
59+
require_c_str(&Cow::from(c_str));
60+
require_c_str(c_str);
61+
62+
require_os_str(os_str);
63+
require_os_str(&Cow::from(os_str));
64+
require_os_str(os_str);
65+
66+
require_path(path);
67+
require_path(&Cow::from(path));
68+
require_path(path);
69+
70+
require_str(s);
71+
require_str(&Cow::from(s));
72+
require_str(s);
73+
require_str(x_ref.as_ref());
74+
75+
require_slice(slice);
76+
require_slice(&Cow::from(slice));
77+
require_slice(array.as_ref());
78+
require_slice(array_ref.as_ref());
79+
require_slice(slice);
80+
require_slice(x_ref);
81+
82+
require_x(&Cow::<X>::Owned(x.clone()));
83+
require_x(x_ref);
84+
85+
require_deref_c_str(c_str);
86+
require_deref_os_str(os_str);
87+
require_deref_path(path);
88+
require_deref_str(s);
89+
require_deref_slice(slice);
90+
91+
require_impl_deref_c_str(c_str);
92+
require_impl_deref_os_str(os_str);
93+
require_impl_deref_path(path);
94+
require_impl_deref_str(s);
95+
require_impl_deref_slice(slice);
96+
97+
require_deref_str_slice(s, slice);
98+
require_deref_slice_str(slice, s);
99+
100+
require_as_ref_c_str(c_str);
101+
require_as_ref_os_str(os_str);
102+
require_as_ref_path(path);
103+
require_as_ref_str(s);
104+
require_as_ref_str(&x);
105+
require_as_ref_slice(array);
106+
require_as_ref_slice(array_ref);
107+
require_as_ref_slice(slice);
108+
109+
require_impl_as_ref_c_str(c_str);
110+
require_impl_as_ref_os_str(os_str);
111+
require_impl_as_ref_path(path);
112+
require_impl_as_ref_str(s);
113+
require_impl_as_ref_str(&x);
114+
require_impl_as_ref_slice(array);
115+
require_impl_as_ref_slice(array_ref);
116+
require_impl_as_ref_slice(slice);
117+
118+
require_as_ref_str_slice(s, array);
119+
require_as_ref_str_slice(s, array_ref);
120+
require_as_ref_str_slice(s, slice);
121+
require_as_ref_slice_str(array, s);
122+
require_as_ref_slice_str(array_ref, s);
123+
require_as_ref_slice_str(slice, s);
124+
125+
let _ = x.join(x_ref);
126+
127+
let _ = slice.iter().copied();
128+
let _ = slice.iter().copied();
129+
let _ = [std::path::PathBuf::new()][..].iter().cloned();
130+
let _ = [std::path::PathBuf::new()][..].iter().cloned();
131+
132+
let _ = slice.iter().copied();
133+
let _ = slice.iter().copied();
134+
let _ = [std::path::PathBuf::new()][..].iter().cloned();
135+
let _ = [std::path::PathBuf::new()][..].iter().cloned();
136+
137+
let _ = check_files(&[FileType::Account]);
138+
139+
// negative tests
140+
require_string(&s.to_string());
141+
require_string(&Cow::from(s).into_owned());
142+
require_string(&s.to_owned());
143+
require_string(&x_ref.to_string());
144+
145+
// `X` isn't copy.
146+
require_slice(&x.to_owned());
147+
require_deref_slice(x.to_owned());
148+
149+
// The following should be flagged by `redundant_clone`, but not by this lint.
150+
require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap());
151+
require_os_str(&OsString::from("x"));
152+
require_path(&std::path::PathBuf::from("x"));
153+
require_str(&String::from("x"));
154+
}
155+
156+
fn require_c_str(_: &CStr) {}
157+
fn require_os_str(_: &OsStr) {}
158+
fn require_path(_: &std::path::Path) {}
159+
fn require_str(_: &str) {}
160+
fn require_slice<T>(_: &[T]) {}
161+
fn require_x(_: &X) {}
162+
163+
fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
164+
fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
165+
fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
166+
fn require_deref_str<T: Deref<Target = str>>(_: T) {}
167+
fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
168+
169+
fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
170+
fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
171+
fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
172+
fn require_impl_deref_str(_: impl Deref<Target = str>) {}
173+
fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
174+
175+
fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
176+
fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
177+
178+
fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
179+
fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
180+
fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
181+
fn require_as_ref_str<T: AsRef<str>>(_: T) {}
182+
fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
183+
184+
fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
185+
fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
186+
fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
187+
fn require_impl_as_ref_str(_: impl AsRef<str>) {}
188+
fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
189+
190+
fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
191+
fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
192+
193+
// `check_files` is based on:
194+
// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
195+
fn check_files(file_types: &[FileType]) -> bool {
196+
for t in file_types {
197+
let path = match get_file_path(t) {
198+
Ok(p) => p,
199+
Err(_) => {
200+
return false;
201+
},
202+
};
203+
if !path.is_file() {
204+
return false;
205+
}
206+
}
207+
true
208+
}
209+
210+
fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
211+
Ok(std::path::PathBuf::new())
212+
}
213+
214+
fn require_string(_: &String) {}

‎tests/ui/unnecessary_to_owned.rs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// run-rustfix
2+
3+
#![allow(clippy::ptr_arg)]
4+
#![warn(clippy::unnecessary_to_owned)]
5+
6+
use std::borrow::Cow;
7+
use std::ffi::{CStr, CString, OsStr, OsString};
8+
use std::ops::Deref;
9+
10+
#[derive(Clone)]
11+
struct X(String);
12+
13+
impl Deref for X {
14+
type Target = [u8];
15+
fn deref(&self) -> &[u8] {
16+
self.0.as_bytes()
17+
}
18+
}
19+
20+
impl AsRef<str> for X {
21+
fn as_ref(&self) -> &str {
22+
self.0.as_str()
23+
}
24+
}
25+
26+
impl ToString for X {
27+
fn to_string(&self) -> String {
28+
self.0.to_string()
29+
}
30+
}
31+
32+
impl X {
33+
fn join(&self, other: impl AsRef<str>) -> Self {
34+
let mut s = self.0.clone();
35+
s.push_str(other.as_ref());
36+
Self(s)
37+
}
38+
}
39+
40+
#[allow(dead_code)]
41+
#[derive(Clone)]
42+
enum FileType {
43+
Account,
44+
PrivateKey,
45+
Certificate,
46+
}
47+
48+
fn main() {
49+
let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
50+
let os_str = OsStr::new("x");
51+
let path = std::path::Path::new("x");
52+
let s = "x";
53+
let array = ["x"];
54+
let array_ref = &["x"];
55+
let slice = &["x"][..];
56+
let x = X(String::from("x"));
57+
let x_ref = &x;
58+
59+
require_c_str(&Cow::from(c_str).into_owned());
60+
require_c_str(&c_str.to_owned());
61+
62+
require_os_str(&os_str.to_os_string());
63+
require_os_str(&Cow::from(os_str).into_owned());
64+
require_os_str(&os_str.to_owned());
65+
66+
require_path(&path.to_path_buf());
67+
require_path(&Cow::from(path).into_owned());
68+
require_path(&path.to_owned());
69+
70+
require_str(&s.to_string());
71+
require_str(&Cow::from(s).into_owned());
72+
require_str(&s.to_owned());
73+
require_str(&x_ref.to_string());
74+
75+
require_slice(&slice.to_vec());
76+
require_slice(&Cow::from(slice).into_owned());
77+
require_slice(&array.to_owned());
78+
require_slice(&array_ref.to_owned());
79+
require_slice(&slice.to_owned());
80+
require_slice(&x_ref.to_owned());
81+
82+
require_x(&Cow::<X>::Owned(x.clone()).into_owned());
83+
require_x(&x_ref.to_owned());
84+
85+
require_deref_c_str(c_str.to_owned());
86+
require_deref_os_str(os_str.to_owned());
87+
require_deref_path(path.to_owned());
88+
require_deref_str(s.to_owned());
89+
require_deref_slice(slice.to_owned());
90+
91+
require_impl_deref_c_str(c_str.to_owned());
92+
require_impl_deref_os_str(os_str.to_owned());
93+
require_impl_deref_path(path.to_owned());
94+
require_impl_deref_str(s.to_owned());
95+
require_impl_deref_slice(slice.to_owned());
96+
97+
require_deref_str_slice(s.to_owned(), slice.to_owned());
98+
require_deref_slice_str(slice.to_owned(), s.to_owned());
99+
100+
require_as_ref_c_str(c_str.to_owned());
101+
require_as_ref_os_str(os_str.to_owned());
102+
require_as_ref_path(path.to_owned());
103+
require_as_ref_str(s.to_owned());
104+
require_as_ref_str(x.to_owned());
105+
require_as_ref_slice(array.to_owned());
106+
require_as_ref_slice(array_ref.to_owned());
107+
require_as_ref_slice(slice.to_owned());
108+
109+
require_impl_as_ref_c_str(c_str.to_owned());
110+
require_impl_as_ref_os_str(os_str.to_owned());
111+
require_impl_as_ref_path(path.to_owned());
112+
require_impl_as_ref_str(s.to_owned());
113+
require_impl_as_ref_str(x.to_owned());
114+
require_impl_as_ref_slice(array.to_owned());
115+
require_impl_as_ref_slice(array_ref.to_owned());
116+
require_impl_as_ref_slice(slice.to_owned());
117+
118+
require_as_ref_str_slice(s.to_owned(), array.to_owned());
119+
require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
120+
require_as_ref_str_slice(s.to_owned(), slice.to_owned());
121+
require_as_ref_slice_str(array.to_owned(), s.to_owned());
122+
require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
123+
require_as_ref_slice_str(slice.to_owned(), s.to_owned());
124+
125+
let _ = x.join(&x_ref.to_string());
126+
127+
let _ = slice.to_vec().into_iter();
128+
let _ = slice.to_owned().into_iter();
129+
let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
130+
let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
131+
132+
let _ = IntoIterator::into_iter(slice.to_vec());
133+
let _ = IntoIterator::into_iter(slice.to_owned());
134+
let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
135+
let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
136+
137+
let _ = check_files(&[FileType::Account]);
138+
139+
// negative tests
140+
require_string(&s.to_string());
141+
require_string(&Cow::from(s).into_owned());
142+
require_string(&s.to_owned());
143+
require_string(&x_ref.to_string());
144+
145+
// `X` isn't copy.
146+
require_slice(&x.to_owned());
147+
require_deref_slice(x.to_owned());
148+
149+
// The following should be flagged by `redundant_clone`, but not by this lint.
150+
require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
151+
require_os_str(&OsString::from("x").to_os_string());
152+
require_path(&std::path::PathBuf::from("x").to_path_buf());
153+
require_str(&String::from("x").to_string());
154+
}
155+
156+
fn require_c_str(_: &CStr) {}
157+
fn require_os_str(_: &OsStr) {}
158+
fn require_path(_: &std::path::Path) {}
159+
fn require_str(_: &str) {}
160+
fn require_slice<T>(_: &[T]) {}
161+
fn require_x(_: &X) {}
162+
163+
fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
164+
fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
165+
fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
166+
fn require_deref_str<T: Deref<Target = str>>(_: T) {}
167+
fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
168+
169+
fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
170+
fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
171+
fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
172+
fn require_impl_deref_str(_: impl Deref<Target = str>) {}
173+
fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
174+
175+
fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
176+
fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
177+
178+
fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
179+
fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
180+
fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
181+
fn require_as_ref_str<T: AsRef<str>>(_: T) {}
182+
fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
183+
184+
fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
185+
fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
186+
fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
187+
fn require_impl_as_ref_str(_: impl AsRef<str>) {}
188+
fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
189+
190+
fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
191+
fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
192+
193+
// `check_files` is based on:
194+
// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
195+
fn check_files(file_types: &[FileType]) -> bool {
196+
for t in file_types.to_vec() {
197+
let path = match get_file_path(&t) {
198+
Ok(p) => p,
199+
Err(_) => {
200+
return false;
201+
},
202+
};
203+
if !path.is_file() {
204+
return false;
205+
}
206+
}
207+
true
208+
}
209+
210+
fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
211+
Ok(std::path::PathBuf::new())
212+
}
213+
214+
fn require_string(_: &String) {}

‎tests/ui/unnecessary_to_owned.stderr

Lines changed: 495 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.