Re: [PATCH] rust: macros: improve `#[vtable]` documentation

From: Wedson Almeida Filho
Date: Thu Oct 12 2023 - 09:48:41 EST


On Thu, 12 Oct 2023 at 10:22, Benno Lossin <benno.lossin@xxxxxxxxx> wrote:
>
> Traits marked with `#[vtable]` need to provide default implementations
> for optional functions. The C side represents these with `NULL` in the
> vtable, so the default functions are never actually called. We do not
> want to replicate the default behavior from C in Rust, because that is
> not maintainable. Therefore we should use `build_error` in those default
> implementations. The error message for that is provided at
> `kernel::error::VTABLE_DEFAULT_ERROR`.
>
> Signed-off-by: Benno Lossin <benno.lossin@xxxxxxxxx>

Reviewed-By: Wedson Almeida Filho <walmeida@xxxxxxxxxxxxx>

> ---
> rust/kernel/error.rs | 4 ++++
> rust/macros/lib.rs | 32 ++++++++++++++++++++++++--------
> 2 files changed, 28 insertions(+), 8 deletions(-)
>
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 05fcab6abfe6..1373cde025ef 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -335,3 +335,7 @@ pub(crate) fn from_result<T, F>(f: F) -> T
> Err(e) => T::from(e.to_errno() as i16),
> }
> }
> +
> +/// Error message for calling a default function of a [`#[vtable]`](macros::vtable) trait.
> +pub const VTABLE_DEFAULT_ERROR: &str =
> + "This function must not be called, see the #[vtable] documentation.";
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index c42105c2ff96..dab9a1080b82 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -87,27 +87,41 @@ pub fn module(ts: TokenStream) -> TokenStream {
> /// implementation could just return `Error::EINVAL`); Linux typically use C
> /// `NULL` pointers to represent these functions.
> ///
> -/// This attribute is intended to close the gap. Traits can be declared and
> -/// implemented with the `#[vtable]` attribute, and a `HAS_*` associated constant
> -/// will be generated for each method in the trait, indicating if the implementor
> -/// has overridden a method.
> +/// This attribute closes that gap. A trait can be annotated with the `#[vtable]` attribute.
> +/// Implementers of the trait will then also have to annotate the trait with `#[vtable]`. This
> +/// attribute generates a `HAS_*` associated constant bool for each method in the trait that is set
> +/// to true if the implementer has overridden the associated method.
> +///
> +/// If you want to make a function optional, you must provide a default implementation. But this

We should standardise how we write our documentation. In the `rust`
branch, we avoided using the imperative mood like you have here; the
rationale was that the documentation was describing how things
are/work, so we shouldn't be giving orders to readers (they may be
authors of traits, implementers of some vtable trait, or neither, just
someone learning about things, etc.).

In the paragraph above, you'll find an example: "Implementers of the
trait will then also have to...".

For the specific case above, I'd suggest: 'For a function to be
optional, it must have a default implementation.', or using the
passive voice, 'To make a function optional, a default implementation
must be provided'.

> +/// default implementation will never be executed, since these functions are exclusively called
> +/// from callbacks from the C side. This is because the vtable will have a `NULL` entry and the C
> +/// side will execute the default behavior. Since it is not maintainable to replicate the default
> +/// behavior in Rust, you should use the following code:
> +///
> +/// ```compile_fail
> +/// # use kernel::error::VTABLE_DEFAULT_ERROR;
> +/// kernel::build_error(VTABLE_DEFAULT_ERROR)
> +/// ```
> +///
> +/// note that you might need to import [`kernel::error::VTABLE_DEFAULT_ERROR`].
> ///
> -/// This attribute is not needed if all methods are required.
> +/// This macro should not be used when all function are required.
> ///
> /// # Examples
> ///
> /// ```ignore
> +/// # use kernel::error::VTABLE_DEFAULT_ERROR;
> /// use kernel::prelude::*;
> ///
> /// // Declares a `#[vtable]` trait
> /// #[vtable]
> -/// pub trait Operations: Send + Sync + Sized {
> +/// pub trait Operations {
> /// fn foo(&self) -> Result<()> {
> -/// Err(EINVAL)
> +/// kernel::build_error(VTABLE_DEFAULT_ERROR)
> /// }
> ///
> /// fn bar(&self) -> Result<()> {
> -/// Err(EINVAL)
> +/// kernel::build_error(VTABLE_DEFAULT_ERROR)
> /// }
> /// }
> ///
> @@ -125,6 +139,8 @@ pub fn module(ts: TokenStream) -> TokenStream {
> /// assert_eq!(<Foo as Operations>::HAS_FOO, true);
> /// 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, ts: TokenStream) -> TokenStream {
> vtable::vtable(attr, ts)
>
> base-commit: b2516f7af9d238ebc391bdbdae01ac9528f1109e
> --
> 2.41.0
>
>