This uses proc_macro_error2::set_dummy
to correctly return the derive input stream and a shim implementation (if possible) when encountering an error, as opposed to the previous implementation which would generate the shim but never return the token stream, leading to the derive input getting consumed by the macro and causing unnecessary errors.
TFKGE6LRNSG2N5Z3LIITYG7WH2VOLWQO7AESTWRWV52BFHESU26AC
OWXLFLRMQDTXWN5QQQLJNAATWFWXIN2S4UQA2LC2A6AWX4UWM6LQC
CESJ4CTO26X4GBZBPXRXLOJT3JQJOGFN5EJSNAAZELNQRZF7QSYAC
73C6NOJ7W7ZCQ2THEZ4HXNZF3734VIPHI5VKQ2CCPLOA4L3JFIIQC
7JPOCQEISAIOD7LV4JYBE6NNUWUKKNE73MEPQYTIZ7PP44ZAD2RAC
LU6IFZFGPIKF3CBWZWITWVBSCYWF7Q4UXJDXVRWZ4XV7PKE5RSTQC
QJC4IQITOQP65AFLA5CMH2EXHB6B3SOLW2XBV72U5ZQU2KOR2EIAC
NEBSVXIASWSJO2CVU3VWRONIWJJDLL3VDS6WNKQBWQBRUUI7RSYAC
6XEMHUGSNX5YSWZYM7PZUTTUMFODMGO74QLHGEXQ5LAC7LPS7JNQC
RUCC2HKZZTUHN3G6IWS4NK3VYGXAI6PORJH2YZKPRAYSDWH63ESQC
EKXWNEPK4FTYKT2RJL2L7HTM64VQGDD3DYD6NZIDGMMV6ITHUVZAC
RA3H7PWCI7WHONXB32IWYECQ3VKPKEXXYAJDIOHWNAGHDO7L5ZLQC
O77KA6C4UJGZXVGPEA7WCRQH6XYQJPWETSPDXI3VOKOSRQND7JEQC
// Emit the relevant error message
macro_impl::error::emit(error, &attribute_stream, &derive_input_stream);
// Using the `proc_macro_error2` crate to generate errors ignores
// the token stream returned from this function, so use the `set_dummy` function
// to make sure a macro error is as self-contained as possible:
// - Derive input streams are not consumed, so structs/enums don't "disappear" when macro returns error
// - Generate a shim trait implementation when possible so that editor completions still work as expected
proc_macro_error2::set_dummy(
match syn::parse2::<syn::DeriveInput>(derive_input_stream.clone()) {
// Generate a minimal `Localize` implementation so the error is self-contained
Ok(derive_input) => {
let ident = derive_input.ident;
let (impl_generics, type_generics, where_clause) =
derive_input.generics.split_for_impl();
// Generate a minimal `Localize` implementation so the error is self-contained
match syn::parse2::<syn::DeriveInput>(derive_input_stream.clone()) {
Ok(derive_input) => {
let ident = derive_input.ident;
quote! {
#derive_input_stream
quote! {
#derive_input_stream
impl ::l10n_embed::Localize for #ident {
fn localize(
&self,
context: &::l10n_embed::Context,
buffer: &mut String,
) {
unimplemented!("Encountered error in derive macro")
impl #impl_generics ::l10n_embed::Localize for #ident #type_generics #where_clause {
fn localize(
&self,
context: &::l10n_embed::Context,
buffer: &mut String,
) {
unimplemented!("Encountered error in derive macro")
}
}
// Unable to fail gracefully if the ident is unknown, so just return the original input
Err(_) => derive_input_stream,
}
// Unable to implement Localize if the ident is unknown, so just return the original input
Err(_) => derive_input_stream.clone(),
},
);
// Emit the relevant error message
macro_impl::error::emit(error, &attribute_stream, &derive_input_stream);
// `proc_macro_error2` will output the relevant token stream, but the function still needs to return something
quote!()