diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 268153c1cbb31..620bf538a4c6a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -69,7 +69,15 @@ fn default_struct_substructure( variant_data: &VariantData, type_ident: Ident, ) -> BlockOrExpr { + // FIXME: This is currently needed to avoid a regression with attribute macros after + // derives that incorrectly rely on the generated code using `Unit {}` instead of `Unit`. + // See https://github.com/rust-lang/rust/issues/164078. + if variant_data.fields().is_empty() { + return BlockOrExpr::new_expr(cx.expr_struct_ident(trait_span, type_ident, ThinVec::new())); + } + let expr = match variant_data { + // This is currently unreachable due to the above hack. VariantData::Unit(_) => cx.expr_ident(trait_span, type_ident), VariantData::Tuple(fields, _) => { let exprs = fields diff --git a/tests/ui/derives/attr-macro-changes-type-kind.rs b/tests/ui/derives/attr-macro-changes-type-kind.rs index 98e05af3b33b6..e655e525a1046 100644 --- a/tests/ui/derives/attr-macro-changes-type-kind.rs +++ b/tests/ui/derives/attr-macro-changes-type-kind.rs @@ -8,7 +8,7 @@ extern crate struct_to_enum; use struct_to_enum::*; -#[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] #[struct_to_enum::phantom] struct Spooky; diff --git a/tests/ui/derives/default/attr-macros-fieldless-types.rs b/tests/ui/derives/default/attr-macros-fieldless-types.rs new file mode 100644 index 0000000000000..9a2e68c50551d --- /dev/null +++ b/tests/ui/derives/default/attr-macros-fieldless-types.rs @@ -0,0 +1,47 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/164078, +//! where some attribute macros relied on the generated code always being +//! `Unit {}` and not `Unit()` or `Unit`. + +//@ check-pass +//@ proc-macro: rewrite-fieldless-type-kind.rs + +extern crate rewrite_fieldless_type_kind; +use rewrite_fieldless_type_kind::*; + +#[derive(Default)] +#[to_unit] +struct UnitToUnit; + +#[derive(Default)] +#[to_tuple] +struct UnitToTuple; + +#[derive(Default)] +#[to_braced] +struct UnitToBraced; + +#[derive(Default)] +#[to_unit] +struct TupleToUnit(); + +#[derive(Default)] +#[to_tuple] +struct TupleToTuple(); + +#[derive(Default)] +#[to_braced] +struct TupleToBraced(); + +#[derive(Default)] +#[to_unit] +struct BracedToUnit {} + +#[derive(Default)] +#[to_tuple] +struct BracedToTuple {} + +#[derive(Default)] +#[to_braced] +struct BracedToBraced {} + +fn main() {} diff --git a/tests/ui/derives/default/auxiliary/rewrite-fieldless-type-kind.rs b/tests/ui/derives/default/auxiliary/rewrite-fieldless-type-kind.rs new file mode 100644 index 0000000000000..f75a03cdba647 --- /dev/null +++ b/tests/ui/derives/default/auxiliary/rewrite-fieldless-type-kind.rs @@ -0,0 +1,20 @@ +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn to_unit(_: TokenStream, struct_item: TokenStream) -> TokenStream { + rewrite(struct_item, ";") +} +#[proc_macro_attribute] +pub fn to_tuple(_: TokenStream, struct_item: TokenStream) -> TokenStream { + rewrite(struct_item, "();") +} +#[proc_macro_attribute] +pub fn to_braced(_: TokenStream, struct_item: TokenStream) -> TokenStream { + rewrite(struct_item, "{}") +} + +fn rewrite(struct_item: TokenStream, suffix: &str) -> TokenStream { + let ident = struct_item.into_iter().nth(1).unwrap(); + format!("struct {ident}{suffix}").parse().unwrap() +} diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 163658d2106dd..865c6e20da91c 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -46,7 +46,7 @@ impl ::core::fmt::Debug for Empty { #[automatically_derived] impl ::core::default::Default for Empty { #[inline] - fn default() -> Self { Empty } + fn default() -> Self { Empty {} } } #[automatically_derived] impl ::core::hash::Hash for Empty {