Skip to content

Commit 668b3e4

Browse files
committed
Auto merge of #8218 - Jarcho:redundant_slicing_deref, r=camsteffen
Improve `redundant_slicing` lint fixes #7972 fixes #7257 This can supersede #7976 changelog: Fix suggestion for `redundant_slicing` when re-borrowing for a method call changelog: New lint `deref_as_slicing`
2 parents 7ee2081 + 7724d67 commit 668b3e4

10 files changed

+295
-48
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3105,6 +3105,7 @@ Released 2018-09-13
31053105
[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
31063106
[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
31073107
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
3108+
[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
31083109
[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
31093110
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
31103111
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ store.register_lints(&[
422422
redundant_else::REDUNDANT_ELSE,
423423
redundant_field_names::REDUNDANT_FIELD_NAMES,
424424
redundant_pub_crate::REDUNDANT_PUB_CRATE,
425+
redundant_slicing::DEREF_BY_SLICING,
425426
redundant_slicing::REDUNDANT_SLICING,
426427
redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
427428
ref_option_ref::REF_OPTION_REF,

clippy_lints/src/lib.register_restriction.rs

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
5151
LintId::of(panic_unimplemented::UNIMPLEMENTED),
5252
LintId::of(panic_unimplemented::UNREACHABLE),
5353
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
54+
LintId::of(redundant_slicing::DEREF_BY_SLICING),
5455
LintId::of(same_name_method::SAME_NAME_METHOD),
5556
LintId::of(shadow::SHADOW_REUSE),
5657
LintId::of(shadow::SHADOW_SAME),

clippy_lints/src/redundant_slicing.rs

+102-22
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::get_parent_expr;
33
use clippy_utils::source::snippet_with_context;
4-
use clippy_utils::ty::is_type_lang_item;
4+
use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs};
55
use if_chain::if_chain;
6+
use rustc_ast::util::parser::PREC_PREFIX;
67
use rustc_errors::Applicability;
78
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
8-
use rustc_lint::{LateContext, LateLintPass};
9+
use rustc_lint::{LateContext, LateLintPass, Lint};
10+
use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
11+
use rustc_middle::ty::subst::GenericArg;
912
use rustc_session::{declare_lint_pass, declare_tool_lint};
1013

1114
declare_clippy_lint! {
@@ -39,7 +42,34 @@ declare_clippy_lint! {
3942
"redundant slicing of the whole range of a type"
4043
}
4144

42-
declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
45+
declare_clippy_lint! {
46+
/// ### What it does
47+
/// Checks for slicing expressions which are equivalent to dereferencing the
48+
/// value.
49+
///
50+
/// ### Why is this bad?
51+
/// Some people may prefer to dereference rather than slice.
52+
///
53+
/// ### Example
54+
/// ```rust
55+
/// let vec = vec![1, 2, 3];
56+
/// let slice = &vec[..];
57+
/// ```
58+
/// Use instead:
59+
/// ```rust
60+
/// let vec = vec![1, 2, 3];
61+
/// let slice = &*vec;
62+
/// ```
63+
#[clippy::version = "1.60.0"]
64+
pub DEREF_BY_SLICING,
65+
restriction,
66+
"slicing instead of dereferencing"
67+
}
68+
69+
declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING, DEREF_BY_SLICING]);
70+
71+
static REDUNDANT_SLICING_LINT: (&Lint, &str) = (REDUNDANT_SLICING, "redundant slicing of the whole range");
72+
static DEREF_BY_SLICING_LINT: (&Lint, &str) = (DEREF_BY_SLICING, "slicing when dereferencing would work");
4373

4474
impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
4575
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -53,34 +83,84 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
5383
if addressee.span.ctxt() == ctxt;
5484
if let ExprKind::Index(indexed, range) = addressee.kind;
5585
if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
56-
if cx.typeck_results().expr_ty(expr) == cx.typeck_results().expr_ty(indexed);
5786
then {
87+
let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr));
88+
let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed));
89+
let parent_expr = get_parent_expr(cx, expr);
90+
let needs_parens_for_prefix = parent_expr.map_or(false, |parent| {
91+
parent.precedence().order() > PREC_PREFIX
92+
});
5893
let mut app = Applicability::MachineApplicable;
59-
let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
6094

61-
let (reborrow_str, help_str) = if mutability == Mutability::Mut {
62-
// The slice was used to reborrow the mutable reference.
63-
("&mut *", "reborrow the original value instead")
64-
} else if matches!(
65-
get_parent_expr(cx, expr),
66-
Some(Expr {
67-
kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
68-
..
69-
})
70-
) {
71-
// The slice was used to make a temporary reference.
72-
("&*", "reborrow the original value instead")
95+
let ((lint, msg), help, sugg) = if expr_ty == indexed_ty {
96+
if expr_ref_count > indexed_ref_count {
97+
// Indexing takes self by reference and can't return a reference to that
98+
// reference as it's a local variable. The only way this could happen is if
99+
// `self` contains a reference to the `Self` type. If this occurs then the
100+
// lint no longer applies as it's essentially a field access, which is not
101+
// redundant.
102+
return;
103+
}
104+
let deref_count = indexed_ref_count - expr_ref_count;
105+
106+
let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut {
107+
// The slice was used to reborrow the mutable reference.
108+
(DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead")
109+
} else if matches!(
110+
parent_expr,
111+
Some(Expr {
112+
kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
113+
..
114+
})
115+
) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| {
116+
matches!(a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })))
117+
}) {
118+
// The slice was used to make a temporary reference.
119+
(DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead")
120+
} else if deref_count != 0 {
121+
(DEREF_BY_SLICING_LINT, "", "dereference the original value instead")
122+
} else {
123+
(REDUNDANT_SLICING_LINT, "", "use the original value instead")
124+
};
125+
126+
let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
127+
let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix {
128+
format!("({}{}{})", reborrow_str, "*".repeat(deref_count), snip)
129+
} else {
130+
format!("{}{}{}", reborrow_str, "*".repeat(deref_count), snip)
131+
};
132+
133+
(lint, help_str, sugg)
134+
} else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
135+
if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
136+
cx.param_env,
137+
cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())),
138+
) {
139+
if deref_ty == expr_ty {
140+
let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
141+
let sugg = if needs_parens_for_prefix {
142+
format!("(&{}{}*{})", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip)
143+
} else {
144+
format!("&{}{}*{}", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip)
145+
};
146+
(DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg)
147+
} else {
148+
return;
149+
}
150+
} else {
151+
return;
152+
}
73153
} else {
74-
("", "use the original value instead")
154+
return;
75155
};
76156

77157
span_lint_and_sugg(
78158
cx,
79-
REDUNDANT_SLICING,
159+
lint,
80160
expr.span,
81-
"redundant slicing of the whole range",
82-
help_str,
83-
format!("{}{}", reborrow_str, snip),
161+
msg,
162+
help,
163+
sugg,
84164
app,
85165
);
86166
}

tests/ui/deref_by_slicing.fixed

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::deref_by_slicing)]
4+
5+
use std::io::Read;
6+
7+
fn main() {
8+
let mut vec = vec![0];
9+
let _ = &*vec;
10+
let _ = &mut *vec;
11+
12+
let ref_vec = &mut vec;
13+
let _ = &**ref_vec;
14+
let mut_slice = &mut **ref_vec;
15+
let _ = &mut *mut_slice; // Err, re-borrows slice
16+
17+
let s = String::new();
18+
let _ = &*s;
19+
20+
static S: &[u8] = &[0, 1, 2];
21+
let _ = &mut &*S; // Err, re-borrows slice
22+
23+
let slice: &[u32] = &[0u32, 1u32];
24+
let slice_ref = &slice;
25+
let _ = *slice_ref; // Err, derefs slice
26+
27+
let bytes: &[u8] = &[];
28+
let _ = (&*bytes).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
29+
}

tests/ui/deref_by_slicing.rs

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::deref_by_slicing)]
4+
5+
use std::io::Read;
6+
7+
fn main() {
8+
let mut vec = vec![0];
9+
let _ = &vec[..];
10+
let _ = &mut vec[..];
11+
12+
let ref_vec = &mut vec;
13+
let _ = &ref_vec[..];
14+
let mut_slice = &mut ref_vec[..];
15+
let _ = &mut mut_slice[..]; // Err, re-borrows slice
16+
17+
let s = String::new();
18+
let _ = &s[..];
19+
20+
static S: &[u8] = &[0, 1, 2];
21+
let _ = &mut &S[..]; // Err, re-borrows slice
22+
23+
let slice: &[u32] = &[0u32, 1u32];
24+
let slice_ref = &slice;
25+
let _ = &slice_ref[..]; // Err, derefs slice
26+
27+
let bytes: &[u8] = &[];
28+
let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
29+
}

tests/ui/deref_by_slicing.stderr

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
error: slicing when dereferencing would work
2+
--> $DIR/deref_by_slicing.rs:9:13
3+
|
4+
LL | let _ = &vec[..];
5+
| ^^^^^^^^ help: dereference the original value instead: `&*vec`
6+
|
7+
= note: `-D clippy::deref-by-slicing` implied by `-D warnings`
8+
9+
error: slicing when dereferencing would work
10+
--> $DIR/deref_by_slicing.rs:10:13
11+
|
12+
LL | let _ = &mut vec[..];
13+
| ^^^^^^^^^^^^ help: dereference the original value instead: `&mut *vec`
14+
15+
error: slicing when dereferencing would work
16+
--> $DIR/deref_by_slicing.rs:13:13
17+
|
18+
LL | let _ = &ref_vec[..];
19+
| ^^^^^^^^^^^^ help: dereference the original value instead: `&**ref_vec`
20+
21+
error: slicing when dereferencing would work
22+
--> $DIR/deref_by_slicing.rs:14:21
23+
|
24+
LL | let mut_slice = &mut ref_vec[..];
25+
| ^^^^^^^^^^^^^^^^ help: dereference the original value instead: `&mut **ref_vec`
26+
27+
error: slicing when dereferencing would work
28+
--> $DIR/deref_by_slicing.rs:15:13
29+
|
30+
LL | let _ = &mut mut_slice[..]; // Err, re-borrows slice
31+
| ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice`
32+
33+
error: slicing when dereferencing would work
34+
--> $DIR/deref_by_slicing.rs:18:13
35+
|
36+
LL | let _ = &s[..];
37+
| ^^^^^^ help: dereference the original value instead: `&*s`
38+
39+
error: slicing when dereferencing would work
40+
--> $DIR/deref_by_slicing.rs:21:18
41+
|
42+
LL | let _ = &mut &S[..]; // Err, re-borrows slice
43+
| ^^^^^^ help: reborrow the original value instead: `&*S`
44+
45+
error: slicing when dereferencing would work
46+
--> $DIR/deref_by_slicing.rs:25:13
47+
|
48+
LL | let _ = &slice_ref[..]; // Err, derefs slice
49+
| ^^^^^^^^^^^^^^ help: dereference the original value instead: `*slice_ref`
50+
51+
error: slicing when dereferencing would work
52+
--> $DIR/deref_by_slicing.rs:28:13
53+
|
54+
LL | let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
55+
| ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)`
56+
57+
error: aborting due to 9 previous errors
58+

tests/ui/redundant_slicing.fixed

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// run-rustfix
2+
3+
#![allow(unused, clippy::deref_by_slicing)]
4+
#![warn(clippy::redundant_slicing)]
5+
6+
use std::io::Read;
7+
8+
fn main() {
9+
let slice: &[u32] = &[0];
10+
let _ = slice; // Redundant slice
11+
12+
let v = vec![0];
13+
let _ = &v[..]; // Ok, results in `&[_]`
14+
let _ = (&*v); // Outer borrow is redundant
15+
16+
static S: &[u8] = &[0, 1, 2];
17+
let _ = &mut &S[..]; // Ok, re-borrows slice
18+
19+
let mut vec = vec![0];
20+
let mut_slice = &mut vec[..]; // Ok, results in `&mut [_]`
21+
let _ = &mut mut_slice[..]; // Ok, re-borrows slice
22+
23+
let ref_vec = &vec;
24+
let _ = &ref_vec[..]; // Ok, results in `&[_]`
25+
26+
macro_rules! m {
27+
($e:expr) => {
28+
$e
29+
};
30+
}
31+
let _ = slice;
32+
33+
macro_rules! m2 {
34+
($e:expr) => {
35+
&$e[..]
36+
};
37+
}
38+
let _ = m2!(slice); // Don't lint in a macro
39+
40+
let slice_ref = &slice;
41+
let _ = &slice_ref[..]; // Ok, derefs slice
42+
43+
// Issue #7972
44+
let bytes: &[u8] = &[];
45+
let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Ok, re-borrows slice
46+
}

tests/ui/redundant_slicing.rs

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1-
#![allow(unused)]
1+
// run-rustfix
2+
3+
#![allow(unused, clippy::deref_by_slicing)]
24
#![warn(clippy::redundant_slicing)]
35

6+
use std::io::Read;
7+
48
fn main() {
59
let slice: &[u32] = &[0];
6-
let _ = &slice[..];
10+
let _ = &slice[..]; // Redundant slice
711

812
let v = vec![0];
9-
let _ = &v[..]; // Changes the type
10-
let _ = &(&v[..])[..]; // Outer borrow is redundant
13+
let _ = &v[..]; // Ok, results in `&[_]`
14+
let _ = &(&*v)[..]; // Outer borrow is redundant
1115

1216
static S: &[u8] = &[0, 1, 2];
13-
let err = &mut &S[..]; // Should reborrow instead of slice
17+
let _ = &mut &S[..]; // Ok, re-borrows slice
1418

1519
let mut vec = vec![0];
16-
let mut_slice = &mut *vec;
17-
let _ = &mut mut_slice[..]; // Should reborrow instead of slice
20+
let mut_slice = &mut vec[..]; // Ok, results in `&mut [_]`
21+
let _ = &mut mut_slice[..]; // Ok, re-borrows slice
22+
23+
let ref_vec = &vec;
24+
let _ = &ref_vec[..]; // Ok, results in `&[_]`
1825

1926
macro_rules! m {
2027
($e:expr) => {
@@ -29,4 +36,11 @@ fn main() {
2936
};
3037
}
3138
let _ = m2!(slice); // Don't lint in a macro
39+
40+
let slice_ref = &slice;
41+
let _ = &slice_ref[..]; // Ok, derefs slice
42+
43+
// Issue #7972
44+
let bytes: &[u8] = &[];
45+
let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Ok, re-borrows slice
3246
}

0 commit comments

Comments
 (0)