about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--README.md2
-rw-r--r--book/src/README.md2
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/methods/mod.rs42
-rw-r--r--clippy_lints/src/methods/needless_as_bytes.rs28
-rw-r--r--tests/ui/needless_as_bytes.fixed50
-rw-r--r--tests/ui/needless_as_bytes.rs50
-rw-r--r--tests/ui/needless_as_bytes.stderr29
9 files changed, 201 insertions, 4 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bdbc91db93..b1f26373e54 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5761,6 +5761,7 @@ Released 2018-09-13
 [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 [`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
+[`needless_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_as_bytes
 [`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
diff --git a/README.md b/README.md
index ec76a6dfb08..1690e2beb16 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 
 A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 
-[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
diff --git a/book/src/README.md b/book/src/README.md
index 7bdfb97c3ac..23527ba896a 100644
--- a/book/src/README.md
+++ b/book/src/README.md
@@ -6,7 +6,7 @@
 A collection of lints to catch common mistakes and improve your
 [Rust](https://github.com/rust-lang/rust) code.
 
-[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 Lints are divided into categories, each with a default [lint
 level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index d8918d37afa..7da3ac9f2fd 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -424,6 +424,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::methods::MAP_UNWRAP_OR_INFO,
     crate::methods::MUT_MUTEX_LOCK_INFO,
     crate::methods::NAIVE_BYTECOUNT_INFO,
+    crate::methods::NEEDLESS_AS_BYTES_INFO,
     crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO,
     crate::methods::NEEDLESS_COLLECT_INFO,
     crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 722290fb68e..f54221fff2a 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -67,6 +67,7 @@ mod map_flatten;
 mod map_identity;
 mod map_unwrap_or;
 mod mut_mutex_lock;
+mod needless_as_bytes;
 mod needless_character_iteration;
 mod needless_collect;
 mod needless_option_as_deref;
@@ -4166,6 +4167,31 @@ declare_clippy_lint! {
     "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
+    ///
+    /// ### Why is this bad?
+    /// The `len()` and `is_empty()` methods are also directly available on strings, and they
+    /// return identical results. In particular, `len()` on a string returns the number of
+    /// bytes.
+    ///
+    /// ### Example
+    /// ```
+    /// let len = "some string".as_bytes().len();
+    /// let b = "some string".as_bytes().is_empty();
+    /// ```
+    /// Use instead:
+    /// ```
+    /// let len = "some string".len();
+    /// let b = "some string".is_empty();
+    /// ```
+    #[clippy::version = "1.84.0"]
+    pub NEEDLESS_AS_BYTES,
+    complexity,
+    "detect useless calls to `as_bytes()`"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -4327,6 +4353,7 @@ impl_lint_pass!(Methods => [
     NEEDLESS_CHARACTER_ITERATION,
     MANUAL_INSPECT,
     UNNECESSARY_MIN_OR_MAX,
+    NEEDLESS_AS_BYTES,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4764,8 +4791,14 @@ impl Methods {
                     unit_hash::check(cx, expr, recv, arg);
                 },
                 ("is_empty", []) => {
-                    if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
-                        redundant_as_str::check(cx, expr, recv, as_str_span, span);
+                    match method_call(recv) {
+                        Some(("as_bytes", prev_recv, [], _, _)) => {
+                            needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span);
+                        },
+                        Some(("as_str", recv, [], as_str_span, _)) => {
+                            redundant_as_str::check(cx, expr, recv, as_str_span, span);
+                        },
+                        _ => {},
                     }
                     is_empty::check(cx, expr, recv);
                 },
@@ -4795,6 +4828,11 @@ impl Methods {
                         );
                     }
                 },
+                ("len", []) => {
+                    if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
+                        needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span);
+                    }
+                },
                 ("lock", []) => {
                     mut_mutex_lock::check(cx, expr, recv, span);
                 },
diff --git a/clippy_lints/src/methods/needless_as_bytes.rs b/clippy_lints/src/methods/needless_as_bytes.rs
new file mode 100644
index 00000000000..75e9f317230
--- /dev/null
+++ b/clippy_lints/src/methods/needless_as_bytes.rs
@@ -0,0 +1,28 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_lang_item;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, LangItem};
+use rustc_lint::LateContext;
+use rustc_span::Span;
+
+use super::NEEDLESS_AS_BYTES;
+
+pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) {
+    if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
+        && let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs()
+        && (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
+    {
+        let mut app = Applicability::MachineApplicable;
+        let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
+        span_lint_and_sugg(
+            cx,
+            NEEDLESS_AS_BYTES,
+            span,
+            "needless call to `as_bytes()`",
+            format!("`{method}()` can be called directly on strings"),
+            format!("{sugg}.{method}()"),
+            app,
+        );
+    }
+}
diff --git a/tests/ui/needless_as_bytes.fixed b/tests/ui/needless_as_bytes.fixed
new file mode 100644
index 00000000000..042342311fd
--- /dev/null
+++ b/tests/ui/needless_as_bytes.fixed
@@ -0,0 +1,50 @@
+#![warn(clippy::needless_as_bytes)]
+#![allow(clippy::const_is_empty)]
+
+struct S;
+
+impl S {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
+
+fn main() {
+    if "some string".is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", "some string".len());
+        //~^ needless_as_bytes
+    }
+
+    let s = String::from("yet another string");
+    if s.is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", s.len());
+        //~^ needless_as_bytes
+    }
+
+    // Do not lint
+    let _ = S.as_bytes().is_empty();
+    let _ = S.as_bytes().len();
+    let _ = (&String::new() as &dyn AsBytes).as_bytes().len();
+    macro_rules! m {
+        (1) => {
+            ""
+        };
+        (2) => {
+            "".as_bytes()
+        };
+    }
+    m!(1).as_bytes().len();
+    m!(2).len();
+}
+
+pub trait AsBytes {
+    fn as_bytes(&self) -> &[u8];
+}
+
+impl AsBytes for String {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
diff --git a/tests/ui/needless_as_bytes.rs b/tests/ui/needless_as_bytes.rs
new file mode 100644
index 00000000000..c481e041e0a
--- /dev/null
+++ b/tests/ui/needless_as_bytes.rs
@@ -0,0 +1,50 @@
+#![warn(clippy::needless_as_bytes)]
+#![allow(clippy::const_is_empty)]
+
+struct S;
+
+impl S {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
+
+fn main() {
+    if "some string".as_bytes().is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", "some string".as_bytes().len());
+        //~^ needless_as_bytes
+    }
+
+    let s = String::from("yet another string");
+    if s.as_bytes().is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", s.as_bytes().len());
+        //~^ needless_as_bytes
+    }
+
+    // Do not lint
+    let _ = S.as_bytes().is_empty();
+    let _ = S.as_bytes().len();
+    let _ = (&String::new() as &dyn AsBytes).as_bytes().len();
+    macro_rules! m {
+        (1) => {
+            ""
+        };
+        (2) => {
+            "".as_bytes()
+        };
+    }
+    m!(1).as_bytes().len();
+    m!(2).len();
+}
+
+pub trait AsBytes {
+    fn as_bytes(&self) -> &[u8];
+}
+
+impl AsBytes for String {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
diff --git a/tests/ui/needless_as_bytes.stderr b/tests/ui/needless_as_bytes.stderr
new file mode 100644
index 00000000000..3391238a142
--- /dev/null
+++ b/tests/ui/needless_as_bytes.stderr
@@ -0,0 +1,29 @@
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:13:8
+   |
+LL |     if "some string".as_bytes().is_empty() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()`
+   |
+   = note: `-D clippy::needless-as-bytes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]`
+
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:15:30
+   |
+LL |         println!("len = {}", "some string".as_bytes().len());
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()`
+
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:20:8
+   |
+LL |     if s.as_bytes().is_empty() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()`
+
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:22:30
+   |
+LL |         println!("len = {}", s.as_bytes().len());
+   |                              ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()`
+
+error: aborting due to 4 previous errors
+