xref: /linux/rust/macros/vtable.rs (revision a52a7596c376ac7bce5a5322c526d96e2780ad17)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 use std::{
4     collections::HashSet,
5     iter::Extend, //
6 };
7 
8 use proc_macro2::{
9     Ident,
10     TokenStream, //
11 };
12 use quote::{
13     quote,
14     ToTokens, //
15 };
16 use syn::{
17     parse_quote,
18     Error,
19     ImplItem,
20     Item,
21     ItemImpl,
22     ItemTrait,
23     Result,
24     TraitItem, //
25 };
26 
27 fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
28     let mut gen_items = Vec::new();
29 
30     gen_items.push(parse_quote! {
31          /// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
32          /// attribute when implementing this trait.
33          const USE_VTABLE_ATTR: ();
34     });
35 
36     // Add `type OwnerModule: ModuleMetadata` as a required associated type if
37     // the trait does not already define it.
38     if !item
39         .items
40         .iter()
41         .any(|i| matches!(i, TraitItem::Type(t) if t.ident == "OwnerModule"))
42     {
43         gen_items.push(parse_quote! {
44             /// The module implementing this vtable trait.
45             ///
46             /// Automatically set to `crate::LocalModule` by the `#[vtable]`
47             /// impl macro.
48             type OwnerModule: ::kernel::ModuleMetadata;
49         });
50     }
51 
52     for item in &mut item.items {
53         if let TraitItem::Fn(fn_item) = item {
54             let name = &fn_item.sig.ident;
55             let gen_const_name = Ident::new(
56                 &format!("HAS_{}", name.to_string().to_uppercase()),
57                 name.span(),
58             );
59 
60             if fn_item
61                 .attrs
62                 .extract_if(.., |attr| attr.path().is_ident("optional"))
63                 .count()
64                 != 0
65             {
66                 if let Some(default) = &fn_item.default {
67                     Err(Error::new_spanned(
68                         default,
69                         "`#[optional]` methods must not have a default implementation",
70                     ))?;
71                 }
72 
73                 // Optional methods in Rust need a default implementation. Inject one that fails
74                 // the build at compile time if not overridden.
75                 fn_item.default = Some(parse_quote!({
76                     ::kernel::build_assert::build_error!(
77                         "This function must not be called, see the `#[vtable]` documentation."
78                     );
79                 }));
80                 // Ensure that the function is never code generated unless used.
81                 fn_item.attrs.push(parse_quote!(#[inline]));
82             }
83 
84             // We don't know on the implementation-site whether a method is required or provided
85             // so we have to generate a const for all methods.
86             // However, hide it for required methods as it will always be true.
87             let doc = if fn_item.default.is_some() {
88                 let comment =
89                     format!("Indicates if the `{name}` method is overridden by the implementor.");
90                 quote!(#[doc = #comment])
91             } else {
92                 quote!(#[doc(hidden)])
93             };
94             let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
95             gen_items.push(parse_quote! {
96                 #(#cfg_attrs)*
97                 #doc
98                 const #gen_const_name: bool = false;
99             });
100         }
101     }
102 
103     item.items.extend(gen_items);
104     Ok(item)
105 }
106 
107 fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
108     let mut gen_items = Vec::new();
109     let mut defined_items = HashSet::new();
110 
111     // Iterate over all user-defined items to gather any possible explicit overrides.
112     for item in &item.items {
113         match item {
114             ImplItem::Const(const_item) => {
115                 defined_items.insert(const_item.ident.clone());
116             }
117             ImplItem::Type(type_item) => {
118                 defined_items.insert(type_item.ident.clone());
119             }
120             _ => {}
121         }
122     }
123 
124     gen_items.push(parse_quote! {
125         const USE_VTABLE_ATTR: () = ();
126     });
127 
128     // Auto-insert `type OwnerModule = crate::LocalModule` if not explicitly defined.
129     // `crate::LocalModule` resolves to the real module type (via `module!`) or a
130     // dummy fallback in non-module contexts (e.g., doctests).
131     if !defined_items.contains(&parse_quote!(OwnerModule)) {
132         gen_items.push(parse_quote! {
133             type OwnerModule = crate::LocalModule;
134         });
135     }
136 
137     for item in &item.items {
138         if let ImplItem::Fn(fn_item) = item {
139             let name = &fn_item.sig.ident;
140             let gen_const_name = Ident::new(
141                 &format!("HAS_{}", name.to_string().to_uppercase()),
142                 name.span(),
143             );
144             // Skip if it's declared already -- this allows user override.
145             if defined_items.contains(&gen_const_name) {
146                 continue;
147             }
148             let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
149             gen_items.push(parse_quote! {
150                 #(#cfg_attrs)*
151                 const #gen_const_name: bool = true;
152             });
153         }
154     }
155 
156     item.items.extend(gen_items);
157     Ok(item)
158 }
159 
160 pub(crate) fn vtable(input: Item) -> Result<TokenStream> {
161     match input {
162         Item::Trait(item) => Ok(handle_trait(item)?.into_token_stream()),
163         Item::Impl(item) => Ok(handle_impl(item)?.into_token_stream()),
164         _ => Err(Error::new_spanned(
165             input,
166             "`#[vtable]` attribute should only be applied to trait or impl block",
167         ))?,
168     }
169 }
170