Skip to content

Commit

Permalink
New lint needless_as_bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
samueltardieu committed Sep 23, 2024
1 parent 43e3384 commit 538193b
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5725,6 +5725,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
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::mutex_atomic::MUTEX_ATOMIC_INFO,
crate::mutex_atomic::MUTEX_INTEGER_INFO,
crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
crate::needless_as_bytes::NEEDLESS_AS_BYTES_INFO,
crate::needless_bool::BOOL_COMPARISON_INFO,
crate::needless_bool::NEEDLESS_BOOL_INFO,
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ mod mut_reference;
mod mutable_debug_assertion;
mod mutex_atomic;
mod needless_arbitrary_self_type;
mod needless_as_bytes;
mod needless_bool;
mod needless_borrowed_ref;
mod needless_borrows_for_generic_args;
Expand Down Expand Up @@ -944,5 +945,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
store.register_late_pass(|_| Box::new(needless_as_bytes::NeedlessAsBytes));
// add lints here, do not remove this comment, it's used in `new_lint`
}
60 changes: 60 additions & 0 deletions clippy_lints/src/needless_as_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
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, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;

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.83.0"]
pub NEEDLESS_AS_BYTES,
complexity,
"detect useless calls to `as_bytes()`"
}

declare_lint_pass!(NeedlessAsBytes => [NEEDLESS_AS_BYTES]);

impl LateLintPass<'_> for NeedlessAsBytes {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::MethodCall(method2, receiver2, &[], _) = expr.kind
&& cx.typeck_results().expr_ty_adjusted(receiver2).peel_refs().is_slice()
&& (method2.ident.name == sym::len || method2.ident.name.as_str() == "is_empty")
&& let ExprKind::MethodCall(method1, receiver1, &[], _) = receiver2.kind
&& let ty1 = cx.typeck_results().expr_ty_adjusted(receiver1).peel_refs()
&& (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
&& method1.ident.name.as_str() == "as_bytes"
{
let mut app = Applicability::MachineApplicable;
let sugg = Sugg::hir_with_context(cx, receiver1, expr.span.ctxt(), "..", &mut app);
span_lint_and_sugg(
cx,
NEEDLESS_AS_BYTES,
expr.span,
"needless call to `as_bytes()`",
format!("`{}()` can be called directly on strings", method2.ident.name),
format!("{sugg}.{}()", method2.ident.name),
app,
);
}
}
}
40 changes: 40 additions & 0 deletions tests/ui/needless_as_bytes.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![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();
}

pub trait AsBytes {
fn as_bytes(&self) -> &[u8];
}

impl AsBytes for String {
fn as_bytes(&self) -> &[u8] {
&[]
}
}
40 changes: 40 additions & 0 deletions tests/ui/needless_as_bytes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![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();
}

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 538193b

Please sign in to comment.