Skip to content

Commit

Permalink
Auto merge of #13437 - samueltardieu:issue-13434, r=y21
Browse files Browse the repository at this point in the history
New lint `needless_as_bytes`

changelog: [`needless_as_bytes`]: new lint

Fix #13434
  • Loading branch information
bors committed Oct 29, 2024
2 parents 528dcc3 + f152bcb commit 625d391
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion book/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
42 changes: 40 additions & 2 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
},
Expand Down Expand Up @@ -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);
},
Expand Down
28 changes: 28 additions & 0 deletions clippy_lints/src/methods/needless_as_bytes.rs
Original file line number Diff line number Diff line change
@@ -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,
);
}
}
50 changes: 50 additions & 0 deletions tests/ui/needless_as_bytes.fixed
Original file line number Diff line number Diff line change
@@ -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] {
&[]
}
}
50 changes: 50 additions & 0 deletions tests/ui/needless_as_bytes.rs
Original file line number Diff line number Diff line change
@@ -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] {
&[]
}
}
29 changes: 29 additions & 0 deletions tests/ui/needless_as_bytes.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 625d391

Please sign in to comment.