syn icon indicating copy to clipboard operation
syn copied to clipboard

parsing fields from `quote` fails with `"expected curly braces"`

Open calbaker opened this issue 1 year ago • 0 comments

Files to regenerate the error

base_crate/Cargo.toml

[package]
name = "add-fields-to-struct"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
proc-macros = { path = "./proc-macros" }

src/main.rs

fn main() {
    let mystruct = MyStruct {
        fieldaroo1: 37.,
        fieldaroo2: 42.,
    };
    println!("{}", mystruct.summity_sumaroo());
}

#[proc_macros::do_the_macro]
pub struct MyStruct {
    pub fieldaroo1: f64,
    pub fieldaroo2: f64,
}

impl MyStruct {
    fn summity_sumaroo(&self) -> f64 {
        self.fieldaroo1 + self.fieldaroo2
    }
}

base_crate/proc-macros/Cargo.toml

[package]
name = "proc-macros"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
proc-macro2 = "1.0.66"
syn = { version = "2.0.28", features = ["full"] }
proc-macro-error = "1.0.4"
quote = "1.0.18"
regex = "1.6.0"

[lib]
proc-macro = true

base_crate/proc-macros/src/lib.rs

pub(crate) use proc_macro::TokenStream;
pub(crate) use proc_macro2::TokenStream as TokenStream2;
pub(crate) use proc_macro_error::{abort, abort_call_site, emit_error, proc_macro_error};
pub(crate) use quote::{quote, ToTokens, TokenStreamExt}; // ToTokens is implicitly used as a trait
pub(crate) use regex::Regex;
pub(crate) use syn::{spanned::Spanned, DeriveInput, Field, FieldsNamed, Ident, Meta};

#[proc_macro_error]
#[proc_macro_attribute]
pub fn do_the_macro(_attr: TokenStream, item: TokenStream) -> TokenStream {
    // make a copy so there is something to return as things are built out
    let dummy_item = item.clone();

    let syn_named = TokenStream2::from(quote! {
        pub fieldaroo1: f64,
        pub fieldaroo2: f64,
    });

    let mut ast = syn::parse_macro_input!(item as syn::ItemStruct);
    if let syn::Fields::Named(FieldsNamed { named, .. }) = &mut ast.fields {
        // prints `reference: pub fieldaroo1 : f64, pub fieldaroo2 : f64,`
        println!("reference: {}", named.to_token_stream().to_string());

        // prints `test: pub fieldaroo1 : f64, pub fieldaroo2_electric_boogaloo : f64,`
        println!("test: {}", syn_named.to_token_stream().to_string());
        assert_eq!(syn_named.to_string(), named.to_token_stream().to_string());

        let parsed_named: TokenStream2 = syn::parse2(TokenStream2::from(named.to_token_stream()))
            .map_err(|e| {
                format!(
                    "[{}:{}] Parse 2 failed on named from macro input. {}",
                    file!(),
                    line!(),
                    e
                )
            })
            .unwrap();
    }

    let syn_named: FieldsNamed = syn::parse2(syn_named)
        .map_err(|e| format!("[{}:{}] `parse2` failed. {}", file!(), line!(), e))
        .unwrap();

    dummy_item
}

Output for cargo run

warning: unused imports: `abort_call_site`, `abort`, `emit_error`
 --> proc-macros/src/lib.rs:3:35
  |
3 | pub(crate) use proc_macro_error::{abort, abort_call_site, emit_error, proc_macro_error};
  |                                   ^^^^^  ^^^^^^^^^^^^^^^  ^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `TokenStreamExt`
 --> proc-macros/src/lib.rs:4:41
  |
4 | pub(crate) use quote::{quote, ToTokens, TokenStreamExt}; // ToTokens is implicitly used as a trait
  |                                         ^^^^^^^^^^^^^^

warning: unused import: `regex::Regex`
 --> proc-macros/src/lib.rs:5:16
  |
5 | pub(crate) use regex::Regex;
  |                ^^^^^^^^^^^^

warning: unused imports: `DeriveInput`, `Field`, `Ident`, `Meta`, `spanned::Spanned`
 --> proc-macros/src/lib.rs:6:22
  |
6 | pub(crate) use syn::{spanned::Spanned, DeriveInput, Field, FieldsNamed, Ident, Meta};
  |                      ^^^^^^^^^^^^^^^^  ^^^^^^^^^^^  ^^^^^               ^^^^^  ^^^^

warning: unused variable: `parsed_named`
  --> proc-macros/src/lib.rs:28:13
   |
28 |         let parsed_named: TokenStream2 = syn::parse2(TokenStream2::from(named.to_token_stream()))
   |             ^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_parsed_named`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `syn_named`
  --> proc-macros/src/lib.rs:40:9
   |
40 |     let syn_named: FieldsNamed = syn::parse2(syn_named)
   |         ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_syn_named`

warning: `proc-macros` (lib) generated 6 warnings (run `cargo fix --lib -p proc-macros` to apply 6 suggestions)
   Compiling add-fields-to-struct v0.1.0 (/Users/cbaker2/Documents/playground/rust/add-fields-to-struct)
reference: pub fieldaroo1 : f64, pub fieldaroo2 : f64,
test: pub fieldaroo1 : f64, pub fieldaroo2 : f64,
error: custom attribute panicked
 --> src/main.rs:9:1
  |
9 | #[proc_macros::do_the_macro]
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = help: message: called `Result::unwrap()` on an `Err` value: "[proc-macros/src/lib.rs:41] `parse2` failed. expected curly braces"

error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope
 --> src/main.rs:2:20
  |
2 |     let mystruct = MyStruct {
  |                    ^^^^^^^^ not found in this scope

error[E0412]: cannot find type `MyStruct` in this scope
  --> src/main.rs:15:6
   |
15 | impl MyStruct {
   |      ^^^^^^^^ not found in this scope

Some errors have detailed explanations: E0412, E0422.
For more information about an error, try `rustc --explain E0412`.
error: could not compile `add-fields-to-struct` (bin "add-fields-to-struct") due to 3 previous errors

Expected outcome

This should be runnable code as best as I can tell

calbaker avatar Aug 14 '23 17:08 calbaker