diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index d2dc47af7ac4f..b559c427f6593 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1474,34 +1474,27 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
typ = c.type_.print(cx),
);
- // FIXME: The code below now prints
- // ` = _; // 100i32`
- // if the expression is
- // `50 + 50`
- // which looks just wrong.
- // Should we print
- // ` = 100i32;`
- // instead?
-
let value = c.value(tcx);
let is_literal = c.is_literal(tcx);
let expr = c.expr(tcx);
- if value.is_some() || is_literal {
+
+ if is_literal {
write!(w, " = {expr};", expr = Escape(&expr));
+ } else if let Some(ref value) = value {
+ write!(w, " = {value};", value = Escape(value));
} else {
w.write_str(";");
}
- if !is_literal {
- if let Some(value) = &value {
- let value_lowercase = value.to_lowercase();
- let expr_lowercase = expr.to_lowercase();
+ let value_lowercase = value.as_ref().map(|s| s.to_lowercase());
+ let expr_lowercase = Some(expr.to_lowercase());
- if value_lowercase != expr_lowercase
- && value_lowercase.trim_end_matches("i32") != expr_lowercase
- {
- write!(w, " // {value}", value = Escape(value));
- }
+ if value_lowercase != expr_lowercase
+ && value_lowercase.as_ref().map(|s| s.trim_end_matches("i32"))
+ != expr_lowercase.as_deref()
+ {
+ if let Some(ref value) = value {
+ write!(w, " // {value}", value = Escape(value.as_str()));
}
}
});
diff --git a/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs b/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs
new file mode 100644
index 0000000000000..6a4861747d267
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs
@@ -0,0 +1,74 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_function_call, paths};
+use rustc_ast::{BorrowKind, LitKind};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal
+ ///
+ /// ### Why is this bad?
+ /// Creating such a `str` would result in undefined behavior
+ ///
+ /// ### Example
+ /// ```rust
+ /// # #[allow(unused)]
+ /// unsafe {
+ /// std::str::from_utf8_unchecked(b"cl\x82ippy");
+ /// }
+ /// ```
+ #[clippy::version = "1.64.0"]
+ pub INVALID_UTF8_IN_UNCHECKED,
+ correctness,
+ "using a non UTF-8 literal in `std::std::from_utf8_unchecked`"
+}
+declare_lint_pass!(InvalidUtf8InUnchecked => [INVALID_UTF8_IN_UNCHECKED]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) {
+ match &arg.kind {
+ ExprKind::Lit(Spanned { node: lit, .. }) => {
+ if let LitKind::ByteStr(bytes, _) = &lit
+ && std::str::from_utf8(bytes).is_err()
+ {
+ lint(cx, expr.span);
+ }
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
+ let elements = args.iter().map(|e|{
+ match &e.kind {
+ ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
+ LitKind::Byte(b) => Some(*b),
+ #[allow(clippy::cast_possible_truncation)]
+ LitKind::Int(b, _) => Some(*b as u8),
+ _ => None
+ }
+ _ => None
+ }
+ }).collect::