From 9fd255b8cacbad8b7c9fbcdf8006372f242b6d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Husi=C4=8Dka?= Date: Sat, 6 Jul 2024 19:17:23 +0200 Subject: [PATCH] changelog: new lint: [`split_with_space`] Adds a new lint for checking whether the `split` method uses ASCII space as an delimeter and if it does so, recommends use of `split_whitespace`. --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 1 + clippy_lints/src/strings.rs | 44 ++++++++++++++++++++++++++++++ tests/ui/split_with_space.rs | 16 +++++++++++ tests/ui/split_with_space.stderr | 20 ++++++++++++++ tests/versioncheck.rs | 2 +- 7 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/ui/split_with_space.rs create mode 100644 tests/ui/split_with_space.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 62fc5e2c5d42..961bcb9dee38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5826,6 +5826,7 @@ Released 2018-09-13 [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization +[`split_with_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#split_with_space [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc [`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 593f59cff423..0730997be017 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -665,6 +665,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO, crate::string_patterns::SINGLE_CHAR_PATTERN_INFO, + crate::strings::SPLIT_WITH_SPACE_INFO, crate::strings::STRING_ADD_INFO, crate::strings::STRING_ADD_ASSIGN_INFO, crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fbe895fa50e8..43ac1a646482 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1016,6 +1016,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(|_| Box::new(strings::SplitWithSpace)); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::::default()); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 7da661485abf..fc7748714919 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{ get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls, peel_blocks, SpanlessEq, }; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath}; @@ -467,6 +468,49 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { } } +declare_clippy_lint! { + /// ### What it does + /// Checks for calling `str::split` with the ASCII space character instead of `str::split_whitespace`. + /// + /// ### Why is this bad? + /// `split` using the ASCII space character does not handle cases where there is a different space character used. `split_whitespace` handles all whitespace characters. + /// + /// ### Example + /// ```no_run + /// "A B C".split(' '); + /// ``` + /// Use instead: + /// ```no_run + /// "A B C".split_whitespace(); + /// ``` + #[clippy::version = "1.81.0"] + pub SPLIT_WITH_SPACE, + pedantic, + "using `str::split` with the ASCII space character instead of `str::split_whitespace`" +} +declare_lint_pass!(SplitWithSpace => [SPLIT_WITH_SPACE]); + +impl<'tcx> LateLintPass<'tcx> for SplitWithSpace { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(path, _split_recv, [arg], split_ws_span) = expr.kind + && path.ident.name == sym!(split) + && let ExprKind::Lit(lit) = arg.kind + && (matches!(lit.node, LitKind::Char(' ')) + || matches!(lit.node, LitKind::Str(str, _) if str.as_str() == " ")) + { + span_lint_and_sugg( + cx, + SPLIT_WITH_SPACE, + split_ws_span.with_hi(split_ws_span.lo()), + "found call to `str::split` that uses the ASCII space character as an argument".to_string(), + "replace the use of `str::split` with `str::split_whitespace`".to_string(), + String::new(), + Applicability::MachineApplicable, + ); + } + } +} + declare_clippy_lint! { /// ### What it does /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. diff --git a/tests/ui/split_with_space.rs b/tests/ui/split_with_space.rs new file mode 100644 index 000000000000..4798c5c65ffb --- /dev/null +++ b/tests/ui/split_with_space.rs @@ -0,0 +1,16 @@ +//@aux-build:proc_macros.rs +//@no-rustfix + +#[deny(clippy::split_with_space)] +#[allow(unused)] +fn main() { + let some_space_delimtetered_string = "Hello everyone! How are you doing?"; + + for substr in some_space_delimtetered_string.split(' ') { + println!("{substr}"); + } + + for substr in some_space_delimtetered_string.split(" ") { + println!("{substr}"); + } +} diff --git a/tests/ui/split_with_space.stderr b/tests/ui/split_with_space.stderr new file mode 100644 index 000000000000..9ac91c18c1fd --- /dev/null +++ b/tests/ui/split_with_space.stderr @@ -0,0 +1,20 @@ +error: found call to `str::split` that uses the ASCII space character as an argument + --> tests/ui/split_with_space.rs:9:50 + | +LL | for substr in some_space_delimtetered_string.split(' ') { + | ^ help: replace the use of `str::split` with `str::split_whitespace` + | +note: the lint level is defined here + --> tests/ui/split_with_space.rs:4:8 + | +LL | #[deny(clippy::split_with_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: found call to `str::split` that uses the ASCII space character as an argument + --> tests/ui/split_with_space.rs:13:50 + | +LL | for substr in some_space_delimtetered_string.split(" ") { + | ^ help: replace the use of `str::split` with `str::split_whitespace` + +error: aborting due to 2 previous errors + diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index eba5405e67ed..4a41f5a23996 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -1,6 +1,6 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] -#![allow(clippy::single_match_else)] +#![allow(clippy::single_match_else, clippy::split_with_space)] use std::fs;