簡體   English   中英

如何解析自定義 rust proc_macro 屬性中的其他屬性?

[英]How to parse other attributes in custom rust proc_macro attribute?

我正在編寫一個 proc_macro 屬性,該屬性將字段添加到結構中,並且還實現了我的特征(以及其他通過添加#[derive(...)] )到擴展結構

這是我想做的事情的簡化版本:

#[foo("some value")]
#[derive(Debug)]
struct A {
    bar: u32,
}

擴展后:

#[derive(Debug, Default, Serialize)]
struct A {
    foo: u64
    bar: u32,
}

impl FooTrait for A {
    ...
}

我如何解析 derive 屬性以便添加Debug特性以及我使用foo proc_macro 添加的特性的所有 rest?

使用syn ,檢查並更改ItemStruct::attrs

use std::collections::HashSet;

use proc_macro2::{Delimiter, TokenTree};
use quote::ToTokens;
use syn::parse::Parser;
use syn::punctuated::Punctuated;

#[proc_macro_attribute]
pub fn foo(
    attr: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let mut input = syn::parse_macro_input!(input as syn::ItemStruct);

    let mut all_derived_traits = HashSet::new();
    for i in 0..input.attrs.len() {
        if !input.attrs[i].path.is_ident("derive") {
            continue;
        }

        let derive = input.attrs.remove(i);
        let mut tokens = derive.tokens.clone().into_iter();
        match [tokens.next(), tokens.next()] {
            [Some(TokenTree::Group(group)), None]
                if group.delimiter() == Delimiter::Parenthesis =>
            {
                match Punctuated::<syn::Path, syn::Token![,]>::parse_terminated
                    .parse2(group.stream())
                {
                    Ok(derived_traits) => all_derived_traits.extend(derived_traits),
                    Err(e) => return e.into_compile_error().into(),
                }
            }
            _ => {
                return syn::Error::new_spanned(derive, "malformed derive")
                    .into_compile_error()
                    .into()
            }
        }
    }
    
    all_derived_traits.extend([
        syn::parse_quote!(Default),
        syn::parse_quote!(Serialize),
    ]);
    
    let all_derived_traits = all_derived_traits.into_iter();
    input.attrs.push(syn::parse_quote! {
        #[derive( #(#all_derived_traits),* )]
    });
    
    input.into_token_stream().into()
}

首先,我們將所有derive()屬性收集到哈希集中,因此如果它們已經存在,我們將不會生成派生。 這個邏輯並不完美:例如,我們不會將std::default::Default標識為與Default相同。 但它應該足夠了。 然后我們添加我們的特征並重新生成派生。 請注意,多個#[derive()]行是統一的,並且可以對特征進行排序,但這並不重要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM