diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-09 21:05:25 +0200 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-09 21:05:25 +0200 |
| commit | bfe67cc3798287bfca334d92f342e093f233f2c2 (patch) | |
| tree | 9cbf2394ec9f808731babf5a73cf965a421660c0 | |
| parent | eb413c99c921f34ccb49044c884d540c025990d8 (diff) | |
| parent | 2dee3d3adcadcc57d62ba6608cd02f50b0c10264 (diff) | |
| download | linux-next-bfe67cc3798287bfca334d92f342e093f233f2c2.tar.gz linux-next-bfe67cc3798287bfca334d92f342e093f233f2c2.zip | |
Merge branch 'rust-next' of https://github.com/Rust-for-Linux/linux.git
| -rw-r--r-- | Documentation/rust/general-information.rst | 4 | ||||
| -rw-r--r-- | init/Kconfig | 3 | ||||
| -rw-r--r-- | rust/.gitignore | 1 | ||||
| -rw-r--r-- | rust/Makefile | 11 | ||||
| -rw-r--r-- | rust/bindgen_parameters | 2 | ||||
| -rw-r--r-- | rust/bindings/bindings_helper.h | 2 | ||||
| -rw-r--r-- | rust/kernel/Kconfig.test | 11 | ||||
| -rw-r--r-- | rust/kernel/bitfield.rs | 1 | ||||
| -rw-r--r-- | rust/kernel/bug.rs | 33 | ||||
| -rw-r--r-- | rust/kernel/kunit.rs | 12 | ||||
| -rw-r--r-- | rust/kernel/lib.rs | 1 | ||||
| -rw-r--r-- | rust/kernel/mem.rs | 70 | ||||
| -rw-r--r-- | rust/kernel/miscdevice.rs | 35 | ||||
| -rw-r--r-- | rust/kernel/str.rs | 2 | ||||
| -rw-r--r-- | rust/macros/helpers.rs | 16 | ||||
| -rw-r--r-- | rust/macros/kunit.rs | 5 | ||||
| -rw-r--r-- | rust/macros/lib.rs | 48 | ||||
| -rw-r--r-- | rust/macros/module.rs | 2 | ||||
| -rw-r--r-- | rust/macros/vtable.rs | 43 | ||||
| -rwxr-xr-x | scripts/remove-stale-files | 2 | ||||
| -rw-r--r-- | scripts/rustdoc_test_builder.rs | 7 | ||||
| -rw-r--r-- | scripts/rustdoc_test_gen.rs | 73 |
22 files changed, 226 insertions, 158 deletions
diff --git a/Documentation/rust/general-information.rst b/Documentation/rust/general-information.rst index 09234bed272c..20e3178ebfd4 100644 --- a/Documentation/rust/general-information.rst +++ b/Documentation/rust/general-information.rst @@ -157,5 +157,5 @@ numerical comparisons, one may define a new Kconfig symbol: .. code-block:: kconfig - config RUSTC_HAS_SPAN_FILE - def_bool RUSTC_VERSION >= 108800 + config RUSTC_HAS_FILE_AS_C_STR + def_bool RUSTC_VERSION >= 109100 diff --git a/init/Kconfig b/init/Kconfig index ad592fdf29af..dfcf108c21d9 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -183,9 +183,6 @@ config LD_CAN_USE_KEEP_IN_OVERLAY # https://github.com/llvm/llvm-project/pull/130661 def_bool LD_IS_BFD || LLD_VERSION >= 210000 -config RUSTC_HAS_SPAN_FILE - def_bool RUSTC_VERSION >= 108800 - config RUSTC_HAS_UNNECESSARY_TRANSMUTES def_bool RUSTC_VERSION >= 108800 diff --git a/rust/.gitignore b/rust/.gitignore index d3829ffab80b..5ba6b490d1e9 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -3,7 +3,6 @@ bindings_generated.rs bindings_helpers_generated.rs doctests_kernel_generated.rs -doctests_kernel_generated_kunit.c uapi_generated.rs exports_*_generated.h doc/ diff --git a/rust/Makefile b/rust/Makefile index da1a7409d984..320ab6bb36c4 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -39,10 +39,8 @@ obj-$(CONFIG_RUST) += exports.o always-$(CONFIG_RUST) += host/libproc_macro2.rlib host/libquote.rlib host/libsyn.rlib always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs -always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.o -obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.o always-$(subst y,$(CONFIG_RUST),$(CONFIG_JUMP_LABEL)) += kernel/generated_arch_static_branch_asm.rs ifndef CONFIG_UML @@ -53,7 +51,6 @@ endif ifdef CONFIG_RUST procmacro-name = $(shell MAKEFLAGS= $(RUSTC) --print file-names --crate-name $(1) --crate-type proc-macro - </dev/null) -procmacro-extension := $(patsubst libname.%,%,$(call procmacro-name,name)) libzerocopy_derive_name := $(call procmacro-name,zerocopy_derive) libmacros_name := $(call procmacro-name,macros) @@ -75,7 +72,8 @@ endif cfgs-to-flags = $(patsubst %,--cfg='%',$1) core-cfgs := \ - no_fp_fmt_parse + no_fp_fmt_parse \ + no_io_statics core-edition := $(if $(call rustc-min-version,108700),2024,2021) @@ -85,6 +83,7 @@ core-skip_flags := \ core-flags := \ --edition=$(core-edition) \ + -Aunused_variables \ $(call cfgs-to-flags,$(core-cfgs)) zerocopy-cfgs := \ @@ -400,7 +399,7 @@ quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $< $< $(rustdoc_test_kernel_quiet); \ $(objtree)/scripts/rustdoc_test_gen -%/doctests_kernel_generated.rs %/doctests_kernel_generated_kunit.c: \ +%/doctests_kernel_generated.rs: \ $(src)/kernel/lib.rs $(obj)/kernel.o \ $(objtree)/scripts/rustdoc_test_builder \ $(objtree)/scripts/rustdoc_test_gen FORCE @@ -620,7 +619,7 @@ quiet_cmd_rustc_procmacro = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) -Clink-args='$(call escsq,$(KBUILD_PROCMACROLDFLAGS))' \ --emit=dep-info=$(depfile) --emit=link=$@ --extern proc_macro \ --crate-type proc-macro -L$(objtree)/$(obj)/host \ - --crate-name $(patsubst lib%.$(procmacro-extension),%,$(notdir $@)) \ + --crate-name $(basename $(@F:lib%=%)) \ @$(objtree)/include/generated/rustc_cfg $< # Procedural macros can only be used with the `rustc` that compiled it. diff --git a/rust/bindgen_parameters b/rust/bindgen_parameters index 8402b0c93545..93e622aaaf78 100644 --- a/rust/bindgen_parameters +++ b/rust/bindgen_parameters @@ -50,7 +50,7 @@ --blocklist-item VM_MAYREAD --blocklist-item VM_MAYWRITE --blocklist-item VM_MAYEXEC ---blocklist-item VM_MAYEXEC +--blocklist-item VM_MAYSHARE --blocklist-item VM_PFNMAP --blocklist-item VM_IO --blocklist-item VM_DONTCOPY diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 4b31aa7f432f..c706dda5b5ca 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -138,7 +138,7 @@ const vm_flags_t RUST_CONST_HELPER_VM_SHARED = VM_SHARED; const vm_flags_t RUST_CONST_HELPER_VM_MAYREAD = VM_MAYREAD; const vm_flags_t RUST_CONST_HELPER_VM_MAYWRITE = VM_MAYWRITE; const vm_flags_t RUST_CONST_HELPER_VM_MAYEXEC = VM_MAYEXEC; -const vm_flags_t RUST_CONST_HELPER_VM_MAYSHARE = VM_MAYEXEC; +const vm_flags_t RUST_CONST_HELPER_VM_MAYSHARE = VM_MAYSHARE; const vm_flags_t RUST_CONST_HELPER_VM_PFNMAP = VM_PFNMAP; const vm_flags_t RUST_CONST_HELPER_VM_IO = VM_IO; const vm_flags_t RUST_CONST_HELPER_VM_DONTCOPY = VM_DONTCOPY; diff --git a/rust/kernel/Kconfig.test b/rust/kernel/Kconfig.test index e6a5c7a795f0..966b07872ba1 100644 --- a/rust/kernel/Kconfig.test +++ b/rust/kernel/Kconfig.test @@ -83,4 +83,15 @@ config RUST_BITFIELD_KUNIT_TEST If unsure, say N. +config RUST_BUG_KUNIT_TEST + bool "KUnit tests for the Rust BUG/WARN functionality" if !KUNIT_ALL_TESTS + depends on BUG + default KUNIT_ALL_TESTS + help + This option enables KUnit tests for the Rust BUG/WARN functionality. + These are only for development and testing, not for regular + kernel use cases. + + If unsure, say N. + endif diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs index a0d089423f21..861522c8cf3e 100644 --- a/rust/kernel/bitfield.rs +++ b/rust/kernel/bitfield.rs @@ -295,7 +295,6 @@ macro_rules! bitfield { $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* } ) => { $crate::bitfield!(@core - #[allow(non_camel_case_types)] $(#[$attr])* $vis $name $storage ); $crate::bitfield!(@fields $vis $name $storage { $($fields)* }); diff --git a/rust/kernel/bug.rs b/rust/kernel/bug.rs index 3566f0234ca4..6abed2c055b1 100644 --- a/rust/kernel/bug.rs +++ b/rust/kernel/bug.rs @@ -85,6 +85,7 @@ macro_rules! warn_flags { ($file:expr, $flags:expr) => { if false { _ = $file; + _ = $flags; } // SAFETY: It is always safe to call `warn_slowpath_fmt()` @@ -93,7 +94,7 @@ macro_rules! warn_flags { $crate::bindings::warn_slowpath_fmt( $crate::str::CStrExt::as_char_ptr($crate::c_str!(::core::file!())), line!() as $crate::ffi::c_int, - $flags as $crate::ffi::c_uint, + $crate::bindings::TAINT_WARN, ::core::ptr::null(), ); } @@ -152,3 +153,33 @@ macro_rules! warn_on { cond }}; } + +#[cfg(CONFIG_RUST_BUG_KUNIT_TEST)] +#[macros::kunit_tests(rust_kernel_bug)] +mod tests { + // The counter is incremented by the kernel warning path, not by `warn_on!` + // itself. A count of one means the warning was really reported. + #[test] + fn test_warn_on() { + // SAFETY: `kunit_get_current_test()` is always safe to call (it has + // fallbacks for when no KUnit test is running). + let test = unsafe { bindings::kunit_get_current_test() }; + + // SAFETY: This function runs only as a KUnit test case, so `test` is a + // valid pointer to the running test. + let handle = unsafe { bindings::kunit_start_suppress_warning(test) }; + + assert!(!warn_on!(false)); + assert!(warn_on!(true)); + + // SAFETY: `kunit_suppressed_warning_count()` accepts any value returned by + // `kunit_start_suppress_warning()`. + let suppressed_count = unsafe { bindings::kunit_suppressed_warning_count(handle) }; + + // SAFETY: `test` is valid as above. `kunit_end_suppress_warning()` accepts any + // value returned by `kunit_start_suppress_warning()`. + unsafe { bindings::kunit_end_suppress_warning(test, handle) }; + + assert_eq!(suppressed_count, 1); + } +} diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 91eaff8c186a..25ff272d29b2 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -58,15 +58,17 @@ pub fn info(args: fmt::Arguments<'_>) { #[doc(hidden)] #[macro_export] macro_rules! kunit_assert { - ($name:literal, $file:literal, $diff:expr, $condition:expr $(,)?) => { + ($name:literal, $condition:expr $(,)?) => { 'out: { // Do nothing if the condition is `true`. if $condition { break 'out; } - static FILE: &'static $crate::str::CStr = $file; - static LINE: i32 = ::core::line!() as i32 - $diff; + // Use `file!()` instead of `::core::file!()` here so it can be overridden. + static FILE: &'static $crate::str::CStr = $crate::c_str!(file!()); + // Use `line!()` instead of `::core::line!()` here so it can be overridden. + static LINE: i32 = line!() as i32; static CONDITION: &'static $crate::str::CStr = $crate::c_str!(stringify!($condition)); // SAFETY: FFI call without safety requirements. @@ -164,10 +166,10 @@ macro_rules! kunit_assert { #[doc(hidden)] #[macro_export] macro_rules! kunit_assert_eq { - ($name:literal, $file:literal, $diff:expr, $left:expr, $right:expr $(,)?) => {{ + ($name:literal, $left:expr, $right:expr $(,)?) => {{ // For the moment, we just forward to the expression assert because, for binary asserts, // KUnit supports only a few types (e.g. integers). - $crate::kunit_assert!($name, $file, $diff, $left == $right); + $crate::kunit_assert!($name, $left == $right); }}; } diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 4d5c96ddc49c..7225abc64084 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -98,6 +98,7 @@ pub mod jump_label; pub mod kunit; pub mod list; pub mod maple_tree; +pub mod mem; pub mod miscdevice; pub mod mm; pub mod module; diff --git a/rust/kernel/mem.rs b/rust/kernel/mem.rs new file mode 100644 index 000000000000..33676760bee2 --- /dev/null +++ b/rust/kernel/mem.rs @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Basic utilities for dealing with memory, values, and types. + +// TODO: `Alignment` should be moved here as well, Rust moved it upstream in 1.96. +pub use crate::ptr::Alignment; + +/// Trait indicating that [`Align<N>`] is a valid alignment. +pub trait ValidAlign { + #[doc(hidden)] + type Repr: Copy; +} + +/// Zero-sized type that provides the alignment specified via the generic parameter. +/// +/// # Examples +/// +/// ``` +/// # use kernel::mem::Align; +/// const ALIGN: usize = 128; +/// struct MyStruct { +/// _align: Align<ALIGN>, +/// } +/// assert_eq!(align_of::<MyStruct>(), ALIGN); +/// ``` +#[repr(transparent)] +#[derive(Default, Clone, Copy)] +pub struct Align<const N: usize>([<Self as ValidAlign>::Repr; 0]) +where + Self: ValidAlign; + +impl<const N: usize> Align<N> +where + Self: ValidAlign, +{ + /// Create a new [`Align<N>`]. + #[inline] + pub const fn new() -> Self { + Align([]) + } +} + +macro_rules! impl_align { + () => {}; + ($a:literal $($rest:literal)*) => { + const _: () = { + #[repr(align($a))] + #[derive(Clone, Copy)] + pub struct Repr; + + impl ValidAlign for Align<$a> { + type Repr = Repr; + } + }; + + impl_align!($($rest)*); + } +} + +impl_align!(1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536); + +impl<const N: usize> core::fmt::Debug for Align<N> +where + Self: ValidAlign, +{ + #[inline] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Align<{N}>") + } +} diff --git a/rust/kernel/miscdevice.rs b/rust/kernel/miscdevice.rs index 8d4b719bd83f..eacea29e3fb0 100644 --- a/rust/kernel/miscdevice.rs +++ b/rust/kernel/miscdevice.rs @@ -11,10 +11,7 @@ use crate::{ bindings, device::Device, - error::{ - to_result, - VTABLE_DEFAULT_ERROR, // - }, + error::to_result, fs::{ File, Kiocb, // @@ -145,37 +142,33 @@ pub trait MiscDevice: Sized { /// the VMA initialization by calling methods of `vma`. If the function does not return an /// error, the kernel will complete initialization of the VMA according to the properties of /// `vma`. + #[optional] fn mmap( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _vma: &VmaNew, - ) -> Result { - build_error!(VTABLE_DEFAULT_ERROR) - } + ) -> Result; /// Read from this miscdevice. - fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterDest<'_>) -> Result<usize> { - build_error!(VTABLE_DEFAULT_ERROR) - } + #[optional] + fn read_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterDest<'_>) -> Result<usize>; /// Write to this miscdevice. - fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterSource<'_>) -> Result<usize> { - build_error!(VTABLE_DEFAULT_ERROR) - } + #[optional] + fn write_iter(_kiocb: Kiocb<'_, Self::Ptr>, _iov: &mut IovIterSource<'_>) -> Result<usize>; /// Handler for ioctls. /// /// The `cmd` argument is usually manipulated using the utilities in [`kernel::ioctl`]. /// /// [`kernel::ioctl`]: mod@crate::ioctl + #[optional] fn ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, - ) -> Result<isize> { - build_error!(VTABLE_DEFAULT_ERROR) - } + ) -> Result<isize>; /// Handler for ioctls. /// @@ -185,23 +178,21 @@ pub trait MiscDevice: Sized { /// that have different layout on 32-bit and 64-bit userspace. If no implementation is /// provided, then `compat_ptr_ioctl` will be used instead. #[cfg(CONFIG_COMPAT)] + #[optional] fn compat_ioctl( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _file: &File, _cmd: u32, _arg: usize, - ) -> Result<isize> { - build_error!(VTABLE_DEFAULT_ERROR) - } + ) -> Result<isize>; /// Show info for this fd. + #[optional] fn show_fdinfo( _device: <Self::Ptr as ForeignOwnable>::Borrowed<'_>, _m: &SeqFile, _file: &File, - ) { - build_error!(VTABLE_DEFAULT_ERROR) - } + ); } /// A vtable for the file operations of a Rust miscdevice. diff --git a/rust/kernel/str.rs b/rust/kernel/str.rs index b3caa9a1c898..644b4279a116 100644 --- a/rust/kernel/str.rs +++ b/rust/kernel/str.rs @@ -411,7 +411,7 @@ impl AsRef<BStr> for CStr { /// const BAD: &CStr = c_str!("literal"); /// /// // `c_str!` is still needed for static non-literal C strings. -/// const GOOD: &CStr = c_str!(concat!(file!(), ":", line!(), ": My CStr!")); +/// const GOOD: &CStr = c_str!(concat!(file!(), ":", ::core::line!(), ": My CStr!")); /// ``` #[macro_export] macro_rules! c_str { diff --git a/rust/macros/helpers.rs b/rust/macros/helpers.rs index d18fbf4daa0a..8cc9a20f470e 100644 --- a/rust/macros/helpers.rs +++ b/rust/macros/helpers.rs @@ -38,22 +38,6 @@ impl AsciiLitStr { } } -pub(crate) fn file() -> String { - #[cfg(not(CONFIG_RUSTC_HAS_SPAN_FILE))] - { - proc_macro::Span::call_site() - .source_file() - .path() - .to_string_lossy() - .into_owned() - } - - #[cfg(CONFIG_RUSTC_HAS_SPAN_FILE)] - { - proc_macro::Span::call_site().file() - } -} - /// Obtain all `#[cfg]` attributes. pub(crate) fn gather_cfg_attrs(attr: &[Attribute]) -> impl Iterator<Item = &Attribute> + '_ { attr.iter().filter(|a| a.path().is_ident("cfg")) diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs index ae20ed6768f1..e9152b9d51f9 100644 --- a/rust/macros/kunit.rs +++ b/rust/macros/kunit.rs @@ -109,12 +109,11 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<Toke // Before the test, override usual `assert!` and `assert_eq!` macros with ones that call // KUnit instead. let test_str = test.to_string(); - let path = CString::new(crate::helpers::file()).expect("file path cannot contain NUL"); processed_items.push(parse_quote! { #[allow(unused)] macro_rules! assert { ($cond:expr $(,)?) => {{ - kernel::kunit_assert!(#test_str, #path, 0, $cond); + kernel::kunit_assert!(#test_str, $cond); }} } }); @@ -122,7 +121,7 @@ pub(crate) fn kunit_tests(test_suite: Ident, mut module: ItemMod) -> Result<Toke #[allow(unused)] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => {{ - kernel::kunit_assert_eq!(#test_str, #path, 0, $left, $right); + kernel::kunit_assert_eq!(#test_str, $left, $right); }} } }); diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 24f96feaeb34..68386b2ed55f 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -8,11 +8,6 @@ // Stable since Rust 1.87.0. #![feature(extract_if)] -// -// Stable since Rust 1.88.0 under a different name, `proc_macro_span_file`, -// which was added in Rust 1.88.0. This is why `cfg_attr` is used here, i.e. -// to avoid depending on the full `proc_macro_span` on Rust >= 1.88.0. -#![cfg_attr(not(CONFIG_RUSTC_HAS_SPAN_FILE), feature(proc_macro_span))] mod concat_idents; mod export; @@ -129,7 +124,7 @@ use syn::parse_macro_input; /// # Supported argument types /// - `type`: type which implements the [`Module`] trait (required). /// - `name`: ASCII string literal of the name of the kernel module (required). -/// - `authors`: array of ASCII string literals of the authors of the kernel module. +/// - `authors`: array of string literals of the authors of the kernel module. /// - `description`: string literal of the description of the kernel module. /// - `license`: ASCII string literal of the license of the kernel module (required). /// - `alias`: array of ASCII string literals of the alias names of the kernel module. @@ -156,25 +151,15 @@ pub fn module(input: TokenStream) -> TokenStream { /// associated constant bool for each method in the trait that is set to true if /// the implementer has overridden the associated method. /// -/// For a trait method to be optional, it must have a default implementation. -/// This is also the case for traits annotated with `#[vtable]`, but in this -/// case the default implementation will never be executed. The reason for this -/// is that the functions will be called through function pointers installed in -/// C side vtables. When an optional method is not implemented on a `#[vtable]` -/// trait, a `NULL` entry is installed in the vtable. Thus the default -/// implementation is never called. Since these traits are not designed to be -/// used on the Rust side, it should not be possible to call the default -/// implementation. This is done to ensure that we call the vtable methods -/// through the C vtable, and not through the Rust vtable. Therefore, the -/// default implementation should call `build_error!`, which prevents -/// calls to this function at compile time: -/// -/// ```compile_fail -/// # // Intentionally missing `use`s to simplify `rusttest`. -/// build_error!(VTABLE_DEFAULT_ERROR) -/// ``` -/// -/// Note that you might need to import [`kernel::error::VTABLE_DEFAULT_ERROR`]. +/// For a trait method to be optional for normal traits, it must have a default +/// implementation. However, for many users of `#[vtable]`, the functions will +/// be called through function pointers installed in C side vtables. When an +/// optional method is not implemented on a `#[vtable]` trait, a `NULL` entry is +/// installed in the vtable; thus the default implementation is never called. If +/// this is the case, `#[optional]` can be applied on the optional method. +/// Methods annotated as such do not need to be implemented nor need a default +/// implementation. Calling these methods on types that do not implement them +/// will fail to build using the `build_error!` mechanism. /// /// This macro should not be used when all functions are required. /// @@ -187,7 +172,6 @@ pub fn module(input: TokenStream) -> TokenStream { /// # Examples /// /// ``` -/// use kernel::error::VTABLE_DEFAULT_ERROR; /// use kernel::prelude::*; /// /// # struct LocalModule; @@ -204,13 +188,11 @@ pub fn module(input: TokenStream) -> TokenStream { /// // Declares a `#[vtable]` trait /// #[vtable] /// pub trait Operations: Send + Sync + Sized { -/// fn foo(&self) -> Result<()> { -/// build_error!(VTABLE_DEFAULT_ERROR) -/// } +/// #[optional] +/// fn foo(&self) -> Result<()>; /// -/// fn bar(&self) -> Result<()> { -/// build_error!(VTABLE_DEFAULT_ERROR) -/// } +/// #[optional] +/// fn bar(&self) -> Result<()>; /// } /// /// struct Foo; @@ -228,8 +210,6 @@ pub fn module(input: TokenStream) -> TokenStream { /// assert_eq!(<Foo as Operations>::HAS_BAR, false); /// # } /// ``` -/// -/// [`kernel::error::VTABLE_DEFAULT_ERROR`]: ../kernel/error/constant.VTABLE_DEFAULT_ERROR.html #[proc_macro_attribute] pub fn vtable(attr: TokenStream, input: TokenStream) -> TokenStream { parse_macro_input!(attr as syn::parse::Nothing); diff --git a/rust/macros/module.rs b/rust/macros/module.rs index bc7027f8dbb2..0a7b9e24c960 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -369,7 +369,7 @@ pub(crate) struct ModuleInfo { type_: Type, license: AsciiLitStr, name: AsciiLitStr, - authors: Option<Punctuated<AsciiLitStr, Token![,]>>, + authors: Option<Punctuated<LitStr, Token![,]>>, description: Option<LitStr>, alias: Option<Punctuated<AsciiLitStr, Token![,]>>, firmware: Option<Punctuated<AsciiLitStr, Token![,]>>, diff --git a/rust/macros/vtable.rs b/rust/macros/vtable.rs index be9a5ed8abe5..981ad2e49647 100644 --- a/rust/macros/vtable.rs +++ b/rust/macros/vtable.rs @@ -9,7 +9,10 @@ use proc_macro2::{ Ident, TokenStream, // }; -use quote::ToTokens; +use quote::{ + quote, + ToTokens, // +}; use syn::{ parse_quote, Error, @@ -46,7 +49,7 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> { }); } - for item in &item.items { + for item in &mut item.items { if let TraitItem::Fn(fn_item) = item { let name = &fn_item.sig.ident; let gen_const_name = Ident::new( @@ -54,14 +57,44 @@ fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> { name.span(), ); + if fn_item + .attrs + .extract_if(.., |attr| attr.path().is_ident("optional")) + .count() + != 0 + { + if let Some(default) = &fn_item.default { + Err(Error::new_spanned( + default, + "`#[optional]` methods must not have a default implementation", + ))?; + } + + // Optional methods in Rust need a default implementation. Inject one that fails + // the build at compile time if not overridden. + fn_item.default = Some(parse_quote!({ + ::kernel::build_assert::build_error!( + "This function must not be called, see the `#[vtable]` documentation." + ); + })); + // Ensure that the function is never code generated unless used. + fn_item.attrs.push(parse_quote!(#[inline])); + } + // We don't know on the implementation-site whether a method is required or provided // so we have to generate a const for all methods. + // However, hide it for required methods as it will always be true. + let doc = if fn_item.default.is_some() { + let comment = + format!("Indicates if the `{name}` method is overridden by the implementor."); + quote!(#[doc = #comment]) + } else { + quote!(#[doc(hidden)]) + }; let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs); - let comment = - format!("Indicates if the `{name}` method is overridden by the implementor."); gen_items.push(parse_quote! { #(#cfg_attrs)* - #[doc = #comment] + #doc const #gen_const_name: bool = false; }); } diff --git a/scripts/remove-stale-files b/scripts/remove-stale-files index 6e39fa8540df..f1f776caae00 100755 --- a/scripts/remove-stale-files +++ b/scripts/remove-stale-files @@ -26,3 +26,5 @@ rm -f scripts/selinux/genheaders/genheaders rm -f *.spec rm -f lib/test_fortify.log + +rm -f rust/doctests_kernel_generated_kunit.c diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs index df864437cef7..0361e22853f2 100644 --- a/scripts/rustdoc_test_builder.rs +++ b/scripts/rustdoc_test_builder.rs @@ -72,6 +72,13 @@ fn main() { // Figure out a smaller test name based on the generated function name. let name = rustdoc_function_name.split_once("_rust_kernel_").unwrap().1; + // The `rustdoc` function name can include the absolute path when building with `O=` which is + // undesirable and creates overlong symbol names. Remap it to use a relative path. + // TODO: Remove when bumping the minimum version since with Rust >= 1.95 we already do the + // path remapping via the `rustdoc` `--remap-path-prefix` flag. + let trimmed_function_name = format!("_doctest_main_rust_kernel_{name}"); + let body = body.replace(&rustdoc_function_name, &trimmed_function_name); + let path = format!("rust/test/doctests/kernel/{name}"); std::fs::write(path, body.as_bytes()).unwrap(); diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index d087c0d9fcb3..754c5aafcce4 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -125,9 +125,7 @@ fn main() { // Sort paths. paths.sort(); - let mut rust_tests = String::new(); - let mut c_test_declarations = String::new(); - let mut c_test_cases = String::new(); + let mut tests = String::new(); let mut body = String::new(); let mut last_file = String::new(); let mut number = 0; @@ -172,28 +170,22 @@ fn main() { use std::fmt::Write; write!( - rust_tests, + tests, r#"/// Generated `{name}` KUnit test case from a Rust documentation test. -#[no_mangle] -pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ - /// Overrides the usual [`assert!`] macro with one that calls KUnit instead. +#[test] +fn {kunit_name}() {{ + /// Overrides the usual [`file!`] macro with one that expands to the real path. #[allow(unused)] - macro_rules! assert {{ - ($cond:expr $(,)?) => {{{{ - ::kernel::kunit_assert!( - "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $cond - ); - }}}} + macro_rules! file {{ + () => {{ "{real_path}" }} }} - /// Overrides the usual [`assert_eq!`] macro with one that calls KUnit instead. + /// Overrides the usual [`line!`] macro with one that expands to the real line number. #[allow(unused)] - macro_rules! assert_eq {{ - ($left:expr, $right:expr $(,)?) => {{{{ - ::kernel::kunit_assert_eq!( - "{kunit_name}", c"{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right - ); - }}}} + macro_rules! line {{ + // NOTE: This does not expand to a literal, but a constant expression. + // Therefore code that depends on `line!()` expanding to a literal needs special adjustment. + () => {{ const {{ ::core::line!() - __DOCTEST_ANCHOR + {line} }} }} }} // Many tests need the prelude, so provide it by default. @@ -213,7 +205,7 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ /// The anchor where the test code body starts. #[allow(unused)] - static __DOCTEST_ANCHOR: i32 = ::core::line!() as i32 + {body_offset} + 2; + static __DOCTEST_ANCHOR: u32 = ::core::line!() + {body_offset} + 2; {{ #![allow(unreachable_pub, clippy::disallowed_names)] {body} @@ -224,14 +216,9 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{ "# ) .unwrap(); - - write!(c_test_declarations, "void {kunit_name}(struct kunit *);\n").unwrap(); - write!(c_test_cases, " KUNIT_CASE({kunit_name}),\n").unwrap(); } - let rust_tests = rust_tests.trim(); - let c_test_declarations = c_test_declarations.trim(); - let c_test_cases = c_test_cases.trim(); + let tests = tests.trim(); write!( BufWriter::new(File::create("rust/doctests_kernel_generated.rs").unwrap()), @@ -257,34 +244,10 @@ impl ModuleMetadata for LocalModule {{ }}; }} -{rust_tests} -"# - ) - .unwrap(); - - write!( - BufWriter::new(File::create("rust/doctests_kernel_generated_kunit.c").unwrap()), - r#"/* - * `kernel` crate documentation tests. - */ - -#include <kunit/test.h> - -{c_test_declarations} - -static struct kunit_case test_cases[] = {{ - {c_test_cases} - {{ }} -}}; - -static struct kunit_suite test_suite = {{ - .name = "rust_doctests_kernel", - .test_cases = test_cases, -}}; - -kunit_test_suite(test_suite); - -MODULE_LICENSE("GPL"); +#[kernel::macros::kunit_tests(rust_doctests_kernel)] +mod tests {{ +{tests} +}} "# ) .unwrap(); |
