Skip to content

Commit 60f18c2

Browse files
bjorn3ojeda
authored andcommitted
rust: macros: add concat_idents! proc macro
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 <[email protected]> Reviewed-by: Finn Behrens <[email protected]> Reviewed-by: Gary Guo <[email protected]> [Reworded, adapted for upstream and applied latest changes] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent c3630df commit 60f18c2

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

rust/macros/concat_idents.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use proc_macro::{token_stream, Ident, TokenStream, TokenTree};
4+
5+
use crate::helpers::expect_punct;
6+
7+
fn expect_ident(it: &mut token_stream::IntoIter) -> Ident {
8+
if let Some(TokenTree::Ident(ident)) = it.next() {
9+
ident
10+
} else {
11+
panic!("Expected Ident")
12+
}
13+
}
14+
15+
pub(crate) fn concat_idents(ts: TokenStream) -> TokenStream {
16+
let mut it = ts.into_iter();
17+
let a = expect_ident(&mut it);
18+
assert_eq!(expect_punct(&mut it), ',');
19+
let b = expect_ident(&mut it);
20+
assert!(it.next().is_none(), "only two idents can be concatenated");
21+
let res = Ident::new(&format!("{a}{b}"), b.span());
22+
TokenStream::from_iter([TokenTree::Ident(res)])
23+
}

rust/macros/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//! Crate for all kernel procedural macros.
44
5+
mod concat_idents;
56
mod helpers;
67
mod module;
78

@@ -70,3 +71,46 @@ use proc_macro::TokenStream;
7071
pub fn module(ts: TokenStream) -> TokenStream {
7172
module::module(ts)
7273
}
74+
75+
/// Concatenate two identifiers.
76+
///
77+
/// This is useful in macros that need to declare or reference items with names
78+
/// starting with a fixed prefix and ending in a user specified name. The resulting
79+
/// identifier has the span of the second argument.
80+
///
81+
/// # Examples
82+
///
83+
/// ```ignore
84+
/// use kernel::macro::concat_idents;
85+
///
86+
/// macro_rules! pub_no_prefix {
87+
/// ($prefix:ident, $($newname:ident),+) => {
88+
/// $(pub(crate) const $newname: u32 = kernel::macros::concat_idents!($prefix, $newname);)+
89+
/// };
90+
/// }
91+
///
92+
/// pub_no_prefix!(
93+
/// binder_driver_return_protocol_,
94+
/// BR_OK,
95+
/// BR_ERROR,
96+
/// BR_TRANSACTION,
97+
/// BR_REPLY,
98+
/// BR_DEAD_REPLY,
99+
/// BR_TRANSACTION_COMPLETE,
100+
/// BR_INCREFS,
101+
/// BR_ACQUIRE,
102+
/// BR_RELEASE,
103+
/// BR_DECREFS,
104+
/// BR_NOOP,
105+
/// BR_SPAWN_LOOPER,
106+
/// BR_DEAD_BINDER,
107+
/// BR_CLEAR_DEATH_NOTIFICATION_DONE,
108+
/// BR_FAILED_REPLY
109+
/// );
110+
///
111+
/// assert_eq!(BR_OK, binder_driver_return_protocol_BR_OK);
112+
/// ```
113+
#[proc_macro]
114+
pub fn concat_idents(ts: TokenStream) -> TokenStream {
115+
concat_idents::concat_idents(ts)
116+
}

0 commit comments

Comments
 (0)