diff --git a/clippy_lints/src/bytes_count_to_len.rs b/clippy_lints/src/bytes_count_to_len.rs deleted file mode 100644 index d70dbf5b2..000000000 --- a/clippy_lints/src/bytes_count_to_len.rs +++ /dev/null @@ -1,70 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, paths}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -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(); - /// String::from("hello").bytes().count(); - /// ``` - /// Use instead: - /// ```rust - /// "hello".len(); - /// String::from("hello").len(); - /// ``` - #[clippy::version = "1.62.0"] - pub BYTES_COUNT_TO_LEN, - complexity, - "Using `bytes().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! { - if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if match_def_path(cx, expr_def_id, &paths::ITER_COUNT); - - if let [bytes_expr] = &**expr_args; - if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind; - if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id); - if match_def_path(cx, bytes_def_id, &paths::STR_BYTES); - - if let [str_expr] = &**bytes_args; - let ty = cx.typeck_results().expr_ty(str_expr).peel_refs(); - - if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str; - then { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - BYTES_COUNT_TO_LEN, - expr.span, - "using long and hard to read `.bytes().count()`", - "consider calling `.len()` instead", - format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)), - applicability - ); - } - }; - } -} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index a0a4b07a7..f48e542ee 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -20,7 +20,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), @@ -151,6 +150,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::BYTES_COUNT_TO_LEN), LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_NEXT_CMP), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 3784d3c68..324b38031 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -6,7 +6,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), LintId::of(dereference::EXPLICIT_AUTO_DEREF), @@ -33,6 +32,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(matches::NEEDLESS_MATCH), LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(methods::BIND_INSTEAD_OF_MAP), + LintId::of(methods::BYTES_COUNT_TO_LEN), LintId::of(methods::CLONE_ON_COPY), LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index c3c24fb16..bfd12b989 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -59,7 +59,6 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, booleans::OVERLY_COMPLEX_BOOL_EXPR, borrow_deref_ref::BORROW_DEREF_REF, - bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, @@ -284,6 +283,7 @@ store.register_lints(&[ mem_replace::MEM_REPLACE_WITH_DEFAULT, mem_replace::MEM_REPLACE_WITH_UNINIT, methods::BIND_INSTEAD_OF_MAP, + methods::BYTES_COUNT_TO_LEN, methods::BYTES_NTH, methods::CHARS_LAST_CMP, methods::CHARS_NEXT_CMP, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 617f16223..f4cb5e5bf 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -180,7 +180,6 @@ mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; mod borrow_deref_ref; -mod bytes_count_to_len; mod cargo; mod case_sensitive_file_extension_comparisons; mod casts; @@ -907,7 +906,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); - store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); let max_include_file_size = conf.max_include_file_size; store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); diff --git a/clippy_lints/src/methods/bytes_count_to_len.rs b/clippy_lints/src/methods/bytes_count_to_len.rs new file mode 100644 index 000000000..fcfc25b52 --- /dev/null +++ b/clippy_lints/src/methods/bytes_count_to_len.rs @@ -0,0 +1,37 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::BYTES_COUNT_TO_LEN; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + count_recv: &'tcx hir::Expr<'_>, + bytes_recv: &'tcx hir::Expr<'_>, +) { + if_chain! { + if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id); + if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id); + if cx.tcx.type_of(impl_id).is_str(); + let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs(); + if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String); + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + "consider calling `.len()` instead", + format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)), + applicability + ); + } + }; +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f0d9dce55..eab3ca184 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,5 +1,6 @@ mod bind_instead_of_map; mod bytecount; +mod bytes_count_to_len; mod bytes_nth; mod chars_cmp; mod chars_cmp_with_unwrap; @@ -2402,6 +2403,31 @@ declare_clippy_lint! { "use of naive `.filter(|&x| x == y).count()` to count byte values" } +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(); + /// String::from("hello").bytes().count(); + /// ``` + /// Use instead: + /// ```rust + /// "hello".len(); + /// String::from("hello").len(); + /// ``` + #[clippy::version = "1.62.0"] + pub BYTES_COUNT_TO_LEN, + complexity, + "Using `bytes().count()` when `len()` performs the same functionality" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2507,6 +2533,7 @@ impl_lint_pass!(Methods => [ ITER_ON_SINGLE_ITEMS, ITER_ON_EMPTY_COLLECTIONS, NAIVE_BYTECOUNT, + BYTES_COUNT_TO_LEN, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2768,6 +2795,7 @@ impl Methods { }, Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg), + Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, ("drain", [arg]) => {