Skip to content

Commit ee2ab7e

Browse files
committed
changelog: Add manual_as_slice lint
1 parent 40bead0 commit ee2ab7e

File tree

7 files changed

+264
-0
lines changed

7 files changed

+264
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5943,6 +5943,7 @@ Released 2018-09-13
59435943
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
59445944
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
59455945
[`manual_abs_diff`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff
5946+
[`manual_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_as_slice
59465947
[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
59475948
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
59485949
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
288288
crate::macro_use::MACRO_USE_IMPORTS_INFO,
289289
crate::main_recursion::MAIN_RECURSION_INFO,
290290
crate::manual_abs_diff::MANUAL_ABS_DIFF_INFO,
291+
crate::manual_as_slice::MANUAL_AS_SLICE_INFO,
291292
crate::manual_assert::MANUAL_ASSERT_INFO,
292293
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
293294
crate::manual_bits::MANUAL_BITS_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ mod macro_metavars_in_unsafe;
204204
mod macro_use;
205205
mod main_recursion;
206206
mod manual_abs_diff;
207+
mod manual_as_slice;
207208
mod manual_assert;
208209
mod manual_async_fn;
209210
mod manual_bits;
@@ -944,5 +945,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
944945
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
945946
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
946947
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
948+
store.register_late_pass(|_| Box::new(manual_as_slice::ManualAsSlice));
947949
// add lints here, do not remove this comment, it's used in `new_lint`
948950
}

clippy_lints/src/manual_as_slice.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use rustc_errors::Applicability;
3+
use rustc_hir::{Expr, ExprKind, LangItem, Mutability, QPath};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_middle::ty::TyKind;
6+
use rustc_session::declare_lint_pass;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
///
11+
/// Detects if a full range slice reference is used instead of using the `.as_slice()` method.
12+
///
13+
/// ### Why is this bad?
14+
///
15+
/// Using the `some_value.as_slice()` method is more explicit then using `&some_value[..]`
16+
///
17+
/// ### Example
18+
/// ```no_run
19+
/// let array: [u8; 4] = [0; 4];
20+
/// let slice = &array[..];
21+
/// ```
22+
/// Use instead:
23+
/// ```no_run
24+
/// let array: [u8; 4] = [0; 4];
25+
/// let slice = array.as_slice();
26+
/// ```
27+
#[clippy::version = "1.88.0"]
28+
pub MANUAL_AS_SLICE,
29+
nursery,
30+
"Use as slice instead of borrow full range."
31+
}
32+
declare_lint_pass!(ManualAsSlice => [MANUAL_AS_SLICE]);
33+
34+
impl LateLintPass<'_> for ManualAsSlice {
35+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
36+
if let ExprKind::AddrOf(_, mutability, borrow) = expr.kind
37+
&& let ExprKind::Index(value, index, index_span) = borrow.kind
38+
&& let ExprKind::Struct(qpath, _, _) = index.kind
39+
&& let QPath::LangItem(LangItem::RangeFull, _) = qpath
40+
{
41+
let sugg_tail = match mutability {
42+
Mutability::Not => ".as_slice()",
43+
Mutability::Mut => ".as_mut_slice()",
44+
};
45+
46+
let borrow_span = expr.span.until(borrow.span);
47+
let app = Applicability::MachineApplicable;
48+
49+
match cx.typeck_results().expr_ty(value).kind() {
50+
TyKind::Array(_, _) | TyKind::Slice(_) => {},
51+
TyKind::Ref(_, t, _) if let TyKind::Array(_, _) | TyKind::Slice(_) = t.kind() => {},
52+
_ => return,
53+
}
54+
55+
span_lint_and_then(
56+
cx,
57+
MANUAL_AS_SLICE,
58+
expr.span,
59+
format!("use `{sugg_tail}` instead of full range slice"),
60+
|diag| {
61+
diag.multipart_suggestion(
62+
"try",
63+
vec![(borrow_span, String::new()), (index_span, sugg_tail.to_string())],
64+
app,
65+
);
66+
},
67+
);
68+
}
69+
}
70+
}

tests/ui/manual_as_slice.fixed

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#![feature(new_range_api)]
2+
#![allow(clippy::redundant_slicing)]
3+
#![warn(clippy::manual_as_slice)]
4+
5+
mod nested_1 {
6+
pub(crate) mod nested_2 {
7+
pub(crate) const SOME_VALUE: [u8; 4] = [1, 2, 3, 4];
8+
}
9+
}
10+
11+
fn main() {
12+
let array: [u8; 4] = [0; 4];
13+
let slice: &[u8] = array.as_slice();
14+
//~^ manual_as_slice
15+
16+
let mut array: [u8; 4] = [0; 4];
17+
let mut slice: &[u8] = array.as_mut_slice();
18+
//~^ manual_as_slice
19+
20+
let slice: &[u8] = nested_1::nested_2::SOME_VALUE.as_slice();
21+
//~^ manual_as_slice
22+
23+
let slice = b"foo";
24+
let slice: &[u8] = slice.as_slice();
25+
//~^ manual_as_slice
26+
27+
let slice = &[1, 2, 3, 4];
28+
let slice: &[u8] = slice.as_slice();
29+
//~^ manual_as_slice
30+
31+
macro_rules! perform_the_slice {
32+
($a:expr) => {
33+
$a.as_slice()
34+
//~^ manual_as_slice
35+
};
36+
}
37+
38+
perform_the_slice!([1, 2, 3]);
39+
40+
let slice: &[u8] = &vec![1, 2, 3][..];
41+
42+
let slice = &"foo"[..];
43+
44+
struct Count;
45+
46+
impl<R: std::range::RangeBounds<()>> std::ops::Index<R> for Count {
47+
type Output = ();
48+
49+
fn index(&self, _: R) -> &Self::Output {
50+
&()
51+
}
52+
}
53+
54+
let count = &Count[..];
55+
}

tests/ui/manual_as_slice.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#![feature(new_range_api)]
2+
#![allow(clippy::redundant_slicing)]
3+
#![warn(clippy::manual_as_slice)]
4+
5+
mod nested_1 {
6+
pub(crate) mod nested_2 {
7+
pub(crate) const SOME_VALUE: [u8; 4] = [1, 2, 3, 4];
8+
}
9+
}
10+
11+
fn main() {
12+
let array: [u8; 4] = [0; 4];
13+
let slice: &[u8] = &array[..];
14+
//~^ manual_as_slice
15+
16+
let mut array: [u8; 4] = [0; 4];
17+
let mut slice: &[u8] = &mut array[..];
18+
//~^ manual_as_slice
19+
20+
let slice: &[u8] = &nested_1::nested_2::SOME_VALUE[..];
21+
//~^ manual_as_slice
22+
23+
let slice = b"foo";
24+
let slice: &[u8] = &slice[..];
25+
//~^ manual_as_slice
26+
27+
let slice = &[1, 2, 3, 4];
28+
let slice: &[u8] = &slice[..];
29+
//~^ manual_as_slice
30+
31+
macro_rules! perform_the_slice {
32+
($a:expr) => {
33+
&$a[..]
34+
//~^ manual_as_slice
35+
};
36+
}
37+
38+
perform_the_slice!([1, 2, 3]);
39+
40+
let slice: &[u8] = &vec![1, 2, 3][..];
41+
42+
let slice = &"foo"[..];
43+
44+
struct Count;
45+
46+
impl<R: std::range::RangeBounds<()>> std::ops::Index<R> for Count {
47+
type Output = ();
48+
49+
fn index(&self, _: R) -> &Self::Output {
50+
&()
51+
}
52+
}
53+
54+
let count = &Count[..];
55+
}

tests/ui/manual_as_slice.stderr

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
error: use `.as_slice()` instead of full range slice
2+
--> tests/ui/manual_as_slice.rs:13:24
3+
|
4+
LL | let slice: &[u8] = &array[..];
5+
| ^^^^^^^^^^
6+
|
7+
= note: `-D clippy::manual-as-slice` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::manual_as_slice)]`
9+
help: try
10+
|
11+
LL - let slice: &[u8] = &array[..];
12+
LL + let slice: &[u8] = array.as_slice();
13+
|
14+
15+
error: use `.as_mut_slice()` instead of full range slice
16+
--> tests/ui/manual_as_slice.rs:17:28
17+
|
18+
LL | let mut slice: &[u8] = &mut array[..];
19+
| ^^^^^^^^^^^^^^
20+
|
21+
help: try
22+
|
23+
LL - let mut slice: &[u8] = &mut array[..];
24+
LL + let mut slice: &[u8] = array.as_mut_slice();
25+
|
26+
27+
error: use `.as_slice()` instead of full range slice
28+
--> tests/ui/manual_as_slice.rs:20:24
29+
|
30+
LL | let slice: &[u8] = &nested_1::nested_2::SOME_VALUE[..];
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
help: try
34+
|
35+
LL - let slice: &[u8] = &nested_1::nested_2::SOME_VALUE[..];
36+
LL + let slice: &[u8] = nested_1::nested_2::SOME_VALUE.as_slice();
37+
|
38+
39+
error: use `.as_slice()` instead of full range slice
40+
--> tests/ui/manual_as_slice.rs:24:24
41+
|
42+
LL | let slice: &[u8] = &slice[..];
43+
| ^^^^^^^^^^
44+
|
45+
help: try
46+
|
47+
LL - let slice: &[u8] = &slice[..];
48+
LL + let slice: &[u8] = slice.as_slice();
49+
|
50+
51+
error: use `.as_slice()` instead of full range slice
52+
--> tests/ui/manual_as_slice.rs:28:24
53+
|
54+
LL | let slice: &[u8] = &slice[..];
55+
| ^^^^^^^^^^
56+
|
57+
help: try
58+
|
59+
LL - let slice: &[u8] = &slice[..];
60+
LL + let slice: &[u8] = slice.as_slice();
61+
|
62+
63+
error: use `.as_slice()` instead of full range slice
64+
--> tests/ui/manual_as_slice.rs:33:13
65+
|
66+
LL | &$a[..]
67+
| ^^^^^^^
68+
...
69+
LL | perform_the_slice!([1, 2, 3]);
70+
| ----------------------------- in this macro invocation
71+
|
72+
= note: this error originates in the macro `perform_the_slice` (in Nightly builds, run with -Z macro-backtrace for more info)
73+
help: try
74+
|
75+
LL - &$a[..]
76+
LL + $a.as_slice()
77+
|
78+
79+
error: aborting due to 6 previous errors
80+

0 commit comments

Comments
 (0)