rust-lang / rust-lang/rust

`proc_macro` when used in conjunction with missing generics causes erroneous rustc error

Open
#149,939 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics A-proc-macros D-imprecise-spans D-invalid-suggestion T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
#[proc_macro_derive(MyFrom)]
pub fn my_derive(input: TokenStream) -> TokenStream {
    let derived_input = parse_macro_input!(input as DeriveInput);
    let ident = &derived_input.ident;
    if let Data::Struct(data) = derived_input.data {
        match data.fields {
            Fields::Named(fields) => {
                if fields.named.len() != 1 {
                    return syn::Error::new(fields.span(), "#[derive(MyFrom)] cannot be implemented on anything other than a struct with one field").to_compile_error().into();
                }
                let field = fields.named.first().unwrap();
                let name = field.ident.as_ref().unwrap();
                let field_type = field.ty.clone();
                if derived_input.generics.params.is_empty() {
                    quote! {
                        impl From<#field_type> for #ident {
                            fn from(value: #field_type) -> Self {
                                Self {
                                    #name: value
                                }
                            }
                        }
                    }.into()
                } else {
                    let generics = derived_input.generics.params;
                    if let Some(where_clause) = derived_input.generics.where_clause {
                        quote! {
                            impl<#generics> From<#field_type> for #ident<#generics> #where_clause {
                                fn from(value: #field_type) -> Self {
                                    Self {
                                        #name: value
                                    }
                                }
                            }
                        }.into()
                    } else {
                        quote! {
                            impl<#generics> From<#field_type> for #ident<#generics> {
                                fn from(value: #field_type) -> Self {
                                    Self {
                                        #name: value
                                    }
                                }
                            }
                        }.into()
                    }
                }
            },
            Fields::Unnamed(fields) => {
                if fields.unnamed.len() != 1 {
                    return syn::Error::new(fields.span(), "#[derive(MyFrom)] cannot be implemented on anything other than a struct with one field").to_compile_error().into();
                }
                let field = fields.unnamed.first().unwrap();
                let field_type = field.ty.clone();
                if derived_input.generics.params.is_empty() {
                    quote! {
                        impl From<#field_type> for #ident {
                            fn from(value: #field_type) -> Self {
                                Self(value)
                            }
                        }
                    }.into()
                } else {
                    let generics = derived_input.generics.params;
                    if let Some(where_clause) = derived_input.generics.where_clause {
                        quote! {
                            impl<#generics> From<#field_type> for #ident<#generics> #where_clause {
                                fn from(value: #field_type) -> Self {
                                    Self(value)
                                }
                            }
                        }.into()
                    } else {
                        quote! {
                            impl<#generics> From<#field_type> for #ident<#generics> {
                                fn from(value: #field_type) -> Self {
                                    Self(value)
                                }
                            }
                        }.into()
                    }
                }
            },
            Fields::Unit => {
                syn::Error::new(ident.span(), "#[derive(MyFrom)] cannot be implemented on anything other than a struct with one field").to_compile_error().into()
            }
        }
    } else {
        syn::Error::new(ident.span(), "#[derive(MyFrom)] cannot be implemented on anything other than a struct with one field").to_compile_error().into()
    }
}


and

#[derive(MyFrom)]
struct MyTupleGeneric<T> {
    name: T
}
Current output
error[E0107]: missing generics for struct `MyTupleGeneric`                                                                                                                      
  --> derive_from\tests\from_works.rs:12:8
   |
12 | struct MyTupleGeneric<T> {
   |        ^^^^^^^^^^^^^^ expected 1 generic argument
   |
note: struct defined here, with 1 generic parameter: `T`
  --> derive_from\tests\from_works.rs:12:8
   |
12 | struct MyTupleGeneric<T> {
   |        ^^^^^^^^^^^^^^ -
help: add missing generic argument
   |
12 | struct MyTupleGeneric<T><T> {
Desired output
error[E0107]: missing generics for struct `MyTupleGeneric`                                                                                                                      
  --> derive_from\tests\from_works.rs:12:8
   |
12 | struct MyTupleGeneric<T> {
   |        ^^^^^^^^^^^^^^ expected 1 generic argument
   |
note: struct defined here, with 1 generic parameter: `T`
  --> derive_from\tests\from_works.rs:12:8
   |
12 | struct MyTupleGeneric<T> {
   |        ^^^^^^^^^^^^^^ -
help: add missing generic parameter to trait impl
   |
   | impl Tr
   |
Rationale and extra context

In proc_macros, when a generic argument is missing, the code errors and erroneously suggests that the user of the macro is responsible.
I believe this must be changed to ensure that the proc_macro itself is shown to be in error.

Other cases

Rust Version
rustc 1.92.0 (ded5c06cf 2025-12-08)
binary: rustc
commit-hash: ded5c06cf21d2b93bffd5d884aa6e96934ee4234
commit-date: 2025-12-08
host: x86_64-pc-windows-msvc
release: 1.92.0
LLVM version: 21.1.3
Anything else?

I wish that this code doesn't produce the erroneous output but rather a complete explanation of where the code errors.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the diagnostic from derive_from\tests\from_works.rs using the provided proc-macro and generic struct. Then trace the rustc diagnostic path for missing generics in proc-macro expansion; done means the error identifies the trait impl or proc-macro as responsible instead of suggesting a generic argument on the user's struct.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.