about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-28 05:59:30 +0000
committerbors <bors@rust-lang.org>2021-07-28 05:59:30 +0000
commitaea2e446f09b923d513013743b37b43fca7282dc (patch)
treeac53a52123dd478795393e478e43608b98871609
parent8b50cc9a2c408caef30a1cf950c948509b2f648e (diff)
parent72465b0a32e689cd6a61120479c13b38281108a7 (diff)
downloadrust-aea2e446f09b923d513013743b37b43fca7282dc.tar.gz
rust-aea2e446f09b923d513013743b37b43fca7282dc.zip
Auto merge of #86735 - jhpratt:rfc-3107, r=petrochenkov
Implement RFC 3107: `#[derive(Default)]` on enums with a `#[default]` attribute

This PR implements RFC 3107, which permits `#[derive(Default)]` on enums where a unit variant has a `#[default]` attribute. See comments for current status.
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs257
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0665.md4
-rw-r--r--compiler/rustc_feature/src/active.rs3
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--library/core/src/default.rs3
-rw-r--r--src/test/ui/deriving/deriving-default-enum.rs19
-rw-r--r--src/test/ui/deriving/deriving-with-helper.rs5
-rw-r--r--src/test/ui/error-codes/E0665.rs8
-rw-r--r--src/test/ui/error-codes/E0665.stderr11
-rw-r--r--src/test/ui/feature-gates/feature-gate-derive_default_enum.rs7
-rw-r--r--src/test/ui/feature-gates/feature-gate-derive_default_enum.stderr13
-rw-r--r--src/test/ui/macros/macros-nonfatal-errors.rs89
-rw-r--r--src/test/ui/macros/macros-nonfatal-errors.stderr155
-rw-r--r--src/test/ui/resolve/issue-2356.rs2
14 files changed, 506 insertions, 71 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 980be3a0050..8c53094b624 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -2,11 +2,16 @@ use crate::deriving::generic::ty::*;
 use crate::deriving::generic::*;
 
 use rustc_ast::ptr::P;
+use rustc_ast::walk_list;
+use rustc_ast::EnumDef;
+use rustc_ast::VariantData;
 use rustc_ast::{Expr, MetaItem};
-use rustc_errors::struct_span_err;
+use rustc_errors::Applicability;
 use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
+use rustc_span::symbol::Ident;
 use rustc_span::symbol::{kw, sym};
 use rustc_span::Span;
+use smallvec::SmallVec;
 
 pub fn expand_deriving_default(
     cx: &mut ExtCtxt<'_>,
@@ -15,6 +20,8 @@ pub fn expand_deriving_default(
     item: &Annotatable,
     push: &mut dyn FnMut(Annotatable),
 ) {
+    item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
+
     let inline = cx.meta_word(span, sym::inline);
     let attrs = vec![cx.attribute(inline)];
     let trait_def = TraitDef {
@@ -34,8 +41,25 @@ pub fn expand_deriving_default(
             attributes: attrs,
             is_unsafe: false,
             unify_fieldless_variants: false,
-            combine_substructure: combine_substructure(Box::new(|a, b, c| {
-                default_substructure(a, b, c)
+            combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
+                match substr.fields {
+                    StaticStruct(_, fields) => {
+                        default_struct_substructure(cx, trait_span, substr, fields)
+                    }
+                    StaticEnum(enum_def, _) => {
+                        if !cx.sess.features_untracked().derive_default_enum {
+                            rustc_session::parse::feature_err(
+                                cx.parse_sess(),
+                                sym::derive_default_enum,
+                                span,
+                                "deriving `Default` on enums is experimental",
+                            )
+                            .emit();
+                        }
+                        default_enum_substructure(cx, trait_span, enum_def)
+                    }
+                    _ => cx.span_bug(trait_span, "method in `derive(Default)`"),
+                }
             })),
         }],
         associated_types: Vec::new(),
@@ -43,44 +67,223 @@ pub fn expand_deriving_default(
     trait_def.expand(cx, mitem, item, push)
 }
 
-fn default_substructure(
+fn default_struct_substructure(
     cx: &mut ExtCtxt<'_>,
     trait_span: Span,
     substr: &Substructure<'_>,
+    summary: &StaticFields,
 ) -> P<Expr> {
     // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
     let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
     let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
 
-    match *substr.fields {
-        StaticStruct(_, ref summary) => match *summary {
-            Unnamed(ref fields, is_tuple) => {
-                if !is_tuple {
-                    cx.expr_ident(trait_span, substr.type_ident)
-                } else {
-                    let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
-                    cx.expr_call_ident(trait_span, substr.type_ident, exprs)
-                }
+    match summary {
+        Unnamed(ref fields, is_tuple) => {
+            if !is_tuple {
+                cx.expr_ident(trait_span, substr.type_ident)
+            } else {
+                let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
+                cx.expr_call_ident(trait_span, substr.type_ident, exprs)
+            }
+        }
+        Named(ref fields) => {
+            let default_fields = fields
+                .iter()
+                .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
+                .collect();
+            cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
+        }
+    }
+}
+
+fn default_enum_substructure(
+    cx: &mut ExtCtxt<'_>,
+    trait_span: Span,
+    enum_def: &EnumDef,
+) -> P<Expr> {
+    let default_variant = match extract_default_variant(cx, enum_def, trait_span) {
+        Ok(value) => value,
+        Err(()) => return DummyResult::raw_expr(trait_span, true),
+    };
+
+    // At this point, we know that there is exactly one variant with a `#[default]` attribute. The
+    // attribute hasn't yet been validated.
+
+    if let Err(()) = validate_default_attribute(cx, default_variant) {
+        return DummyResult::raw_expr(trait_span, true);
+    }
+
+    // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
+
+    cx.expr_path(cx.path(
+        default_variant.span,
+        vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
+    ))
+}
+
+fn extract_default_variant<'a>(
+    cx: &mut ExtCtxt<'_>,
+    enum_def: &'a EnumDef,
+    trait_span: Span,
+) -> Result<&'a rustc_ast::Variant, ()> {
+    let default_variants: SmallVec<[_; 1]> = enum_def
+        .variants
+        .iter()
+        .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
+        .collect();
+
+    let variant = match default_variants.as_slice() {
+        [variant] => variant,
+        [] => {
+            let possible_defaults = enum_def
+                .variants
+                .iter()
+                .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
+                .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive));
+
+            let mut diag = cx.struct_span_err(trait_span, "no default declared");
+            diag.help("make a unit variant default by placing `#[default]` above it");
+            for variant in possible_defaults {
+                // Suggest making each unit variant default.
+                diag.tool_only_span_suggestion(
+                    variant.span,
+                    &format!("make `{}` default", variant.ident),
+                    format!("#[default] {}", variant.ident),
+                    Applicability::MaybeIncorrect,
+                );
             }
-            Named(ref fields) => {
-                let default_fields = fields
+            diag.emit();
+
+            return Err(());
+        }
+        [first, rest @ ..] => {
+            let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults");
+            diag.span_label(first.span, "first default");
+            diag.span_labels(rest.iter().map(|variant| variant.span), "additional default");
+            diag.note("only one variant can be default");
+            for variant in &default_variants {
+                // Suggest making each variant already tagged default.
+                let suggestion = default_variants
                     .iter()
-                    .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
+                    .filter_map(|v| {
+                        if v.ident == variant.ident {
+                            None
+                        } else {
+                            Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new()))
+                        }
+                    })
                     .collect();
-                cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
+
+                diag.tool_only_multipart_suggestion(
+                    &format!("make `{}` default", variant.ident),
+                    suggestion,
+                    Applicability::MaybeIncorrect,
+                );
             }
-        },
-        StaticEnum(..) => {
-            struct_span_err!(
-                &cx.sess.parse_sess.span_diagnostic,
-                trait_span,
-                E0665,
-                "`Default` cannot be derived for enums, only structs"
+            diag.emit();
+
+            return Err(());
+        }
+    };
+
+    if !matches!(variant.data, VariantData::Unit(..)) {
+        cx.struct_span_err(
+            variant.ident.span,
+            "the `#[default]` attribute may only be used on unit enum variants",
+        )
+        .help("consider a manual implementation of `Default`")
+        .emit();
+
+        return Err(());
+    }
+
+    if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) {
+        cx.struct_span_err(variant.ident.span, "default variant must be exhaustive")
+            .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
+            .help("consider a manual implementation of `Default`")
+            .emit();
+
+        return Err(());
+    }
+
+    Ok(variant)
+}
+
+fn validate_default_attribute(
+    cx: &mut ExtCtxt<'_>,
+    default_variant: &rustc_ast::Variant,
+) -> Result<(), ()> {
+    let attrs: SmallVec<[_; 1]> =
+        cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
+
+    let attr = match attrs.as_slice() {
+        [attr] => attr,
+        [] => cx.bug(
+            "this method must only be called with a variant that has a `#[default]` attribute",
+        ),
+        [first, rest @ ..] => {
+            // FIXME(jhpratt) Do we want to perform this check? It doesn't exist
+            // for `#[inline]`, `#[non_exhaustive]`, and presumably others.
+
+            let suggestion_text =
+                if rest.len() == 1 { "try removing this" } else { "try removing these" };
+
+            cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
+                .note("only one `#[default]` attribute is needed")
+                .span_label(first.span, "`#[default]` used here")
+                .span_label(rest[0].span, "`#[default]` used again here")
+                .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
+                // This would otherwise display the empty replacement, hence the otherwise
+                // repetitive `.span_help` call above.
+                .tool_only_multipart_suggestion(
+                    suggestion_text,
+                    rest.iter().map(|attr| (attr.span, String::new())).collect(),
+                    Applicability::MachineApplicable,
+                )
+                .emit();
+
+            return Err(());
+        }
+    };
+    if !attr.is_word() {
+        cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
+            .span_suggestion_hidden(
+                attr.span,
+                "try using `#[default]`",
+                "#[default]".into(),
+                Applicability::MaybeIncorrect,
             )
             .emit();
-            // let compilation continue
-            DummyResult::raw_expr(trait_span, true)
+
+        return Err(());
+    }
+    Ok(())
+}
+
+struct DetectNonVariantDefaultAttr<'a, 'b> {
+    cx: &'a ExtCtxt<'b>,
+}
+
+impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
+    fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
+        if attr.has_name(kw::Default) {
+            self.cx
+                .struct_span_err(
+                    attr.span,
+                    "the `#[default]` attribute may only be used on unit enum variants",
+                )
+                .emit();
+        }
+
+        rustc_ast::visit::walk_attribute(self, attr);
+    }
+    fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
+        self.visit_ident(v.ident);
+        self.visit_vis(&v.vis);
+        self.visit_variant_data(&v.data);
+        walk_list!(self, visit_anon_const, &v.disr_expr);
+        for attr in &v.attrs {
+            rustc_ast::visit::walk_attribute(self, attr);
         }
-        _ => cx.span_bug(trait_span, "method in `derive(Default)`"),
     }
 }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0665.md b/compiler/rustc_error_codes/src/error_codes/E0665.md
index a15f4021c71..ae54d6d1579 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0665.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0665.md
@@ -1,8 +1,10 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 The `Default` trait was derived on an enum.
 
 Erroneous code example:
 
-```compile_fail,E0665
+```compile_fail
 #[derive(Default)]
 enum Food {
     Sweet,
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 8ab61a5d200..3cb1dd17038 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -677,6 +677,9 @@ declare_features! (
     /// Infer generic args for both consts and types.
     (active, generic_arg_infer, "1.55.0", Some(85077), None),
 
+    /// Allows `#[derive(Default)]` and `#[default]` on enums.
+    (active, derive_default_enum, "1.56.0", Some(86985), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 536ebdef426..5fc773e431c 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -489,6 +489,7 @@ symbols! {
         deref_mut,
         deref_target,
         derive,
+        derive_default_enum,
         destructuring_assignment,
         diagnostic,
         direct,
diff --git a/library/core/src/default.rs b/library/core/src/default.rs
index fd7159d35fa..0fe88222805 100644
--- a/library/core/src/default.rs
+++ b/library/core/src/default.rs
@@ -161,7 +161,8 @@ pub fn default<T: Default>() -> T {
 }
 
 /// Derive macro generating an impl of the trait `Default`.
-#[rustc_builtin_macro]
+#[cfg_attr(not(bootstrap), rustc_builtin_macro(Default, attributes(default)))]
+#[cfg_attr(bootstrap, rustc_builtin_macro)]
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
 #[allow_internal_unstable(core_intrinsics)]
 pub macro Default($item:item) {
diff --git a/src/test/ui/deriving/deriving-default-enum.rs b/src/test/ui/deriving/deriving-default-enum.rs
new file mode 100644
index 00000000000..931ff1a5847
--- /dev/null
+++ b/src/test/ui/deriving/deriving-default-enum.rs
@@ -0,0 +1,19 @@
+// run-pass
+
+#![feature(derive_default_enum)]
+
+// nb: does not impl Default
+#[derive(Debug, PartialEq)]
+struct NotDefault;
+
+#[derive(Debug, Default, PartialEq)]
+enum Foo {
+    #[default]
+    Alpha,
+    #[allow(dead_code)]
+    Beta(NotDefault),
+}
+
+fn main() {
+    assert_eq!(Foo::default(), Foo::Alpha);
+}
diff --git a/src/test/ui/deriving/deriving-with-helper.rs b/src/test/ui/deriving/deriving-with-helper.rs
index ea74a15624c..d8f0b27a2e5 100644
--- a/src/test/ui/deriving/deriving-with-helper.rs
+++ b/src/test/ui/deriving/deriving-with-helper.rs
@@ -5,6 +5,7 @@
 #![feature(lang_items)]
 #![feature(no_core)]
 #![feature(rustc_attrs)]
+#![feature(derive_default_enum)]
 
 #![no_core]
 
@@ -30,7 +31,7 @@ mod default {
 trait Sized {}
 
 #[derive(Default)]
-struct S {
+enum S {
     #[default] // OK
-    field: u8,
+    Foo,
 }
diff --git a/src/test/ui/error-codes/E0665.rs b/src/test/ui/error-codes/E0665.rs
deleted file mode 100644
index cfd42bd9aac..00000000000
--- a/src/test/ui/error-codes/E0665.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-#[derive(Default)] //~ ERROR E0665
-enum Food {
-    Sweet,
-    Salty,
-}
-
-fn main() {
-}
diff --git a/src/test/ui/error-codes/E0665.stderr b/src/test/ui/error-codes/E0665.stderr
deleted file mode 100644
index bb8b3906ca6..00000000000
--- a/src/test/ui/error-codes/E0665.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-error[E0665]: `Default` cannot be derived for enums, only structs
-  --> $DIR/E0665.rs:1:10
-   |
-LL | #[derive(Default)]
-   |          ^^^^^^^
-   |
-   = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0665`.
diff --git a/src/test/ui/feature-gates/feature-gate-derive_default_enum.rs b/src/test/ui/feature-gates/feature-gate-derive_default_enum.rs
new file mode 100644
index 00000000000..05a5d4e1422
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-derive_default_enum.rs
@@ -0,0 +1,7 @@
+#[derive(Default)] //~ ERROR deriving `Default` on enums is experimental
+enum Foo {
+    #[default]
+    Alpha,
+}
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-derive_default_enum.stderr b/src/test/ui/feature-gates/feature-gate-derive_default_enum.stderr
new file mode 100644
index 00000000000..58dd4d508a7
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-derive_default_enum.stderr
@@ -0,0 +1,13 @@
+error[E0658]: deriving `Default` on enums is experimental
+  --> $DIR/feature-gate-derive_default_enum.rs:1:10
+   |
+LL | #[derive(Default)]
+   |          ^^^^^^^
+   |
+   = note: see issue #86985 <https://github.com/rust-lang/rust/issues/86985> for more information
+   = help: add `#![feature(derive_default_enum)]` to the crate attributes to enable
+   = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/macros/macros-nonfatal-errors.rs b/src/test/ui/macros/macros-nonfatal-errors.rs
index 0a496c9dc3d..faacc6f081d 100644
--- a/src/test/ui/macros/macros-nonfatal-errors.rs
+++ b/src/test/ui/macros/macros-nonfatal-errors.rs
@@ -5,9 +5,94 @@
 
 #![feature(asm, llvm_asm)]
 #![feature(trace_macros, concat_idents)]
+#![feature(stmt_expr_attributes, arbitrary_enum_discriminant)]
+#![feature(derive_default_enum)]
 
-#[derive(Default)] //~ ERROR
-enum OrDeriveThis {}
+#[derive(Default)]
+struct DefaultInnerAttrStruct {
+    #[default] //~ ERROR the `#[default]` attribute may only be used on unit enum variants
+    foo: (),
+}
+
+#[derive(Default)]
+struct DefaultInnerAttrTupleStruct(#[default] ());
+//~^ ERROR the `#[default]` attribute may only be used on unit enum variants
+
+#[derive(Default)]
+#[default] //~ ERROR the `#[default]` attribute may only be used on unit enum variants
+struct DefaultOuterAttrStruct {}
+
+#[derive(Default)]
+#[default] //~ ERROR the `#[default]` attribute may only be used on unit enum variants
+enum DefaultOuterAttrEnum {
+    #[default]
+    Foo,
+}
+
+#[rustfmt::skip] // needs some work to handle this case
+#[repr(u8)]
+#[derive(Default)]
+enum AttrOnInnerExpression {
+    Foo = #[default] 0, //~ ERROR the `#[default]` attribute may only be used on unit enum variants
+    Bar([u8; #[default] 1]), //~ ERROR the `#[default]` attribute may only be used on unit enum variants
+    #[default]
+    Baz,
+}
+
+#[derive(Default)] //~ ERROR no default declared
+enum NoDeclaredDefault {
+    Foo,
+    Bar,
+}
+
+#[derive(Default)] //~ ERROR multiple declared defaults
+enum MultipleDefaults {
+    #[default]
+    Foo,
+    #[default]
+    Bar,
+    #[default]
+    Baz,
+}
+
+#[derive(Default)]
+enum ExtraDeriveTokens {
+    #[default = 1] //~ ERROR `#[default]` attribute does not accept a value
+    Foo,
+}
+
+#[derive(Default)]
+enum TwoDefaultAttrs {
+    #[default]
+    #[default]
+    Foo, //~ERROR multiple `#[default]` attributes
+    Bar,
+}
+
+#[derive(Default)]
+enum ManyDefaultAttrs {
+    #[default]
+    #[default]
+    #[default]
+    #[default]
+    Foo, //~ERROR multiple `#[default]` attributes
+    Bar,
+}
+
+#[derive(Default)]
+enum DefaultHasFields {
+    #[default]
+    Foo {}, //~ ERROR the `#[default]` attribute may only be used on unit enum variants
+    Bar,
+}
+
+#[derive(Default)]
+enum NonExhaustiveDefault {
+    #[default]
+    #[non_exhaustive]
+    Foo, //~ ERROR default variant must be exhaustive
+    Bar,
+}
 
 fn main() {
     asm!(invalid); //~ ERROR
diff --git a/src/test/ui/macros/macros-nonfatal-errors.stderr b/src/test/ui/macros/macros-nonfatal-errors.stderr
index 14058f86639..e0ed8522bf6 100644
--- a/src/test/ui/macros/macros-nonfatal-errors.stderr
+++ b/src/test/ui/macros/macros-nonfatal-errors.stderr
@@ -1,49 +1,169 @@
-error[E0665]: `Default` cannot be derived for enums, only structs
-  --> $DIR/macros-nonfatal-errors.rs:9:10
+error: the `#[default]` attribute may only be used on unit enum variants
+  --> $DIR/macros-nonfatal-errors.rs:13:5
+   |
+LL |     #[default]
+   |     ^^^^^^^^^^
+
+error: the `#[default]` attribute may only be used on unit enum variants
+  --> $DIR/macros-nonfatal-errors.rs:18:36
+   |
+LL | struct DefaultInnerAttrTupleStruct(#[default] ());
+   |                                    ^^^^^^^^^^
+
+error: the `#[default]` attribute may only be used on unit enum variants
+  --> $DIR/macros-nonfatal-errors.rs:22:1
+   |
+LL | #[default]
+   | ^^^^^^^^^^
+
+error: the `#[default]` attribute may only be used on unit enum variants
+  --> $DIR/macros-nonfatal-errors.rs:26:1
+   |
+LL | #[default]
+   | ^^^^^^^^^^
+
+error: the `#[default]` attribute may only be used on unit enum variants
+  --> $DIR/macros-nonfatal-errors.rs:36:11
+   |
+LL |     Foo = #[default] 0,
+   |           ^^^^^^^^^^
+
+error: the `#[default]` attribute may only be used on unit enum variants
+  --> $DIR/macros-nonfatal-errors.rs:37:14
+   |
+LL |     Bar([u8; #[default] 1]),
+   |              ^^^^^^^^^^
+
+error: no default declared
+  --> $DIR/macros-nonfatal-errors.rs:42:10
    |
 LL | #[derive(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)
+
+error: multiple declared defaults
+  --> $DIR/macros-nonfatal-errors.rs:48:10
+   |
+LL | #[derive(Default)]
+   |          ^^^^^^^
+...
+LL |     Foo,
+   |     --- first default
+LL |     #[default]
+LL |     Bar,
+   |     --- additional default
+LL |     #[default]
+LL |     Baz,
+   |     --- additional default
+   |
+   = note: only one variant can be default
    = 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
+   |
+LL |     #[default = 1]
+   |     ^^^^^^^^^^^^^^
+   |
+   = help: try using `#[default]`
+
+error: multiple `#[default]` attributes
+  --> $DIR/macros-nonfatal-errors.rs:68:5
+   |
+LL |     #[default]
+   |     ---------- `#[default]` used here
+LL |     #[default]
+   |     ---------- `#[default]` used again here
+LL |     Foo,
+   |     ^^^
+   |
+   = note: only one `#[default]` attribute is needed
+help: try removing this
+  --> $DIR/macros-nonfatal-errors.rs:67:5
+   |
+LL |     #[default]
+   |     ^^^^^^^^^^
+
+error: multiple `#[default]` attributes
+  --> $DIR/macros-nonfatal-errors.rs:78:5
+   |
+LL |     #[default]
+   |     ---------- `#[default]` used here
+LL |     #[default]
+   |     ---------- `#[default]` used again here
+...
+LL |     Foo,
+   |     ^^^
+   |
+   = note: only one `#[default]` attribute is needed
+help: try removing these
+  --> $DIR/macros-nonfatal-errors.rs:75:5
+   |
+LL |     #[default]
+   |     ^^^^^^^^^^
+LL |     #[default]
+   |     ^^^^^^^^^^
+LL |     #[default]
+   |     ^^^^^^^^^^
+
+error: the `#[default]` attribute may only be used on unit enum variants
+  --> $DIR/macros-nonfatal-errors.rs:85:5
+   |
+LL |     Foo {},
+   |     ^^^
+   |
+   = help: consider a manual implementation of `Default`
+
+error: default variant must be exhaustive
+  --> $DIR/macros-nonfatal-errors.rs:93:5
+   |
+LL |     #[non_exhaustive]
+   |     ----------------- declared `#[non_exhaustive]` here
+LL |     Foo,
+   |     ^^^
+   |
+   = help: consider a manual implementation of `Default`
+
 error: asm template must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:13:10
+  --> $DIR/macros-nonfatal-errors.rs:98:10
    |
 LL |     asm!(invalid);
    |          ^^^^^^^
 
 error: inline assembly must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:14:15
+  --> $DIR/macros-nonfatal-errors.rs:99:15
    |
 LL |     llvm_asm!(invalid);
    |               ^^^^^^^
 
 error: concat_idents! requires ident args.
-  --> $DIR/macros-nonfatal-errors.rs:16:5
+  --> $DIR/macros-nonfatal-errors.rs:101:5
    |
 LL |     concat_idents!("not", "idents");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:18:17
+  --> $DIR/macros-nonfatal-errors.rs:103:17
    |
 LL |     option_env!(invalid);
    |                 ^^^^^^^
 
 error: expected string literal
-  --> $DIR/macros-nonfatal-errors.rs:19:10
+  --> $DIR/macros-nonfatal-errors.rs:104:10
    |
 LL |     env!(invalid);
    |          ^^^^^^^
 
 error: expected string literal
-  --> $DIR/macros-nonfatal-errors.rs:20:10
+  --> $DIR/macros-nonfatal-errors.rs:105:10
    |
 LL |     env!(foo, abr, baz);
    |          ^^^
 
 error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined
-  --> $DIR/macros-nonfatal-errors.rs:21:5
+  --> $DIR/macros-nonfatal-errors.rs:106:5
    |
 LL |     env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -51,7 +171,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:23:13
+  --> $DIR/macros-nonfatal-errors.rs:108:13
    |
 LL |     format!(invalid);
    |             ^^^^^^^
@@ -62,19 +182,19 @@ LL |     format!("{}", invalid);
    |             ^^^^^
 
 error: argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:25:14
+  --> $DIR/macros-nonfatal-errors.rs:110:14
    |
 LL |     include!(invalid);
    |              ^^^^^^^
 
 error: argument must be a string literal
-  --> $DIR/macros-nonfatal-errors.rs:27:18
+  --> $DIR/macros-nonfatal-errors.rs:112: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 (os error 2)
-  --> $DIR/macros-nonfatal-errors.rs:28:5
+  --> $DIR/macros-nonfatal-errors.rs:113:5
    |
 LL |     include_str!("i'd be quite surprised if a file with this name existed");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,13 +202,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:29:20
+  --> $DIR/macros-nonfatal-errors.rs:114: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 (os error 2)
-  --> $DIR/macros-nonfatal-errors.rs:30:5
+  --> $DIR/macros-nonfatal-errors.rs:115:5
    |
 LL |     include_bytes!("i'd be quite surprised if a file with this name existed");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,11 +216,10 @@ 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:32:5
+  --> $DIR/macros-nonfatal-errors.rs:117:5
    |
 LL |     trace_macros!(invalid);
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 15 previous errors
+error: aborting due to 27 previous errors
 
-For more information about this error, try `rustc --explain E0665`.
diff --git a/src/test/ui/resolve/issue-2356.rs b/src/test/ui/resolve/issue-2356.rs
index f7b2dd13dcb..fe9bf4d443e 100644
--- a/src/test/ui/resolve/issue-2356.rs
+++ b/src/test/ui/resolve/issue-2356.rs
@@ -29,7 +29,7 @@ impl Clone for Cat {
 impl Default for Cat {
   fn default() -> Self {
     default();
-    //~^ ERROR cannot find function `default`
+    //~^ ERROR cannot find function `default` in this scope [E0425]
     loop {}
   }
 }