Skip to content

Commit

Permalink
uninhabited_reference: new lint
Browse files Browse the repository at this point in the history
  • Loading branch information
samueltardieu committed Nov 26, 2023
1 parent 7af811b commit d805db1
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5581,6 +5581,7 @@ Released 2018-09-13
[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
[`uninhabited_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninhabited_reference
[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::unicode::INVISIBLE_CHARACTERS_INFO,
crate::unicode::NON_ASCII_LITERAL_INFO,
crate::unicode::UNICODE_NOT_NFC_INFO,
crate::uninhabited_reference::UNINHABITED_REFERENCE_INFO,
crate::uninit_vec::UNINIT_VEC_INFO,
crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO,
crate::unit_types::LET_UNIT_VALUE_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ mod tuple_array_conversions;
mod types;
mod undocumented_unsafe_blocks;
mod unicode;
mod uninhabited_reference;
mod uninit_vec;
mod unit_return_expecting_ord;
mod unit_types;
Expand Down Expand Up @@ -1069,6 +1070,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes));
store.register_late_pass(|_| Box::new(uninhabited_reference::UninhabitedReference));
// add lints here, do not remove this comment, it's used in `new_lint`
}

Expand Down
65 changes: 65 additions & 0 deletions clippy_lints/src/uninhabited_reference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use clippy_utils::diagnostics::span_lint;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, FnRetTy, TyKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
/// It detects references to uninhabited types, such as `!`.
///
/// ### Why is this bad?
/// Instances of uninhabited types cannot be created. Creating
/// a reference on such a type (which can be done in `unsafe` code)
/// would create a risk of dereferencing it, which would be
/// undefined behavior.
///
/// ### Example
/// The following function could dereference its parameter `x` without
/// needing `unsafe`. This would be undefined behavior as no instance of
/// type `std::convert::Infallible` can ever exist.
/// ```no_run
/// fn f(x: &std::convert::Infallible) { todo!() }
/// ```
#[clippy::version = "1.76.0"]
pub UNINHABITED_REFERENCE,
suspicious,
"reference on uninhabited type"
}

declare_lint_pass!(UninhabitedReference => [UNINHABITED_REFERENCE]);

// This lint checks function parameters and return: this is where references to uninhabited types
// are most susceptible to be exchanged between safe and unsafe parts of the program.
impl LateLintPass<'_> for UninhabitedReference {
fn check_fn(
&mut self,
cx: &LateContext<'_>,
kind: FnKind<'_>,
fndecl: &'_ FnDecl<'_>,
_: &'_ Body<'_>,
span: rustc_span::Span,
_: rustc_span::def_id::LocalDefId,
) {
if span.from_expansion() || matches!(kind, FnKind::Closure) {
return;
}
let ret_ty = match fndecl.output {
FnRetTy::Return(ty) => Some(ty),
FnRetTy::DefaultReturn(_) => None,
};
for hir_ty in fndecl.inputs.iter().chain(ret_ty) {
if 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_REFERENCE,
hir_ty.span,
"dereferencing a reference to an uninhabited type would be undefined behavior",
);
}
}
}
}
2 changes: 1 addition & 1 deletion tests/ui/error_impl_error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![allow(unused)]
#![allow(unused, clippy::uninhabited_reference)]
#![warn(clippy::error_impl_error)]
#![no_main]

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/infallible_destructuring_match.fixed
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![feature(exhaustive_patterns, never_type)]
#![allow(dead_code, unreachable_code, unused_variables)]
#![allow(clippy::let_and_return)]
#![allow(clippy::let_and_return, clippy::uninhabited_reference)]

enum SingleVariantEnum {
Variant(i32),
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/infallible_destructuring_match.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![feature(exhaustive_patterns, never_type)]
#![allow(dead_code, unreachable_code, unused_variables)]
#![allow(clippy::let_and_return)]
#![allow(clippy::let_and_return, clippy::uninhabited_reference)]

enum SingleVariantEnum {
Variant(i32),
Expand Down
31 changes: 31 additions & 0 deletions tests/ui/uninhabited_reference.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#![warn(clippy::uninhabited_reference)]
#![allow(unused, clippy::diverging_sub_expression, clippy::trivially_copy_pass_by_ref)]
#![feature(never_type)]

fn f1(x: &!) {
todo!()
}

fn f2(x: &mut !) {
todo!()
}

fn g(x: &std::convert::Infallible) {
todo!()
}

struct S {
a: std::convert::Infallible,
b: u32,
}

fn h(x: &S) {
todo!()
}

fn i() -> &'static ! {
let x: &! = panic!();
x
}

fn main() {}
35 changes: 35 additions & 0 deletions tests/ui/uninhabited_reference.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
error: dereferencing a reference to an uninhabited type would be undefined behavior
--> $DIR/uninhabited_reference.rs:5:10
|
LL | fn f1(x: &!) {
| ^^
|
= note: `-D clippy::uninhabited-reference` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::uninhabited_reference)]`

error: dereferencing a reference to an uninhabited type would be undefined behavior
--> $DIR/uninhabited_reference.rs:9:10
|
LL | fn f2(x: &mut !) {
| ^^^^^^

error: dereferencing a reference to an uninhabited type would be undefined behavior
--> $DIR/uninhabited_reference.rs:13:9
|
LL | fn g(x: &std::convert::Infallible) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error: dereferencing a reference to an uninhabited type would be undefined behavior
--> $DIR/uninhabited_reference.rs:22:9
|
LL | fn h(x: &S) {
| ^^

error: dereferencing a reference to an uninhabited type would be undefined behavior
--> $DIR/uninhabited_reference.rs:26:11
|
LL | fn i() -> &'static ! {
| ^^^^^^^^^^

error: aborting due to 5 previous errors

0 comments on commit d805db1

Please sign in to comment.