From 60e19951b6e79923f8924a6db1136ea6e7dde326 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 21 Sep 2024 22:40:50 +0200 Subject: [PATCH] New lint `needless_as_bytes` --- CHANGELOG.md | 1 + clippy_lints/src/.byte_char_slices.rs.swp | Bin 0 -> 12288 bytes clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/needless_as_bytes.rs | 60 ++++++++++++++++++++++ tests/ui/needless_as_bytes.fixed | 25 +++++++++ tests/ui/needless_as_bytes.rs | 25 +++++++++ tests/ui/needless_as_bytes.stderr | 29 +++++++++++ 8 files changed, 143 insertions(+) create mode 100644 clippy_lints/src/.byte_char_slices.rs.swp create mode 100644 clippy_lints/src/needless_as_bytes.rs create mode 100644 tests/ui/needless_as_bytes.fixed create mode 100644 tests/ui/needless_as_bytes.rs create mode 100644 tests/ui/needless_as_bytes.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 41a86e8ce510..d1bbc7001319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/clippy_lints/src/.byte_char_slices.rs.swp b/clippy_lints/src/.byte_char_slices.rs.swp new file mode 100644 index 0000000000000000000000000000000000000000..42436e09a4d2d276f04fe5c0a02e5a101063065e GIT binary patch literal 12288 zcmeI2&5ImG7{)82Mx#+NAP6G0y)ruslbKz;Xm4EItbtv#`5apUEyUBzjeb42xD&)V2ZA^>;MuFWF*iW8$dUi%z zOr%d z1&jhl0i%FXz$jo8FbWt2i~>dhqkvJsDDWRtfHer2xQCGVUUVM+|L^_%|LwzsdJ!3ua0JPGaww;v?rNALr< z20jK7%z)bu5b_=P23!YMK?qjC0dQv@A>V`R;3IGuybs<5?|_%VU-uL8Gx!{Q06cIO zoB{K|0>9r!$XDPBcpV%CkAYiz;R`+i0hj{^z#V9H1AGZS1=qk8@HXfGt>rc_mr=kd zU=%P4j8ULVD6aIbpN>Z%C;FjFxi2~GB$ebv{-kRTeSSVMt_-~H4pz0LzN&8I``lHw zedt6>c~N+1RS7TLn4d3Clf+`^)*b&^TSaP<3*CD1Fupx)w;Bb!Y^|+LS<~7&t)jB4 zhgEb~TU$eYyiYY?t@5RS#dYqy(T)_qL<6SWP2BTW1p#%Zde=XUZwF<2p1SPzMZpFO5965N&JIVmTOzfCLmBKK zN2VkX>Rj|hECormsvF$N%~KqM9vM^WiSvrRPWQ@wp7-A9QgGE4VUo>rr1*n=_L?*c zSM{XBgO-ZR(?k8~4|72X7MC-JWS061D9w!$hbF~Ap#h&OT8PnTh!f57;Yf(+VyfOL z-mK7LZN=)I@2S|fk4LIPbtJSw2SPSOihd5C-Yjcdo0fDd)AZmx9bASO4;kJG2DO?V zRc4{p!pYFQOUHE5FNZF=jg>x{6U|6Kfr+wJU$Z*NnbPj~rDZ7*TciG#%R*Y`G>8PJ zvhBKF=qf*^7lo%34=IzBQ9W*ol`zI%D^Qg!7LJBK{OT)5oW-SuQ_kwj^5T)zQ44o_ z@STp69F}!h*l=WfW22mBPJhOA{*+2|B5%6d6j9)y%0h`bz)h*JpyKqiw-#qC9AW&Gp={g6qhaG=VI{YETtnm9a)TKl)TL zO$KQtqDd9|*(TCz*XeMqRQbts=@DhyzQBbR5BgZM)n{j`GuC<%rdF%b^XJd!&vk2w z`+k(Rk^Sgxgh-#< zdkSgW1G`kwfJuZ?(H2L;9ekQ9y^q>Jn8Yf%b^04J+Qs10yWXISdvoTUjkc{kADw7; Q42dU|=b|#Ycg&H$0mx(DBLDyZ literal 0 HcmV?d00001 diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2b229d2fe6ac..b9767f8f2478 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -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, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b21d3f8d09ec..abd8b9db4913 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -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; @@ -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` } diff --git a/clippy_lints/src/needless_as_bytes.rs b/clippy_lints/src/needless_as_bytes.rs new file mode 100644 index 000000000000..0516af860414 --- /dev/null +++ b/clippy_lints/src/needless_as_bytes.rs @@ -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, + ); + } + } +} diff --git a/tests/ui/needless_as_bytes.fixed b/tests/ui/needless_as_bytes.fixed new file mode 100644 index 000000000000..70edbd17f25e --- /dev/null +++ b/tests/ui/needless_as_bytes.fixed @@ -0,0 +1,25 @@ +#![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() { + println!("len = {}", "some string".len()); + } + + let s = String::from("yet another string"); + if s.is_empty() { + println!("len = {}", s.len()); + } + + // Do not lint + let _ = S.as_bytes().is_empty(); + let _ = S.as_bytes().len(); +} diff --git a/tests/ui/needless_as_bytes.rs b/tests/ui/needless_as_bytes.rs new file mode 100644 index 000000000000..005a147c2b24 --- /dev/null +++ b/tests/ui/needless_as_bytes.rs @@ -0,0 +1,25 @@ +#![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() { + println!("len = {}", "some string".as_bytes().len()); + } + + let s = String::from("yet another string"); + if s.as_bytes().is_empty() { + println!("len = {}", s.as_bytes().len()); + } + + // Do not lint + let _ = S.as_bytes().is_empty(); + let _ = S.as_bytes().len(); +} diff --git a/tests/ui/needless_as_bytes.stderr b/tests/ui/needless_as_bytes.stderr new file mode 100644 index 000000000000..bb4590dd192a --- /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:14: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:18: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:19: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 +