From d520b18316c71e1b61f2ed4df761f03149ff59e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 13 Dec 2024 22:59:26 +0000 Subject: [PATCH 1/2] Mention `#[default]` in E0655 code index --- .../rustc_error_codes/src/error_codes/E0665.md | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0665.md b/compiler/rustc_error_codes/src/error_codes/E0665.md index ae54d6d15798d..d5fd2130840de 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0665.md +++ b/compiler/rustc_error_codes/src/error_codes/E0665.md @@ -16,18 +16,30 @@ The `Default` cannot be derived on an enum for the simple reason that the compiler doesn't know which value to pick by default whereas it can for a struct as long as all its fields implement the `Default` trait as well. -If you still want to implement `Default` on your enum, you'll have to do it "by -hand": +For the case where the desired default variant has no data, you can annotate +it with `#[default]` to derive it: ``` +#[derive(Default)] enum Food { + #[default] Sweet, Salty, } +``` + +In the case where the default variant does have data, you will have to +implement `Default` on your enum "by hand": + +``` +enum Food { + Sweet(i32), + Salty, +} impl Default for Food { fn default() -> Food { - Food::Sweet + Food::Sweet(1) } } ``` From 94812f1c8f455579a3a1c127c2bbac48fc7cecef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 21 Dec 2024 02:43:09 +0000 Subject: [PATCH 2/2] Use E0665 for missing `#[default]` error Use orphaned error code for the same error it belonged to before. ``` error[E0665]: `#[derive(Default)]` on enum with no `#[default]` --> $DIR/macros-nonfatal-errors.rs:42:10 | LL | #[derive(Default)] | ^^^^^^^ LL | / enum NoDeclaredDefault { LL | | Foo, LL | | Bar, LL | | } | |_- this enum needs a unit variant marked with `#[default]` | = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) help: make this unit variant default by placing `#[default]` on it | LL | #[default] Foo, | ~~~~~~~~~~~~~~ help: make this unit variant default by placing `#[default]` on it | LL | #[default] Bar, | ~~~~~~~~~~~~~~ ``` --- compiler/rustc_builtin_macros/messages.ftl | 6 +- .../src/deriving/default.rs | 13 ++- compiler/rustc_builtin_macros/src/errors.rs | 13 +-- .../src/error_codes/E0665.md | 15 ++-- tests/ui/macros/macros-nonfatal-errors.rs | 8 +- tests/ui/macros/macros-nonfatal-errors.stderr | 82 ++++++++++++------- 6 files changed, 84 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 87d3d288013a3..7a31d2a223917 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -249,9 +249,9 @@ builtin_macros_naked_functions_testing_attribute = .label = function marked with testing attribute here .naked_attribute = `#[naked]` is incompatible with testing attributes -builtin_macros_no_default_variant = no default declared - .help = make a unit variant default by placing `#[default]` above it - .suggestion = make `{$ident}` default +builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[default]` + .label = this enum needs a unit variant marked with `#[default]` + .suggestion = make this unit variant default by placing `#[default]` on it builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi` diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index a7d9f608cbdd0..3c7bebd0f192e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -42,7 +42,9 @@ pub(crate) fn expand_deriving_default( StaticStruct(_, fields) => { default_struct_substructure(cx, trait_span, substr, fields) } - StaticEnum(enum_def, _) => default_enum_substructure(cx, trait_span, enum_def), + StaticEnum(enum_def, _) => { + default_enum_substructure(cx, trait_span, enum_def, item.span()) + } _ => cx.dcx().span_bug(trait_span, "method in `derive(Default)`"), } })), @@ -96,9 +98,10 @@ fn default_enum_substructure( cx: &ExtCtxt<'_>, trait_span: Span, enum_def: &EnumDef, + item_span: Span, ) -> BlockOrExpr { let expr = match try { - let default_variant = extract_default_variant(cx, enum_def, trait_span)?; + let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?; validate_default_attribute(cx, default_variant)?; default_variant } { @@ -146,6 +149,7 @@ fn extract_default_variant<'a>( cx: &ExtCtxt<'_>, enum_def: &'a EnumDef, trait_span: Span, + item_span: Span, ) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> { let default_variants: SmallVec<[_; 1]> = enum_def .variants @@ -163,9 +167,10 @@ fn extract_default_variant<'a>( .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive)); let suggs = possible_defaults - .map(|v| errors::NoDefaultVariantSugg { span: v.span, ident: v.ident }) + .map(|v| errors::NoDefaultVariantSugg { span: v.span.shrink_to_lo() }) .collect(); - let guar = cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, suggs }); + let guar = + cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, item_span, suggs }); return Err(guar); } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b3198e7743d8b..1abdfdb9c65c9 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -369,26 +369,21 @@ pub(crate) struct DerivePathArgsValue { } #[derive(Diagnostic)] -#[diag(builtin_macros_no_default_variant)] -#[help] +#[diag(builtin_macros_no_default_variant, code = E0665)] pub(crate) struct NoDefaultVariant { #[primary_span] pub(crate) span: Span, + #[label] + pub(crate) item_span: Span, #[subdiagnostic] pub(crate) suggs: Vec, } #[derive(Subdiagnostic)] -#[suggestion( - builtin_macros_suggestion, - code = "#[default] {ident}", - applicability = "maybe-incorrect", - style = "tool-only" -)] +#[suggestion(builtin_macros_suggestion, code = "#[default] ", applicability = "maybe-incorrect")] pub(crate) struct NoDefaultVariantSugg { #[primary_span] pub(crate) span: Span, - pub(crate) ident: Ident, } #[derive(Diagnostic)] diff --git a/compiler/rustc_error_codes/src/error_codes/E0665.md b/compiler/rustc_error_codes/src/error_codes/E0665.md index d5fd2130840de..caa944233778b 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0665.md +++ b/compiler/rustc_error_codes/src/error_codes/E0665.md @@ -1,10 +1,9 @@ -#### Note: this error code is no longer emitted by the compiler. - -The `Default` trait was derived on an enum. +The `Default` trait was derived on an enum without specifying the default +variant. Erroneous code example: -```compile_fail +```compile_fail,E0665 #[derive(Default)] enum Food { Sweet, @@ -16,8 +15,8 @@ The `Default` cannot be derived on an enum for the simple reason that the compiler doesn't know which value to pick by default whereas it can for a struct as long as all its fields implement the `Default` trait as well. -For the case where the desired default variant has no data, you can annotate -it with `#[default]` to derive it: +For the case where the desired default variant has no payload, you can +annotate it with `#[default]` to derive it: ``` #[derive(Default)] @@ -28,8 +27,8 @@ enum Food { } ``` -In the case where the default variant does have data, you will have to -implement `Default` on your enum "by hand": +In the case where the default variant does have a payload, you will have to +implement `Default` on your enum manually: ``` enum Food { diff --git a/tests/ui/macros/macros-nonfatal-errors.rs b/tests/ui/macros/macros-nonfatal-errors.rs index 46e865031ecd5..658455b1b5b8b 100644 --- a/tests/ui/macros/macros-nonfatal-errors.rs +++ b/tests/ui/macros/macros-nonfatal-errors.rs @@ -39,12 +39,18 @@ enum AttrOnInnerExpression { Baz, } -#[derive(Default)] //~ ERROR no default declared +#[derive(Default)] //~ ERROR `#[derive(Default)]` on enum with no `#[default]` enum NoDeclaredDefault { Foo, Bar, } +#[derive(Default)] //~ ERROR `#[derive(Default)]` on enum with no `#[default]` +enum NoDeclaredDefaultWithoutUnitVariant { + Foo(i32), + Bar(i32), +} + #[derive(Default)] //~ ERROR multiple declared defaults enum MultipleDefaults { #[default] diff --git a/tests/ui/macros/macros-nonfatal-errors.stderr b/tests/ui/macros/macros-nonfatal-errors.stderr index abf43e2a009d7..fd5e41986a836 100644 --- a/tests/ui/macros/macros-nonfatal-errors.stderr +++ b/tests/ui/macros/macros-nonfatal-errors.stderr @@ -46,18 +46,43 @@ LL | Bar([u8; #[default] 1]), | = help: consider a manual implementation of `Default` -error: no default declared +error[E0665]: `#[derive(Default)]` on enum with no `#[default]` --> $DIR/macros-nonfatal-errors.rs:42:10 | -LL | #[derive(Default)] - | ^^^^^^^ +LL | #[derive(Default)] + | ^^^^^^^ +LL | / enum NoDeclaredDefault { +LL | | Foo, +LL | | Bar, +LL | | } + | |_- this enum needs a unit variant marked with `#[default]` | - = help: make a unit variant default by placing `#[default]` above it = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) +help: make this unit variant default by placing `#[default]` on it + | +LL | #[default] Foo, + | ++++++++++ +help: make this unit variant default by placing `#[default]` on it + | +LL | #[default] Bar, + | ++++++++++ -error: multiple declared defaults +error[E0665]: `#[derive(Default)]` on enum with no `#[default]` --> $DIR/macros-nonfatal-errors.rs:48:10 | +LL | #[derive(Default)] + | ^^^^^^^ +LL | / enum NoDeclaredDefaultWithoutUnitVariant { +LL | | Foo(i32), +LL | | Bar(i32), +LL | | } + | |_- this enum needs a unit variant marked with `#[default]` + | + = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: multiple declared defaults + --> $DIR/macros-nonfatal-errors.rs:54:10 + | LL | #[derive(Default)] | ^^^^^^^ ... @@ -74,7 +99,7 @@ LL | Baz, = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info) error: `#[default]` attribute does not accept a value - --> $DIR/macros-nonfatal-errors.rs:60:5 + --> $DIR/macros-nonfatal-errors.rs:66:5 | LL | #[default = 1] | ^^^^^^^^^^^^^^ @@ -82,7 +107,7 @@ LL | #[default = 1] = help: try using `#[default]` error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:68:5 + --> $DIR/macros-nonfatal-errors.rs:74:5 | LL | #[default] | ---------- `#[default]` used here @@ -93,13 +118,13 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing this - --> $DIR/macros-nonfatal-errors.rs:67:5 + --> $DIR/macros-nonfatal-errors.rs:73:5 | LL | #[default] | ^^^^^^^^^^ error: multiple `#[default]` attributes - --> $DIR/macros-nonfatal-errors.rs:78:5 + --> $DIR/macros-nonfatal-errors.rs:84:5 | LL | #[default] | ---------- `#[default]` used here @@ -111,7 +136,7 @@ LL | Foo, | = note: only one `#[default]` attribute is needed help: try removing these - --> $DIR/macros-nonfatal-errors.rs:75:5 + --> $DIR/macros-nonfatal-errors.rs:81:5 | LL | #[default] | ^^^^^^^^^^ @@ -121,7 +146,7 @@ LL | #[default] | ^^^^^^^^^^ error: the `#[default]` attribute may only be used on unit enum variants - --> $DIR/macros-nonfatal-errors.rs:85:5 + --> $DIR/macros-nonfatal-errors.rs:91:5 | LL | Foo {}, | ^^^ @@ -129,7 +154,7 @@ LL | Foo {}, = help: consider a manual implementation of `Default` error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:93:5 + --> $DIR/macros-nonfatal-errors.rs:99:5 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -139,37 +164,37 @@ LL | Foo, = help: consider a manual implementation of `Default` error: asm template must be a string literal - --> $DIR/macros-nonfatal-errors.rs:98:10 + --> $DIR/macros-nonfatal-errors.rs:104:10 | LL | asm!(invalid); | ^^^^^^^ error: `concat_idents!()` requires ident args - --> $DIR/macros-nonfatal-errors.rs:101:5 + --> $DIR/macros-nonfatal-errors.rs:107:5 | LL | concat_idents!("not", "idents"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:103:17 + --> $DIR/macros-nonfatal-errors.rs:109:17 | LL | option_env!(invalid); | ^^^^^^^ error: expected string literal - --> $DIR/macros-nonfatal-errors.rs:104:10 + --> $DIR/macros-nonfatal-errors.rs:110:10 | LL | env!(invalid); | ^^^^^^^ error: `env!()` takes 1 or 2 arguments - --> $DIR/macros-nonfatal-errors.rs:105:5 + --> $DIR/macros-nonfatal-errors.rs:111:5 | LL | env!(foo, abr, baz); | ^^^^^^^^^^^^^^^^^^^ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at compile time - --> $DIR/macros-nonfatal-errors.rs:106:5 + --> $DIR/macros-nonfatal-errors.rs:112:5 | LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -178,7 +203,7 @@ LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) error: format argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:108:13 + --> $DIR/macros-nonfatal-errors.rs:114:13 | LL | format!(invalid); | ^^^^^^^ @@ -189,19 +214,19 @@ LL | format!("{}", invalid); | +++++ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:110:14 + --> $DIR/macros-nonfatal-errors.rs:116:14 | LL | include!(invalid); | ^^^^^^^ error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:112:18 + --> $DIR/macros-nonfatal-errors.rs:118:18 | LL | include_str!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:113:5 + --> $DIR/macros-nonfatal-errors.rs:119:5 | LL | include_str!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -209,13 +234,13 @@ LL | include_str!("i'd be quite surprised if a file with this name existed") = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) error: argument must be a string literal - --> $DIR/macros-nonfatal-errors.rs:114:20 + --> $DIR/macros-nonfatal-errors.rs:120:20 | LL | include_bytes!(invalid); | ^^^^^^^ error: couldn't read `$DIR/i'd be quite surprised if a file with this name existed`: $FILE_NOT_FOUND_MSG - --> $DIR/macros-nonfatal-errors.rs:115:5 + --> $DIR/macros-nonfatal-errors.rs:121:5 | LL | include_bytes!("i'd be quite surprised if a file with this name existed"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -223,13 +248,13 @@ LL | include_bytes!("i'd be quite surprised if a file with this name existed = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) error: trace_macros! accepts only `true` or `false` - --> $DIR/macros-nonfatal-errors.rs:117:5 + --> $DIR/macros-nonfatal-errors.rs:123:5 | LL | trace_macros!(invalid); | ^^^^^^^^^^^^^^^^^^^^^^ error: default variant must be exhaustive - --> $DIR/macros-nonfatal-errors.rs:127:9 + --> $DIR/macros-nonfatal-errors.rs:133:9 | LL | #[non_exhaustive] | ----------------- declared `#[non_exhaustive]` here @@ -239,10 +264,11 @@ LL | Foo, = help: consider a manual implementation of `Default` error: cannot find macro `llvm_asm` in this scope - --> $DIR/macros-nonfatal-errors.rs:99:5 + --> $DIR/macros-nonfatal-errors.rs:105:5 | LL | llvm_asm!(invalid); | ^^^^^^^^ -error: aborting due to 28 previous errors +error: aborting due to 29 previous errors +For more information about this error, try `rustc --explain E0665`.