diff --git a/CHANGELOG.md b/CHANGELOG.md index 9548366daf9a..3149a9b7511e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2894,6 +2894,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons diff --git a/clippy_lints/src/bytes_count_to_len.rs b/clippy_lints/src/bytes_count_to_len.rs new file mode 100644 index 000000000000..799d3783f74f --- /dev/null +++ b/clippy_lints/src/bytes_count_to_len.rs @@ -0,0 +1,54 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use if_chain::if_chain; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// It checks for `str::bytes().count()` and suggests replacing it with + /// `str::len()`. + /// + /// ### Why is this bad? + /// `str::bytes().count()` is longer and may not be as performant as using + /// `str::len()`. + /// + /// ### Example + /// ```rust + /// "hello".bytes().count(); + /// ``` + /// Use instead: + /// ```rust + /// "hello".len(); + /// ``` + #[clippy::version = "1.60.0"] + pub BYTES_COUNT_TO_LEN, + complexity, + "Using bytest().count() when len() performs the same functionality" +} + +declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); + +impl<'tcx> LateLintPass<'tcx> for BytesCountToLen { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + //check for method call called "count" + if let hir::ExprKind::MethodCall(count_path, count_args, _) = &expr.kind; + if count_path.ident.name == rustc_span::sym::count; + if let [bytes_expr] = &**count_args; + //check for method call called "bytes" that was linked to "count" + if let hir::ExprKind::MethodCall(bytes_path, _, _) = &bytes_expr.kind; + if bytes_path.ident.name.as_str() == "bytes"; + then { + span_lint_and_note( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + None, + "`.len()` achieves same functionality" + ); + } + }; + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 4721b7f2b472..e567bcf218c9 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -21,6 +21,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::FN_TO_NUMERIC_CAST), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index bd5ff613447c..57daf4ad8740 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), LintId::of(derivable_impls::DERIVABLE_IMPLS), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index e5e1c052c15e..5582f6b0874e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -61,6 +61,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, + bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo_common_metadata::CARGO_COMMON_METADATA, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, casts::CAST_LOSSLESS, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 9d42185dc0ec..15f553ebb2c3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -175,6 +175,7 @@ mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; mod bytecount; +mod bytes_count_to_len; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; mod casts; @@ -861,6 +862,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); + store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/bytes_count_to_len.rs b/tests/ui/bytes_count_to_len.rs new file mode 100644 index 000000000000..b2dd2bfeeed7 --- /dev/null +++ b/tests/ui/bytes_count_to_len.rs @@ -0,0 +1,15 @@ +#![warn(clippy::bytes_count_to_len)] + +fn main() { + let s1 = String::from("world"); + + //test warning against a string literal + "hello".bytes().count(); + + //test warning against a string variable + s1.bytes().count(); + + //make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let size = vector.iter().count(); +} diff --git a/tests/ui/bytes_count_to_len.stderr b/tests/ui/bytes_count_to_len.stderr new file mode 100644 index 000000000000..ddd08ee824c1 --- /dev/null +++ b/tests/ui/bytes_count_to_len.stderr @@ -0,0 +1,19 @@ +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:7:5 + | +LL | "hello".bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::bytes-count-to-len` implied by `-D warnings` + = note: `.len()` achieves same functionality + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:10:5 + | +LL | s1.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `.len()` achieves same functionality + +error: aborting due to 2 previous errors +