This commit is contained in:
wowinter13 2025-01-15 15:58:18 +01:00
parent 4825666bd3
commit d8752dbf40
9 changed files with 159 additions and 3 deletions

View file

@ -503,6 +503,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::WAKER_CLONE_WAKE_INFO,
crate::methods::WRONG_SELF_CONVENTION_INFO,
crate::methods::ZST_OFFSET_INFO,
crate::methods::SLICE_AS_BYTES_INFO,
crate::min_ident_chars::MIN_IDENT_CHARS_INFO,
crate::minmax::MIN_MAX_INFO,
crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,

View file

@ -102,6 +102,7 @@ mod single_char_add_str;
mod single_char_insert_string;
mod single_char_push_string;
mod skip_while_next;
mod slice_as_bytes;
mod stable_sort_primitive;
mod str_split;
mod str_splitn;
@ -4363,6 +4364,34 @@ declare_clippy_lint! {
"detect `repeat().take()` that can be replaced with `repeat_n()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for string slices immediantly followed by `as_bytes`.
///
/// ### Why is this bad?
/// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic.
///
/// ### Known problems
/// In some cases, the UTF-8 validation and potential panic from string slicing may be required for
/// the code's correctness. If you need to ensure the slice boundaries fall on valid UTF-8 character
/// boundaries, the original form (`s[1..5].as_bytes()`) should be preferred.
///
/// ### Example
/// ```rust
/// let s = "Lorem ipsum";
/// s[1..5].as_bytes();
/// ```
/// Use instead:
/// ```rust
/// let s = "Lorem ipsum";
/// &s.as_bytes()[1..5];
/// ```
#[clippy::version = "1.72.0"]
pub SLICE_AS_BYTES,
pedantic,
"slicing a string and immediately calling as_bytes is less efficient and can lead to panics"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -4531,6 +4560,7 @@ impl_lint_pass!(Methods => [
DOUBLE_ENDED_ITERATOR_LAST,
USELESS_NONZERO_NEW_UNCHECKED,
MANUAL_REPEAT_N,
SLICE_AS_BYTES,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4798,6 +4828,7 @@ impl Methods {
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
redundant_as_str::check(cx, expr, recv, as_str_span, span);
}
slice_as_bytes::check(cx, expr, recv);
},
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),

View file

@ -0,0 +1,30 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_lang_item;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal};
use rustc_lint::LateContext;
use super::SLICE_AS_BYTES;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) {
if let ExprKind::Index(indexed, index) = recv.kind
&& is_range_literal(index)
{
let ty = cx.typeck_results().expr_ty(indexed).peel_refs();
if ty.is_str() || is_type_lang_item(cx, ty, LangItem::String) {
let mut applicability = Applicability::MaybeIncorrect;
let stringish = snippet_with_applicability(cx, indexed.span, "..", &mut applicability);
let range = snippet_with_applicability(cx, index.span, "..", &mut applicability);
span_lint_and_sugg(
cx,
SLICE_AS_BYTES,
expr.span,
"calling `as_bytes` after slicing a string",
"try",
format!("&{stringish}.as_bytes()[{range}]"),
applicability,
);
}
}
}