1

我正在尝试创建一个名为derive_pattern 的简单库。我的最终目标是能够写出这样的东西:

#[derive(Pattern)]
struct TestStruct {
    x: i32,
    y: i32,
}

#[test]
fn it_works() {
    let test = TestStruct { x: 5, y: 10 };
    match test {
        // this macro should have been created by the derive,
        // and should expand to a pattern that includes all of the field names
        test_struct_pattern!() => {
            // in this scope x and y should be defined because the pattern bound them
            assert!(x == 5);
            assert!(y == 5);
        }
        _ => unreachable!("pattern should have matched"),
    }
}

我很好奇这是否可能。到目前为止,我的尝试都失败了。在定义程序宏时,可以控制标识符的范围以绕过卫生,但据我所知,程序宏无法定义新的程序宏。因此,我的程序宏必须生成 macro_rules 宏,而这些宏最终会强制执行卫生。

这是我的程序宏定义:

use inflector::Inflector;
use proc_macro2::Span;
use quote::quote;
use syn::{Fields, FieldsNamed, Ident, ItemStruct};

#[proc_macro_derive(Pattern)]
pub fn derive_pattern(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = proc_macro2::TokenStream::from(input);

    let structure: ItemStruct = syn::parse2(input).expect("derive(Pattern) only works on structs.");

    let name = structure.ident;

    let macro_name = Ident::new(
        &(name.to_string().to_snake_case() + "_pattern"),
        name.span(),
    );

    match &structure.fields {
        Fields::Named(FieldsNamed {
            brace_token: _,
            named,
        }) => {
            let mut field_names: Vec<Ident> = named
                .iter()
                .map(|f| f.ident.as_ref().unwrap().clone())
                .collect();
            for field_name in &mut field_names {
                field_name.set_span(Span::call_site()); // try (and fail) to bypass hygiene
            }
            let output = quote! {
                macro_rules! #macro_name {
                    () => {
                        #name{ #(#field_names),* }
                    }
                }
            };
            output.into()
        }
        _ => panic!("derive(Pattern) only supports structs with named fields"),
    }
}

我已将带有完整 Cargo.toml 等的版本上传到 github:https ://github.com/jgarvin/derive_pattern

我得到的错误是在测试中的第一个断言上:

   Compiling derive_pattern_test_cases v0.1.0 (/home/prophet/derive_pattern/derive_pattern_test_cases)
error[E0425]: cannot find value `x` in this scope
  --> derive_pattern_test_cases/src/lib.rs:16:25
   |
16 |                 assert!(x == 5);
   |                         ^ not found in this scope

error[E0425]: cannot find value `y` in this scope
  --> derive_pattern_test_cases/src/lib.rs:17:25
   |
17 |                 assert!(y == 5);
   |                         ^ not found in this scope
4

0 回答 0