rustc_lint/
invalid_from_utf8.rs1use std::str::Utf8Error;
2
3use rustc_ast::LitKind;
4use rustc_hir::{Expr, ExprKind};
5use rustc_session::{declare_lint, declare_lint_pass};
6use rustc_span::source_map::Spanned;
7use rustc_span::sym;
8
9use crate::lints::InvalidFromUtf8Diag;
10use crate::{LateContext, LateLintPass, LintContext};
11
12declare_lint! {
13 pub INVALID_FROM_UTF8_UNCHECKED,
33 Deny,
34 "using a non UTF-8 literal in `std::str::from_utf8_unchecked`"
35}
36
37declare_lint! {
38 pub INVALID_FROM_UTF8,
56 Warn,
57 "using a non UTF-8 literal in `std::str::from_utf8`"
58}
59
60declare_lint_pass!(InvalidFromUtf8 => [INVALID_FROM_UTF8_UNCHECKED, INVALID_FROM_UTF8]);
61
62impl<'tcx> LateLintPass<'tcx> for InvalidFromUtf8 {
63 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
64 if let ExprKind::Call(path, [arg]) = expr.kind
65 && let ExprKind::Path(ref qpath) = path.kind
66 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
67 && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
68 && [
69 sym::str_from_utf8,
70 sym::str_from_utf8_mut,
71 sym::str_from_utf8_unchecked,
72 sym::str_from_utf8_unchecked_mut,
73 ]
74 .contains(&diag_item)
75 {
76 let lint = |label, utf8_error: Utf8Error| {
77 let method = diag_item.as_str().strip_prefix("str_").unwrap();
78 let method = format!("std::str::{method}");
79 let valid_up_to = utf8_error.valid_up_to();
80 let is_unchecked_variant = diag_item.as_str().contains("unchecked");
81
82 cx.emit_span_lint(
83 if is_unchecked_variant {
84 INVALID_FROM_UTF8_UNCHECKED
85 } else {
86 INVALID_FROM_UTF8
87 },
88 expr.span,
89 if is_unchecked_variant {
90 InvalidFromUtf8Diag::Unchecked { method, valid_up_to, label }
91 } else {
92 InvalidFromUtf8Diag::Checked { method, valid_up_to, label }
93 },
94 )
95 };
96
97 let mut init = cx.expr_or_init_with_outside_body(arg);
98 while let ExprKind::AddrOf(.., inner) = init.kind {
99 init = cx.expr_or_init_with_outside_body(inner);
100 }
101 match init.kind {
102 ExprKind::Lit(Spanned { node: lit, .. }) => {
103 if let LitKind::ByteStr(bytes, _) = &lit
104 && let Err(utf8_error) = std::str::from_utf8(bytes)
105 {
106 lint(init.span, utf8_error);
107 }
108 }
109 ExprKind::Array(args) => {
110 let elements = args
111 .iter()
112 .map(|e| match &e.kind {
113 ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
114 LitKind::Byte(b) => Some(*b),
115 LitKind::Int(b, _) => Some(b.get() as u8),
116 _ => None,
117 },
118 _ => None,
119 })
120 .collect::<Option<Vec<_>>>();
121
122 if let Some(elements) = elements
123 && let Err(utf8_error) = std::str::from_utf8(&elements)
124 {
125 lint(init.span, utf8_error);
126 }
127 }
128 _ => {}
129 }
130 }
131 }
132}