Skip to main content

rustc_lint/
c_void_returns.rs

1use rustc_abi::ExternAbi;
2use rustc_hir::def::Res;
3use rustc_hir::def_id::LocalDefId;
4use rustc_hir::intravisit::FnKind;
5use rustc_hir::{self as hir, LangItem};
6use rustc_session::{declare_lint, declare_lint_pass};
7use rustc_span::Span;
8
9use crate::lints::{CVoidReturn, ExternCVoidReturn};
10use crate::{LateContext, LateLintPass, LintContext};
11
12#[doc =
r" The `c_void_returns` lint detects the use of [`core::ffi::c_void`] as a return type."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" use std::ffi::c_void;"]
#[doc = r""]
#[doc = r#" unsafe extern "C" {"#]
#[doc = r"     fn foo() -> c_void;"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" `c_void` is designed for use through a [`pointer`], equivalent to C's `void*` type. It is a"]
#[doc =
r" mistake to use it directly as a return type, and calling `extern` functions declared as such"]
#[doc =
r" may result in undefined behavior. C functions that return `void` must be declared to return"]
#[doc =
r" [`()`] in Rust (omitting the return type implicitly returns `()`)."]
#[doc = r""]
#[doc =
r" [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html"]
#[doc =
r" [`pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html"]
#[doc = r" [`()`]: https://doc.rust-lang.org/core/primitive.unit.html"]
pub static C_VOID_RETURNS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "C_VOID_RETURNS",
            default_level: ::rustc_lint_defs::Warn,
            desc: "detects use of `c_void` as a return type",
            is_externally_loaded: false,
            ..::rustc_lint_defs::Lint::default_fields_for_macro()
        };declare_lint! {
13    /// The `c_void_returns` lint detects the use of [`core::ffi::c_void`] as a return type.
14    ///
15    /// ### Example
16    ///
17    /// ```rust
18    /// use std::ffi::c_void;
19    ///
20    /// unsafe extern "C" {
21    ///     fn foo() -> c_void;
22    /// }
23    /// ```
24    ///
25    /// {{produces}}
26    ///
27    /// ### Explanation
28    ///
29    /// `c_void` is designed for use through a [`pointer`], equivalent to C's `void*` type. It is a
30    /// mistake to use it directly as a return type, and calling `extern` functions declared as such
31    /// may result in undefined behavior. C functions that return `void` must be declared to return
32    /// [`()`] in Rust (omitting the return type implicitly returns `()`).
33    ///
34    /// [`core::ffi::c_void`]: https://doc.rust-lang.org/core/ffi/enum.c_void.html
35    /// [`pointer`]: https://doc.rust-lang.org/core/primitive.pointer.html
36    /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html
37    pub C_VOID_RETURNS,
38    Warn,
39    "detects use of `c_void` as a return type"
40}
41
42pub struct CVoidReturns;
#[automatically_derived]
impl ::core::marker::Copy for CVoidReturns { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for CVoidReturns { }
#[automatically_derived]
impl ::core::clone::Clone for CVoidReturns {
    #[inline]
    fn clone(&self) -> CVoidReturns { *self }
}
impl ::rustc_lint_defs::LintPass for CVoidReturns {
    fn name(&self) -> &'static str { "CVoidReturns" }
    fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [C_VOID_RETURNS]))
    }
}
impl CVoidReturns {
    #[allow(unused)]
    pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
        ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                [C_VOID_RETURNS]))
    }
}declare_lint_pass!(CVoidReturns => [C_VOID_RETURNS]);
43
44impl<'tcx> LateLintPass<'tcx> for CVoidReturns {
45    fn check_fn(
46        &mut self,
47        cx: &LateContext<'tcx>,
48        fn_kind: FnKind<'tcx>,
49        decl: &'tcx hir::FnDecl<'tcx>,
50        _: &'tcx hir::Body<'tcx>,
51        _: Span,
52        _: LocalDefId,
53    ) {
54        check_decl(
55            cx,
56            decl,
57            !#[allow(non_exhaustive_omitted_patterns)] match fn_kind {
    FnKind::ItemFn(.., hir::FnHeader { abi: ExternAbi::Rust, .. }) => true,
    _ => false,
}matches!(fn_kind, FnKind::ItemFn(.., hir::FnHeader { abi: ExternAbi::Rust, .. })),
58        );
59    }
60
61    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'tcx>) {
62        if let hir::ForeignItemKind::Fn(sig, ..) = item.kind {
63            check_decl(cx, sig.decl, true);
64        }
65    }
66
67    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
68        if let hir::TyKind::FnPtr(fn_ptr_ty) = ty.kind {
69            check_decl(cx, fn_ptr_ty.decl, fn_ptr_ty.abi != ExternAbi::Rust);
70        }
71    }
72}
73
74fn check_decl(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, is_extern: bool) {
75    if let hir::FnRetTy::Return(output_ty) = decl.output
76        && let hir::TyKind::Path(qpath) = output_ty.kind
77        && let Res::Def(.., def_id) = cx.qpath_res(&qpath, output_ty.hir_id)
78        && cx.tcx.is_lang_item(def_id, LangItem::CVoid)
79    {
80        let suggestion =
81            cx.sess().source_map().span_extend_to_prev_char(decl.output.span(), ')', true);
82
83        if is_extern {
84            cx.emit_span_lint(C_VOID_RETURNS, decl.output.span(), ExternCVoidReturn { suggestion });
85        } else {
86            cx.emit_span_lint(C_VOID_RETURNS, decl.output.span(), CVoidReturn { suggestion });
87        }
88    }
89}