use clippy_utils::diagnostics::span_lint; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnRetTy, TyKind, UnOp}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::Span; declare_clippy_lint! { /// ### What it does /// It detects references to uninhabited types, such as `!` and /// warns when those are either dereferenced or returned from a function. /// /// ### Why is this bad? /// Dereferencing a reference to an uninhabited type would create /// an instance of such a type, which cannot exist. This constitutes /// undefined behaviour. Such a reference could have been created /// by `unsafe` code. /// /// ### Example /// The following function can return a reference to an uninhabited type /// (`Infallible`) because it uses `unsafe` code to create it. However, /// the user of such a function could dereference the return value and /// trigger an undefined behavior from safe code. /// /// ```no_run /// fn create_ref() -> &'static std::convert::Infallible { /// unsafe { std::mem::transmute(&()) } /// } /// ``` #[clippy::version = "1.76.0"] pub UNINHABITED_REFERENCES, nursery, "reference to uninhabited type" } declare_lint_pass!(UninhabitedReferences => [UNINHABITED_REFERENCES]); impl LateLintPass<'_> for UninhabitedReferences { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if in_external_macro(cx.tcx.sess, expr.span) { return; } if let ExprKind::Unary(UnOp::Deref, _) = expr.kind { let ty = cx.typeck_results().expr_ty_adjusted(expr); if ty.is_privately_uninhabited(cx.tcx, cx.param_env) { span_lint( cx, UNINHABITED_REFERENCES, expr.span, "dereferencing a reference to an uninhabited type is undefined behavior", ); } } } fn check_fn( &mut self, cx: &LateContext<'_>, kind: FnKind<'_>, fndecl: &'_ FnDecl<'_>, _: &'_ Body<'_>, span: Span, _: LocalDefId, ) { if in_external_macro(cx.tcx.sess, span) || matches!(kind, FnKind::Closure) { return; } if let FnRetTy::Return(hir_ty) = fndecl.output && let TyKind::Ref(_, mut_ty) = hir_ty.kind && hir_ty_to_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env) { span_lint( cx, UNINHABITED_REFERENCES, hir_ty.span, "dereferencing a reference to an uninhabited type would be undefined behavior", ); } } }