Skip to content

Commit 2b01d69

Browse files
committed
Auto merge of #10155 - TheNeikos:feature/add_byte_char_slice_lint, r=Manishearth
Add new lint for byte char slices This patch adds a new lint that checks for potentially harder to read byte char slices: `&[b'a', b'b']` and suggests to replace them with the easier to read `b"ab"` form. Fixes #10147 --- changelog: new lint: [`byte_char_slices`] [#10155](#10155) <!-- changelog_checked -->
2 parents d2400a4 + 88c4a22 commit 2b01d69

File tree

7 files changed

+148
-0
lines changed

7 files changed

+148
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5236,6 +5236,7 @@ Released 2018-09-13
52365236
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
52375237
[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
52385238
[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
5239+
[`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices
52395240
[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
52405241
[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
52415242
[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata

clippy_lints/src/byte_char_slices.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use rustc_ast::ast::{BorrowKind, Expr, ExprKind, Mutability};
3+
use rustc_ast::token::{Lit, LitKind};
4+
use rustc_errors::Applicability;
5+
use rustc_lint::{EarlyContext, EarlyLintPass};
6+
use rustc_session::declare_lint_pass;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Checks for hard to read slices of byte characters, that could be more easily expressed as a
11+
/// byte string.
12+
///
13+
/// ### Why is this bad?
14+
///
15+
/// Potentially makes the string harder to read.
16+
///
17+
/// ### Example
18+
/// ```ignore
19+
/// &[b'H', b'e', b'l', b'l', b'o'];
20+
/// ```
21+
/// Use instead:
22+
/// ```ignore
23+
/// b"Hello"
24+
/// ```
25+
#[clippy::version = "1.68.0"]
26+
pub BYTE_CHAR_SLICES,
27+
style,
28+
"hard to read byte char slice"
29+
}
30+
declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]);
31+
32+
impl EarlyLintPass for ByteCharSlice {
33+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
34+
if let Some(slice) = is_byte_char_slices(expr)
35+
&& !expr.span.from_expansion()
36+
{
37+
span_lint_and_sugg(
38+
cx,
39+
BYTE_CHAR_SLICES,
40+
expr.span,
41+
"can be more succinctly written as a byte str",
42+
"try",
43+
format!("b\"{slice}\""),
44+
Applicability::MaybeIncorrect,
45+
);
46+
}
47+
}
48+
}
49+
50+
fn is_byte_char_slices(expr: &Expr) -> Option<String> {
51+
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = &expr.kind {
52+
match &expr.kind {
53+
ExprKind::Array(members) => {
54+
if members.is_empty() {
55+
return None;
56+
}
57+
58+
members
59+
.iter()
60+
.map(|member| match &member.kind {
61+
ExprKind::Lit(Lit {
62+
kind: LitKind::Byte,
63+
symbol,
64+
..
65+
}) => Some(symbol.as_str()),
66+
_ => None,
67+
})
68+
.map(|maybe_quote| match maybe_quote {
69+
Some("\"") => Some("\\\""),
70+
Some("\\'") => Some("'"),
71+
other => other,
72+
})
73+
.collect::<Option<String>>()
74+
},
75+
_ => None,
76+
}
77+
} else {
78+
None
79+
}
80+
}

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
7373
crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO,
7474
crate::borrow_deref_ref::BORROW_DEREF_REF_INFO,
7575
crate::box_default::BOX_DEFAULT_INFO,
76+
crate::byte_char_slices::BYTE_CHAR_SLICES_INFO,
7677
crate::cargo::CARGO_COMMON_METADATA_INFO,
7778
crate::cargo::LINT_GROUPS_PRIORITY_INFO,
7879
crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ mod bool_to_int_with_if;
8989
mod booleans;
9090
mod borrow_deref_ref;
9191
mod box_default;
92+
mod byte_char_slices;
9293
mod cargo;
9394
mod casts;
9495
mod checked_conversions;
@@ -1174,6 +1175,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
11741175
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(msrv())));
11751176
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
11761177
store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains));
1178+
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
11771179
// add lints here, do not remove this comment, it's used in `new_lint`
11781180
}
11791181

tests/ui/byte_char_slices.fixed

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![allow(unused)]
2+
#![warn(clippy::byte_char_slices)]
3+
4+
fn main() {
5+
let bad = b"abc";
6+
let quotes = b"\"Hi";
7+
let quotes = b"'Sup";
8+
let escapes = b"\x42Esc";
9+
10+
let good = &[b'a', 0x42];
11+
let good = [b'a', b'a'];
12+
let good: u8 = [b'a', b'c'].into_iter().sum();
13+
}

tests/ui/byte_char_slices.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![allow(unused)]
2+
#![warn(clippy::byte_char_slices)]
3+
4+
fn main() {
5+
let bad = &[b'a', b'b', b'c'];
6+
let quotes = &[b'"', b'H', b'i'];
7+
let quotes = &[b'\'', b'S', b'u', b'p'];
8+
let escapes = &[b'\x42', b'E', b's', b'c'];
9+
10+
let good = &[b'a', 0x42];
11+
let good = vec![b'a', b'a'];
12+
let good: u8 = [b'a', b'c'].into_iter().sum();
13+
}

tests/ui/byte_char_slices.stderr

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: can be more succinctly written as a byte str
2+
--> tests/ui/byte_char_slices.rs:5:15
3+
|
4+
LL | let bad = &[b'a', b'b', b'c'];
5+
| ^^^^^^^^^^^^^^^^^^^ help: try: `b"abc"`
6+
|
7+
= note: `-D clippy::byte-char-slices` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::byte_char_slices)]`
9+
10+
error: can be more succinctly written as a byte str
11+
--> tests/ui/byte_char_slices.rs:6:18
12+
|
13+
LL | let quotes = &[b'"', b'H', b'i'];
14+
| ^^^^^^^^^^^^^^^^^^^ help: try: `b"\"Hi"`
15+
16+
error: can be more succinctly written as a byte str
17+
--> tests/ui/byte_char_slices.rs:7:18
18+
|
19+
LL | let quotes = &[b'\'', b'S', b'u', b'p'];
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'Sup"`
21+
22+
error: can be more succinctly written as a byte str
23+
--> tests/ui/byte_char_slices.rs:8:19
24+
|
25+
LL | let escapes = &[b'\x42', b'E', b's', b'c'];
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"\x42Esc"`
27+
28+
error: useless use of `vec!`
29+
--> tests/ui/byte_char_slices.rs:11:16
30+
|
31+
LL | let good = vec![b'a', b'a'];
32+
| ^^^^^^^^^^^^^^^^ help: you can use an array directly: `[b'a', b'a']`
33+
|
34+
= note: `-D clippy::useless-vec` implied by `-D warnings`
35+
= help: to override `-D warnings` add `#[allow(clippy::useless_vec)]`
36+
37+
error: aborting due to 5 previous errors
38+

0 commit comments

Comments
 (0)