public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
From: Gary Guo <gary@garyguo.net>
To: Tamir Duberstein <tamird@gmail.com>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Brendan Higgins" <brendan.higgins@linux.dev>,
	"David Gow" <davidgow@google.com>,
	"Rae Moar" <raemoar63@gmail.com>,
	"Guilherme Giacomo Simoes" <trintaeoitogc@gmail.com>,
	"Patrick Miller" <paddymills@proton.me>,
	"José Expósito" <jose.exposito89@gmail.com>,
	rust-for-linux@vger.kernel.org, linux-kselftest@vger.kernel.org,
	kunit-dev@googlegroups.com, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 08/11] rust: macros: convert `#[kunit_tests]` macro to use `syn`
Date: Mon, 5 Jan 2026 02:28:18 +0000	[thread overview]
Message-ID: <20260105022818.5a5c839e.gary@garyguo.net> (raw)
In-Reply-To: <CAJ-ks9mCivfa3kL6ep3QHYw_-p56qGeZn0ApLKo7qvsES-0ArQ@mail.gmail.com>

On Sun, 4 Jan 2026 18:38:53 -0500
Tamir Duberstein <tamird@gmail.com> wrote:

> On Thu, Dec 11, 2025 at 2:30 PM Gary Guo <gary@kernel.org> wrote:
> >
> > From: Gary Guo <gary@garyguo.net>
> >
> > This allows significant cleanups.
> >
> > Signed-off-by: Gary Guo <gary@garyguo.net>
> > ---
> >  rust/macros/kunit.rs | 274 +++++++++++++++++++------------------------
> >  rust/macros/lib.rs   |   6 +-
> >  2 files changed, 123 insertions(+), 157 deletions(-)
> >
> > diff --git a/rust/macros/kunit.rs b/rust/macros/kunit.rs
> > index 7427c17ee5f5c..516219f5b1356 100644
> > --- a/rust/macros/kunit.rs
> > +++ b/rust/macros/kunit.rs
> > ...
> > +    // Make the entire module gated behind `CONFIG_KUNIT`.
> > +    module
> > +        .attrs
> > +        .insert(0, parse_quote!(#[cfg(CONFIG_KUNIT="y")]));  
> 
> Does this need to be the first attribute? I think it can just be
> pushed to the end.

This is the current behaviour, and I think it should be the first.
Other attributes shouldn't need to be processed if kunit is not enabled.

> 
> 
> >
> > -    // Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration.
> > -    let config_kunit = "#[cfg(CONFIG_KUNIT=\"y\")]".to_owned().parse().unwrap();
> > -    tokens.insert(
> > -        0,
> > -        TokenTree::Group(Group::new(Delimiter::None, config_kunit)),
> > -    );
> > +    let mut processed_items = Vec::new();
> > +    let mut test_cases = Vec::new();
> >
> >      // Generate the test KUnit test suite and a test case for each `#[test]`.
> > +    //
> >      // The code generated for the following test module:
> >      //
> >      // ```
> > @@ -110,98 +79,93 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
> >      //
> >      // ::kernel::kunit_unsafe_test_suite!(kunit_test_suit_name, TEST_CASES);
> >      // ```
> > -    let mut kunit_macros = "".to_owned();
> > -    let mut test_cases = "".to_owned();
> > -    let mut assert_macros = "".to_owned();
> > -    let path = crate::helpers::file();
> > -    let num_tests = tests.len();
> > -    for (test, cfg_attr) in tests {
> > -        let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}");
> > -        // Append any `cfg` attributes the user might have written on their tests so we don't
> > -        // attempt to call them when they are `cfg`'d out. An extra `use` is used here to reduce
> > -        // the length of the assert message.
> > -        let kunit_wrapper = format!(
> > -            r#"unsafe extern "C" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit)
> > -            {{
> > -                (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED;
> > -                {cfg_attr} {{
> > -                    (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS;
> > -                    use ::kernel::kunit::is_test_result_ok;
> > -                    assert!(is_test_result_ok({test}()));
> > +    //
> > +    // Non-function items (e.g. imports) are preserved.
> > +    for item in module_items {
> > +        let Item::Fn(mut f) = item else {
> > +            processed_items.push(item);
> > +            continue;
> > +        };
> > +
> > +        // TODO: Replace below with `extract_if` when MSRV is bumped above 1.85.
> > +        // Remove `#[test]` attributes applied on the function and count if any.  
> 
> What does "count if any" mean here?

Sorry, I think the last half of the sentence was missing. I meant
"count if any is removed".

> 
> > +        if !f.attrs.iter().any(|attr| attr.path().is_ident("test")) {
> > +            processed_items.push(Item::Fn(f));
> > +            continue;
> > +        }
> > +        f.attrs.retain(|attr| !attr.path().is_ident("test"));  
> 
> Can this code be something like this:
> 
>         let before = f.attrs.len();
>         f.attrs.retain(|attr| !attr.path().is_ident("test"));
>         let after = f.attrs.len();
> 
>         if after == before {
>             processed_items.push(Item::Fn(f));
>             continue;
>         }

Indeed looks better.

> 
> > +
> > +        let test = f.sig.ident.clone();
> > +
> > +        // Retrieve `#[cfg]` applied on the function which needs to be present on derived items too.
> > +        let cfg_attrs: Vec<_> = f
> > +            .attrs
> > +            .iter()
> > +            .filter(|attr| attr.path().is_ident("cfg"))
> > +            .cloned()
> > +            .collect();
> > +
> > +        // Before the test, override usual `assert!` and `assert_eq!` macros with ones that call
> > +        // KUnit instead.
> > +        let test_str = test.to_string();
> > +        let path = crate::helpers::file();
> > +        processed_items.push(parse_quote! {
> > +            #[allow(unused)]
> > +            macro_rules! assert {
> > +                ($cond:expr $(,)?) => {{
> > +                    kernel::kunit_assert!(#test_str, #path, 0, $cond);
> > +                }}
> > +            }
> > +        });
> > +        processed_items.push(parse_quote! {
> > +            #[allow(unused)]
> > +            macro_rules! assert_eq {
> > +                ($left:expr, $right:expr $(,)?) => {{
> > +                    kernel::kunit_assert_eq!(#test_str, #path, 0, $left, $right);
> >                  }}
> > -            }}"#,
> > +            }
> > +        });  
> 
> Am I reading this right that the macros will be repeatedly redefined
> before each test? Could we put them inside each test body instead?

Indeed. The `test_str` is different for each test so this needs to be
redefined for each test. I experimented about better approaches but
none of them are too satisfactory. I think the existing approach is not
unreasonable.

Putting them into test body sounds more reasonable (I suppose the
existing impl doesn't want to parse functions), but I'll leave it to
the future as this commit is focusing on converting with minimal
behavioural change.

Best,
Gary

  reply	other threads:[~2026-01-05  2:28 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-11 18:56 [PATCH 00/11] refactor Rust proc macros with `syn` Gary Guo
2025-12-11 18:56 ` [PATCH 01/11] rust: pin-init: internal: remove proc-macro[2] and quote workarounds Gary Guo
2025-12-11 21:50   ` Christian Schrefl
2025-12-15 13:01     ` Gary Guo
2025-12-16  9:43       ` Benno Lossin
2026-01-04 13:35   ` Tamir Duberstein
2026-01-05  8:53     ` Benno Lossin
2025-12-11 18:56 ` [PATCH 02/11] rust: macros: use `quote!` from vendored crate Gary Guo
2025-12-16  9:47   ` Benno Lossin
2026-01-04 13:39     ` Tamir Duberstein
2025-12-11 18:56 ` [PATCH 03/11] rust: macros: convert `#[vtable]` macro to use `syn` Gary Guo
2026-01-04 23:44   ` Tamir Duberstein
2026-01-05  2:17     ` Gary Guo
2026-01-05 11:02       ` Tamir Duberstein
2026-01-05 14:27         ` Gary Guo
2026-01-05 15:19           ` Tamir Duberstein
2026-01-05 16:05             ` Gary Guo
2025-12-11 18:56 ` [PATCH 04/11] rust: macros: use `syn` to parse `module!` macro Gary Guo
2026-01-04 22:53   ` Tamir Duberstein
2026-01-05  2:20     ` Gary Guo
2025-12-11 18:56 ` [PATCH 05/11] rust: macros: use `quote!` for " Gary Guo
2026-01-04 21:34   ` Tamir Duberstein
2025-12-11 18:56 ` [PATCH 06/11] rust: macros: convert `#[export]` to use `syn` Gary Guo
2025-12-22  6:39   ` Benno Lossin
2026-01-04 22:56   ` Tamir Duberstein
2025-12-11 18:56 ` [PATCH 07/11] rust: macros: convert `concat_idents!` " Gary Guo
2025-12-22  6:34   ` Benno Lossin
2025-12-22 11:59     ` Gary Guo
2026-01-04 22:59   ` Tamir Duberstein
2025-12-11 18:56 ` [PATCH 08/11] rust: macros: convert `#[kunit_tests]` macro " Gary Guo
2026-01-04 23:38   ` Tamir Duberstein
2026-01-05  2:28     ` Gary Guo [this message]
2025-12-11 18:56 ` [PATCH 09/11] rust: macros: allow arbitrary types to be used in `module!` macro Gary Guo
2025-12-22  6:36   ` Benno Lossin
2026-01-04 23:42   ` Tamir Duberstein
2025-12-11 18:56 ` [PATCH 10/11] rust: macros: rearrange `#[doc(hidden)]` " Gary Guo
2025-12-22  6:37   ` Benno Lossin
2026-01-04 23:42   ` Tamir Duberstein
2025-12-11 18:56 ` [PATCH 11/11] rust: kunit: use `pin_init::zeroed` instead of custom null value Gary Guo
2025-12-16 12:48   ` Benno Lossin
2026-01-04 23:43   ` Tamir Duberstein

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260105022818.5a5c839e.gary@garyguo.net \
    --to=gary@garyguo.net \
    --cc=a.hindborg@kernel.org \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=brendan.higgins@linux.dev \
    --cc=dakr@kernel.org \
    --cc=davidgow@google.com \
    --cc=jose.exposito89@gmail.com \
    --cc=kunit-dev@googlegroups.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=paddymills@proton.me \
    --cc=raemoar63@gmail.com \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tamird@gmail.com \
    --cc=tmgross@umich.edu \
    --cc=trintaeoitogc@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox