xref: /DragonOS/kernel/crates/intertrait/macros/src/item_impl.rs (revision c635d8a9cfe25bc11779f323ef0c7d7a0f597d4a)
1 use hashbrown::HashSet;
2 use proc_macro2::TokenStream;
3 use quote::{quote, quote_spanned, ToTokens};
4 use syn::punctuated::Punctuated;
5 use syn::spanned::Spanned;
6 use syn::Token;
7 use syn::{
8     AngleBracketedGenericArguments, Binding, GenericArgument, ImplItem, ItemImpl, Path,
9     PathArguments,
10 };
11 use PathArguments::AngleBracketed;
12 
13 use crate::args::Flag;
14 use crate::gen_caster::generate_caster;
15 
16 pub fn process(flags: &HashSet<Flag>, input: ItemImpl) -> TokenStream {
17     let ItemImpl {
18         ref self_ty,
19         ref trait_,
20         ref items,
21         ..
22     } = input;
23 
24     let generated = match trait_ {
25         None => quote_spanned! {
26             self_ty.span() => compile_error!("#[cast_to] should only be on an impl of a trait");
27         },
28         Some(trait_) => match trait_ {
29             (Some(bang), _, _) => quote_spanned! {
30                 bang.span() => compile_error!("#[cast_to] is not for !Trait impl");
31             },
32             (None, path, _) => {
33                 let path = fully_bound_trait(path, items);
34                 generate_caster(self_ty, &path, flags.contains(&Flag::Sync))
35             }
36         },
37     };
38 
39     quote! {
40         #input
41         #generated
42     }
43 }
44 
45 fn fully_bound_trait(path: &Path, items: &[ImplItem]) -> impl ToTokens {
46     let bindings = items
47         .iter()
48         .filter_map(|item| {
49             if let ImplItem::Type(assoc_ty) = item {
50                 Some(GenericArgument::Binding(Binding {
51                     ident: assoc_ty.ident.to_owned(),
52                     eq_token: Default::default(),
53                     ty: assoc_ty.ty.to_owned(),
54                 }))
55             } else {
56                 None
57             }
58         })
59         .collect::<Punctuated<_, Token![,]>>();
60 
61     let mut path = path.clone();
62 
63     if bindings.is_empty() {
64         return path;
65     }
66 
67     if let Some(last) = path.segments.last_mut() {
68         match &mut last.arguments {
69             PathArguments::None => {
70                 last.arguments = AngleBracketed(AngleBracketedGenericArguments {
71                     colon2_token: None,
72                     lt_token: Default::default(),
73                     args: bindings,
74                     gt_token: Default::default(),
75                 })
76             }
77             AngleBracketed(args) => args.args.extend(bindings),
78             _ => {}
79         }
80     }
81     path
82 }
83