diff --git a/crates/turbo-tasks-macros-shared/src/expand.rs b/crates/turbo-tasks-macros-shared/src/expand.rs index 7f27c42130f9da..caaf24f2b90e45 100644 --- a/crates/turbo-tasks-macros-shared/src/expand.rs +++ b/crates/turbo-tasks-macros-shared/src/expand.rs @@ -20,9 +20,9 @@ use syn::{ /// These helpers should themselves call [generate_destructuring] to generate /// the destructure necessary to access the fields of the value. pub fn match_expansion< - EN: Fn(&Ident, &FieldsNamed) -> (TokenStream, TokenStream), - EU: Fn(&Ident, &FieldsUnnamed) -> (TokenStream, TokenStream), - U: Fn(&Ident) -> TokenStream, + EN: Fn(TokenStream, &FieldsNamed) -> (TokenStream, TokenStream), + EU: Fn(TokenStream, &FieldsUnnamed) -> (TokenStream, TokenStream), + U: Fn(TokenStream) -> TokenStream, >( derive_input: &DeriveInput, expand_named: &EN, @@ -33,36 +33,41 @@ pub fn match_expansion< let expand_unit = move |ident| (TokenStream::new(), expand_unit(ident)); match &derive_input.data { Data::Enum(DataEnum { variants, .. }) => { - let (variants_idents, (variants_fields_capture, expansion)): ( - Vec<_>, - (Vec<_>, Vec<_>), - ) = variants - .iter() - .map(|variant| { - ( - &variant.ident, - expand_fields( - &variant.ident, - &variant.fields, - expand_named, - expand_unnamed, - expand_unit, - ), - ) - }) - .unzip(); + let (idents, (variants_fields_capture, expansion)): (Vec<_>, (Vec<_>, Vec<_>)) = + variants + .iter() + .map(|variant| { + let variants_idents = &variant.ident; + let ident = quote! { #ident::#variants_idents }; + ( + ident.clone(), + expand_fields( + ident, + &variant.fields, + expand_named, + expand_unnamed, + expand_unit, + ), + ) + }) + .unzip(); quote! { match self { #( - #ident::#variants_idents #variants_fields_capture => #expansion, + #idents #variants_fields_capture => #expansion, )* } } } Data::Struct(DataStruct { fields, .. }) => { - let (captures, expansion) = - expand_fields(ident, fields, expand_named, expand_unnamed, expand_unit); + let (captures, expansion) = expand_fields( + quote! { #ident }, + fields, + expand_named, + expand_unnamed, + expand_unit, + ); if fields.is_empty() { assert!(captures.is_empty()); @@ -100,12 +105,12 @@ pub fn match_expansion< pub fn expand_fields< 'ident, 'fields, - EN: Fn(&'ident Ident, &'fields FieldsNamed) -> R, - EU: Fn(&'ident Ident, &'fields FieldsUnnamed) -> R, - U: Fn(&'ident Ident) -> R, + EN: Fn(TokenStream, &'fields FieldsNamed) -> R, + EU: Fn(TokenStream, &'fields FieldsUnnamed) -> R, + U: Fn(TokenStream) -> R, R, >( - ident: &'ident Ident, + ident: TokenStream, fields: &'fields Fields, expand_named: EN, expand_unnamed: EU, diff --git a/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs b/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs index 8e928395055558..33774a1c90602d 100644 --- a/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/deterministic_hash_macro.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{parse_macro_input, Data, DeriveInput, FieldsNamed, FieldsUnnamed}; use turbo_tasks_macros_shared::{generate_exhaustive_destructuring, match_expansion}; @@ -35,7 +35,7 @@ pub fn derive_deterministic_hash(input: TokenStream) -> TokenStream { /// Hashes a struct or enum variant with named fields (e.g. `struct Foo { /// bar: u32 }`, `Foo::Bar { baz: u32 }`). -fn hash_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { +fn hash_named(_ident: TokenStream2, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_exhaustive_destructuring(fields.named.iter()); ( captures, @@ -49,7 +49,7 @@ fn hash_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStrea /// Hashes a struct or enum variant with unnamed fields (e.g. `struct /// Foo(u32)`, `Foo::Bar(u32)`). -fn hash_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { +fn hash_unnamed(_ident: TokenStream2, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_exhaustive_destructuring(fields.unnamed.iter()); ( captures, @@ -62,6 +62,6 @@ fn hash_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenS } /// Hashes a unit struct or enum variant (e.g. `struct Foo;`, `Foo::Bar`). -fn hash_unit(_ident: &Ident) -> TokenStream2 { +fn hash_unit(_ident: TokenStream2) -> TokenStream2 { quote! { { } } } diff --git a/crates/turbo-tasks-macros/src/derive/task_input_macro.rs b/crates/turbo-tasks-macros/src/derive/task_input_macro.rs index 56586ee27fcfad..7a96916784d598 100644 --- a/crates/turbo-tasks-macros/src/derive/task_input_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/task_input_macro.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; -use proc_macro2::Ident; use quote::quote; -use syn::{parse_macro_input, spanned::Spanned, Data, DataEnum, DataStruct, DeriveInput}; +use syn::{parse_macro_input, spanned::Spanned, DeriveInput}; +use turbo_tasks_macros_shared::{generate_exhaustive_destructuring, match_expansion}; pub fn derive_task_input(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); @@ -51,177 +51,90 @@ pub fn derive_task_input(input: TokenStream) -> TokenStream { } } - let is_resolved_impl; - let is_transient_impl; - let resolve_impl; - - match &derive_input.data { - Data::Enum(DataEnum { variants, .. }) => { - let variants = if variants.is_empty() { - vec![( - quote! { _ => true }, - quote! { _ => false }, - quote! { _ => unreachable!() }, - )] - } else { - variants - .iter() - .map(|variant| { - let variant_ident = &variant.ident; - let pattern; - let construct; - let field_names: Vec<_>; - match &variant.fields { - syn::Fields::Named(fields) => { - field_names = fields - .named - .iter() - .map(|field| field.ident.clone().unwrap()) - .collect(); - pattern = quote! { - #ident::#variant_ident { #(#field_names,)* } - }; - construct = quote! { - #ident::#variant_ident { #(#field_names,)* } - }; - } - syn::Fields::Unnamed(fields) => { - field_names = (0..fields.unnamed.len()) - .map(|i| Ident::new(&format!("field{}", i), fields.span())) - .collect(); - pattern = quote! { - #ident::#variant_ident ( #(#field_names,)* ) - }; - construct = quote! { - #ident::#variant_ident ( #(#field_names,)* ) - }; - } - syn::Fields::Unit => { - field_names = vec![]; - pattern = quote! { - #ident::#variant_ident - }; - construct = quote! { - #ident::#variant_ident - }; - } - } - ( - quote! { - #pattern => { - #(#field_names.is_resolved() &&)* true - }, - }, - quote! { - #pattern => { - #(#field_names.is_transient() ||)* false - }, - }, - quote! { - #pattern => { - #( - let #field_names = #field_names.resolve().await?; - )* - #construct - }, - }, - ) - }) - .collect::>() - }; - - let is_resolve_variants = variants.iter().map(|(is_resolved, _, _)| is_resolved); - let is_transient_variants = variants.iter().map(|(_, is_transient, _)| is_transient); - let resolve_variants = variants.iter().map(|(_, _, resolve)| resolve); - - is_resolved_impl = quote! { - match self { - #( - #is_resolve_variants - )* - } - }; - is_transient_impl = quote! { - match self { - #( - #is_transient_variants - )* - } - }; - resolve_impl = quote! { - Ok(match self { - #( - #resolve_variants - )* - }) - }; - } - Data::Struct(DataStruct { fields, .. }) => { - let destruct; - let construct; - let field_names: Vec; - match fields { - syn::Fields::Named(fields) => { - field_names = fields - .named - .iter() - .map(|field| field.ident.clone().unwrap()) - .collect(); - destruct = quote! { - let #ident { #(#field_names,)* } = self; - }; - construct = quote! { - #ident { #(#field_names,)* } - }; - } - syn::Fields::Unnamed(fields) => { - field_names = (0..fields.unnamed.len()) - .map(|i| Ident::new(&format!("field{}", i), fields.span())) - .collect(); - destruct = quote! { - let #ident ( #(#field_names,)* ) = self; - }; - construct = quote! { - #ident ( #(#field_names,)* ) - }; - } - syn::Fields::Unit => { - field_names = vec![]; - destruct = quote! {}; - construct = quote! { - #ident - }; - } - } - - is_resolved_impl = quote! { - #destruct - #(#field_names.is_resolved() &&)* true - }; - is_transient_impl = quote! { - #destruct - #(#field_names.is_transient() ||)* false - }; - resolve_impl = quote! { - #destruct - #( - let #field_names = #field_names.resolve().await?; - )* - Ok(#construct) - }; - } - _ => { - derive_input - .span() - .unwrap() - .error("unsupported syntax") - .emit(); - - is_resolved_impl = quote! {}; - is_transient_impl = quote! {}; - resolve_impl = quote! {}; - } - }; + let is_resolved_impl = match_expansion( + &derive_input, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter()); + ( + capture, + quote! { + {#( + #fields.is_resolved() && + )* true} + }, + ) + }, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter()); + ( + capture, + quote! { + {#( + #fields.is_resolved() && + )* true} + }, + ) + }, + &|_ident| quote! {true}, + ); + let is_transient_impl = match_expansion( + &derive_input, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter()); + ( + capture, + quote! { + {#( + #fields.is_transient() || + )* false} + }, + ) + }, + &|_ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter()); + ( + capture, + quote! { + {#( + #fields.is_transient() || + )* false} + }, + ) + }, + &|_ident| quote! {false}, + ); + let resolve_impl = match_expansion( + &derive_input, + &|ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.named.iter()); + ( + capture, + quote! { + { + #( + let #fields = #fields.resolve().await?; + )* + Ok(#ident { #(#fields),* }) + } + }, + ) + }, + &|ident, fields| { + let (capture, fields) = generate_exhaustive_destructuring(fields.unnamed.iter()); + ( + capture, + quote! { + { + #( + let #fields = #fields.resolve().await?; + )* + Ok(#ident(#(#fields),*)) + } + }, + ) + }, + &|ident| quote! {Ok(#ident)}, + ); let generic_params: Vec<_> = generics .params diff --git a/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs b/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs index 95e38814eacdeb..f735624997d2b3 100644 --- a/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/trace_raw_vcs_macro.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{parse_macro_input, DeriveInput, Field, FieldsNamed, FieldsUnnamed}; use turbo_tasks_macros_shared::{generate_destructuring, match_expansion}; @@ -32,7 +32,7 @@ pub fn derive_trace_raw_vcs(input: TokenStream) -> TokenStream { .into() } -fn trace_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { +fn trace_named(_ident: TokenStream2, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.named.iter(), &filter_field); ( captures, @@ -44,7 +44,7 @@ fn trace_named(_ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStre ) } -fn trace_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { +fn trace_unnamed(_ident: TokenStream2, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.unnamed.iter(), &filter_field); ( captures, @@ -56,6 +56,6 @@ fn trace_unnamed(_ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, Token ) } -fn trace_unit(_ident: &Ident) -> TokenStream2 { +fn trace_unit(_ident: TokenStream2) -> TokenStream2 { quote! { { } } } diff --git a/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs b/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs index 52d6fa1e529618..5f721893c5f199 100644 --- a/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs +++ b/crates/turbo-tasks-macros/src/derive/value_debug_format_macro.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Ident, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{parse_macro_input, DeriveInput, Field, FieldsNamed, FieldsUnnamed}; use turbo_tasks_macros_shared::{generate_destructuring, match_expansion}; @@ -62,7 +62,7 @@ fn format_field(value: TokenStream2) -> TokenStream2 { /// Formats a struct or enum variant with named fields (e.g. `struct Foo { /// bar: u32 }`, `Foo::Bar { baz: u32 }`). -fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { +fn format_named(ident: TokenStream2, fields: &FieldsNamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.named.iter(), &filter_field); ( captures, @@ -70,13 +70,13 @@ fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStre // this can happen if all fields are ignored, we must special-case this to avoid // rustc being unable to infer the type of an empty vec of futures quote! { - FormattingStruct::new_named(stringify!(#ident), vec![]) + FormattingStruct::new_named(turbo_tasks::stringify_path!(#ident), vec![]) } } else { let fields_values = fields_idents.iter().cloned().map(format_field); quote! { FormattingStruct::new_named_async( - stringify!(#ident), + turbo_tasks::stringify_path!(#ident), vec![#( AsyncFormattingField::new( stringify!(#fields_idents), @@ -91,7 +91,7 @@ fn format_named(ident: &Ident, fields: &FieldsNamed) -> (TokenStream2, TokenStre /// Formats a struct or enum variant with unnamed fields (e.g. `struct /// Foo(u32)`, `Foo::Bar(u32)`). -fn format_unnamed(ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { +fn format_unnamed(ident: TokenStream2, fields: &FieldsUnnamed) -> (TokenStream2, TokenStream2) { let (captures, fields_idents) = generate_destructuring(fields.unnamed.iter(), &filter_field); ( captures, @@ -99,13 +99,13 @@ fn format_unnamed(ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, Token // this can happen if all fields are ignored, we must special-case this to avoid // rustc being unable to infer the type of an empty vec of futures quote! { - FormattingStruct::new_unnamed(stringify!(#ident), vec![]) + FormattingStruct::new_unnamed(turbo_tasks::stringify_path!(#ident), vec![]) } } else { let fields_values = fields_idents.into_iter().map(format_field); quote! { FormattingStruct::new_unnamed_async( - stringify!(#ident), + turbo_tasks::stringify_path!(#ident), vec![#( #fields_values, )*], @@ -116,10 +116,10 @@ fn format_unnamed(ident: &Ident, fields: &FieldsUnnamed) -> (TokenStream2, Token } /// Formats a unit struct or enum variant (e.g. `struct Foo;`, `Foo::Bar`). -fn format_unit(ident: &Ident) -> TokenStream2 { +fn format_unit(ident: TokenStream2) -> TokenStream2 { quote! { FormattingStruct::new_unnamed( - stringify!(#ident), + turbo_tasks::stringify_path!(#ident), vec![], ) } diff --git a/crates/turbo-tasks-memory/tests/debug.rs b/crates/turbo-tasks-memory/tests/debug.rs index 3959cb036ed741..ecc2c1c6e4d112 100644 --- a/crates/turbo-tasks-memory/tests/debug.rs +++ b/crates/turbo-tasks-memory/tests/debug.rs @@ -27,7 +27,7 @@ async fn transparent_debug() { async fn enum_none_debug() { run! { let a: Vc = Enum::None.cell(); - assert_eq!(format!("{:?}", a.dbg().await?), "None"); + assert_eq!(format!("{:?}", a.dbg().await?), "Enum::None"); } } @@ -35,7 +35,7 @@ async fn enum_none_debug() { async fn enum_transparent_debug() { run! { let a: Vc = Enum::Transparent(Transparent(42).cell()).cell(); - assert_eq!(format!("{:?}", a.dbg().await?), r#"Transparent( + assert_eq!(format!("{:?}", a.dbg().await?), r#"Enum::Transparent( 42, )"#); } @@ -45,8 +45,8 @@ async fn enum_transparent_debug() { async fn enum_inner_vc_debug() { run! { let a: Vc = Enum::Enum(Enum::None.cell()).cell(); - assert_eq!(format!("{:?}", a.dbg().await?), r#"Enum( - None, + assert_eq!(format!("{:?}", a.dbg().await?), r#"Enum::Enum( + Enum::None, )"#); } } diff --git a/crates/turbo-tasks/src/macro_helpers.rs b/crates/turbo-tasks/src/macro_helpers.rs index 29f07838b9e398..00b58c2cfbf42a 100644 --- a/crates/turbo-tasks/src/macro_helpers.rs +++ b/crates/turbo-tasks/src/macro_helpers.rs @@ -20,3 +20,10 @@ pub async fn value_debug_format_field(value: ValueDebugFormatString<'_>) -> Stri Err(err) => format!("{0:?}", err), } } + +#[macro_export] +macro_rules! stringify_path { + ($path:path) => { + stringify!($path) + }; +}