// 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 協議》,轉載必須註明作者和本文連結