| 
 | 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 | +}  | 
0 commit comments