]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs
:arrow_up: rust-analyzer
[rust.git] / src / tools / clippy / clippy_lints / src / invalid_utf8_in_unchecked.rs
1 use clippy_utils::diagnostics::span_lint;
2 use clippy_utils::{match_function_call, paths};
3 use rustc_ast::{BorrowKind, LitKind};
4 use rustc_hir::{Expr, ExprKind};
5 use rustc_lint::{LateContext, LateLintPass};
6 use rustc_session::{declare_lint_pass, declare_tool_lint};
7 use rustc_span::source_map::Spanned;
8 use rustc_span::Span;
9
10 declare_clippy_lint! {
11     /// ### What it does
12     /// Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal
13     ///
14     /// ### Why is this bad?
15     /// Creating such a `str` would result in undefined behavior
16     ///
17     /// ### Example
18     /// ```rust
19     /// # #[allow(unused)]
20     /// unsafe {
21     ///     std::str::from_utf8_unchecked(b"cl\x82ippy");
22     /// }
23     /// ```
24     #[clippy::version = "1.64.0"]
25     pub INVALID_UTF8_IN_UNCHECKED,
26     correctness,
27     "using a non UTF-8 literal in `std::std::from_utf8_unchecked`"
28 }
29 declare_lint_pass!(InvalidUtf8InUnchecked => [INVALID_UTF8_IN_UNCHECKED]);
30
31 impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked {
32     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
33         if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) {
34             match &arg.kind {
35                 ExprKind::Lit(Spanned { node: lit, .. }) => {
36                     if let LitKind::ByteStr(bytes) = &lit
37                         && std::str::from_utf8(bytes).is_err()
38                     {
39                         lint(cx, expr.span);
40                     }
41                 },
42                 ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
43                     let elements = args.iter().map(|e|{
44                         match &e.kind {
45                             ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
46                                 LitKind::Byte(b) => Some(*b),
47                                 #[allow(clippy::cast_possible_truncation)]
48                                 LitKind::Int(b, _) => Some(*b as u8),
49                                 _ => None
50                             }
51                             _ => None
52                         }
53                     }).collect::<Option<Vec<_>>>();
54
55                     if let Some(elements) = elements
56                         && std::str::from_utf8(&elements).is_err()
57                     {
58                         lint(cx, expr.span);
59                     }
60                 }
61                 _ => {}
62             }
63         }
64     }
65 }
66
67 fn lint(cx: &LateContext<'_>, span: Span) {
68     span_lint(
69         cx,
70         INVALID_UTF8_IN_UNCHECKED,
71         span,
72         "non UTF-8 literal in `std::str::from_utf8_unchecked`",
73     );
74 }