New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Special-case quote! macro #3406
Comments
Can you provide an example ? |
I think the first example from the README would do: let tokens = quote! {
struct SerializeWith #generics #where_clause {
value: &'a #field_ty,
phantom: core::marker::PhantomData<#item_ty>,
}
impl #generics serde::Serialize for SerializeWith #generics #where_clause {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#path(self.value, serializer)
}
}
SerializeWith {
value: #value,
phantom: core::marker::PhantomData::<#item_ty>,
}
}; E.g. if it would be copy-pasted without indentation: fn main() {
let tokens = quote! {
struct SerializeWith #generics #where_clause {
value: &'a #field_ty,
phantom: core::marker::PhantomData<#item_ty>,
}
impl #generics serde::Serialize for SerializeWith #generics #where_clause {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#path(self.value, serializer)
}
}
SerializeWith {
value: #value,
phantom: core::marker::PhantomData::<#item_ty>,
}
};
} I'd expect rustfmt to restore the formatting as above. Just like with macro definitions, there are edge cases like repetitions or substitutions that don't make much sense when replaced with identifier, but even best-effort formatting would be already good to have. |
Actually after writing realised that example above already contains substitutions that make formatting hard to do, but something simpler would do. |
I'd very much like to see this. Currently working on a project (not mine) that uses proc macros, and there are tons of things that could use fixing. Needless to say it's quite tedious to do it by hand. |
Actually, now ( Before: use quote::quote;
fn somehting() {
run_compare_test(
quote! {
struct NavPosLLH {
itow: u32,
}
},
quote! {
pub struct NavPosLLHRef<'a>(&'a [u8]);
impl<'a> NavPosLLHRef<'a> {
#[inline]
pub fn itow(&self) -> u32 {
u32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]])
}
}
},
);
}
fn run_compare_test(input: proc_macro2::TokenStream, expect_output: proc_macro2::TokenStream) {} after: use quote::quote;
fn somehting() {
run_compare_test(
quote! {
struct NavPosLLH {
itow: u32,
}
},
quote! {
pub struct NavPosLLHRef<'a>(&'a [u8]);
impl<'a> NavPosLLHRef<'a> {
#[inline]
pub fn itow(&self) -> u32 {
u32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]])
}
}
},
);
}
fn run_compare_test(input: proc_macro2::TokenStream, expect_output: proc_macro2::TokenStream) {} |
@Dushistov - I can't reproduce that. On rustmft 1.4.12 (which is the same as 1.4.11 plus a dependency version bump on the compiler internals) the original formatting remains/rustfmt does not break indentation. Are you using any configuration options? |
Actually, I have no idea why you can not reproduce. I use plain standard rustfmt from rustup installation. I attached "hello world" crate with exactly this lines. ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/rustfmt --edition 2018 --emit stdout src/lib.rs
/tmp/foo/src/lib.rs:
use quote::quote;
fn somehting() {
run_compare_test(
quote! {
struct NavPosLLH {
itow: u32,
}
},
quote! {
pub struct NavPosLLHRef<'a>(&'a [u8]);
impl<'a> NavPosLLHRef<'a> {
#[inline]
pub fn itow(&self) -> u32 {
u32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]])
}
}
},
);
}
fn run_compare_test(input: proc_macro2::TokenStream, expect_output: proc_macro2::TokenStream) {} |
That helped thanks! The problem with your original/before snippet @Dushistov is that you have a mixture of hard tabs and spaces in your indentation, not related to this issue. When you run rustfmt, the hard tabs are converted to spaces (default behaviour is 4 spaces). Certain renderers (including GH) use 8 spaces for tabs which is why your snippet looks aligned differently in different places (GH vs. Rust Playground). In order to resolve this, you'll want to use tabs or spaces consistently. Specifically, on line 7 ( original snippet with trailing comments added to highlight hard tab usage use quote::quote;
fn somehting() {
run_compare_test(
quote! {
struct NavPosLLH {
itow: u32, // 2 hard tabs
}
},
quote! {
pub struct NavPosLLHRef<'a>(&'a [u8]);
impl<'a> NavPosLLHRef<'a> {
#[inline] // 2 hard tabs
pub fn itow(&self) -> u32 { // 2 hard tabs
u32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]]) // 2 hard tabs and 4 spaces
} // 2 hard tabs
}
},
);
}
fn run_compare_test(input: proc_macro2::TokenStream, expect_output: proc_macro2::TokenStream) {} |
quote!
is fairly popular for code generation, and more often than not its argument could be passed and formatted as valid Rust code.The only thing that prevents that from happening right now is parser stumbling on
#name
substitutions, but if they're temporarily replaced with regular identifiers as we already do for macro definitions, this shouldn't be a problem.The text was updated successfully, but these errors were encountered: