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 协议》,转载必须注明作者和本文链接