2023-12-16 07:12:50 -06:00
|
|
|
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,
|
2023-12-28 12:33:07 -06:00
|
|
|
nursery,
|
2023-12-16 07:12:50 -06:00
|
|
|
"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",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-01 08:23:51 -06:00
|
|
|
fn check_fn<'tcx>(
|
2023-12-16 07:12:50 -06:00
|
|
|
&mut self,
|
2023-02-01 08:23:51 -06:00
|
|
|
cx: &LateContext<'tcx>,
|
2023-12-16 07:12:50 -06:00
|
|
|
kind: FnKind<'_>,
|
2023-02-01 08:23:51 -06:00
|
|
|
fndecl: &'_ FnDecl<'tcx>,
|
2023-12-16 07:12:50 -06:00
|
|
|
_: &'_ 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",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|