Skip to content

clippy::missing_safety_doc triggers on cxx::bridge-generated code #16317

@anforowicz

Description

@anforowicz

Summary

It seems that missing_safety_doc triggers on pub unsafe fn my_fn generated by cxx::bridge, even though the macro expansion does contain a safety doc.

PS. At first glance this seems similar to #4949, but in the other bug the macro expansion did not propagate the safety doc.

Lint Name

missing_safety_doc

Reproducer

I tried this code:

// src/lib.rs:
#![deny(clippy::missing_safety_doc)]

#[cxx::bridge]
pub mod ffi {
    unsafe extern "C++" {
        type R;

        /// # Safety
        ///
        /// Caller needs to ensure that X, Y, Z.
        unsafe fn my_fn(&self);
    }
}

pub fn public_my_fn(r: &ffi::R) {
    // SAFETY: X, Y, and Z are guaranteed based on ...
    unsafe { r.my_fn() }
}
# Cargo.toml:
[package]
name = "clippy-cxx-missing-safety-doc-repro"
version = "0.1.0"
edition = "2024"

[dependencies]
cxx = "1.0.192"

[lib]

I saw this happen:

$ cargo clippy
    Checking clippy-cxx-missing-safety-doc-repro v0.1.0 (/usr/local/google/home/lukasza/src/cargo/clippy-cxx-missing-safety-doc-repro)
error: unsafe function's docs are missing a `# Safety` section
  --> src/lib.rs:11:9
   |
11 |         unsafe fn my_fn(&self);
   |         ^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
note: the lint level is defined here
  --> src/lib.rs:1:9
   |
 1 | #![deny(clippy::missing_safety_doc)]
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: could not compile `clippy-cxx-missing-safety-doc-repro` (lib) due to 1 previous error

I expected to see no errors/warnings, because the proc macro expansion preserves the doc comment as follows:

$ cargo expand
    Checking clippy-cxx-missing-safety-doc-repro v0.1.0 (/usr/local/google/home/lukasza/src/cargo/clippy-cxx-missing-safety-doc-repro)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s

#![feature(prelude_import)]
#![deny(clippy::missing_safety_doc)]
#[macro_use]
extern crate std;
#[prelude_import]
use std::prelude::rust_2024::*;
#[deny(improper_ctypes, improper_ctypes_definitions)]
#[allow(clippy::unknown_lints)]
#[allow(
    non_camel_case_types,
    non_snake_case,
    clippy::extra_unused_type_parameters,
    clippy::items_after_statements,
    clippy::no_effect_underscore_binding,
    clippy::unsafe_derive_deserialize,
    clippy::upper_case_acronyms,
    clippy::use_self,
)]
pub mod ffi {
    #[repr(C)]
    pub struct R {
        _private: ::cxx::private::Opaque,
    }
    #[automatically_derived]
    unsafe impl ::cxx::ExternType for R {
        #[allow(unused_attributes)]
        #[doc(hidden)]
        type Id = (::cxx::R,);
        type Kind = ::cxx::kind::Opaque;
    }
    impl R {
        /// # Safety
        ///
        /// Caller needs to ensure that X, Y, Z.
        pub unsafe fn my_fn(&self) {
            unsafe extern "C" {
                #[link_name = "cxxbridge1$192$R$my_fn"]
                fn __my_fn(_: &R);
            }
            unsafe {
                __my_fn(self);
            }
        }
    }
    #[doc(hidden)]
    const _: () = {
        let _: fn() = {
            trait __AmbiguousIfImpl<A> {
                fn infer() {}
            }
            #[automatically_derived]
            impl<T> __AmbiguousIfImpl<()> for T
            where
                T: ?::cxx::core::marker::Sized,
            {}
            #[allow(dead_code)]
            struct __Invalid;
            #[automatically_derived]
            impl<T> __AmbiguousIfImpl<__Invalid> for T
            where
                T: ?::cxx::core::marker::Sized + ::cxx::core::marker::Unpin,
            {}
            <R as __AmbiguousIfImpl<_>>::infer
        };
    };
}
pub fn public_my_fn(r: &ffi::R) {
    unsafe { r.my_fn() }
}

Version

$ rustc -Vv
rustc 1.94.0-nightly (0208ee09b 2025-12-14)
binary: rustc
commit-hash: 0208ee09be465f69005a7a12c28d5eccac7d5f34
commit-date: 2025-12-14
host: x86_64-unknown-linux-gnu
release: 1.94.0-nightly
LLVM version: 21.1.5

$ clippy-driver --version
clippy 0.1.94 (0208ee09be 2025-12-14)

Additional Labels

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-positiveIssue: The lint was triggered on code it shouldn't have

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions