proc-macro-workshop:sorted-1

审题

// This test checks that an attribute macro #[sorted] exists and is imported
// correctly in the module system. If you make the macro return an empty token
// stream or exactly the original token stream, this test will already pass!
//
// Be aware that the meaning of the return value of an attribute macro is
// slightly different from that of a derive macro. Derive macros are only
// allowed to *add* code to the caller's crate. Thus, what they return is
// compiled *in addition to* the struct/enum that is the macro input. On the
// other hand attribute macros are allowed to add code to the caller's crate but
// also modify or remove whatever input the attribute is on. The TokenStream
// returned by the attribute macro completely replaces the input item.
//
// Before moving on to the next test, I recommend also parsing the input token
// stream as a syn::Item. In order for Item to be available you will need to
// enable `features = ["full"]` of your dependency on Syn, since by default Syn
// only builds the minimum set of parsers needed for derive macros.
//
// After parsing, the macro can return back exactly the original token stream so
// that the input enum remains in the callers code and continues to be usable by
// code in the rest of the crate.
//
//
// Resources:
//
//   - The Syn crate for parsing procedural macro input:
//     https://github.com/dtolnay/syn
//
//   - The syn::Item type which represents a parsed enum as a syntax tree:
//     https://docs.rs/syn/1.0/syn/enum.Item.html

use sorted::sorted;

#[sorted]
pub enum Conference {
    RustBeltRust,
    RustConf,
    RustFest,
    RustLatam,
    RustRush,
}

fn main() {}

这道题还是和之前一样的套路,估计也就是一个proc_macro2::TokenStream ::new().into()的事情。

题解

// solution1
pub(crate) fn solution(item: &syn::Item) -> syn::Result<proc_macro2::TokenStream> {
    crate::solution2::solution(item)
}

其实这里直接生成proc_macro2::TokenStream就可以的,但这道题只是个架子,我就卖个关子,包一下。

完整

#[proc_macro_attribute]
pub fn sorted(
    _args: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let item = syn::parse_macro_input!(input as syn::Item);
    match solution1::solution(&item) {
        syn::Result::Ok(stream) => stream,
        syn::Result::Err(e) => {
            let mut res = e.into_compile_error();
            res.extend(crate::common::to_token_stream(item));
            res
        }
    }
    .into()
}

和其他的macro比起来,这里比较特殊。
因为属性宏输入的input其实并非有经过编译器的解析,但确实是原生的代码。
和派生宏相比,基础的代码是编译器已经解析完成的。
和函数式宏相比,的确是我们需要给出的解析结果。

但是属性宏本身标记的代码需要我们解析,却不是自定义语法。
就算我们解析失败,我们也必须保证原有代码的解析完毕。
因此,就算是解析失败,我们还是要把原有代码的解析给加上
res.extend(crate:: common::to_token_stream(item));

// common.rs
pub(crate) fn  to_token_stream(t: impl  quote::ToTokens) ->  proc_macro2::TokenStream {
    t.to_token_stream()
}

也可以直接调用to_token_stream,不过要额外引入quote::ToTokens

#[proc_macro_attribute],这个就不多说了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!