From 137fdb256f004e0f82f39852cc8cf1b771c2bc17 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 26 Jul 2026 18:41:28 -0400 Subject: [PATCH 1/5] Rename module --- compiler/rustc_lint/src/{c_void_returns.rs => c_void.rs} | 0 compiler/rustc_lint/src/lib.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename compiler/rustc_lint/src/{c_void_returns.rs => c_void.rs} (100%) diff --git a/compiler/rustc_lint/src/c_void_returns.rs b/compiler/rustc_lint/src/c_void.rs similarity index 100% rename from compiler/rustc_lint/src/c_void_returns.rs rename to compiler/rustc_lint/src/c_void.rs diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 244db06a11d60..5a99a841cfd42 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -33,7 +33,7 @@ mod async_closures; mod async_fn_in_trait; mod autorefs; pub mod builtin; -mod c_void_returns; +mod c_void; mod context; mod dangling; mod default_could_be_derived; @@ -88,7 +88,7 @@ use async_closures::AsyncClosureUsage; use async_fn_in_trait::AsyncFnInTrait; use autorefs::*; use builtin::*; -use c_void_returns::*; +use c_void::*; use dangling::*; use default_could_be_derived::DefaultCouldBeDerived; use deref_into_dyn_supertrait::*; From 29499564183e679a6c34378b042ab70a697fe4b9 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 26 Jul 2026 21:37:32 -0400 Subject: [PATCH 2/5] Lint on references to `c_void` Also, fix false negatives in `c_void_returns`. --- compiler/rustc_lint/src/c_void.rs | 76 ++++++++++++++++--- compiler/rustc_lint/src/lib.rs | 1 + compiler/rustc_lint/src/lints.rs | 13 +++- src/tools/miri/src/shims/native_lib/mod.rs | 2 +- tests/ui/lint/c-void-references.alias.stderr | 61 +++++++++++++++ tests/ui/lint/c-void-references.direct.stderr | 61 +++++++++++++++ tests/ui/lint/c-void-references.rs | 39 ++++++++++ ...rns.stderr => c-void-returns.alias.stderr} | 8 +- tests/ui/lint/c-void-returns.direct.stderr | 38 ++++++++++ tests/ui/lint/c-void-returns.rs | 10 ++- 10 files changed, 292 insertions(+), 17 deletions(-) create mode 100644 tests/ui/lint/c-void-references.alias.stderr create mode 100644 tests/ui/lint/c-void-references.direct.stderr create mode 100644 tests/ui/lint/c-void-references.rs rename tests/ui/lint/{c-void-returns.stderr => c-void-returns.alias.stderr} (89%) create mode 100644 tests/ui/lint/c-void-returns.direct.stderr diff --git a/compiler/rustc_lint/src/c_void.rs b/compiler/rustc_lint/src/c_void.rs index f4dd260dd5ae4..6c4b17f35ab07 100644 --- a/compiler/rustc_lint/src/c_void.rs +++ b/compiler/rustc_lint/src/c_void.rs @@ -1,14 +1,33 @@ use rustc_abi::ExternAbi; -use rustc_hir::def::Res; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{self as hir, LangItem}; +use rustc_middle::ty; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; -use crate::lints::{CVoidReturn, ExternCVoidReturn}; +use crate::lints::{CVoidReference, CVoidReturn, ExternCVoidReturn}; use crate::{LateContext, LateLintPass, LintContext}; +fn is_c_void(cx: &LateContext<'_>, ty: hir::Ty<'_>) -> bool { + if let hir::TyKind::Path(qpath) = ty.kind + && let Res::Def(.., def_id) = cx.qpath_res(&qpath, ty.hir_id) + { + // need to look through type aliases (like `std::os::raw::c_void`) + let def_id = if DefKind::TyAlias == cx.tcx.def_kind(def_id) + && let ty::Adt(adt_def, _) = cx.tcx.type_of(def_id).skip_binder().kind() + { + adt_def.did() + } else { + def_id + }; + cx.tcx.is_lang_item(def_id, LangItem::CVoid) + } else { + false + } +} + declare_lint! { /// The `c_void_returns` lint detects the use of [`core::ffi::c_void`] as a return type. /// @@ -51,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for CVoidReturns { _: Span, _: LocalDefId, ) { - check_decl( + check_decl_for_c_void_return( cx, decl, !matches!(fn_kind, FnKind::ItemFn(.., hir::FnHeader { abi: ExternAbi::Rust, .. })), @@ -60,22 +79,20 @@ impl<'tcx> LateLintPass<'tcx> for CVoidReturns { fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'tcx>) { if let hir::ForeignItemKind::Fn(sig, ..) = item.kind { - check_decl(cx, sig.decl, true); + check_decl_for_c_void_return(cx, sig.decl, true); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { if let hir::TyKind::FnPtr(fn_ptr_ty) = ty.kind { - check_decl(cx, fn_ptr_ty.decl, fn_ptr_ty.abi != ExternAbi::Rust); + check_decl_for_c_void_return(cx, fn_ptr_ty.decl, fn_ptr_ty.abi != ExternAbi::Rust); } } } -fn check_decl(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_extern: bool) { +fn check_decl_for_c_void_return(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_extern: bool) { if let hir::FnRetTy::Return(output_ty) = decl.output - && let hir::TyKind::Path(qpath) = output_ty.kind - && let Res::Def(.., def_id) = cx.qpath_res(&qpath, output_ty.hir_id) - && cx.tcx.is_lang_item(def_id, LangItem::CVoid) + && is_c_void(cx, *output_ty) { let suggestion = cx.sess().source_map().span_extend_to_prev_char(decl.output.span(), ')', true); @@ -87,3 +104,44 @@ fn check_decl(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_extern: bool) { } } } + +declare_lint! { + /// The `c_void_references` lint detects the use of [`core::ffi::c_void`] as the referent of an `&` or `&mut` reference. + /// + /// ### Example + /// + /// ```rust + /// use std::ffi::c_void; + /// + /// fn foo(v: &c_void) { + /// // .... + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `c_void` is designed for use through a [`raw pointer`], equivalent to C's `void*` type. + /// However, for historical reasons, Rust considers it to have size 1, and so using it via + /// a Rust reference can easily cause Undefined Behavior. + /// + /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html + /// [`raw pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + pub C_VOID_REFERENCES, + Warn, + "detects use of `c_void` as the referent of a Rust reference type" +} + +declare_lint_pass!(CVoidReferences => [C_VOID_REFERENCES]); + +impl<'tcx> LateLintPass<'tcx> for CVoidReferences { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { + if let hir::TyKind::Ref(_, hir::MutTy { ty: pointee_ty, .. }) = ty.kind + && is_c_void(cx, *pointee_ty) + { + cx.emit_span_lint(C_VOID_REFERENCES, ty.span, CVoidReference); + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5a99a841cfd42..496315d32dfb5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -273,6 +273,7 @@ late_lint_methods!( InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, ImplicitProvenanceCasts: ImplicitProvenanceCasts, CVoidReturns: CVoidReturns, + CVoidReferences: CVoidReferences, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b084b412417c5..caeebb2e2d53e 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -612,7 +612,7 @@ pub(crate) enum BuiltinSpecialModuleNameUsed { Main, } -// c_void_return.rs +// c_void.rs #[derive(Diagnostic)] #[diag("`c_void` should not be used as a return type")] #[help("returning `()` in Rust is equivalent to returning `void` in C")] @@ -625,7 +625,7 @@ pub(crate) struct CVoidReturn { pub suggestion: Span, } -// c_void_return.rs +// c_void.rs #[derive(Diagnostic)] #[diag("declarations returning `c_void` are not compatible with C functions returning `void`")] #[help("returning `()` in Rust is equivalent to returning `void` in C")] @@ -639,6 +639,15 @@ pub(crate) struct ExternCVoidReturn { pub suggestion: Span, } +// c_void.rs +#[derive(Diagnostic)] +#[diag("`c_void` should not be used as the referent of an `&` or `&mut` reference")] +#[help("use a raw pointer, or a reference to `()`, instead")] +#[note( + "for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior" +)] +pub(crate) struct CVoidReference; + // deref_into_dyn_supertrait.rs #[derive(Diagnostic)] #[diag("this `Deref` implementation is covered by an implicit supertrait coercion")] diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 9cca7c30817f9..c9c0238eca196 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -492,7 +492,7 @@ pub fn build_libffi_closure<'tcx, 'this>( /// As future improvement we might continue execution in the interpreter here. unsafe extern "C" fn libffi_closure_callback<'tcx>( _cif: &libffi::low::ffi_cif, - _result: &mut c_void, + _result: &mut (), _args: *const *const c_void, data: &LibffiClosureData<'tcx>, ) { diff --git a/tests/ui/lint/c-void-references.alias.stderr b/tests/ui/lint/c-void-references.alias.stderr new file mode 100644 index 0000000000000..14e8b36bcd358 --- /dev/null +++ b/tests/ui/lint/c-void-references.alias.stderr @@ -0,0 +1,61 @@ +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:17:13 + | +LL | fn bar() -> &'static mut c_void { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior +note: the lint level is defined here + --> $DIR/c-void-references.rs:4:9 + | +LL | #![deny(c_void_references)] + | ^^^^^^^^^^^^^^^^^ + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:22:20 + | +LL | fn baz() -> Option<&'static c_void> { + | ^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:27:12 + | +LL | fn quux(_: &c_void) {} + | ^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:29:16 + | +LL | type Boo<'a> = &'a c_void; + | ^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:32:12 + | +LL | let _: &'static c_void = panic!(); + | ^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:38:20 + | +LL | impl<'a> Trait for &'a c_void {} + | ^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: aborting due to 6 previous errors + diff --git a/tests/ui/lint/c-void-references.direct.stderr b/tests/ui/lint/c-void-references.direct.stderr new file mode 100644 index 0000000000000..14e8b36bcd358 --- /dev/null +++ b/tests/ui/lint/c-void-references.direct.stderr @@ -0,0 +1,61 @@ +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:17:13 + | +LL | fn bar() -> &'static mut c_void { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior +note: the lint level is defined here + --> $DIR/c-void-references.rs:4:9 + | +LL | #![deny(c_void_references)] + | ^^^^^^^^^^^^^^^^^ + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:22:20 + | +LL | fn baz() -> Option<&'static c_void> { + | ^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:27:12 + | +LL | fn quux(_: &c_void) {} + | ^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:29:16 + | +LL | type Boo<'a> = &'a c_void; + | ^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:32:12 + | +LL | let _: &'static c_void = panic!(); + | ^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:38:20 + | +LL | impl<'a> Trait for &'a c_void {} + | ^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: aborting due to 6 previous errors + diff --git a/tests/ui/lint/c-void-references.rs b/tests/ui/lint/c-void-references.rs new file mode 100644 index 0000000000000..23ffae8a5ca23 --- /dev/null +++ b/tests/ui/lint/c-void-references.rs @@ -0,0 +1,39 @@ +//@ revisions: direct alias + +#![allow(unused)] +#![deny(c_void_references)] + +#[cfg(direct)] +use std::ffi::c_void; +#[cfg(alias)] +#[expect(non_camel_case_types)] +type c_void = std::ffi::c_void; + +fn foo() -> *mut c_void { + // fine + std::ptr::null_mut() +} + +fn bar() -> &'static mut c_void { + //~^ ERROR c_void + panic!(); +} + +fn baz() -> Option<&'static c_void> { + //~^ ERROR c_void + None +} + +fn quux(_: &c_void) {} //~ ERROR c_void + +type Boo<'a> = &'a c_void; +//~^ ERROR c_void +fn main() { + let _: &'static c_void = panic!(); + //~^ ERROR c_void +} + +trait Trait {} + +impl<'a> Trait for &'a c_void {} +//~^ ERROR c_void diff --git a/tests/ui/lint/c-void-returns.stderr b/tests/ui/lint/c-void-returns.alias.stderr similarity index 89% rename from tests/ui/lint/c-void-returns.stderr rename to tests/ui/lint/c-void-returns.alias.stderr index 7dc724955ecb4..f55ea73332380 100644 --- a/tests/ui/lint/c-void-returns.stderr +++ b/tests/ui/lint/c-void-returns.alias.stderr @@ -1,5 +1,5 @@ error: `c_void` should not be used as a return type - --> $DIR/c-void-returns.rs:7:13 + --> $DIR/c-void-returns.rs:14:13 | LL | fn foo() -> c_void { | ----^^^^^^ @@ -8,13 +8,13 @@ LL | fn foo() -> c_void { | = help: returning `()` in Rust is equivalent to returning `void` in C note: the lint level is defined here - --> $DIR/c-void-returns.rs:2:9 + --> $DIR/c-void-returns.rs:4:9 | LL | #![deny(c_void_returns)] | ^^^^^^^^^^^^^^ error: declarations returning `c_void` are not compatible with C functions returning `void` - --> $DIR/c-void-returns.rs:16:17 + --> $DIR/c-void-returns.rs:24:17 | LL | fn baz() -> c_void; | ----^^^^^^ @@ -25,7 +25,7 @@ LL | fn baz() -> c_void; = note: `c_void` is only used through raw pointers for compatibility with `void` pointers error: `c_void` should not be used as a return type - --> $DIR/c-void-returns.rs:20:22 + --> $DIR/c-void-returns.rs:28:22 | LL | type Xyzzy = fn() -> c_void; | ----^^^^^^ diff --git a/tests/ui/lint/c-void-returns.direct.stderr b/tests/ui/lint/c-void-returns.direct.stderr new file mode 100644 index 0000000000000..f55ea73332380 --- /dev/null +++ b/tests/ui/lint/c-void-returns.direct.stderr @@ -0,0 +1,38 @@ +error: `c_void` should not be used as a return type + --> $DIR/c-void-returns.rs:14:13 + | +LL | fn foo() -> c_void { + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C +note: the lint level is defined here + --> $DIR/c-void-returns.rs:4:9 + | +LL | #![deny(c_void_returns)] + | ^^^^^^^^^^^^^^ + +error: declarations returning `c_void` are not compatible with C functions returning `void` + --> $DIR/c-void-returns.rs:24:17 + | +LL | fn baz() -> c_void; + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C + = note: `c_void` is only used through raw pointers for compatibility with `void` pointers + +error: `c_void` should not be used as a return type + --> $DIR/c-void-returns.rs:28:22 + | +LL | type Xyzzy = fn() -> c_void; + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/c-void-returns.rs b/tests/ui/lint/c-void-returns.rs index 7531da3d72f41..d4aea87b5e9f1 100644 --- a/tests/ui/lint/c-void-returns.rs +++ b/tests/ui/lint/c-void-returns.rs @@ -1,10 +1,18 @@ +//@ revisions: direct alias + #![allow(unused)] #![deny(c_void_returns)] +#[cfg(direct)] use std::ffi::c_void; +#[cfg(alias)] +#[expect(non_camel_case_types)] +type c_void = std::ffi::c_void; + use std::ptr; -fn foo() -> c_void { //~ ERROR c_void +fn foo() -> c_void { + //~^ ERROR c_void unreachable!() } From 911267fd696595b626752bbb843bde6d3a62d623 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 28 Jul 2026 16:49:16 -0400 Subject: [PATCH 3/5] Lint against `static`s and `const`s of type `c_void` Also, fix another false negative in `c_void_returns`. --- compiler/rustc_lint/src/c_void.rs | 73 +++++++++++++++++++++- compiler/rustc_lint/src/lib.rs | 1 + compiler/rustc_lint/src/lints.rs | 17 ++++- tests/ui/lint/c-void-returns.alias.stderr | 14 ++++- tests/ui/lint/c-void-returns.direct.stderr | 14 ++++- tests/ui/lint/c-void-returns.rs | 4 ++ tests/ui/lint/c-void-statics.alias.stderr | 45 +++++++++++++ tests/ui/lint/c-void-statics.direct.stderr | 45 +++++++++++++ tests/ui/lint/c-void-statics.rs | 27 ++++++++ 9 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 tests/ui/lint/c-void-statics.alias.stderr create mode 100644 tests/ui/lint/c-void-statics.direct.stderr create mode 100644 tests/ui/lint/c-void-statics.rs diff --git a/compiler/rustc_lint/src/c_void.rs b/compiler/rustc_lint/src/c_void.rs index 6c4b17f35ab07..0cb76db1909cc 100644 --- a/compiler/rustc_lint/src/c_void.rs +++ b/compiler/rustc_lint/src/c_void.rs @@ -7,7 +7,7 @@ use rustc_middle::ty; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; -use crate::lints::{CVoidReference, CVoidReturn, ExternCVoidReturn}; +use crate::lints::{CVoidConst, CVoidReference, CVoidReturn, CVoidStatic, ExternCVoidReturn}; use crate::{LateContext, LateLintPass, LintContext}; fn is_c_void(cx: &LateContext<'_>, ty: hir::Ty<'_>) -> bool { @@ -83,6 +83,12 @@ impl<'tcx> LateLintPass<'tcx> for CVoidReturns { } } + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { + if let hir::TraitItemKind::Fn(sig, ..) = item.kind { + check_decl_for_c_void_return(cx, sig.decl, false); + } + } + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { if let hir::TyKind::FnPtr(fn_ptr_ty) = ty.kind { check_decl_for_c_void_return(cx, fn_ptr_ty.decl, fn_ptr_ty.abi != ExternAbi::Rust); @@ -145,3 +151,68 @@ impl<'tcx> LateLintPass<'tcx> for CVoidReferences { } } } + +declare_lint! { + /// The `c_void_statics` lint detects the use of [`core::ffi::c_void`] as the type of a `static` or `const` item. + /// + /// ### Example + /// + /// ```rust + /// use std::ffi::c_void; + /// + /// unsafe extern "C" { + /// static FOO: c_void; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `c_void` is designed for use through a [`raw pointer`], equivalent to C's `void*` type. + /// For historical reasons, Rust considers it to have size 1. For a `static` item used + /// only for its address, the correct type is [`()`]. + /// + /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html + /// [`raw pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + pub C_VOID_STATICS, + Warn, + "detects use of `c_void` as the type of a `static` or `const` item" +} + +declare_lint_pass!(CVoidStatics => [C_VOID_STATICS]); + +impl<'tcx> LateLintPass<'tcx> for CVoidStatics { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) { + match item.kind { + hir::ItemKind::Static(_mut, _name, ty, _) if is_c_void(cx, *ty) => { + cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidStatic { suggestion: ty.span }) + } + hir::ItemKind::Const(_name, _generics, ty, _) if is_c_void(cx, *ty) => { + cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidConst) + } + _ => (), + } + } + + fn check_foreign_item( + &mut self, + cx: &LateContext<'tcx>, + item: &'tcx rustc_hir::ForeignItem<'tcx>, + ) { + if let hir::ForeignItemKind::Static(ty, ..) = item.kind + && is_c_void(cx, *ty) + { + cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidStatic { suggestion: ty.span }) + } + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::TraitItem<'tcx>) { + if let hir::TraitItemKind::Const(ty, ..) = item.kind + && is_c_void(cx, *ty) + { + cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidConst) + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 496315d32dfb5..9246eaa857322 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -274,6 +274,7 @@ late_lint_methods!( ImplicitProvenanceCasts: ImplicitProvenanceCasts, CVoidReturns: CVoidReturns, CVoidReferences: CVoidReferences, + CVoidStatics: CVoidStatics, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index caeebb2e2d53e..cca58d8693672 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -629,7 +629,7 @@ pub(crate) struct CVoidReturn { #[derive(Diagnostic)] #[diag("declarations returning `c_void` are not compatible with C functions returning `void`")] #[help("returning `()` in Rust is equivalent to returning `void` in C")] -#[note("`c_void` is only used through raw pointers for compatibility with `void` pointers")] +#[note("`c_void` is only used through raw pointers, for compatibility with `void` pointers")] pub(crate) struct ExternCVoidReturn { #[suggestion( "remove the return type to implicitly return `()`", @@ -648,6 +648,21 @@ pub(crate) struct ExternCVoidReturn { )] pub(crate) struct CVoidReference; +// c_void.rs +#[derive(Diagnostic)] +#[diag("`static` items should not have type `c_void`")] +#[help("for a `static` used only for its address, use `()` instead")] +#[note("`c_void` is only used through raw pointers, for compatibility with C `void` pointers")] +pub(crate) struct CVoidStatic { + #[suggestion("use `()` instead", code = "()", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("`const` items should not have type `c_void`")] +#[note("`c_void` is only used through raw pointers, for compatibility with C `void` pointers")] +pub(crate) struct CVoidConst; + // deref_into_dyn_supertrait.rs #[derive(Diagnostic)] #[diag("this `Deref` implementation is covered by an implicit supertrait coercion")] diff --git a/tests/ui/lint/c-void-returns.alias.stderr b/tests/ui/lint/c-void-returns.alias.stderr index f55ea73332380..3f593e8d7cb2f 100644 --- a/tests/ui/lint/c-void-returns.alias.stderr +++ b/tests/ui/lint/c-void-returns.alias.stderr @@ -22,7 +22,7 @@ LL | fn baz() -> c_void; | help: remove the return type to implicitly return `()` | = help: returning `()` in Rust is equivalent to returning `void` in C - = note: `c_void` is only used through raw pointers for compatibility with `void` pointers + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers error: `c_void` should not be used as a return type --> $DIR/c-void-returns.rs:28:22 @@ -34,5 +34,15 @@ LL | type Xyzzy = fn() -> c_void; | = help: returning `()` in Rust is equivalent to returning `void` in C -error: aborting due to 3 previous errors +error: `c_void` should not be used as a return type + --> $DIR/c-void-returns.rs:31:17 + | +LL | fn foo() -> c_void; + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C + +error: aborting due to 4 previous errors diff --git a/tests/ui/lint/c-void-returns.direct.stderr b/tests/ui/lint/c-void-returns.direct.stderr index f55ea73332380..3f593e8d7cb2f 100644 --- a/tests/ui/lint/c-void-returns.direct.stderr +++ b/tests/ui/lint/c-void-returns.direct.stderr @@ -22,7 +22,7 @@ LL | fn baz() -> c_void; | help: remove the return type to implicitly return `()` | = help: returning `()` in Rust is equivalent to returning `void` in C - = note: `c_void` is only used through raw pointers for compatibility with `void` pointers + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers error: `c_void` should not be used as a return type --> $DIR/c-void-returns.rs:28:22 @@ -34,5 +34,15 @@ LL | type Xyzzy = fn() -> c_void; | = help: returning `()` in Rust is equivalent to returning `void` in C -error: aborting due to 3 previous errors +error: `c_void` should not be used as a return type + --> $DIR/c-void-returns.rs:31:17 + | +LL | fn foo() -> c_void; + | ----^^^^^^ + | | + | help: remove the return type to implicitly return `()` + | + = help: returning `()` in Rust is equivalent to returning `void` in C + +error: aborting due to 4 previous errors diff --git a/tests/ui/lint/c-void-returns.rs b/tests/ui/lint/c-void-returns.rs index d4aea87b5e9f1..d6eb5a2076331 100644 --- a/tests/ui/lint/c-void-returns.rs +++ b/tests/ui/lint/c-void-returns.rs @@ -27,4 +27,8 @@ unsafe extern "C" { type Xyzzy = fn() -> c_void; //~ ERROR c_void +trait Trait { + fn foo() -> c_void; //~ ERROR c_void +} + fn main() {} diff --git a/tests/ui/lint/c-void-statics.alias.stderr b/tests/ui/lint/c-void-statics.alias.stderr new file mode 100644 index 0000000000000..c05b6f0c5ffe7 --- /dev/null +++ b/tests/ui/lint/c-void-statics.alias.stderr @@ -0,0 +1,45 @@ +error: `const` items should not have type `c_void` + --> $DIR/c-void-statics.rs:12:1 + | +LL | const FOO: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers +note: the lint level is defined here + --> $DIR/c-void-statics.rs:4:9 + | +LL | #![deny(c_void_statics)] + | ^^^^^^^^^^^^^^ + +error: `static` items should not have type `c_void` + --> $DIR/c-void-statics.rs:14:1 + | +LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: use `()` instead: `()` + | + = help: for a `static` used only for its address, use `()` instead + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers + +error: `static` items should not have type `c_void` + --> $DIR/c-void-statics.rs:18:5 + | +LL | safe static BAZ: c_void; + | ^^^^^^^^^^^^^^^^^------^ + | | + | help: use `()` instead: `()` + | + = help: for a `static` used only for its address, use `()` instead + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers + +error: `const` items should not have type `c_void` + --> $DIR/c-void-statics.rs:23:5 + | +LL | const FOO: c_void; + | ^^^^^^^^^^^^^^^^^^ + | + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lint/c-void-statics.direct.stderr b/tests/ui/lint/c-void-statics.direct.stderr new file mode 100644 index 0000000000000..c05b6f0c5ffe7 --- /dev/null +++ b/tests/ui/lint/c-void-statics.direct.stderr @@ -0,0 +1,45 @@ +error: `const` items should not have type `c_void` + --> $DIR/c-void-statics.rs:12:1 + | +LL | const FOO: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers +note: the lint level is defined here + --> $DIR/c-void-statics.rs:4:9 + | +LL | #![deny(c_void_statics)] + | ^^^^^^^^^^^^^^ + +error: `static` items should not have type `c_void` + --> $DIR/c-void-statics.rs:14:1 + | +LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: use `()` instead: `()` + | + = help: for a `static` used only for its address, use `()` instead + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers + +error: `static` items should not have type `c_void` + --> $DIR/c-void-statics.rs:18:5 + | +LL | safe static BAZ: c_void; + | ^^^^^^^^^^^^^^^^^------^ + | | + | help: use `()` instead: `()` + | + = help: for a `static` used only for its address, use `()` instead + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers + +error: `const` items should not have type `c_void` + --> $DIR/c-void-statics.rs:23:5 + | +LL | const FOO: c_void; + | ^^^^^^^^^^^^^^^^^^ + | + = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lint/c-void-statics.rs b/tests/ui/lint/c-void-statics.rs new file mode 100644 index 0000000000000..4e08a8920a776 --- /dev/null +++ b/tests/ui/lint/c-void-statics.rs @@ -0,0 +1,27 @@ +//@ revisions: direct alias + +#![allow(unused)] +#![deny(c_void_statics)] + +#[cfg(direct)] +use std::ffi::c_void; +#[cfg(alias)] +#[expect(non_camel_case_types)] +type c_void = std::ffi::c_void; + +const FOO: c_void = unsafe { std::mem::transmute(0u8) }; +//~^ ERROR c_void +static BAR: c_void = unsafe { std::mem::transmute(0u8) }; +//~^ ERROR c_void + +unsafe extern "C" { + safe static BAZ: c_void; + //~^ ERROR c_void +} + +trait Trait { + const FOO: c_void; + //~^ ERROR c_void +} + +fn main() {} From 835dbec13748957a6c2b6c328f451f33c600712d Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 28 Jul 2026 17:14:58 -0400 Subject: [PATCH 4/5] Lint against `c_void` parameters --- compiler/rustc_lint/src/c_void.rs | 67 ++++++++++++++----- compiler/rustc_lint/src/lib.rs | 2 +- compiler/rustc_lint/src/lints.rs | 7 ++ tests/ui/lint/c-void-parameters.alias.stderr | 43 ++++++++++++ tests/ui/lint/c-void-parameters.direct.stderr | 43 ++++++++++++ tests/ui/lint/c-void-parameters.rs | 31 +++++++++ 6 files changed, 177 insertions(+), 16 deletions(-) create mode 100644 tests/ui/lint/c-void-parameters.alias.stderr create mode 100644 tests/ui/lint/c-void-parameters.direct.stderr create mode 100644 tests/ui/lint/c-void-parameters.rs diff --git a/compiler/rustc_lint/src/c_void.rs b/compiler/rustc_lint/src/c_void.rs index 0cb76db1909cc..ee1353d1ffc7c 100644 --- a/compiler/rustc_lint/src/c_void.rs +++ b/compiler/rustc_lint/src/c_void.rs @@ -7,10 +7,12 @@ use rustc_middle::ty; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; -use crate::lints::{CVoidConst, CVoidReference, CVoidReturn, CVoidStatic, ExternCVoidReturn}; +use crate::lints::{ + CVoidConst, CVoidParameter, CVoidReference, CVoidReturn, CVoidStatic, ExternCVoidReturn, +}; use crate::{LateContext, LateLintPass, LintContext}; -fn is_c_void(cx: &LateContext<'_>, ty: hir::Ty<'_>) -> bool { +fn is_c_void(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let hir::TyKind::Path(qpath) = ty.kind && let Res::Def(.., def_id) = cx.qpath_res(&qpath, ty.hir_id) { @@ -28,6 +30,35 @@ fn is_c_void(cx: &LateContext<'_>, ty: hir::Ty<'_>) -> bool { } } +declare_lint! { + /// The `c_void_parameters` lint detects the use of [`core::ffi::c_void`] as a parameter type. + /// + /// ### Example + /// + /// ```rust + /// use std::ffi::c_void; + /// + /// unsafe extern "C" { + /// fn foo(_: c_void); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `c_void` is designed for use through a [`pointer`], equivalent to C's `void*` type. It is a + /// mistake to use it directly as a parameter type. If you intended to use a pointer type, use + /// `*mut c_void` or `*const c_void`. + /// + /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html + /// [`pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + pub C_VOID_PARAMETERS, + Warn, + "detects use of `c_void` as a parameter type" +} + declare_lint! { /// The `c_void_returns` lint detects the use of [`core::ffi::c_void`] as a return type. /// @@ -58,9 +89,9 @@ declare_lint! { "detects use of `c_void` as a return type" } -declare_lint_pass!(CVoidReturns => [C_VOID_RETURNS]); +declare_lint_pass!(CVoidParamsAndReturns => [C_VOID_PARAMETERS, C_VOID_RETURNS]); -impl<'tcx> LateLintPass<'tcx> for CVoidReturns { +impl<'tcx> LateLintPass<'tcx> for CVoidParamsAndReturns { fn check_fn( &mut self, cx: &LateContext<'tcx>, @@ -70,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for CVoidReturns { _: Span, _: LocalDefId, ) { - check_decl_for_c_void_return( + check_fn_decl_for_c_void( cx, decl, !matches!(fn_kind, FnKind::ItemFn(.., hir::FnHeader { abi: ExternAbi::Rust, .. })), @@ -79,26 +110,26 @@ impl<'tcx> LateLintPass<'tcx> for CVoidReturns { fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'tcx>) { if let hir::ForeignItemKind::Fn(sig, ..) = item.kind { - check_decl_for_c_void_return(cx, sig.decl, true); + check_fn_decl_for_c_void(cx, sig.decl, true); } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Fn(sig, ..) = item.kind { - check_decl_for_c_void_return(cx, sig.decl, false); + check_fn_decl_for_c_void(cx, sig.decl, false); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { if let hir::TyKind::FnPtr(fn_ptr_ty) = ty.kind { - check_decl_for_c_void_return(cx, fn_ptr_ty.decl, fn_ptr_ty.abi != ExternAbi::Rust); + check_fn_decl_for_c_void(cx, fn_ptr_ty.decl, fn_ptr_ty.abi != ExternAbi::Rust); } } } -fn check_decl_for_c_void_return(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_extern: bool) { +fn check_fn_decl_for_c_void(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_extern: bool) { if let hir::FnRetTy::Return(output_ty) = decl.output - && is_c_void(cx, *output_ty) + && is_c_void(cx, output_ty) { let suggestion = cx.sess().source_map().span_extend_to_prev_char(decl.output.span(), ')', true); @@ -109,6 +140,12 @@ fn check_decl_for_c_void_return(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is cx.emit_span_lint(C_VOID_RETURNS, decl.output.span(), CVoidReturn { suggestion }); } } + + for param_ty in decl.inputs { + if is_c_void(cx, param_ty) { + cx.emit_span_lint(C_VOID_PARAMETERS, param_ty.span, CVoidParameter); + } + } } declare_lint! { @@ -145,7 +182,7 @@ declare_lint_pass!(CVoidReferences => [C_VOID_REFERENCES]); impl<'tcx> LateLintPass<'tcx> for CVoidReferences { fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { if let hir::TyKind::Ref(_, hir::MutTy { ty: pointee_ty, .. }) = ty.kind - && is_c_void(cx, *pointee_ty) + && is_c_void(cx, pointee_ty) { cx.emit_span_lint(C_VOID_REFERENCES, ty.span, CVoidReference); } @@ -186,10 +223,10 @@ declare_lint_pass!(CVoidStatics => [C_VOID_STATICS]); impl<'tcx> LateLintPass<'tcx> for CVoidStatics { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::Item<'tcx>) { match item.kind { - hir::ItemKind::Static(_mut, _name, ty, _) if is_c_void(cx, *ty) => { + hir::ItemKind::Static(_mut, _name, ty, _) if is_c_void(cx, ty) => { cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidStatic { suggestion: ty.span }) } - hir::ItemKind::Const(_name, _generics, ty, _) if is_c_void(cx, *ty) => { + hir::ItemKind::Const(_name, _generics, ty, _) if is_c_void(cx, ty) => { cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidConst) } _ => (), @@ -202,7 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for CVoidStatics { item: &'tcx rustc_hir::ForeignItem<'tcx>, ) { if let hir::ForeignItemKind::Static(ty, ..) = item.kind - && is_c_void(cx, *ty) + && is_c_void(cx, ty) { cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidStatic { suggestion: ty.span }) } @@ -210,7 +247,7 @@ impl<'tcx> LateLintPass<'tcx> for CVoidStatics { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx rustc_hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Const(ty, ..) = item.kind - && is_c_void(cx, *ty) + && is_c_void(cx, ty) { cx.emit_span_lint(C_VOID_STATICS, item.span, CVoidConst) } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 9246eaa857322..70488d4e26096 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -272,7 +272,7 @@ late_lint_methods!( LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, ImplicitProvenanceCasts: ImplicitProvenanceCasts, - CVoidReturns: CVoidReturns, + CVoidParamsAndReturns: CVoidParamsAndReturns, CVoidReferences: CVoidReferences, CVoidStatics: CVoidStatics, ] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index cca58d8693672..217ada34292ce 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -612,6 +612,13 @@ pub(crate) enum BuiltinSpecialModuleNameUsed { Main, } +// c_void.rs +#[derive(Diagnostic)] +#[diag("`c_void` should not be used directly as the type of a function parameter")] +#[help("did you mean `*mut c_void` or `*const c_void`?")] +#[note("`c_void` is only used through raw pointers, for compatibility with `void` pointers")] +pub(crate) struct CVoidParameter; + // c_void.rs #[derive(Diagnostic)] #[diag("`c_void` should not be used as a return type")] diff --git a/tests/ui/lint/c-void-parameters.alias.stderr b/tests/ui/lint/c-void-parameters.alias.stderr new file mode 100644 index 0000000000000..726f4064ac137 --- /dev/null +++ b/tests/ui/lint/c-void-parameters.alias.stderr @@ -0,0 +1,43 @@ +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:14:11 + | +LL | fn foo(_: c_void) { + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers +note: the lint level is defined here + --> $DIR/c-void-parameters.rs:4:9 + | +LL | #![deny(c_void_parameters)] + | ^^^^^^^^^^^^^^^^^ + +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:21:15 + | +LL | fn baz(_: c_void); + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:25:17 + | +LL | type Xyzzy = fn(c_void); + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:28:15 + | +LL | fn foo(_: c_void); + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lint/c-void-parameters.direct.stderr b/tests/ui/lint/c-void-parameters.direct.stderr new file mode 100644 index 0000000000000..726f4064ac137 --- /dev/null +++ b/tests/ui/lint/c-void-parameters.direct.stderr @@ -0,0 +1,43 @@ +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:14:11 + | +LL | fn foo(_: c_void) { + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers +note: the lint level is defined here + --> $DIR/c-void-parameters.rs:4:9 + | +LL | #![deny(c_void_parameters)] + | ^^^^^^^^^^^^^^^^^ + +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:21:15 + | +LL | fn baz(_: c_void); + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:25:17 + | +LL | type Xyzzy = fn(c_void); + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: `c_void` should not be used directly as the type of a function parameter + --> $DIR/c-void-parameters.rs:28:15 + | +LL | fn foo(_: c_void); + | ^^^^^^ + | + = help: did you mean `*mut c_void` or `*const c_void`? + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: aborting due to 4 previous errors + diff --git a/tests/ui/lint/c-void-parameters.rs b/tests/ui/lint/c-void-parameters.rs new file mode 100644 index 0000000000000..5179d367ff8a2 --- /dev/null +++ b/tests/ui/lint/c-void-parameters.rs @@ -0,0 +1,31 @@ +//@ revisions: direct alias + +#![allow(unused)] +#![deny(c_void_parameters)] + +#[cfg(direct)] +use std::ffi::c_void; +#[cfg(alias)] +#[expect(non_camel_case_types)] +type c_void = std::ffi::c_void; + +use std::ptr; + +fn foo(_: c_void) { + //~^ ERROR c_void +} + +fn bar(_: *mut c_void) {} + +unsafe extern "C" { + fn baz(_: c_void); //~ ERROR c_void + fn quux(_: *const c_void); +} + +type Xyzzy = fn(c_void); //~ ERROR c_void + +trait Trait { + fn foo(_: c_void); //~ ERROR c_void +} + +fn main() {} From bf41d85734782b708f16bd8540acf1b0ba3cd21c Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Tue, 28 Jul 2026 21:54:02 -0400 Subject: [PATCH 5/5] Add MIR lint for using values of type `c_void` Also, extend `c_void_references` to lint on references to `c_void` in MIR. --- compiler/rustc_lint/src/c_void.rs | 30 +------ compiler/rustc_lint_defs/src/builtin.rs | 59 +++++++++++++ .../rustc_mir_transform/src/check_c_void.rs | 85 +++++++++++++++++++ .../rustc_mir_transform/src/diagnostics.rs | 16 ++++ compiler/rustc_mir_transform/src/lib.rs | 2 + tests/ui/lint/c-void-references.alias.stderr | 50 ++++++++--- tests/ui/lint/c-void-references.direct.stderr | 50 ++++++++--- tests/ui/lint/c-void-references.rs | 26 +++++- tests/ui/lint/c-void-statics.alias.stderr | 8 +- tests/ui/lint/c-void-statics.direct.stderr | 8 +- tests/ui/lint/c-void-statics.rs | 1 + tests/ui/lint/c-void-values.alias.stderr | 34 ++++++++ tests/ui/lint/c-void-values.direct.stderr | 34 ++++++++ tests/ui/lint/c-void-values.rs | 23 +++++ 14 files changed, 363 insertions(+), 63 deletions(-) create mode 100644 compiler/rustc_mir_transform/src/check_c_void.rs create mode 100644 tests/ui/lint/c-void-values.alias.stderr create mode 100644 tests/ui/lint/c-void-values.direct.stderr create mode 100644 tests/ui/lint/c-void-values.rs diff --git a/compiler/rustc_lint/src/c_void.rs b/compiler/rustc_lint/src/c_void.rs index ee1353d1ffc7c..8d9ca2b1e40c8 100644 --- a/compiler/rustc_lint/src/c_void.rs +++ b/compiler/rustc_lint/src/c_void.rs @@ -4,6 +4,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{self as hir, LangItem}; use rustc_middle::ty; +use rustc_session::lint::builtin::C_VOID_REFERENCES; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::Span; @@ -148,35 +149,6 @@ fn check_fn_decl_for_c_void(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_ext } } -declare_lint! { - /// The `c_void_references` lint detects the use of [`core::ffi::c_void`] as the referent of an `&` or `&mut` reference. - /// - /// ### Example - /// - /// ```rust - /// use std::ffi::c_void; - /// - /// fn foo(v: &c_void) { - /// // .... - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// `c_void` is designed for use through a [`raw pointer`], equivalent to C's `void*` type. - /// However, for historical reasons, Rust considers it to have size 1, and so using it via - /// a Rust reference can easily cause Undefined Behavior. - /// - /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html - /// [`raw pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html - /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html - pub C_VOID_REFERENCES, - Warn, - "detects use of `c_void` as the referent of a Rust reference type" -} - declare_lint_pass!(CVoidReferences => [C_VOID_REFERENCES]); impl<'tcx> LateLintPass<'tcx> for CVoidReferences { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 82b4dd5e2babd..fd41888906b65 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -34,6 +34,7 @@ pub mod hardwired { CONFLICTING_REPR_HINTS, CONST_EVALUATABLE_UNCHECKED, CONST_ITEM_MUTATION, + C_VOID_VALUES, DEAD_CODE, DEAD_CODE_PUB_IN_BINARY, DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK, @@ -1477,6 +1478,64 @@ declare_lint! { "detects attempts to mutate a `const` item", } +declare_lint! { + /// The `c_void_references` lint detects the use of [`core::ffi::c_void`] as the referent of an `&` or `&mut` reference. + /// + /// ### Example + /// + /// ```rust + /// use std::ffi::c_void; + /// + /// fn foo(v: &c_void) { + /// // .... + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `c_void` is designed for use through a [`raw pointer`], equivalent to C's `void*` type. + /// However, for historical reasons, Rust considers it to have size 1, and so using it via + /// a Rust reference can easily cause Undefined Behavior. + /// + /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html + /// [`raw pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + pub C_VOID_REFERENCES, + Warn, + "detects use of `c_void` as the referent of a Rust reference type" +} + +declare_lint! { + /// The `c_void_values` lint detects the use of values of type [`core::ffi::c_void`] + /// + /// ### Example + /// + /// ```rust + /// use std::ffi::c_void; + /// + /// fn foo(v: *const c_void) { + /// let val = unsafe { v.read() }; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `c_void` is designed for use through a [`raw pointer`], equivalent to C's `void*` type. + /// However, for historical reasons, Rust considers it to have size 1, and so using it directly + /// via a value can easily cause Undefined Behavior. + /// + /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html + /// [`raw pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html + /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html + pub C_VOID_VALUES, + Warn, + "detects use of `c_void` as the referent of a Rust reference type" +} + declare_lint! { /// The `patterns_in_fns_without_body` lint detects `mut` identifier /// patterns as a parameter in functions without a body. diff --git a/compiler/rustc_mir_transform/src/check_c_void.rs b/compiler/rustc_mir_transform/src/check_c_void.rs new file mode 100644 index 0000000000000..99529733d03fa --- /dev/null +++ b/compiler/rustc_mir_transform/src/check_c_void.rs @@ -0,0 +1,85 @@ +use rustc_hir::LangItem; +use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; +use rustc_middle::mir::*; +use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_session::lint::builtin::{C_VOID_REFERENCES, C_VOID_VALUES}; + +use crate::diagnostics; + +pub(super) struct CheckCVoid; + +impl<'tcx> crate::MirLint<'tcx> for CheckCVoid { + fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { + let source_info = SourceInfo::outermost(body.span); + let mut checker = CVoidChecker { body, tcx, source_info }; + checker.visit_body(body); + } +} + +struct CVoidChecker<'a, 'tcx> { + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + source_info: SourceInfo, +} + +impl<'tcx> Visitor<'tcx> for CVoidChecker<'_, 'tcx> { + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + // Make sure we know where in the MIR we are. + self.source_info = terminator.source_info; + self.super_terminator(terminator, location); + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + // Make sure we know where in the MIR we are. + self.source_info = statement.source_info; + self.super_statement(statement, location); + } + + fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, _location: Location) { + match context { + PlaceContext::NonMutatingUse( + NonMutatingUseContext::Move | NonMutatingUseContext::SharedBorrow, + ) + | PlaceContext::MutatingUse( + MutatingUseContext::Store + | MutatingUseContext::AsmOutput + | MutatingUseContext::Call + | MutatingUseContext::Yield + | MutatingUseContext::Borrow, + ) => (), + _ => return, + }; + + let ty = place.ty(self.body, self.tcx).ty; + + if is_c_void(self.tcx, ty.peel_refs()) { + let Some(hir_id) = self.source_info.scope.lint_root(&self.body.source_scopes) else { + return; + }; + + if context.is_borrow() || ty.is_ref() { + self.tcx.emit_node_span_lint( + C_VOID_REFERENCES, + hir_id, + self.source_info.span, + diagnostics::CVoidRef, + ); + } else { + self.tcx.emit_node_span_lint( + C_VOID_VALUES, + hir_id, + self.source_info.span, + diagnostics::CVoidValue, + ); + } + } + } +} + +fn is_c_void(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool { + if let Some(adt_def) = ty.ty_adt_def() { + tcx.is_lang_item(adt_def.did(), LangItem::CVoid) + } else { + false + } +} diff --git a/compiler/rustc_mir_transform/src/diagnostics.rs b/compiler/rustc_mir_transform/src/diagnostics.rs index e597c10511b0f..afb0d8f0f0de5 100644 --- a/compiler/rustc_mir_transform/src/diagnostics.rs +++ b/compiler/rustc_mir_transform/src/diagnostics.rs @@ -74,6 +74,22 @@ pub(crate) struct UnalignedPackedRef { pub align: u64, } +#[derive(Diagnostic)] +#[diag("`c_void` should not be used as the referent of an `&` or `&mut` reference")] +#[help("use `&raw const` or `&raw mut` instead, to create a raw pointer")] +#[note( + "for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior" +)] +pub(crate) struct CVoidRef; + +#[derive(Diagnostic)] +#[diag("`c_void` should not be used by value")] +#[note( + "for legacy reasons, Rust considers `c_void` to have size 1, so this can cause Undefined Behavior" +)] +#[note("`c_void` is only used through raw pointers, for compatibility with `void` pointers")] +pub(crate) struct CVoidValue; + #[derive(Diagnostic)] #[diag("MIR pass `{$name}` is unknown and will be ignored")] pub(crate) struct UnknownPassName<'a> { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 905907aa279ea..360391b4eb24c 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -131,6 +131,7 @@ declare_passes! { mod check_const_item_mutation : CheckConstItemMutation; mod check_null : CheckNull; mod check_packed_ref : CheckPackedRef; + mod check_c_void : CheckCVoid; // This pass is public to allow external drivers to perform MIR cleanup pub mod cleanup_post_borrowck : CleanupPostBorrowck; @@ -415,6 +416,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { &Lint(check_inline::CheckForceInline), &Lint(check_call_recursion::CheckCallRecursion), &Lint(check_packed_ref::CheckPackedRef), + &Lint(check_c_void::CheckCVoid), &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), // What we need to do constant evaluation. diff --git a/tests/ui/lint/c-void-references.alias.stderr b/tests/ui/lint/c-void-references.alias.stderr index 14e8b36bcd358..5e86c2649050c 100644 --- a/tests/ui/lint/c-void-references.alias.stderr +++ b/tests/ui/lint/c-void-references.alias.stderr @@ -1,10 +1,10 @@ error: `c_void` should not be used as the referent of an `&` or `&mut` reference - --> $DIR/c-void-references.rs:17:13 + --> $DIR/c-void-references.rs:38:22 | -LL | fn bar() -> &'static mut c_void { - | ^^^^^^^^^^^^^^^^^^^ +LL | let _ = unsafe { &*raw }; + | ^^^^^ | - = help: use a raw pointer, or a reference to `()`, instead + = help: use `&raw const` or `&raw mut` instead, to create a raw pointer = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior note: the lint level is defined here --> $DIR/c-void-references.rs:4:9 @@ -12,6 +12,34 @@ note: the lint level is defined here LL | #![deny(c_void_references)] | ^^^^^^^^^^^^^^^^^ +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:38:22 + | +LL | let _ = unsafe { &*raw }; + | ^^^^^ + | + = help: use `&raw const` or `&raw mut` instead, to create a raw pointer + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:49:9 + | +LL | do_stuff_helper(raw); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use `&raw const` or `&raw mut` instead, to create a raw pointer + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:17:13 + | +LL | fn bar() -> &'static mut c_void { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + error: `c_void` should not be used as the referent of an `&` or `&mut` reference --> $DIR/c-void-references.rs:22:20 | @@ -40,22 +68,22 @@ LL | type Boo<'a> = &'a c_void; = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior error: `c_void` should not be used as the referent of an `&` or `&mut` reference - --> $DIR/c-void-references.rs:32:12 + --> $DIR/c-void-references.rs:34:20 | -LL | let _: &'static c_void = panic!(); - | ^^^^^^^^^^^^^^^ +LL | impl<'a> Trait for &'a c_void {} + | ^^^^^^^^^^ | = help: use a raw pointer, or a reference to `()`, instead = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior error: `c_void` should not be used as the referent of an `&` or `&mut` reference - --> $DIR/c-void-references.rs:38:20 + --> $DIR/c-void-references.rs:55:12 | -LL | impl<'a> Trait for &'a c_void {} - | ^^^^^^^^^^ +LL | let _: &'static c_void = panic!(); + | ^^^^^^^^^^^^^^^ | = help: use a raw pointer, or a reference to `()`, instead = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/lint/c-void-references.direct.stderr b/tests/ui/lint/c-void-references.direct.stderr index 14e8b36bcd358..5e86c2649050c 100644 --- a/tests/ui/lint/c-void-references.direct.stderr +++ b/tests/ui/lint/c-void-references.direct.stderr @@ -1,10 +1,10 @@ error: `c_void` should not be used as the referent of an `&` or `&mut` reference - --> $DIR/c-void-references.rs:17:13 + --> $DIR/c-void-references.rs:38:22 | -LL | fn bar() -> &'static mut c_void { - | ^^^^^^^^^^^^^^^^^^^ +LL | let _ = unsafe { &*raw }; + | ^^^^^ | - = help: use a raw pointer, or a reference to `()`, instead + = help: use `&raw const` or `&raw mut` instead, to create a raw pointer = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior note: the lint level is defined here --> $DIR/c-void-references.rs:4:9 @@ -12,6 +12,34 @@ note: the lint level is defined here LL | #![deny(c_void_references)] | ^^^^^^^^^^^^^^^^^ +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:38:22 + | +LL | let _ = unsafe { &*raw }; + | ^^^^^ + | + = help: use `&raw const` or `&raw mut` instead, to create a raw pointer + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:49:9 + | +LL | do_stuff_helper(raw); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use `&raw const` or `&raw mut` instead, to create a raw pointer + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + +error: `c_void` should not be used as the referent of an `&` or `&mut` reference + --> $DIR/c-void-references.rs:17:13 + | +LL | fn bar() -> &'static mut c_void { + | ^^^^^^^^^^^^^^^^^^^ + | + = help: use a raw pointer, or a reference to `()`, instead + = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior + error: `c_void` should not be used as the referent of an `&` or `&mut` reference --> $DIR/c-void-references.rs:22:20 | @@ -40,22 +68,22 @@ LL | type Boo<'a> = &'a c_void; = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior error: `c_void` should not be used as the referent of an `&` or `&mut` reference - --> $DIR/c-void-references.rs:32:12 + --> $DIR/c-void-references.rs:34:20 | -LL | let _: &'static c_void = panic!(); - | ^^^^^^^^^^^^^^^ +LL | impl<'a> Trait for &'a c_void {} + | ^^^^^^^^^^ | = help: use a raw pointer, or a reference to `()`, instead = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior error: `c_void` should not be used as the referent of an `&` or `&mut` reference - --> $DIR/c-void-references.rs:38:20 + --> $DIR/c-void-references.rs:55:12 | -LL | impl<'a> Trait for &'a c_void {} - | ^^^^^^^^^^ +LL | let _: &'static c_void = panic!(); + | ^^^^^^^^^^^^^^^ | = help: use a raw pointer, or a reference to `()`, instead = note: for legacy reasons, Rust considers `c_void` to have size 1, so references to it can cause Undefined Behavior -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/lint/c-void-references.rs b/tests/ui/lint/c-void-references.rs index 23ffae8a5ca23..9c83194ae815f 100644 --- a/tests/ui/lint/c-void-references.rs +++ b/tests/ui/lint/c-void-references.rs @@ -28,12 +28,30 @@ fn quux(_: &c_void) {} //~ ERROR c_void type Boo<'a> = &'a c_void; //~^ ERROR c_void -fn main() { - let _: &'static c_void = panic!(); - //~^ ERROR c_void -} trait Trait {} impl<'a> Trait for &'a c_void {} //~^ ERROR c_void + +fn do_stuff(raw: *mut c_void) { + let _ = unsafe { &*raw }; + //~^ ERROR c_void + //~| ERROR c_void +} + +unsafe fn do_stuff_helper<'a, T>(raw: *mut T) -> &'a T { + unsafe { &*raw } +} + +fn do_stuff_2(raw: *mut c_void) { + unsafe { + do_stuff_helper(raw); + //~^ ERROR c_void + } +} + +fn main() { + let _: &'static c_void = panic!(); + //~^ ERROR c_void +} diff --git a/tests/ui/lint/c-void-statics.alias.stderr b/tests/ui/lint/c-void-statics.alias.stderr index c05b6f0c5ffe7..21e85892fbfe3 100644 --- a/tests/ui/lint/c-void-statics.alias.stderr +++ b/tests/ui/lint/c-void-statics.alias.stderr @@ -1,5 +1,5 @@ error: `const` items should not have type `c_void` - --> $DIR/c-void-statics.rs:12:1 + --> $DIR/c-void-statics.rs:13:1 | LL | const FOO: c_void = unsafe { std::mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(c_void_statics)] | ^^^^^^^^^^^^^^ error: `static` items should not have type `c_void` - --> $DIR/c-void-statics.rs:14:1 + --> $DIR/c-void-statics.rs:15:1 | LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; | ^^^^^^^^^^^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers error: `static` items should not have type `c_void` - --> $DIR/c-void-statics.rs:18:5 + --> $DIR/c-void-statics.rs:19:5 | LL | safe static BAZ: c_void; | ^^^^^^^^^^^^^^^^^------^ @@ -34,7 +34,7 @@ LL | safe static BAZ: c_void; = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers error: `const` items should not have type `c_void` - --> $DIR/c-void-statics.rs:23:5 + --> $DIR/c-void-statics.rs:24:5 | LL | const FOO: c_void; | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/c-void-statics.direct.stderr b/tests/ui/lint/c-void-statics.direct.stderr index c05b6f0c5ffe7..21e85892fbfe3 100644 --- a/tests/ui/lint/c-void-statics.direct.stderr +++ b/tests/ui/lint/c-void-statics.direct.stderr @@ -1,5 +1,5 @@ error: `const` items should not have type `c_void` - --> $DIR/c-void-statics.rs:12:1 + --> $DIR/c-void-statics.rs:13:1 | LL | const FOO: c_void = unsafe { std::mem::transmute(0u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(c_void_statics)] | ^^^^^^^^^^^^^^ error: `static` items should not have type `c_void` - --> $DIR/c-void-statics.rs:14:1 + --> $DIR/c-void-statics.rs:15:1 | LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; | ^^^^^^^^^^^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers error: `static` items should not have type `c_void` - --> $DIR/c-void-statics.rs:18:5 + --> $DIR/c-void-statics.rs:19:5 | LL | safe static BAZ: c_void; | ^^^^^^^^^^^^^^^^^------^ @@ -34,7 +34,7 @@ LL | safe static BAZ: c_void; = note: `c_void` is only used through raw pointers, for compatibility with C `void` pointers error: `const` items should not have type `c_void` - --> $DIR/c-void-statics.rs:23:5 + --> $DIR/c-void-statics.rs:24:5 | LL | const FOO: c_void; | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/c-void-statics.rs b/tests/ui/lint/c-void-statics.rs index 4e08a8920a776..68905a6ea9148 100644 --- a/tests/ui/lint/c-void-statics.rs +++ b/tests/ui/lint/c-void-statics.rs @@ -2,6 +2,7 @@ #![allow(unused)] #![deny(c_void_statics)] +#![allow(c_void_values)] #[cfg(direct)] use std::ffi::c_void; diff --git a/tests/ui/lint/c-void-values.alias.stderr b/tests/ui/lint/c-void-values.alias.stderr new file mode 100644 index 0000000000000..ce9022a7c3c93 --- /dev/null +++ b/tests/ui/lint/c-void-values.alias.stderr @@ -0,0 +1,34 @@ +error: `c_void` should not be used by value + --> $DIR/c-void-values.rs:13:30 + | +LL | const FOO: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for legacy reasons, Rust considers `c_void` to have size 1, so this can cause Undefined Behavior + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers +note: the lint level is defined here + --> $DIR/c-void-values.rs:4:9 + | +LL | #![deny(c_void_values)] + | ^^^^^^^^^^^^^ + +error: `c_void` should not be used by value + --> $DIR/c-void-values.rs:15:31 + | +LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for legacy reasons, Rust considers `c_void` to have size 1, so this can cause Undefined Behavior + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: `c_void` should not be used by value + --> $DIR/c-void-values.rs:19:14 + | +LL | unsafe { r.read() }; + | ^^^^^^^^ + | + = note: for legacy reasons, Rust considers `c_void` to have size 1, so this can cause Undefined Behavior + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/c-void-values.direct.stderr b/tests/ui/lint/c-void-values.direct.stderr new file mode 100644 index 0000000000000..ce9022a7c3c93 --- /dev/null +++ b/tests/ui/lint/c-void-values.direct.stderr @@ -0,0 +1,34 @@ +error: `c_void` should not be used by value + --> $DIR/c-void-values.rs:13:30 + | +LL | const FOO: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for legacy reasons, Rust considers `c_void` to have size 1, so this can cause Undefined Behavior + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers +note: the lint level is defined here + --> $DIR/c-void-values.rs:4:9 + | +LL | #![deny(c_void_values)] + | ^^^^^^^^^^^^^ + +error: `c_void` should not be used by value + --> $DIR/c-void-values.rs:15:31 + | +LL | static BAR: c_void = unsafe { std::mem::transmute(0u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for legacy reasons, Rust considers `c_void` to have size 1, so this can cause Undefined Behavior + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: `c_void` should not be used by value + --> $DIR/c-void-values.rs:19:14 + | +LL | unsafe { r.read() }; + | ^^^^^^^^ + | + = note: for legacy reasons, Rust considers `c_void` to have size 1, so this can cause Undefined Behavior + = note: `c_void` is only used through raw pointers, for compatibility with `void` pointers + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/c-void-values.rs b/tests/ui/lint/c-void-values.rs new file mode 100644 index 0000000000000..26d4d968884cd --- /dev/null +++ b/tests/ui/lint/c-void-values.rs @@ -0,0 +1,23 @@ +//@ revisions: direct alias + +#![allow(unused)] +#![deny(c_void_values)] +#![allow(c_void_statics)] + +#[cfg(direct)] +use std::ffi::c_void; +#[cfg(alias)] +#[expect(non_camel_case_types)] +type c_void = std::ffi::c_void; + +const FOO: c_void = unsafe { std::mem::transmute(0u8) }; +//~^ ERROR c_void +static BAR: c_void = unsafe { std::mem::transmute(0u8) }; +//~^ ERROR c_void + +fn foo(r: *mut c_void) { + unsafe { r.read() }; + //~^ ERROR c_void +} + +fn main() {}