Skip to content

Commit 7da77c0

Browse files
committed
New lint needless_as_bytes
1 parent 43e3384 commit 7da77c0

File tree

7 files changed

+199
-2
lines changed

7 files changed

+199
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5725,6 +5725,7 @@ Released 2018-09-13
57255725
[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
57265726
[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
57275727
[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
5728+
[`needless_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_as_bytes
57285729
[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
57295730
[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
57305731
[`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
420420
crate::methods::MAP_UNWRAP_OR_INFO,
421421
crate::methods::MUT_MUTEX_LOCK_INFO,
422422
crate::methods::NAIVE_BYTECOUNT_INFO,
423+
crate::methods::NEEDLESS_AS_BYTES_INFO,
423424
crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO,
424425
crate::methods::NEEDLESS_COLLECT_INFO,
425426
crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,

clippy_lints/src/methods/mod.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ mod map_flatten;
6767
mod map_identity;
6868
mod map_unwrap_or;
6969
mod mut_mutex_lock;
70+
mod needless_as_bytes;
7071
mod needless_character_iteration;
7172
mod needless_collect;
7273
mod needless_option_as_deref;
@@ -4166,6 +4167,31 @@ declare_clippy_lint! {
41664167
"calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
41674168
}
41684169

4170+
declare_clippy_lint! {
4171+
/// ### What it does
4172+
/// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
4173+
///
4174+
/// ### Why is this bad?
4175+
/// The `len()` and `is_empty()` methods are also directly available on strings, and they
4176+
/// return identical results. In particular, `len()` on a string returns the number of
4177+
/// bytes.
4178+
///
4179+
/// ### Example
4180+
/// ```
4181+
/// let len = "some string".as_bytes().len();
4182+
/// let b = "some string".as_bytes().is_empty();
4183+
/// ```
4184+
/// Use instead:
4185+
/// ```
4186+
/// let len = "some string".len();
4187+
/// let b = "some string".is_empty();
4188+
/// ```
4189+
#[clippy::version = "1.83.0"]
4190+
pub NEEDLESS_AS_BYTES,
4191+
complexity,
4192+
"detect useless calls to `as_bytes()`"
4193+
}
4194+
41694195
pub struct Methods {
41704196
avoid_breaking_exported_api: bool,
41714197
msrv: Msrv,
@@ -4327,6 +4353,7 @@ impl_lint_pass!(Methods => [
43274353
NEEDLESS_CHARACTER_ITERATION,
43284354
MANUAL_INSPECT,
43294355
UNNECESSARY_MIN_OR_MAX,
4356+
NEEDLESS_AS_BYTES,
43304357
]);
43314358

43324359
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4764,8 +4791,14 @@ impl Methods {
47644791
unit_hash::check(cx, expr, recv, arg);
47654792
},
47664793
("is_empty", []) => {
4767-
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
4768-
redundant_as_str::check(cx, expr, recv, as_str_span, span);
4794+
match method_call(recv) {
4795+
Some(("as_bytes", prev_recv, [], _, _)) => {
4796+
needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span)
4797+
},
4798+
Some(("as_str", recv, [], as_str_span, _)) => {
4799+
redundant_as_str::check(cx, expr, recv, as_str_span, span)
4800+
},
4801+
_ => {},
47694802
}
47704803
is_empty::check(cx, expr, recv);
47714804
},
@@ -4795,6 +4828,11 @@ impl Methods {
47954828
);
47964829
}
47974830
},
4831+
("len", []) => {
4832+
if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
4833+
needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span);
4834+
}
4835+
},
47984836
("lock", []) => {
47994837
mut_mutex_lock::check(cx, expr, recv, span);
48004838
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::sugg::Sugg;
3+
use clippy_utils::ty::is_type_lang_item;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, LangItem};
6+
use rustc_lint::LateContext;
7+
use rustc_span::Span;
8+
9+
use super::NEEDLESS_AS_BYTES;
10+
11+
pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) {
12+
if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
13+
&& let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs()
14+
&& (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
15+
{
16+
let mut app = Applicability::MachineApplicable;
17+
let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
18+
span_lint_and_sugg(
19+
cx,
20+
NEEDLESS_AS_BYTES,
21+
span,
22+
"needless call to `as_bytes()`",
23+
format!("`{method}()` can be called directly on strings"),
24+
format!("{sugg}.{method}()"),
25+
app,
26+
);
27+
}
28+
}

tests/ui/needless_as_bytes.fixed

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#![warn(clippy::needless_as_bytes)]
2+
#![allow(clippy::const_is_empty)]
3+
4+
struct S;
5+
6+
impl S {
7+
fn as_bytes(&self) -> &[u8] {
8+
&[]
9+
}
10+
}
11+
12+
fn main() {
13+
if "some string".is_empty() {
14+
//~^ needless_as_bytes
15+
println!("len = {}", "some string".len());
16+
//~^ needless_as_bytes
17+
}
18+
19+
let s = String::from("yet another string");
20+
if s.is_empty() {
21+
//~^ needless_as_bytes
22+
println!("len = {}", s.len());
23+
//~^ needless_as_bytes
24+
}
25+
26+
// Do not lint
27+
let _ = S.as_bytes().is_empty();
28+
let _ = S.as_bytes().len();
29+
let _ = (&String::new() as &dyn AsBytes).as_bytes().len();
30+
macro_rules! m {
31+
(1) => {
32+
""
33+
};
34+
(2) => {
35+
"".as_bytes()
36+
};
37+
}
38+
m!(1).as_bytes().len();
39+
m!(2).len();
40+
}
41+
42+
pub trait AsBytes {
43+
fn as_bytes(&self) -> &[u8];
44+
}
45+
46+
impl AsBytes for String {
47+
fn as_bytes(&self) -> &[u8] {
48+
&[]
49+
}
50+
}

tests/ui/needless_as_bytes.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#![warn(clippy::needless_as_bytes)]
2+
#![allow(clippy::const_is_empty)]
3+
4+
struct S;
5+
6+
impl S {
7+
fn as_bytes(&self) -> &[u8] {
8+
&[]
9+
}
10+
}
11+
12+
fn main() {
13+
if "some string".as_bytes().is_empty() {
14+
//~^ needless_as_bytes
15+
println!("len = {}", "some string".as_bytes().len());
16+
//~^ needless_as_bytes
17+
}
18+
19+
let s = String::from("yet another string");
20+
if s.as_bytes().is_empty() {
21+
//~^ needless_as_bytes
22+
println!("len = {}", s.as_bytes().len());
23+
//~^ needless_as_bytes
24+
}
25+
26+
// Do not lint
27+
let _ = S.as_bytes().is_empty();
28+
let _ = S.as_bytes().len();
29+
let _ = (&String::new() as &dyn AsBytes).as_bytes().len();
30+
macro_rules! m {
31+
(1) => {
32+
""
33+
};
34+
(2) => {
35+
"".as_bytes()
36+
};
37+
}
38+
m!(1).as_bytes().len();
39+
m!(2).len();
40+
}
41+
42+
pub trait AsBytes {
43+
fn as_bytes(&self) -> &[u8];
44+
}
45+
46+
impl AsBytes for String {
47+
fn as_bytes(&self) -> &[u8] {
48+
&[]
49+
}
50+
}

tests/ui/needless_as_bytes.stderr

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error: needless call to `as_bytes()`
2+
--> tests/ui/needless_as_bytes.rs:13:8
3+
|
4+
LL | if "some string".as_bytes().is_empty() {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()`
6+
|
7+
= note: `-D clippy::needless-as-bytes` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]`
9+
10+
error: needless call to `as_bytes()`
11+
--> tests/ui/needless_as_bytes.rs:15:30
12+
|
13+
LL | println!("len = {}", "some string".as_bytes().len());
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()`
15+
16+
error: needless call to `as_bytes()`
17+
--> tests/ui/needless_as_bytes.rs:20:8
18+
|
19+
LL | if s.as_bytes().is_empty() {
20+
| ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()`
21+
22+
error: needless call to `as_bytes()`
23+
--> tests/ui/needless_as_bytes.rs:22:30
24+
|
25+
LL | println!("len = {}", s.as_bytes().len());
26+
| ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()`
27+
28+
error: aborting due to 4 previous errors
29+

0 commit comments

Comments
 (0)