Skip to content

Commit 8b075f9

Browse files
committed
use new api
1 parent 37ccd49 commit 8b075f9

8 files changed

+203
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5525,6 +5525,7 @@ Released 2018-09-13
55255525
[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
55265526
[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
55275527
[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
5528+
[`cloned_ref_to_slice_refs`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_ref_to_slice_refs
55285529
[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
55295530
[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
55305531
[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use clippy_config::Conf;
2+
use clippy_utils::diagnostics::span_lint_and_sugg;
3+
use clippy_utils::msrvs::{self, Msrv};
4+
use clippy_utils::sugg::Sugg;
5+
use clippy_utils::{is_in_const_context, is_trait_method};
6+
use rustc_errors::Applicability;
7+
use rustc_hir::{Expr, ExprKind};
8+
use rustc_lint::{LateContext, LateLintPass};
9+
use rustc_session::impl_lint_pass;
10+
use rustc_span::sym;
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
///
15+
/// Checks for slice references with cloned references such as `&[f.clone()]`.
16+
///
17+
/// ### Why is this bad
18+
///
19+
/// A reference does not need to be owned in order to used as a slice.
20+
///
21+
/// ### Known problems
22+
///
23+
/// This lint does not know whether or not a clone has side effects such as with `Rc` clones or clones with interior
24+
/// mutability.
25+
///
26+
/// ### Example
27+
///
28+
/// ```ignore
29+
/// let data = 10;
30+
/// let data_ref = &data;
31+
/// take_slice(&[data_ref.clone()]);
32+
/// ```
33+
/// Use instead:
34+
/// ```ignore
35+
/// use std::slice;
36+
/// let data = 10;
37+
/// let data_ref = &data;
38+
/// take_slice(slice::from_ref(data_ref));
39+
/// ```
40+
#[clippy::version = "1.87.0"]
41+
pub CLONED_REF_TO_SLICE_REFS,
42+
perf,
43+
"cloning a reference for slice references"
44+
}
45+
46+
pub struct ClonedRefToSliceRefs<'a> {
47+
msrv: &'a Msrv,
48+
}
49+
impl<'a> ClonedRefToSliceRefs<'a> {
50+
pub fn new(conf: &'a Conf) -> Self {
51+
Self { msrv: &conf.msrv }
52+
}
53+
}
54+
55+
impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]);
56+
57+
impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> {
58+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
59+
if self.msrv.meets(cx, {
60+
if is_in_const_context(cx) {
61+
msrvs::CONST_SLICE_FROM_REF
62+
} else {
63+
msrvs::SLICE_FROM_REF
64+
}
65+
})
66+
67+
// `&[foo.clone()]` expressions
68+
&& let ExprKind::AddrOf(_, mutability, arr) = &expr.kind
69+
// mutable references would have a different meaning
70+
&& mutability.is_not()
71+
72+
// check for single item arrays
73+
&& let ExprKind::Array([item]) = &arr.kind
74+
75+
// check for clones
76+
&& let ExprKind::MethodCall(_, val, _, _) = item.kind
77+
&& is_trait_method(cx, item, sym::Clone)
78+
79+
// get appropriate crate for `slice::from_ref`
80+
&& let Some(builtin_crate) = clippy_utils::std_or_core(cx)
81+
{
82+
let mut sugg = Sugg::hir(cx, val, "_");
83+
if !cx.typeck_results().expr_ty(val).is_ref() {
84+
sugg = sugg.addr();
85+
}
86+
87+
span_lint_and_sugg(
88+
cx,
89+
CLONED_REF_TO_SLICE_REFS,
90+
expr.span,
91+
format!("this call to `clone` can be replaced with `{builtin_crate}::slice::from_ref`"),
92+
"try",
93+
format!("{builtin_crate}::slice::from_ref({sugg})"),
94+
Applicability::MaybeIncorrect,
95+
);
96+
}
97+
}
98+
}

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
103103
crate::casts::ZERO_PTR_INFO,
104104
crate::cfg_not_test::CFG_NOT_TEST_INFO,
105105
crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
106+
crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO,
106107
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
107108
crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
108109
crate::collapsible_if::COLLAPSIBLE_IF_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ mod cargo;
9797
mod casts;
9898
mod cfg_not_test;
9999
mod checked_conversions;
100+
mod cloned_ref_to_slice_refs;
100101
mod cognitive_complexity;
101102
mod collapsible_if;
102103
mod collection_is_never_read;
@@ -984,5 +985,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
984985
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
985986
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
986987
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
988+
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
987989
// add lints here, do not remove this comment, it's used in `new_lint`
988990
}

clippy_utils/src/msrvs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ msrv_aliases! {
3838
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
3939
1,68,0 { PATH_MAIN_SEPARATOR_STR }
4040
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
41-
1,63,0 { CLONE_INTO }
41+
1,63,0 { CLONE_INTO, CONST_SLICE_FROM_REF }
4242
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
4343
1,59,0 { THREAD_LOCAL_CONST_INIT }
4444
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
@@ -66,7 +66,7 @@ msrv_aliases! {
6666
1,31,0 { OPTION_REPLACE }
6767
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
6868
1,29,0 { ITER_FLATTEN }
69-
1,28,0 { FROM_BOOL, REPEAT_WITH }
69+
1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF }
7070
1,27,0 { ITERATOR_TRY_FOLD }
7171
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
7272
1,24,0 { IS_ASCII_DIGIT }
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#![warn(clippy::cloned_ref_to_slice_refs)]
2+
3+
use std::sync::Arc;
4+
5+
fn take_slice(_data: &[Data]) {}
6+
fn take_slice_mut(_data: &mut [Data]) {}
7+
fn take_arc(_data: &[Arc<Data>]) {}
8+
9+
#[derive(Clone)]
10+
struct Data;
11+
12+
fn main() {
13+
{
14+
let data = Data;
15+
let data_ref = &data;
16+
take_slice(std::slice::from_ref(data_ref)); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref`
17+
}
18+
{
19+
take_slice(std::slice::from_ref(&Data)); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref`
20+
}
21+
22+
// mutable borrows may have the intention to clone
23+
{
24+
let data = Data;
25+
let data_ref = &data;
26+
take_slice_mut(&mut [data_ref.clone()]);
27+
}
28+
29+
// `T::clone` is used to denote a clone with side effects
30+
{
31+
let data = Arc::new(Data);
32+
take_arc(&[Arc::clone(&data)]);
33+
}
34+
35+
// slices with multiple members can only be made from a singular reference
36+
{
37+
let data_1 = Data;
38+
let data_2 = Data;
39+
take_slice(&[data_1.clone(), data_2.clone()]);
40+
}
41+
}

tests/ui/cloned_ref_to_slice_refs.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#![warn(clippy::cloned_ref_to_slice_refs)]
2+
3+
use std::sync::Arc;
4+
5+
fn take_slice(_data: &[Data]) {}
6+
fn take_slice_mut(_data: &mut [Data]) {}
7+
fn take_arc(_data: &[Arc<Data>]) {}
8+
9+
#[derive(Clone)]
10+
struct Data;
11+
12+
fn main() {
13+
{
14+
let data = Data;
15+
let data_ref = &data;
16+
take_slice(&[data_ref.clone()]); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref`
17+
}
18+
{
19+
take_slice(&[Data.clone()]); //~ ERROR: this call to `clone` can be replaced with `std::slice::from_ref`
20+
}
21+
22+
// mutable borrows may have the intention to clone
23+
{
24+
let data = Data;
25+
let data_ref = &data;
26+
take_slice_mut(&mut [data_ref.clone()]);
27+
}
28+
29+
// `T::clone` is used to denote a clone with side effects
30+
{
31+
let data = Arc::new(Data);
32+
take_arc(&[Arc::clone(&data)]);
33+
}
34+
35+
// slices with multiple members can only be made from a singular reference
36+
{
37+
let data_1 = Data;
38+
let data_2 = Data;
39+
take_slice(&[data_1.clone(), data_2.clone()]);
40+
}
41+
}
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: this call to `clone` can be replaced with `std::slice::from_ref`
2+
--> tests/ui/cloned_ref_to_slice_refs.rs:16:20
3+
|
4+
LL | take_slice(&[data_ref.clone()]);
5+
| ^^^^^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(data_ref)`
6+
|
7+
= note: `-D clippy::cloned-ref-to-slice-refs` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::cloned_ref_to_slice_refs)]`
9+
10+
error: this call to `clone` can be replaced with `std::slice::from_ref`
11+
--> tests/ui/cloned_ref_to_slice_refs.rs:19:20
12+
|
13+
LL | take_slice(&[Data.clone()]);
14+
| ^^^^^^^^^^^^^^^ help: try: `std::slice::from_ref(&Data)`
15+
16+
error: aborting due to 2 previous errors
17+

0 commit comments

Comments
 (0)