Skip to content

Commit 9b34cf5

Browse files
committed
uninhabited_reference: new lint
1 parent 2c56b4f commit 9b34cf5

9 files changed

+138
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
}

tests/ui/error_impl_error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(unused)]
1+
#![allow(unused, clippy::uninhabited_reference)]
22
#![warn(clippy::error_impl_error)]
33
#![no_main]
44

tests/ui/infallible_destructuring_match.fixed

Lines changed: 1 addition & 1 deletion
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#![warn(clippy::uninhabited_reference)]
2+
#![allow(unused, clippy::diverging_sub_expression, clippy::trivially_copy_pass_by_ref)]
3+
#![feature(never_type)]
4+
5+
fn f1(x: &!) {
6+
todo!()
7+
}
8+
9+
fn f2(x: &mut !) {
10+
todo!()
11+
}
12+
13+
fn g(x: &std::convert::Infallible) {
14+
todo!()
15+
}
16+
17+
struct S {
18+
a: std::convert::Infallible,
19+
b: u32,
20+
}
21+
22+
fn h(x: &S) {
23+
todo!()
24+
}
25+
26+
fn i() -> &'static ! {
27+
let x: &! = panic!();
28+
x
29+
}
30+
31+
fn main() {}

tests/ui/uninhabited_reference.stderr

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: dereferencing a reference to an uninhabited type would be undefined behavior
2+
--> $DIR/uninhabited_reference.rs:5:10
3+
|
4+
LL | fn f1(x: &!) {
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 would be undefined behavior
11+
--> $DIR/uninhabited_reference.rs:9:10
12+
|
13+
LL | fn f2(x: &mut !) {
14+
| ^^^^^^
15+
16+
error: dereferencing a reference to an uninhabited type would be undefined behavior
17+
--> $DIR/uninhabited_reference.rs:13:9
18+
|
19+
LL | fn g(x: &std::convert::Infallible) {
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
21+
22+
error: dereferencing a reference to an uninhabited type would be undefined behavior
23+
--> $DIR/uninhabited_reference.rs:22:9
24+
|
25+
LL | fn h(x: &S) {
26+
| ^^
27+
28+
error: dereferencing a reference to an uninhabited type would be undefined behavior
29+
--> $DIR/uninhabited_reference.rs:26:11
30+
|
31+
LL | fn i() -> &'static ! {
32+
| ^^^^^^^^^^
33+
34+
error: aborting due to 5 previous errors
35+

0 commit comments

Comments
 (0)