Skip to content

Commit afa010e

Browse files
committed
Add slice_as_bytes lint
Detects patterns like ```rust s[1..5].as_bytes(); ``` and suggest replacing with ```rust &s.as_bytes()[1..5]; ```
1 parent e11f36c commit afa010e

File tree

7 files changed

+106
-0
lines changed

7 files changed

+106
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5170,6 +5170,7 @@ Released 2018-09-13
51705170
[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
51715171
[`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref
51725172
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
5173+
[`slice_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#slice_as_bytes
51735174
[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
51745175
[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
51755176
[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
575575
crate::single_range_in_vec_init::SINGLE_RANGE_IN_VEC_INIT_INFO,
576576
crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
577577
crate::size_of_ref::SIZE_OF_REF_INFO,
578+
crate::slice_as_bytes::SLICE_AS_BYTES_INFO,
578579
crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
579580
crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
580581
crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ mod single_component_path_imports;
292292
mod single_range_in_vec_init;
293293
mod size_of_in_element_count;
294294
mod size_of_ref;
295+
mod slice_as_bytes;
295296
mod slow_vector_initialization;
296297
mod std_instead_of_core;
297298
mod strings;
@@ -1050,6 +1051,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10501051
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
10511052
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
10521053
store.register_late_pass(|_| Box::new(incorrect_impls::IncorrectImpls));
1054+
store.register_late_pass(|_| Box::new(slice_as_bytes::SliceAsBytes));
10531055
// add lints here, do not remove this comment, it's used in `new_lint`
10541056
}
10551057

clippy_lints/src/slice_as_bytes.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_with_applicability, ty::is_type_lang_item};
2+
use rustc_errors::Applicability::MachineApplicable;
3+
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
7+
declare_clippy_lint! {
8+
/// ### What it does
9+
/// Checks for string slices immediantly followed by `as_bytes`.
10+
/// ### Why is this bad?
11+
/// This involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic.
12+
/// ### Example
13+
/// ```rust
14+
/// s[1..5].as_bytes();
15+
/// ```
16+
/// Use instead:
17+
/// ```rust
18+
/// &s.as_bytes()[1..5];
19+
/// ```
20+
#[clippy::version = "1.72.0"]
21+
pub SLICE_AS_BYTES,
22+
nursery,
23+
"slicing a string and immediantly calling as_bytes is less efficient and can lead to panics"
24+
}
25+
declare_lint_pass!(SliceAsBytes => [SLICE_AS_BYTES]);
26+
27+
impl<'tcx> LateLintPass<'tcx> for SliceAsBytes {
28+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
29+
if let ExprKind::MethodCall(path, recv, [], _) = expr.kind && path.ident.name == sym!(as_bytes){
30+
if let ExprKind::Index(indexed, index) = recv.kind {
31+
use LangItem::{Range,RangeFrom,RangeFull,RangeTo,RangeToInclusive};
32+
if let ExprKind::Struct(
33+
QPath::LangItem(Range | RangeFrom | RangeTo | RangeToInclusive | RangeFull, ..),
34+
..,
35+
) = index.kind
36+
{
37+
let ty = cx.typeck_results().expr_ty(indexed).peel_refs();
38+
let is_str = ty.is_str();
39+
let is_string = is_type_lang_item(cx, ty, LangItem::String);
40+
if is_str|| is_string
41+
{
42+
let mut applicability = MachineApplicable;
43+
let stringish = snippet_with_applicability(cx,indexed.span,"..",&mut applicability);
44+
let range = snippet_with_applicability(cx,index.span,"..",&mut applicability);
45+
let sugg = format!("&{stringish}.as_bytes()[{range}]");
46+
let type_name = if is_str {"str"} else {"String"};
47+
let msg = format!("Slicing a {type_name} before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking");
48+
let sp = expr.span;
49+
span_lint_and_sugg(cx,SLICE_AS_BYTES ,sp , &msg, "try",sugg ,applicability);
50+
}
51+
52+
}
53+
}
54+
}
55+
}
56+
}

tests/ui/slice_as_bytes.fixed

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::slice_as_bytes)]
4+
5+
fn main() {
6+
let s = "Lorem ipsum";
7+
let string: String = "dolor sit amet".to_owned();
8+
9+
let bytes = &s.as_bytes()[1..5];
10+
let bytes = &string.as_bytes()[1..];
11+
let bytes = &"consectetur adipiscing".as_bytes()[..=5];
12+
}

tests/ui/slice_as_bytes.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@run-rustfix
2+
#![allow(unused)]
3+
#![warn(clippy::slice_as_bytes)]
4+
5+
fn main() {
6+
let s = "Lorem ipsum";
7+
let string: String = "dolor sit amet".to_owned();
8+
9+
let bytes = s[1..5].as_bytes();
10+
let bytes = string[1..].as_bytes();
11+
let bytes = "consectetur adipiscing"[..=5].as_bytes();
12+
}

tests/ui/slice_as_bytes.stderr

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: Slicing a str before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking
2+
--> $DIR/slice_as_bytes.rs:9:17
3+
|
4+
LL | let bytes = s[1..5].as_bytes();
5+
| ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]`
6+
|
7+
= note: `-D clippy::slice-as-bytes` implied by `-D warnings`
8+
9+
error: Slicing a String before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking
10+
--> $DIR/slice_as_bytes.rs:10:17
11+
|
12+
LL | let bytes = string[1..].as_bytes();
13+
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&string.as_bytes()[1..]`
14+
15+
error: Slicing a str before calling `as_bytes` results in needless UTF-8 alignment checks, and has the possiblity of panicking
16+
--> $DIR/slice_as_bytes.rs:11:17
17+
|
18+
LL | let bytes = "consectetur adipiscing"[..=5].as_bytes();
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&"consectetur adipiscing".as_bytes()[..=5]`
20+
21+
error: aborting due to 3 previous errors
22+

0 commit comments

Comments
 (0)