vtable.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // SPDX-License-Identifier: GPL-2.0
  2. use std::{
  3. collections::HashSet,
  4. iter::Extend, //
  5. };
  6. use proc_macro2::{
  7. Ident,
  8. TokenStream, //
  9. };
  10. use quote::ToTokens;
  11. use syn::{
  12. parse_quote,
  13. Error,
  14. ImplItem,
  15. Item,
  16. ItemImpl,
  17. ItemTrait,
  18. Result,
  19. TraitItem, //
  20. };
  21. fn handle_trait(mut item: ItemTrait) -> Result<ItemTrait> {
  22. let mut gen_items = Vec::new();
  23. gen_items.push(parse_quote! {
  24. /// A marker to prevent implementors from forgetting to use [`#[vtable]`](vtable)
  25. /// attribute when implementing this trait.
  26. const USE_VTABLE_ATTR: ();
  27. });
  28. for item in &item.items {
  29. if let TraitItem::Fn(fn_item) = item {
  30. let name = &fn_item.sig.ident;
  31. let gen_const_name = Ident::new(
  32. &format!("HAS_{}", name.to_string().to_uppercase()),
  33. name.span(),
  34. );
  35. // We don't know on the implementation-site whether a method is required or provided
  36. // so we have to generate a const for all methods.
  37. let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
  38. let comment =
  39. format!("Indicates if the `{name}` method is overridden by the implementor.");
  40. gen_items.push(parse_quote! {
  41. #(#cfg_attrs)*
  42. #[doc = #comment]
  43. const #gen_const_name: bool = false;
  44. });
  45. }
  46. }
  47. item.items.extend(gen_items);
  48. Ok(item)
  49. }
  50. fn handle_impl(mut item: ItemImpl) -> Result<ItemImpl> {
  51. let mut gen_items = Vec::new();
  52. let mut defined_consts = HashSet::new();
  53. // Iterate over all user-defined constants to gather any possible explicit overrides.
  54. for item in &item.items {
  55. if let ImplItem::Const(const_item) = item {
  56. defined_consts.insert(const_item.ident.clone());
  57. }
  58. }
  59. gen_items.push(parse_quote! {
  60. const USE_VTABLE_ATTR: () = ();
  61. });
  62. for item in &item.items {
  63. if let ImplItem::Fn(fn_item) = item {
  64. let name = &fn_item.sig.ident;
  65. let gen_const_name = Ident::new(
  66. &format!("HAS_{}", name.to_string().to_uppercase()),
  67. name.span(),
  68. );
  69. // Skip if it's declared already -- this allows user override.
  70. if defined_consts.contains(&gen_const_name) {
  71. continue;
  72. }
  73. let cfg_attrs = crate::helpers::gather_cfg_attrs(&fn_item.attrs);
  74. gen_items.push(parse_quote! {
  75. #(#cfg_attrs)*
  76. const #gen_const_name: bool = true;
  77. });
  78. }
  79. }
  80. item.items.extend(gen_items);
  81. Ok(item)
  82. }
  83. pub(crate) fn vtable(input: Item) -> Result<TokenStream> {
  84. match input {
  85. Item::Trait(item) => Ok(handle_trait(item)?.into_token_stream()),
  86. Item::Impl(item) => Ok(handle_impl(item)?.into_token_stream()),
  87. _ => Err(Error::new_spanned(
  88. input,
  89. "`#[vtable]` attribute should only be applied to trait or impl block",
  90. ))?,
  91. }
  92. }