Re: [PATCH v1 05/28] rust: macros: add `concat_idents!` proc macro

Finn Behrens
2022-11-11T09:33:41Z

On 10 Nov 2022, at 17:41, Miguel Ojeda wrote:

> From: Björn Roy Baron <bjorn3_gh@protonmail.com>
>
> This macro provides similar functionality to the unstable feature
> `concat_idents` without having to rely on it.
>
> For instance:
>
> let x_1 = 42;
> let x_2 = concat_idents!(x, _1);
> assert!(x_1 == x_2);
>
> It has different behavior with respect to macro hygiene. Unlike
> the unstable `concat_idents!` macro, it allows, for example,
> referring to local variables by taking the span of the second
> macro as span for the output identifier.
>
> Signed-off-by: Björn Roy Baron <bjorn3_gh@protonmail.com>
> [Reworded, adapted for upstream and applied latest changes]
> Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
Reviewed-by: Finn Behrens <me@kloenk.dev>

Regards,
Finn

> ---
> rust/macros/concat_idents.rs | 23 +++++++++++++++++++
> rust/macros/lib.rs | 44 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 67 insertions(+)
> create mode 100644 rust/macros/concat_idents.rs
>
> diff --git a/rust/macros/concat_idents.rs b/rust/macros/concat_idents.rs
> new file mode 100644
> index 000000000000..3b5a9dd70e8a
> --- /dev/null
> +++ b/rust/macros/concat_idents.rs
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +use proc_macro::{token_stream, Ident, TokenStream, TokenTree};
> +
> +use crate::helpers::expect_punct;
> +
> +fn expect_ident(it: &mut token_stream::IntoIter) -> Ident {
> + if let Some(TokenTree::Ident(ident)) = it.next() {
> + ident
> + } else {
> + panic!("Expected Ident")
> + }
> +}
> +
> +pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream {
> + let mut it = ts.into_iter();
> + let a = expect_ident(&mut it);
> + assert_eq!(expect_punct(&mut it), ',');
> + let b = expect_ident(&mut it);
> + assert!(it.next().is_none(), "only two idents can be concatenated");
> + let res = Ident::new(&(a.to_string() + &b.to_string()), b.span());
> + TokenStream::from_iter([TokenTree::Ident(res)])
> +}
> diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs
> index 91764bfb1f89..15555e7ff487 100644
> --- a/rust/macros/lib.rs
> +++ b/rust/macros/lib.rs
> @@ -2,6 +2,7 @@
>
> //! Crate for all kernel procedural macros.
>
> +mod concat_idents;
> mod helpers;
> mod module;
>
> @@ -70,3 +71,46 @@ use proc_macro::TokenStream;
> pub fn module(ts: TokenStream) -> TokenStream {
> module::module(ts)
> }
> +
> +/// Concatenate two identifiers.
> +///
> +/// This is useful in macros that need to declare or reference items with names
> +/// starting with a fixed prefix and ending in a user specified name. The resulting
> +/// identifier has the span of the second argument.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// use kernel::macro::concat_idents;
> +///
> +/// macro_rules! pub_no_prefix {
> +/// ($prefix:ident, $($newname:ident),+) => {
> +/// $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+
> +/// };
> +/// }
> +///
> +/// pub_no_prefix!(
> +/// binder_driver_return_protocol_,
> +/// BR_OK,
> +/// BR_ERROR,
> +/// BR_TRANSACTION,
> +/// BR_REPLY,
> +/// BR_DEAD_REPLY,
> +/// BR_TRANSACTION_COMPLETE,
> +/// BR_INCREFS,
> +/// BR_ACQUIRE,
> +/// BR_RELEASE,
> +/// BR_DECREFS,
> +/// BR_NOOP,
> +/// BR_SPAWN_LOOPER,
> +/// BR_DEAD_BINDER,
> +/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
> +/// BR_FAILED_REPLY
> +/// );
> +///
> +/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
> +/// ```
> +#[proc_macro]
> +pub fn concat_idents(ts: TokenStream) -> TokenStream {
> + concat_idents::concat_idents(ts)
> +}
> --
> 2.38.1