mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Enhance byte_char_slices to cover arrays
This commit is contained in:
@@ -1,9 +1,15 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_ast::ast::{BorrowKind, Expr, ExprKind, Mutability};
|
||||
use rustc_ast::token::{Lit, LitKind};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_expr, span_contains_cfg, span_contains_comment};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@@ -30,47 +36,73 @@
|
||||
|
||||
declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]);
|
||||
|
||||
impl EarlyLintPass for ByteCharSlice {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
impl<'tcx> LateLintPass<'tcx> for ByteCharSlice {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if !expr.span.from_expansion()
|
||||
&& let Some(slice) = is_byte_char_slices(expr)
|
||||
&& let Some((has_ref, slice)) = is_byte_char_slices(cx, expr)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
BYTE_CHAR_SLICES,
|
||||
expr.span,
|
||||
"can be more succinctly written as a byte str",
|
||||
"try",
|
||||
format!("b\"{slice}\""),
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let mut sugg = Sugg::hir_from_snippet(cx, expr, |_| {
|
||||
let mut slice = slice.iter().fold("b\"".to_owned(), |mut acc, span| {
|
||||
let snippet = snippet_with_applicability(cx, *span, "b'?'", &mut app);
|
||||
acc.push_str(match &snippet[2..snippet.len() - 1] {
|
||||
"\"" => "\\\"",
|
||||
"\\'" => "'",
|
||||
other => other,
|
||||
});
|
||||
acc
|
||||
});
|
||||
slice.push('"');
|
||||
Cow::Owned(slice)
|
||||
});
|
||||
if !has_ref && !cx.typeck_results().expr_ty_adjusted(expr).is_array_slice() {
|
||||
sugg = sugg.deref();
|
||||
}
|
||||
|
||||
diag.span_suggestion(expr.span, "try", sugg, app);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the slice is that of byte chars, and if so, builds a byte-string out of it
|
||||
fn is_byte_char_slices(expr: &Expr) -> Option<String> {
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = &expr.kind
|
||||
&& let ExprKind::Array(members) = &expr.kind
|
||||
&& !members.is_empty()
|
||||
fn is_byte_char_slices<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<(bool, Vec<Span>)> {
|
||||
let (has_ref, expr) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = expr.kind {
|
||||
(true, inner)
|
||||
} else if let Some(parent) = get_parent_expr(cx, expr) // Already checked by the parent expr.
|
||||
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind
|
||||
{
|
||||
members
|
||||
.iter()
|
||||
.map(|member| match &member.kind {
|
||||
ExprKind::Lit(Lit {
|
||||
kind: LitKind::Byte,
|
||||
symbol,
|
||||
..
|
||||
}) => Some(symbol.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.map(|maybe_quote| match maybe_quote {
|
||||
Some("\"") => Some("\\\""),
|
||||
Some("\\'") => Some("'"),
|
||||
other => other,
|
||||
})
|
||||
.collect::<Option<String>>()
|
||||
return None;
|
||||
} else {
|
||||
None
|
||||
(false, expr)
|
||||
};
|
||||
|
||||
if let ExprKind::Array(members) = expr.kind
|
||||
&& !members.is_empty()
|
||||
&& !span_contains_comment(cx, expr.span)
|
||||
&& !span_contains_cfg(cx, expr.span)
|
||||
{
|
||||
return members
|
||||
.iter()
|
||||
.try_fold(Vec::new(), |mut acc, member| {
|
||||
if let ExprKind::Lit(lit) = member.kind
|
||||
&& let LitKind::Byte(_) = lit.node
|
||||
&& expr.span.eq_ctxt(member.span)
|
||||
{
|
||||
acc.push(lit.span);
|
||||
return Some(acc);
|
||||
}
|
||||
None
|
||||
})
|
||||
.map(|s| (has_ref, s));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -515,7 +515,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
||||
Box::new(|| Box::new(visibility::Visibility)),
|
||||
Box::new(|| Box::new(multiple_bound_locations::MultipleBoundLocations)),
|
||||
Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)),
|
||||
Box::new(|| Box::new(byte_char_slices::ByteCharSlice)),
|
||||
Box::new(|| Box::new(cfg_not_test::CfgNotTest)),
|
||||
Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())),
|
||||
// add early passes here, used by `cargo dev new_lint`
|
||||
@@ -867,6 +866,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
|
||||
Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)),
|
||||
Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))),
|
||||
Box::new(move |_| Box::new(manual_noop_waker::ManualNoopWaker::new(conf))),
|
||||
Box::new(|_| Box::new(byte_char_slices::ByteCharSlice)),
|
||||
// add late passes here, used by `cargo dev new_lint`
|
||||
];
|
||||
store.late_passes.extend(late_lints);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![warn(clippy::byte_char_slices)]
|
||||
#![allow(clippy::useless_vec)]
|
||||
|
||||
fn main() {
|
||||
let bad = b"abc";
|
||||
@@ -11,7 +12,40 @@ fn main() {
|
||||
//~^ byte_char_slices
|
||||
|
||||
let good = &[b'a', 0x42];
|
||||
let good = [b'a', b'a'];
|
||||
//~^ useless_vec
|
||||
let good: u8 = [b'a', b'c'].into_iter().sum();
|
||||
let good = vec![b'a', b'a'];
|
||||
}
|
||||
|
||||
fn takes_array_ref(_: &[u8; 2]) {}
|
||||
|
||||
fn takes_array_ref_ref(_: &&[u8; 2]) {}
|
||||
|
||||
fn issue16759(bytes: [u32; 3]) {
|
||||
const START: u32 = u32::from_le_bytes(*b"WORK");
|
||||
//~^ byte_char_slices
|
||||
|
||||
let auto_deref_to_slice: u8 = b"ac".iter().copied().sum();
|
||||
//~^ byte_char_slices
|
||||
|
||||
let with_comment = [
|
||||
// 1 2 3
|
||||
b'a', b'b', b'c', // x
|
||||
b'd', b'e', b'f', // 2x
|
||||
b'g', b'h', b'i', // 3x
|
||||
];
|
||||
let with_cfg = [
|
||||
b'a',
|
||||
b'b',
|
||||
b'c',
|
||||
#[cfg(feature = "foo")]
|
||||
b'd',
|
||||
];
|
||||
|
||||
let with_escape: u8 = b"'\"\x00\n\\".iter().copied().sum();
|
||||
//~^ byte_char_slices
|
||||
|
||||
takes_array_ref(b"ab");
|
||||
//~^ byte_char_slices
|
||||
|
||||
takes_array_ref_ref(&b"ab");
|
||||
//~^ byte_char_slices
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![warn(clippy::byte_char_slices)]
|
||||
#![allow(clippy::useless_vec)]
|
||||
|
||||
fn main() {
|
||||
let bad = &[b'a', b'b', b'c'];
|
||||
@@ -12,6 +13,39 @@ fn main() {
|
||||
|
||||
let good = &[b'a', 0x42];
|
||||
let good = vec![b'a', b'a'];
|
||||
//~^ useless_vec
|
||||
let good: u8 = [b'a', b'c'].into_iter().sum();
|
||||
}
|
||||
|
||||
fn takes_array_ref(_: &[u8; 2]) {}
|
||||
|
||||
fn takes_array_ref_ref(_: &&[u8; 2]) {}
|
||||
|
||||
fn issue16759(bytes: [u32; 3]) {
|
||||
const START: u32 = u32::from_le_bytes([b'W', b'O', b'R', b'K']);
|
||||
//~^ byte_char_slices
|
||||
|
||||
let auto_deref_to_slice: u8 = [b'a', b'c'].iter().copied().sum();
|
||||
//~^ byte_char_slices
|
||||
|
||||
let with_comment = [
|
||||
// 1 2 3
|
||||
b'a', b'b', b'c', // x
|
||||
b'd', b'e', b'f', // 2x
|
||||
b'g', b'h', b'i', // 3x
|
||||
];
|
||||
let with_cfg = [
|
||||
b'a',
|
||||
b'b',
|
||||
b'c',
|
||||
#[cfg(feature = "foo")]
|
||||
b'd',
|
||||
];
|
||||
|
||||
let with_escape: u8 = [b'\'', b'"', b'\x00', b'\n', b'\\'].iter().copied().sum();
|
||||
//~^ byte_char_slices
|
||||
|
||||
takes_array_ref(&[b'a', b'b']);
|
||||
//~^ byte_char_slices
|
||||
|
||||
takes_array_ref_ref(&&[b'a', b'b']);
|
||||
//~^ byte_char_slices
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:4:15
|
||||
--> tests/ui/byte_char_slices.rs:5:15
|
||||
|
|
||||
LL | let bad = &[b'a', b'b', b'c'];
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: try: `b"abc"`
|
||||
@@ -8,31 +8,52 @@ LL | let bad = &[b'a', b'b', b'c'];
|
||||
= help: to override `-D warnings` add `#[allow(clippy::byte_char_slices)]`
|
||||
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:6:18
|
||||
--> tests/ui/byte_char_slices.rs:7:18
|
||||
|
|
||||
LL | let quotes = &[b'"', b'H', b'i'];
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: try: `b"\"Hi"`
|
||||
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:8:18
|
||||
--> tests/ui/byte_char_slices.rs:9:18
|
||||
|
|
||||
LL | let quotes = &[b'\'', b'S', b'u', b'p'];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'Sup"`
|
||||
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:10:19
|
||||
--> tests/ui/byte_char_slices.rs:11:19
|
||||
|
|
||||
LL | let escapes = &[b'\x42', b'E', b's', b'c'];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"\x42Esc"`
|
||||
|
||||
error: useless use of `vec!`
|
||||
--> tests/ui/byte_char_slices.rs:14:16
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:23:43
|
||||
|
|
||||
LL | let good = vec![b'a', b'a'];
|
||||
| ^^^^^^^^^^^^^^^^ help: you can use an array directly: `[b'a', b'a']`
|
||||
LL | const START: u32 = u32::from_le_bytes([b'W', b'O', b'R', b'K']);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `*b"WORK"`
|
||||
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:26:35
|
||||
|
|
||||
= note: `-D clippy::useless-vec` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::useless_vec)]`
|
||||
LL | let auto_deref_to_slice: u8 = [b'a', b'c'].iter().copied().sum();
|
||||
| ^^^^^^^^^^^^ help: try: `b"ac"`
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:43:27
|
||||
|
|
||||
LL | let with_escape: u8 = [b'\'', b'"', b'\x00', b'\n', b'\\'].iter().copied().sum();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'\"\x00\n\\"`
|
||||
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:46:21
|
||||
|
|
||||
LL | takes_array_ref(&[b'a', b'b']);
|
||||
| ^^^^^^^^^^^^^ help: try: `b"ab"`
|
||||
|
||||
error: can be more succinctly written as a byte str
|
||||
--> tests/ui/byte_char_slices.rs:49:26
|
||||
|
|
||||
LL | takes_array_ref_ref(&&[b'a', b'b']);
|
||||
| ^^^^^^^^^^^^^ help: try: `b"ab"`
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![warn(clippy::ptr_offset_by_literal)]
|
||||
#![allow(clippy::inconsistent_digit_grouping)]
|
||||
#![allow(clippy::inconsistent_digit_grouping, clippy::byte_char_slices)]
|
||||
|
||||
fn main() {
|
||||
let arr = [b'a', b'b', b'c'];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![warn(clippy::ptr_offset_by_literal)]
|
||||
#![allow(clippy::inconsistent_digit_grouping)]
|
||||
#![allow(clippy::inconsistent_digit_grouping, clippy::byte_char_slices)]
|
||||
|
||||
fn main() {
|
||||
let arr = [b'a', b'b', b'c'];
|
||||
|
||||
Reference in New Issue
Block a user