mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-03-03 18:28:01 +01:00
Currently, we generate `HAS_` constants as long as the definition exists in the source, regardless if it is cfg-ed out or not. Currently, uses of `#[cfg]` present in both trait and impl, so it is not a problem; however if only the impl side uses `#[cfg]` then `HAS_` constants will incorrectly be true while it shouldnt't. With `syn` support, we can now implement `#[cfg]` handling properly by propagating the `#[cfg]` attributes to the constants. Signed-off-by: Gary Guo <gary@garyguo.net> Reviewed-by: Benno Lossin <lossin@kernel.org> Link: https://patch.msgid.link/20260113170529.2240744-1-gary@kernel.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
106 lines
3.0 KiB
Rust
106 lines
3.0 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
use std::{
|
|
collections::HashSet,
|
|
iter::Extend, //
|
|
};
|
|
|
|
use proc_macro2::{
|
|
Ident,
|
|
TokenStream, //
|
|
};
|
|
use quote::ToTokens;
|
|
use syn::{
|
|
parse_quote,
|
|
Error,
|
|
ImplItem,
|
|
Item,
|
|
ItemImpl,
|
|
ItemTrait,
|
|
Result,
|
|
TraitItem, //
|
|
};
|
|
|
|
fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
|
|
let mut gen_items = Vec::new();
|
|
|
|
gen_items.push(parse_quote! {
|
|
/// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
|
|
/// attribute when implementing this trait.
|
|
const USE_VTABLE_ATTR: ();
|
|
});
|
|
|
|
for item in &item.items {
|
|
if let TraitItem::Fn(fn_item) = item {
|
|
let name = &fn_item.sig.ident;
|
|
let gen_const_name = Ident::new(
|
|
&format!("HAS_{}", name.to_string().to_uppercase()),
|
|
name.span(),
|
|
);
|
|
|
|
// 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.
|
|
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]
|
|
const #gen_const_name: bool = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
item.items.extend(gen_items);
|
|
Ok(item)
|
|
}
|
|
|
|
fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
|
|
let mut gen_items = Vec::new();
|
|
let mut defined_consts = HashSet::new();
|
|
|
|
// Iterate over all user-defined constants to gather any possible explicit overrides.
|
|
for item in &item.items {
|
|
if let ImplItem::Const(const_item) = item {
|
|
defined_consts.insert(const_item.ident.clone());
|
|
}
|
|
}
|
|
|
|
gen_items.push(parse_quote! {
|
|
const USE_VTABLE_ATTR: () = ();
|
|
});
|
|
|
|
for item in &item.items {
|
|
if let ImplItem::Fn(fn_item) = item {
|
|
let name = &fn_item.sig.ident;
|
|
let gen_const_name = Ident::new(
|
|
&format!("HAS_{}", name.to_string().to_uppercase()),
|
|
name.span(),
|
|
);
|
|
// Skip if it's declared already -- this allows user override.
|
|
if defined_consts.contains(&gen_const_name) {
|
|
continue;
|
|
}
|
|
let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
|
|
gen_items.push(parse_quote! {
|
|
#(#cfg_attrs)*
|
|
const #gen_const_name: bool = true;
|
|
});
|
|
}
|
|
}
|
|
|
|
item.items.extend(gen_items);
|
|
Ok(item)
|
|
}
|
|
|
|
pub(crate) fn vtable(input: Item) -> Result<TokenStream> {
|
|
match input {
|
|
Item::Trait(item) => Ok(handle_trait(item)?.into_token_stream()),
|
|
Item::Impl(item) => Ok(handle_impl(item)?.into_token_stream()),
|
|
_ => Err(Error::new_spanned(
|
|
input,
|
|
"`#[vtable]` attribute should only be applied to trait or impl block",
|
|
))?,
|
|
}
|
|
}
|