proc-macro-workshop:sorted-1

godme發表於2022-07-06
// 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 協議》,轉載必須註明作者和本文連結