Skip to content

Commit 1ff8162

Browse files
committed
uninhabited_reference: new lint
1 parent 57397a5 commit 1ff8162

8 files changed

+115
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5581,6 +5581,7 @@ Released 2018-09-13
55815581
[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
55825582
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
55835583
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
5584+
[`uninhabited_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninhabited_reference
55845585
[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
55855586
[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
55865587
[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
677677
crate::unicode::INVISIBLE_CHARACTERS_INFO,
678678
crate::unicode::NON_ASCII_LITERAL_INFO,
679679
crate::unicode::UNICODE_NOT_NFC_INFO,
680+
crate::uninhabited_reference::UNINHABITED_REFERENCE_INFO,
680681
crate::uninit_vec::UNINIT_VEC_INFO,
681682
crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO,
682683
crate::unit_types::LET_UNIT_VALUE_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ mod tuple_array_conversions;
325325
mod types;
326326
mod undocumented_unsafe_blocks;
327327
mod unicode;
328+
mod uninhabited_reference;
328329
mod uninit_vec;
329330
mod unit_return_expecting_ord;
330331
mod unit_types;
@@ -1069,6 +1070,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
10691070
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
10701071
store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
10711072
store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes));
1073+
store.register_late_pass(|_| Box::new(uninhabited_reference::UninhabitedReference));
10721074
// add lints here, do not remove this comment, it's used in `new_lint`
10731075
}
10741076

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use rustc_hir::intravisit::FnKind;
3+
use rustc_hir::{Body, ExprKind, FnDecl, FnRetTy, TyKind, UnOp};
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 `!` and
11+
/// warns when those are either dereferenced or returned from a function.
12+
///
13+
/// ### Why is this bad?
14+
/// Dereferencing a reference to an uninhabited type would create
15+
/// an instance of such a type, which cannot exist. This constitutes
16+
/// undefined behaviour. Such a reference could have been created
17+
/// by `unsafe` code.
18+
///
19+
/// ### Example
20+
/// The following function can return a reference to an uninhabited type
21+
/// (`Infallible`) because it uses `unsafe` code to create it. However,
22+
/// the user of such a function could dereference the return value and
23+
/// trigger an undefined behaviour from safe code.
24+
///
25+
/// ```no_run
26+
/// fn create_ref() -> &'static std::convert::Infallible {
27+
/// unsafe { std::mem::transmute(&()) }
28+
/// }
29+
/// ```
30+
#[clippy::version = "1.76.0"]
31+
pub UNINHABITED_REFERENCE,
32+
suspicious,
33+
"reference to uninhabited type"
34+
}
35+
36+
declare_lint_pass!(UninhabitedReference => [UNINHABITED_REFERENCE]);
37+
38+
impl LateLintPass<'_> for UninhabitedReference {
39+
fn check_expr_post(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) {
40+
if expr.span.from_expansion() {
41+
return;
42+
}
43+
44+
if let ExprKind::Unary(UnOp::Deref, _) = expr.kind {
45+
let ty = cx.typeck_results().expr_ty_adjusted(expr);
46+
if ty.is_privately_uninhabited(cx.tcx, cx.param_env) {
47+
span_lint(
48+
cx,
49+
UNINHABITED_REFERENCE,
50+
expr.span,
51+
"dereferencing a reference to an uninhabited type is undefined behavior",
52+
);
53+
}
54+
}
55+
}
56+
57+
fn check_fn(
58+
&mut self,
59+
cx: &LateContext<'_>,
60+
kind: FnKind<'_>,
61+
fndecl: &'_ FnDecl<'_>,
62+
_: &'_ Body<'_>,
63+
span: rustc_span::Span,
64+
_: rustc_span::def_id::LocalDefId,
65+
) {
66+
if span.from_expansion() || matches!(kind, FnKind::Closure) {
67+
return;
68+
}
69+
if let FnRetTy::Return(hir_ty) = fndecl.output
70+
&& let TyKind::Ref(_, mut_ty) = hir_ty.kind
71+
&& hir_ty_to_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env)
72+
{
73+
span_lint(
74+
cx,
75+
UNINHABITED_REFERENCE,
76+
hir_ty.span,
77+
"dereferencing a reference to an uninhabited type would be undefined behavior",
78+
);
79+
}
80+
}
81+
}

tests/ui/infallible_destructuring_match.fixed

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(exhaustive_patterns, never_type)]
22
#![allow(dead_code, unreachable_code, unused_variables)]
3-
#![allow(clippy::let_and_return)]
3+
#![allow(clippy::let_and_return, clippy::uninhabited_reference)]
44

55
enum SingleVariantEnum {
66
Variant(i32),

tests/ui/infallible_destructuring_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(exhaustive_patterns, never_type)]
22
#![allow(dead_code, unreachable_code, unused_variables)]
3-
#![allow(clippy::let_and_return)]
3+
#![allow(clippy::let_and_return, clippy::uninhabited_reference)]
44

55
enum SingleVariantEnum {
66
Variant(i32),

tests/ui/uninhabited_reference.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#![warn(clippy::uninhabited_reference)]
2+
#![feature(never_type)]
3+
4+
fn ret_uninh_ref() -> &'static std::convert::Infallible {
5+
unsafe { std::mem::transmute(&()) }
6+
}
7+
8+
fn main() {
9+
let x = ret_uninh_ref();
10+
let _ = *x;
11+
}

tests/ui/uninhabited_reference.stderr

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: dereferencing a reference to an uninhabited type would be undefined behavior
2+
--> $DIR/uninhabited_reference.rs:4:23
3+
|
4+
LL | fn ret_uninh_ref() -> &'static std::convert::Infallible {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::uninhabited-reference` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::uninhabited_reference)]`
9+
10+
error: dereferencing a reference to an uninhabited type is undefined behavior
11+
--> $DIR/uninhabited_reference.rs:10:13
12+
|
13+
LL | let _ = *x;
14+
| ^^
15+
16+
error: aborting due to 2 previous errors
17+

0 commit comments

Comments
 (0)