diff --git a/CHANGELOG.md b/CHANGELOG.md index 28147dfbea3e..75a86aa62eb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5943,6 +5943,7 @@ Released 2018-09-13 [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion [`manual_abs_diff`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff +[`manual_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_as_slice [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index bb825c7655f8..ab343957cb7a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -288,6 +288,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::macro_use::MACRO_USE_IMPORTS_INFO, crate::main_recursion::MAIN_RECURSION_INFO, crate::manual_abs_diff::MANUAL_ABS_DIFF_INFO, + crate::manual_as_slice::MANUAL_AS_SLICE_INFO, crate::manual_assert::MANUAL_ASSERT_INFO, crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 006145cc623c..8fdb03db2b1f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -204,6 +204,7 @@ mod macro_metavars_in_unsafe; mod macro_use; mod main_recursion; mod manual_abs_diff; +mod manual_as_slice; mod manual_assert; mod manual_async_fn; mod manual_bits; @@ -944,5 +945,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); + store.register_late_pass(|_| Box::new(manual_as_slice::ManualAsSlice)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/manual_as_slice.rs b/clippy_lints/src/manual_as_slice.rs new file mode 100644 index 000000000000..279a990bc2ce --- /dev/null +++ b/clippy_lints/src/manual_as_slice.rs @@ -0,0 +1,72 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, Mutability, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyKind; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects if a full range slice reference is used instead of using the `.as_slice()` method. + /// + /// ### Why is this bad? + /// + /// Using the `some_value.as_slice()` method is more explicit then using `&some_value[..]` + /// + /// ### Example + /// ```no_run + /// let array: [u8; 4] = [0; 4]; + /// let slice = &array[..]; + /// ``` + /// Use instead: + /// ```no_run + /// let array: [u8; 4] = [0; 4]; + /// let slice = array.as_slice(); + /// ``` + #[clippy::version = "1.88.0"] + pub MANUAL_AS_SLICE, + nursery, + "Use as slice instead of borrow full range." +} +declare_lint_pass!(ManualAsSlice => [MANUAL_AS_SLICE]); + +impl LateLintPass<'_> for ManualAsSlice { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::AddrOf(_, mutability, borrow) = expr.kind + && let ExprKind::Index(value, index, index_span) = borrow.kind + && let ExprKind::Struct(qpath, _, _) = index.kind + && let QPath::LangItem(LangItem::RangeFull, _) = qpath + { + let sugg_tail = match mutability { + Mutability::Not => ".as_slice()", + Mutability::Mut => ".as_mut_slice()", + }; + + let borrow_span = expr.span.until(borrow.span); + let app = Applicability::MachineApplicable; + + match cx.typeck_results().expr_ty(value).kind() { + TyKind::Array(_, _) | TyKind::Slice(_) => {}, + TyKind::Ref(_, t, _) if let TyKind::Array(_, _) | TyKind::Slice(_) = t.kind() => {}, + TyKind::Adt(adt, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did()) => {}, + _ => return, + } + + span_lint_and_then( + cx, + MANUAL_AS_SLICE, + expr.span, + format!("use `{sugg_tail}` instead of full range slice"), + |diag| { + diag.multipart_suggestion( + "try", + vec![(borrow_span, String::new()), (index_span, sugg_tail.to_string())], + app, + ); + }, + ); + } + } +} diff --git a/tests/ui/manual_as_slice.fixed b/tests/ui/manual_as_slice.fixed new file mode 100644 index 000000000000..419c864e2086 --- /dev/null +++ b/tests/ui/manual_as_slice.fixed @@ -0,0 +1,56 @@ +#![feature(new_range_api)] +#![allow(clippy::redundant_slicing)] +#![warn(clippy::manual_as_slice)] + +mod nested_1 { + pub(crate) mod nested_2 { + pub(crate) const SOME_VALUE: [u8; 4] = [1, 2, 3, 4]; + } +} + +fn main() { + let array: [u8; 4] = [0; 4]; + let slice: &[u8] = array.as_slice(); + //~^ manual_as_slice + + let mut array: [u8; 4] = [0; 4]; + let mut slice: &[u8] = array.as_mut_slice(); + //~^ manual_as_slice + + let slice: &[u8] = nested_1::nested_2::SOME_VALUE.as_slice(); + //~^ manual_as_slice + + let slice = b"foo"; + let slice: &[u8] = slice.as_slice(); + //~^ manual_as_slice + + let slice = &[1, 2, 3, 4]; + let slice: &[u8] = slice.as_slice(); + //~^ manual_as_slice + + macro_rules! perform_the_slice { + ($a:expr) => { + $a.as_slice() + //~^ manual_as_slice + }; + } + + perform_the_slice!([1, 2, 3]); + + let slice: &[u8] = vec![1, 2, 3].as_slice(); + //~^ manual_as_slice + + let slice = &"foo"[..]; + + struct Count; + + impl> std::ops::Index for Count { + type Output = (); + + fn index(&self, _: R) -> &Self::Output { + &() + } + } + + let count = &Count[..]; +} diff --git a/tests/ui/manual_as_slice.rs b/tests/ui/manual_as_slice.rs new file mode 100644 index 000000000000..fceca0d90fc4 --- /dev/null +++ b/tests/ui/manual_as_slice.rs @@ -0,0 +1,56 @@ +#![feature(new_range_api)] +#![allow(clippy::redundant_slicing)] +#![warn(clippy::manual_as_slice)] + +mod nested_1 { + pub(crate) mod nested_2 { + pub(crate) const SOME_VALUE: [u8; 4] = [1, 2, 3, 4]; + } +} + +fn main() { + let array: [u8; 4] = [0; 4]; + let slice: &[u8] = &array[..]; + //~^ manual_as_slice + + let mut array: [u8; 4] = [0; 4]; + let mut slice: &[u8] = &mut array[..]; + //~^ manual_as_slice + + let slice: &[u8] = &nested_1::nested_2::SOME_VALUE[..]; + //~^ manual_as_slice + + let slice = b"foo"; + let slice: &[u8] = &slice[..]; + //~^ manual_as_slice + + let slice = &[1, 2, 3, 4]; + let slice: &[u8] = &slice[..]; + //~^ manual_as_slice + + macro_rules! perform_the_slice { + ($a:expr) => { + &$a[..] + //~^ manual_as_slice + }; + } + + perform_the_slice!([1, 2, 3]); + + let slice: &[u8] = &vec![1, 2, 3][..]; + //~^ manual_as_slice + + let slice = &"foo"[..]; + + struct Count; + + impl> std::ops::Index for Count { + type Output = (); + + fn index(&self, _: R) -> &Self::Output { + &() + } + } + + let count = &Count[..]; +} diff --git a/tests/ui/manual_as_slice.stderr b/tests/ui/manual_as_slice.stderr new file mode 100644 index 000000000000..01b4dd8459ac --- /dev/null +++ b/tests/ui/manual_as_slice.stderr @@ -0,0 +1,92 @@ +error: use `.as_slice()` instead of full range slice + --> tests/ui/manual_as_slice.rs:13:24 + | +LL | let slice: &[u8] = &array[..]; + | ^^^^^^^^^^ + | + = note: `-D clippy::manual-as-slice` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_as_slice)]` +help: try + | +LL - let slice: &[u8] = &array[..]; +LL + let slice: &[u8] = array.as_slice(); + | + +error: use `.as_mut_slice()` instead of full range slice + --> tests/ui/manual_as_slice.rs:17:28 + | +LL | let mut slice: &[u8] = &mut array[..]; + | ^^^^^^^^^^^^^^ + | +help: try + | +LL - let mut slice: &[u8] = &mut array[..]; +LL + let mut slice: &[u8] = array.as_mut_slice(); + | + +error: use `.as_slice()` instead of full range slice + --> tests/ui/manual_as_slice.rs:20:24 + | +LL | let slice: &[u8] = &nested_1::nested_2::SOME_VALUE[..]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - let slice: &[u8] = &nested_1::nested_2::SOME_VALUE[..]; +LL + let slice: &[u8] = nested_1::nested_2::SOME_VALUE.as_slice(); + | + +error: use `.as_slice()` instead of full range slice + --> tests/ui/manual_as_slice.rs:24:24 + | +LL | let slice: &[u8] = &slice[..]; + | ^^^^^^^^^^ + | +help: try + | +LL - let slice: &[u8] = &slice[..]; +LL + let slice: &[u8] = slice.as_slice(); + | + +error: use `.as_slice()` instead of full range slice + --> tests/ui/manual_as_slice.rs:28:24 + | +LL | let slice: &[u8] = &slice[..]; + | ^^^^^^^^^^ + | +help: try + | +LL - let slice: &[u8] = &slice[..]; +LL + let slice: &[u8] = slice.as_slice(); + | + +error: use `.as_slice()` instead of full range slice + --> tests/ui/manual_as_slice.rs:33:13 + | +LL | &$a[..] + | ^^^^^^^ +... +LL | perform_the_slice!([1, 2, 3]); + | ----------------------------- in this macro invocation + | + = note: this error originates in the macro `perform_the_slice` (in Nightly builds, run with -Z macro-backtrace for more info) +help: try + | +LL - &$a[..] +LL + $a.as_slice() + | + +error: use `.as_slice()` instead of full range slice + --> tests/ui/manual_as_slice.rs:40:24 + | +LL | let slice: &[u8] = &vec![1, 2, 3][..]; + | ^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - let slice: &[u8] = &vec![1, 2, 3][..]; +LL + let slice: &[u8] = vec![1, 2, 3].as_slice(); + | + +error: aborting due to 7 previous errors +