Fix some things with builtin derives

1. Err on unions on derive where it's required.
 2. Err on `#[derive(Default)]` on enums without `#[default]` variant.
 3. Don't add where bounds `T: Default` when expanding `Default` on enums (structs need that, enums not).

Also, because I was annoyed by that, in minicore, add a way to filter on multiple flags in the line-filter (`// :`). This is required for the `Debug` and `Hash` derives, because the derive should be in the prelude but the trait not.
This commit is contained in:
Chayim Refael Friedman 2025-07-03 23:05:56 +03:00
parent f14bf95931
commit 9c4a7705b1
5 changed files with 191 additions and 73 deletions

View file

@ -746,3 +746,83 @@ struct Struct9<#[pointee] T, U>(T) where T: ?Sized;
623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]], 623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]],
); );
} }
#[test]
fn union_derive() {
check_errors(
r#"
//- minicore: clone, copy, default, fmt, hash, ord, eq, derive
#[derive(Copy)]
union Foo1 { _v: () }
#[derive(Clone)]
union Foo2 { _v: () }
#[derive(Default)]
union Foo3 { _v: () }
#[derive(Debug)]
union Foo4 { _v: () }
#[derive(Hash)]
union Foo5 { _v: () }
#[derive(Ord)]
union Foo6 { _v: () }
#[derive(PartialOrd)]
union Foo7 { _v: () }
#[derive(Eq)]
union Foo8 { _v: () }
#[derive(PartialEq)]
union Foo9 { _v: () }
"#,
expect![[r#"
78..118: this trait cannot be derived for unions
119..157: this trait cannot be derived for unions
158..195: this trait cannot be derived for unions
196..232: this trait cannot be derived for unions
233..276: this trait cannot be derived for unions
313..355: this trait cannot be derived for unions"#]],
);
}
#[test]
fn default_enum_without_default_attr() {
check_errors(
r#"
//- minicore: default, derive
#[derive(Default)]
enum Foo {
Bar,
}
"#,
expect!["1..41: `#[derive(Default)]` on enum with no `#[default]`"],
);
}
#[test]
fn generic_enum_default() {
check(
r#"
//- minicore: default, derive
#[derive(Default)]
enum Foo<T> {
Bar(T),
#[default]
Baz,
}
"#,
expect![[r#"
#[derive(Default)]
enum Foo<T> {
Bar(T),
#[default]
Baz,
}
impl <T, > $crate::default::Default for Foo<T, > where {
fn default() -> Self {
Foo::Baz
}
}"#]],
);
}

View file

@ -458,6 +458,7 @@ fn expand_simple_derive(
invoc_span: Span, invoc_span: Span,
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
trait_path: tt::TopSubtree, trait_path: tt::TopSubtree,
allow_unions: bool,
make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree, make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let info = match parse_adt(db, tt, invoc_span) { let info = match parse_adt(db, tt, invoc_span) {
@ -469,6 +470,12 @@ fn expand_simple_derive(
); );
} }
}; };
if !allow_unions && matches!(info.shape, AdtShape::Union) {
return ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan::from_single(invoc_span)),
ExpandError::other(invoc_span, "this trait cannot be derived for unions"),
);
}
ExpandResult::ok(expand_simple_derive_with_parsed( ExpandResult::ok(expand_simple_derive_with_parsed(
invoc_span, invoc_span,
info, info,
@ -535,7 +542,14 @@ fn copy_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span); let krate = dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) expand_simple_derive(
db,
span,
tt,
quote! {span => #krate::marker::Copy },
true,
|_| quote! {span =>},
)
} }
fn clone_expand( fn clone_expand(
@ -544,7 +558,7 @@ fn clone_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span); let krate = dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, |adt| { expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, true, |adt| {
if matches!(adt.shape, AdtShape::Union) { if matches!(adt.shape, AdtShape::Union) {
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
return quote! {span => return quote! {span =>
@ -599,41 +613,63 @@ fn default_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span); let krate = &dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::default::Default }, |adt| { let adt = match parse_adt(db, tt, span) {
let body = match &adt.shape { Ok(info) => info,
AdtShape::Struct(fields) => { Err(e) => {
let name = &adt.name; return ExpandResult::new(
fields.as_pattern_map( tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }),
quote!(span =>#name), e,
);
}
};
let (body, constrain_to_trait) = match &adt.shape {
AdtShape::Struct(fields) => {
let name = &adt.name;
let body = fields.as_pattern_map(
quote!(span =>#name),
span,
|_| quote!(span =>#krate::default::Default::default()),
);
(body, true)
}
AdtShape::Enum { default_variant, variants } => {
if let Some(d) = default_variant {
let (name, fields) = &variants[*d];
let adt_name = &adt.name;
let body = fields.as_pattern_map(
quote!(span =>#adt_name :: #name),
span, span,
|_| quote!(span =>#krate::default::Default::default()), |_| quote!(span =>#krate::default::Default::default()),
) );
} (body, false)
AdtShape::Enum { default_variant, variants } => { } else {
if let Some(d) = default_variant { return ExpandResult::new(
let (name, fields) = &variants[*d]; tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
let adt_name = &adt.name; ExpandError::other(span, "`#[derive(Default)]` on enum with no `#[default]`"),
fields.as_pattern_map( );
quote!(span =>#adt_name :: #name),
span,
|_| quote!(span =>#krate::default::Default::default()),
)
} else {
// FIXME: Return expand error here
quote!(span =>)
}
}
AdtShape::Union => {
// FIXME: Return expand error here
quote!(span =>)
}
};
quote! {span =>
fn default() -> Self {
#body
} }
} }
}) AdtShape::Union => {
return ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
ExpandError::other(span, "this trait cannot be derived for unions"),
);
}
};
ExpandResult::ok(expand_simple_derive_with_parsed(
span,
adt,
quote! {span => #krate::default::Default },
|_adt| {
quote! {span =>
fn default() -> Self {
#body
}
}
},
constrain_to_trait,
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
))
} }
fn debug_expand( fn debug_expand(
@ -642,7 +678,7 @@ fn debug_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span); let krate = &dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| { expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, false, |adt| {
let for_variant = |name: String, v: &VariantShape| match v { let for_variant = |name: String, v: &VariantShape| match v {
VariantShape::Struct(fields) => { VariantShape::Struct(fields) => {
let for_fields = fields.iter().map(|it| { let for_fields = fields.iter().map(|it| {
@ -697,10 +733,7 @@ fn debug_expand(
} }
}) })
.collect(), .collect(),
AdtShape::Union => { AdtShape::Union => unreachable!(),
// FIXME: Return expand error here
vec![]
}
}; };
quote! {span => quote! {span =>
fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result { fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
@ -718,11 +751,7 @@ fn hash_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span); let krate = &dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, |adt| { expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, false, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {span =>};
}
if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
return quote! {span => return quote! {span =>
@ -769,7 +798,14 @@ fn eq_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span); let krate = dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) expand_simple_derive(
db,
span,
tt,
quote! {span => #krate::cmp::Eq },
true,
|_| quote! {span =>},
)
} }
fn partial_eq_expand( fn partial_eq_expand(
@ -778,11 +814,7 @@ fn partial_eq_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = dollar_crate(span); let krate = dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| { expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, false, |adt| {
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote! {span =>};
}
let name = &adt.name; let name = &adt.name;
let (self_patterns, other_patterns) = self_and_other_patterns(adt, name, span); let (self_patterns, other_patterns) = self_and_other_patterns(adt, name, span);
@ -854,7 +886,7 @@ fn ord_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span); let krate = &dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, |adt| { expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, false, |adt| {
fn compare( fn compare(
krate: &tt::Ident, krate: &tt::Ident,
left: tt::TopSubtree, left: tt::TopSubtree,
@ -873,10 +905,6 @@ fn ord_expand(
} }
} }
} }
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote!(span =>);
}
let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name, span); let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name, span);
let arms = izip!(self_patterns, other_patterns, adt.shape.field_names(span)).map( let arms = izip!(self_patterns, other_patterns, adt.shape.field_names(span)).map(
|(pat1, pat2, fields)| { |(pat1, pat2, fields)| {
@ -916,7 +944,7 @@ fn partial_ord_expand(
tt: &tt::TopSubtree, tt: &tt::TopSubtree,
) -> ExpandResult<tt::TopSubtree> { ) -> ExpandResult<tt::TopSubtree> {
let krate = &dollar_crate(span); let krate = &dollar_crate(span);
expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| { expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, false, |adt| {
fn compare( fn compare(
krate: &tt::Ident, krate: &tt::Ident,
left: tt::TopSubtree, left: tt::TopSubtree,
@ -935,10 +963,6 @@ fn partial_ord_expand(
} }
} }
} }
if matches!(adt.shape, AdtShape::Union) {
// FIXME: Return expand error here
return quote!(span =>);
}
let left = quote!(span =>#krate::intrinsics::discriminant_value(self)); let left = quote!(span =>#krate::intrinsics::discriminant_value(self));
let right = quote!(span =>#krate::intrinsics::discriminant_value(other)); let right = quote!(span =>#krate::intrinsics::discriminant_value(other));

View file

@ -878,6 +878,7 @@ mod derive {
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Debug macro Debug
de Default macro Default de Default macro Default
de PartialEq macro PartialEq de PartialEq macro PartialEq
de PartialEq, Eq de PartialEq, Eq
@ -900,6 +901,7 @@ mod derive {
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Debug macro Debug
de Default macro Default de Default macro Default
de Eq de Eq
de Eq, PartialOrd, Ord de Eq, PartialOrd, Ord
@ -921,6 +923,7 @@ mod derive {
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Debug macro Debug
de Default macro Default de Default macro Default
de Eq de Eq
de Eq, PartialOrd, Ord de Eq, PartialOrd, Ord
@ -942,6 +945,7 @@ mod derive {
expect![[r#" expect![[r#"
de Clone macro Clone de Clone macro Clone
de Clone, Copy de Clone, Copy
de Debug macro Debug
de Default macro Default de Default macro Default
de PartialOrd de PartialOrd
de PartialOrd, Ord de PartialOrd, Ord

View file

@ -435,14 +435,16 @@ impl MiniCore {
continue; continue;
} }
let mut active_line_region = false; let mut active_line_region = 0;
let mut inactive_line_region = false; let mut inactive_line_region = 0;
if let Some(idx) = trimmed.find("// :!") { if let Some(idx) = trimmed.find("// :!") {
inactive_line_region = true; let regions = trimmed[idx + "// :!".len()..].split(", ");
inactive_regions.push(&trimmed[idx + "// :!".len()..]); inactive_line_region += regions.clone().count();
inactive_regions.extend(regions);
} else if let Some(idx) = trimmed.find("// :") { } else if let Some(idx) = trimmed.find("// :") {
active_line_region = true; let regions = trimmed[idx + "// :".len()..].split(", ");
active_regions.push(&trimmed[idx + "// :".len()..]); active_line_region += regions.clone().count();
active_regions.extend(regions);
} }
let mut keep = true; let mut keep = true;
@ -462,11 +464,11 @@ impl MiniCore {
if keep { if keep {
buf.push_str(line); buf.push_str(line);
} }
if active_line_region { if active_line_region > 0 {
active_regions.pop().unwrap(); active_regions.drain(active_regions.len() - active_line_region..);
} }
if inactive_line_region { if inactive_line_region > 0 {
inactive_regions.pop().unwrap(); inactive_regions.drain(inactive_regions.len() - active_line_region..);
} }
} }

View file

@ -228,8 +228,11 @@ pub mod hash {
} }
// region:derive // region:derive
#[rustc_builtin_macro] pub(crate) mod derive {
pub macro Hash($item:item) {} #[rustc_builtin_macro]
pub macro Hash($item:item) {}
}
pub use derive::Hash;
// endregion:derive // endregion:derive
} }
// endregion:hash // endregion:hash
@ -1264,8 +1267,11 @@ pub mod fmt {
} }
// region:derive // region:derive
#[rustc_builtin_macro] pub(crate) mod derive {
pub macro Debug($item:item) {} #[rustc_builtin_macro]
pub macro Debug($item:item) {}
}
pub use derive::Debug;
// endregion:derive // endregion:derive
// region:builtin_impls // region:builtin_impls
@ -1931,6 +1937,8 @@ pub mod prelude {
panic, // :panic panic, // :panic
result::Result::{self, Err, Ok}, // :result result::Result::{self, Err, Ok}, // :result
str::FromStr, // :str str::FromStr, // :str
fmt::derive::Debug, // :fmt, derive
hash::derive::Hash, // :hash, derive
}; };
} }