|
| 1 | +use clippy_utils::diagnostics::span_lint; |
| 2 | +use rustc_hir::intravisit::FnKind; |
| 3 | +use rustc_hir::{Body, FnDecl, FnRetTy, TyKind}; |
| 4 | +use rustc_hir_analysis::hir_ty_to_ty; |
| 5 | +use rustc_lint::{LateContext, LateLintPass}; |
| 6 | +use rustc_session::declare_lint_pass; |
| 7 | + |
| 8 | +declare_clippy_lint! { |
| 9 | + /// ### What it does |
| 10 | + /// It detects references to uninhabited types, such as `!`. |
| 11 | + /// |
| 12 | + /// ### Why is this bad? |
| 13 | + /// Instances of uninhabited types cannot be created. Creating |
| 14 | + /// a reference on such a type (which can be done in `unsafe` code) |
| 15 | + /// would create a risk of dereferencing it, which would be |
| 16 | + /// undefined behavior. |
| 17 | + /// |
| 18 | + /// ### Example |
| 19 | + /// The following function could dereference its parameter `x` without |
| 20 | + /// needing `unsafe`. This would be undefined behavior as no instance of |
| 21 | + /// type `std::convert::Infallible` can ever exist. |
| 22 | + /// ```no_run |
| 23 | + /// fn f(x: &std::convert::Infallible) { todo!() } |
| 24 | + /// ``` |
| 25 | + #[clippy::version = "1.76.0"] |
| 26 | + pub UNINHABITED_REFERENCE, |
| 27 | + suspicious, |
| 28 | + "reference on uninhabited type" |
| 29 | +} |
| 30 | + |
| 31 | +declare_lint_pass!(UninhabitedReference => [UNINHABITED_REFERENCE]); |
| 32 | + |
| 33 | +// This lint checks function parameters and return: this is where references to uninhabited types |
| 34 | +// are most susceptible to be exchanged between safe and unsafe parts of the program. |
| 35 | +impl LateLintPass<'_> for UninhabitedReference { |
| 36 | + fn check_fn( |
| 37 | + &mut self, |
| 38 | + cx: &LateContext<'_>, |
| 39 | + kind: FnKind<'_>, |
| 40 | + fndecl: &'_ FnDecl<'_>, |
| 41 | + _: &'_ Body<'_>, |
| 42 | + span: rustc_span::Span, |
| 43 | + _: rustc_span::def_id::LocalDefId, |
| 44 | + ) { |
| 45 | + if span.from_expansion() || matches!(kind, FnKind::Closure) { |
| 46 | + return; |
| 47 | + } |
| 48 | + let ret_ty = match fndecl.output { |
| 49 | + FnRetTy::Return(ty) => Some(ty), |
| 50 | + FnRetTy::DefaultReturn(_) => None, |
| 51 | + }; |
| 52 | + for hir_ty in fndecl.inputs.iter().chain(ret_ty) { |
| 53 | + if let TyKind::Ref(_, mut_ty) = hir_ty.kind |
| 54 | + && hir_ty_to_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env) |
| 55 | + { |
| 56 | + span_lint( |
| 57 | + cx, |
| 58 | + UNINHABITED_REFERENCE, |
| 59 | + hir_ty.span, |
| 60 | + "dereferencing a reference to an uninhabited type would be undefined behavior", |
| 61 | + ); |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | +} |
0 commit comments