diff --git a/CHANGELOG.md b/CHANGELOG.md index 6db04a3d5257..21384c169386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6401,6 +6401,7 @@ Released 2018-09-13 [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_semicolon`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by +[`unnecessary_split_off`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_split_off [`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 71388779b08a..d2aab898a93c 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -748,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO, + crate::unnecessary_split_off::UNNECESSARY_SPLIT_OFF_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 6ec14486c20c..2c250bfdca58 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -377,6 +377,7 @@ mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_semicolon; +mod unnecessary_split_off; mod unnecessary_struct_initialization; mod unnecessary_wraps; mod unneeded_struct_pattern; @@ -948,5 +949,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { 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(infallible_try_from::InfallibleTryFrom)); + store.register_late_pass(|_| Box::new(unnecessary_split_off::UnnecessarySplitOff)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unnecessary_split_off.rs b/clippy_lints/src/unnecessary_split_off.rs new file mode 100644 index 000000000000..b3d12a12a7ec --- /dev/null +++ b/clippy_lints/src/unnecessary_split_off.rs @@ -0,0 +1,54 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_integer_const, sym, ty}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Suggests using `drain(..).collect()` when a `split_off(0)` is being called on a `vec`. + /// ### Why is this bad? + /// Because splitting implies dividing the vec into two parts, so the modified vector being emptied could be unexpected. + /// ### Example + /// ```no_run + /// let mut vec = vec![1, 2, 3]; + /// let vec1 = vec.split_off(0); + /// ``` + /// Use instead: + /// ```no_run + /// let mut vec = vec![1, 2, 3]; + /// let vec1 = vec.drain(..).collect(); + /// ``` + #[clippy::version = "1.88.0"] + pub UNNECESSARY_SPLIT_OFF, + style, + "unnecessary `split_off(0)`" +} +declare_lint_pass!(UnnecessarySplitOff => [UNNECESSARY_SPLIT_OFF]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessarySplitOff { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(path, value, args, span) = &expr.kind + && path.ident.name == sym::split_off + { + let ty = cx.typeck_results().expr_ty(value); + if ty::is_type_diagnostic_item(cx, ty, sym::Vec) { + let &[arg] = args else { + return; + }; + if is_integer_const(cx, arg, 0) { + span_lint_and_sugg( + cx, + UNNECESSARY_SPLIT_OFF, + *span, + "unnecessary `split_off(0)`", + "use", + "drain(..).collect()".to_string(), + Applicability::MachineApplicable, + ); + } + } + } + } +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 67e5145033e0..cac33c567ea2 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -307,6 +307,7 @@ generate! { split_at_mut, split_at_mut_checked, split_inclusive, + split_off, split_once, split_terminator, split_whitespace, diff --git a/tests/ui/unnecessary_split_off.rs b/tests/ui/unnecessary_split_off.rs new file mode 100644 index 000000000000..b4ff2cdc6076 --- /dev/null +++ b/tests/ui/unnecessary_split_off.rs @@ -0,0 +1,31 @@ +//@no-rustfix +#![warn(clippy::unnecessary_split_off)] +#![allow(unused)] + +struct A; +impl A { + fn split_off(&mut self, _: usize) {} +} + +fn main() { + let mut vec1 = vec![1, 2, 3]; + + let vec2: Vec<_> = vec1.split_off(0); + //~^ unnecessary_split_off + + let vec3: Vec<_> = vec1.split_off(1); + + const ZERO: usize = 0; + let vec4: Vec<_> = vec1.split_off(ZERO); + //~^ unnecessary_split_off + + let vec5: Vec<_> = vec1.split_off(const { 0 }); + //~^ unnecessary_split_off + + let zero = 0; + let vec6: Vec<_> = vec1.split_off(zero); + //~^ unnecessary_split_off + + let mut a = A; + a.split_off(0); +} diff --git a/tests/ui/unnecessary_split_off.stderr b/tests/ui/unnecessary_split_off.stderr new file mode 100644 index 000000000000..c89fdad951fe --- /dev/null +++ b/tests/ui/unnecessary_split_off.stderr @@ -0,0 +1,23 @@ +error: unnecessary `split_off(0)` + --> tests/ui/unnecessary_split_off.rs:15:29 + | +LL | let vec2: Vec<_> = vec1.split_off(0); + | ^^^^^^^^^^^^ help: use: `drain(..).collect()` + | + = note: `-D clippy::unnecessary-split-off` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_split_off)]` + +error: unnecessary `split_off(0)` + --> tests/ui/unnecessary_split_off.rs:20:29 + | +LL | let vec4: Vec<_> = vec1.split_off(ZERO); + | ^^^^^^^^^^^^^^^ help: use: `drain(..).collect()` + +error: unnecessary `split_off(0)` + --> tests/ui/unnecessary_split_off.rs:23:29 + | +LL | let vec5: Vec<_> = vec1.split_off(const { 0 }); + | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `drain(..).collect()` + +error: aborting due to 3 previous errors +