about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs1
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs1
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs8
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_typeck/src/check/check.rs29
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs25
-rw-r--r--compiler/rustc_typeck/src/collect.rs106
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of.rs44
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of.stderr15
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs19
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.stderr34
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.rs13
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr11
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs38
-rw-r--r--src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr82
17 files changed, 431 insertions, 4 deletions
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 46817bc9c3f..5309f217b4a 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -682,6 +682,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
         from method dispatch when the receiver is an array, for compatibility in editions < 2021."
     ),
+    rustc_attr!(
+        rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."), ErrorFollowing,
+        "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \
+        definition of a trait, it's currently in experimental form and should be changed before \
+        being exposed outside of the std"
+    ),
 
     // ==========================================================================
     // Internal attributes, Testing:
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 39aa1844d69..220bc9c5f75 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -820,6 +820,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                     data.skip_array_during_method_dispatch,
                     data.specialization_kind,
                     self.def_path_hash(item_id),
+                    data.must_implement_one_of,
                 )
             }
             EntryKind::TraitAlias => ty::TraitDef::new(
@@ -831,6 +832,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                 false,
                 ty::trait_def::TraitSpecializationKind::None,
                 self.def_path_hash(item_id),
+                None,
             ),
             _ => bug!("def-index does not refer to trait or trait alias"),
         }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index ee6656ce372..ebb78adf343 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1513,6 +1513,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                     is_marker: trait_def.is_marker,
                     skip_array_during_method_dispatch: trait_def.skip_array_during_method_dispatch,
                     specialization_kind: trait_def.specialization_kind,
+                    must_implement_one_of: trait_def.must_implement_one_of.clone(),
                 };
 
                 EntryKind::Trait(self.lazy(data))
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 75c5880f05d..8424a31d59f 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -378,6 +378,7 @@ struct TraitData {
     is_marker: bool,
     skip_array_during_method_dispatch: bool,
     specialization_kind: ty::trait_def::TraitSpecializationKind,
+    must_implement_one_of: Option<Box<[Ident]>>,
 }
 
 #[derive(TyEncodable, TyDecodable)]
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 34d059f4ec8..9f8053d4a4e 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -1,7 +1,7 @@
 use crate::traits::specialization_graph;
 use crate::ty::fast_reject::{self, SimplifiedType, SimplifyParams, StripReferences};
 use crate::ty::fold::TypeFoldable;
-use crate::ty::{Ty, TyCtxt};
+use crate::ty::{Ident, Ty, TyCtxt};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::definitions::DefPathHash;
@@ -44,6 +44,10 @@ pub struct TraitDef {
     /// The ICH of this trait's DefPath, cached here so it doesn't have to be
     /// recomputed all the time.
     pub def_path_hash: DefPathHash,
+
+    /// List of functions from `#[rustc_must_implement_one_of]` attribute one of which
+    /// must be implemented.
+    pub must_implement_one_of: Option<Box<[Ident]>>,
 }
 
 /// Whether this trait is treated specially by the standard library
@@ -87,6 +91,7 @@ impl<'tcx> TraitDef {
         skip_array_during_method_dispatch: bool,
         specialization_kind: TraitSpecializationKind,
         def_path_hash: DefPathHash,
+        must_implement_one_of: Option<Box<[Ident]>>,
     ) -> TraitDef {
         TraitDef {
             def_id,
@@ -97,6 +102,7 @@ impl<'tcx> TraitDef {
             skip_array_during_method_dispatch,
             specialization_kind,
             def_path_hash,
+            must_implement_one_of,
         }
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8020893e166..21f4312de35 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1162,6 +1162,7 @@ symbols! {
         rustc_macro_transparency,
         rustc_main,
         rustc_mir,
+        rustc_must_implement_one_of,
         rustc_nonnull_optimization_guaranteed,
         rustc_object_lifetime_default,
         rustc_on_unimplemented,
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index 7a3d5990329..eb49cc0233d 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -978,6 +978,10 @@ fn check_impl_items_against_trait<'tcx>(
     if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) {
         // Check for missing items from trait
         let mut missing_items = Vec::new();
+
+        let mut must_implement_one_of: Option<&[Ident]> =
+            trait_def.must_implement_one_of.as_deref();
+
         for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) {
             let is_implemented = ancestors
                 .leaf_def(tcx, trait_item_id)
@@ -986,12 +990,37 @@ fn check_impl_items_against_trait<'tcx>(
             if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
                 missing_items.push(tcx.associated_item(trait_item_id));
             }
+
+            if let Some(required_items) = &must_implement_one_of {
+                // true if this item is specifically implemented in this impl
+                let is_implemented_here = ancestors
+                    .leaf_def(tcx, trait_item_id)
+                    .map_or(false, |node_item| !node_item.defining_node.is_from_trait());
+
+                if is_implemented_here {
+                    let trait_item = tcx.associated_item(trait_item_id);
+                    if required_items.contains(&trait_item.ident) {
+                        must_implement_one_of = None;
+                    }
+                }
+            }
         }
 
         if !missing_items.is_empty() {
             let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span);
             missing_items_err(tcx, impl_span, &missing_items, full_impl_span);
         }
+
+        if let Some(missing_items) = must_implement_one_of {
+            let impl_span = tcx.sess.source_map().guess_head_span(full_impl_span);
+            let attr_span = tcx
+                .get_attrs(impl_trait_ref.def_id)
+                .iter()
+                .find(|attr| attr.has_name(sym::rustc_must_implement_one_of))
+                .map(|attr| attr.span);
+
+            missing_items_must_implement_one_of_err(tcx, impl_span, missing_items, attr_span);
+        }
     }
 }
 
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index 719266ad5a4..059d4a7f3a4 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -637,6 +637,31 @@ fn missing_items_err(
     err.emit();
 }
 
+fn missing_items_must_implement_one_of_err(
+    tcx: TyCtxt<'_>,
+    impl_span: Span,
+    missing_items: &[Ident],
+    annotation_span: Option<Span>,
+) {
+    let missing_items_msg =
+        missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `");
+
+    let mut err = struct_span_err!(
+        tcx.sess,
+        impl_span,
+        E0046,
+        "not all trait items implemented, missing one of: `{}`",
+        missing_items_msg
+    );
+    err.span_label(impl_span, format!("missing one of `{}` in implementation", missing_items_msg));
+
+    if let Some(annotation_span) = annotation_span {
+        err.span_note(annotation_span, "required because of this annotation");
+    }
+
+    err.emit();
+}
+
 /// Resugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
 fn bounds_from_generic_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 2ed98e20f1d..314174e0f85 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1190,9 +1190,11 @@ fn super_predicates_that_define_assoc_type(
 fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
     let item = tcx.hir().expect_item(def_id.expect_local());
 
-    let (is_auto, unsafety) = match item.kind {
-        hir::ItemKind::Trait(is_auto, unsafety, ..) => (is_auto == hir::IsAuto::Yes, unsafety),
-        hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal),
+    let (is_auto, unsafety, items) = match item.kind {
+        hir::ItemKind::Trait(is_auto, unsafety, .., items) => {
+            (is_auto == hir::IsAuto::Yes, unsafety, items)
+        }
+        hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal, &[][..]),
         _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"),
     };
 
@@ -1219,6 +1221,103 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
         ty::trait_def::TraitSpecializationKind::None
     };
     let def_path_hash = tcx.def_path_hash(def_id);
+
+    let must_implement_one_of = tcx
+        .get_attrs(def_id)
+        .iter()
+        .find(|attr| attr.has_name(sym::rustc_must_implement_one_of))
+        // Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]`
+        // and that they are all identifiers
+        .and_then(|attr| match attr.meta_item_list() {
+            Some(items) if items.len() < 2 => {
+                tcx.sess
+                    .struct_span_err(
+                        attr.span,
+                        "the `#[rustc_must_implement_one_of]` attribute must be \
+                        used with at least 2 args",
+                    )
+                    .emit();
+
+                None
+            }
+            Some(items) => items
+                .into_iter()
+                .map(|item| item.ident().ok_or(item.span()))
+                .collect::<Result<Box<[_]>, _>>()
+                .map_err(|span| {
+                    tcx.sess
+                        .struct_span_err(span, "must be a name of an associated function")
+                        .emit();
+                })
+                .ok()
+                .zip(Some(attr.span)),
+            // Error is reported by `rustc_attr!`
+            None => None,
+        })
+        // Check that all arguments of `#[rustc_must_implement_one_of]` reference
+        // functions in the trait with default implementations
+        .and_then(|(list, attr_span)| {
+            let errors = list.iter().filter_map(|ident| {
+                let item = items.iter().find(|item| item.ident == *ident);
+
+                match item {
+                    Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => {
+                        if !item.defaultness.has_value() {
+                            tcx.sess
+                                .struct_span_err(
+                                    item.span,
+                                    "This function doesn't have a default implementation",
+                                )
+                                .span_note(attr_span, "required by this annotation")
+                                .emit();
+
+                            return Some(());
+                        }
+
+                        return None;
+                    }
+                    Some(item) => tcx
+                        .sess
+                        .struct_span_err(item.span, "Not a function")
+                        .span_note(attr_span, "required by this annotation")
+                        .note(
+                            "All `#[rustc_must_implement_one_of]` arguments \
+                            must be associated function names",
+                        )
+                        .emit(),
+                    None => tcx
+                        .sess
+                        .struct_span_err(ident.span, "Function not found in this trait")
+                        .emit(),
+                }
+
+                Some(())
+            });
+
+            (errors.count() == 0).then_some(list)
+        })
+        // Check for duplicates
+        .and_then(|list| {
+            let mut set: FxHashMap<Symbol, Span> = FxHashMap::default();
+            let mut no_dups = true;
+
+            for ident in &*list {
+                if let Some(dup) = set.insert(ident.name, ident.span) {
+                    tcx.sess
+                        .struct_span_err(vec![dup, ident.span], "Functions names are duplicated")
+                        .note(
+                            "All `#[rustc_must_implement_one_of]` arguments \
+                            must be unique",
+                        )
+                        .emit();
+
+                    no_dups = false;
+                }
+            }
+
+            no_dups.then_some(list)
+        });
+
     ty::TraitDef::new(
         def_id,
         unsafety,
@@ -1228,6 +1327,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
         skip_array_during_method_dispatch,
         spec_kind,
         def_path_hash,
+        must_implement_one_of,
     )
 }
 
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of.rs b/src/test/ui/traits/default-method/rustc_must_implement_one_of.rs
new file mode 100644
index 00000000000..5ba2f5ce334
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of.rs
@@ -0,0 +1,44 @@
+#![feature(rustc_attrs)]
+
+#[rustc_must_implement_one_of(eq, neq)]
+trait Equal {
+    fn eq(&self, other: &Self) -> bool {
+        !self.neq(other)
+    }
+
+    fn neq(&self, other: &Self) -> bool {
+        !self.eq(other)
+    }
+}
+
+struct T0;
+struct T1;
+struct T2;
+struct T3;
+
+impl Equal for T0 {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+}
+
+impl Equal for T1 {
+    fn neq(&self, _other: &Self) -> bool {
+        false
+    }
+}
+
+impl Equal for T2 {
+    fn eq(&self, _other: &Self) -> bool {
+        true
+    }
+
+    fn neq(&self, _other: &Self) -> bool {
+        false
+    }
+}
+
+impl Equal for T3 {}
+//~^ not all trait items implemented, missing one of: `eq`, `neq`
+
+fn main() {}
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of.stderr b/src/test/ui/traits/default-method/rustc_must_implement_one_of.stderr
new file mode 100644
index 00000000000..5a4dd1388b2
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of.stderr
@@ -0,0 +1,15 @@
+error[E0046]: not all trait items implemented, missing one of: `eq`, `neq`
+  --> $DIR/rustc_must_implement_one_of.rs:41:1
+   |
+LL | impl Equal for T3 {}
+   | ^^^^^^^^^^^^^^^^^ missing one of `eq`, `neq` in implementation
+   |
+note: required because of this annotation
+  --> $DIR/rustc_must_implement_one_of.rs:3:1
+   |
+LL | #[rustc_must_implement_one_of(eq, neq)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0046`.
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs b/src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs
new file mode 100644
index 00000000000..56e8fcff0fc
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.rs
@@ -0,0 +1,19 @@
+#![feature(rustc_attrs)]
+
+#[rustc_must_implement_one_of(a, a)]
+//~^ Functions names are duplicated
+trait Trait {
+    fn a() {}
+}
+
+#[rustc_must_implement_one_of(b, a, a, c, b, c)]
+//~^ Functions names are duplicated
+//~| Functions names are duplicated
+//~| Functions names are duplicated
+trait Trait1 {
+    fn a() {}
+    fn b() {}
+    fn c() {}
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.stderr b/src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.stderr
new file mode 100644
index 00000000000..777beba6182
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_duplicates.stderr
@@ -0,0 +1,34 @@
+error: Functions names are duplicated
+  --> $DIR/rustc_must_implement_one_of_duplicates.rs:3:31
+   |
+LL | #[rustc_must_implement_one_of(a, a)]
+   |                               ^  ^
+   |
+   = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+
+error: Functions names are duplicated
+  --> $DIR/rustc_must_implement_one_of_duplicates.rs:9:34
+   |
+LL | #[rustc_must_implement_one_of(b, a, a, c, b, c)]
+   |                                  ^  ^
+   |
+   = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+
+error: Functions names are duplicated
+  --> $DIR/rustc_must_implement_one_of_duplicates.rs:9:31
+   |
+LL | #[rustc_must_implement_one_of(b, a, a, c, b, c)]
+   |                               ^           ^
+   |
+   = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+
+error: Functions names are duplicated
+  --> $DIR/rustc_must_implement_one_of_duplicates.rs:9:40
+   |
+LL | #[rustc_must_implement_one_of(b, a, a, c, b, c)]
+   |                                        ^     ^
+   |
+   = note: All `#[rustc_must_implement_one_of]` arguments must be unique
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.rs b/src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.rs
new file mode 100644
index 00000000000..ec2995872de
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.rs
@@ -0,0 +1,13 @@
+#[rustc_must_implement_one_of(eq, neq)]
+//~^ the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std
+trait Equal {
+    fn eq(&self, other: &Self) -> bool {
+        !self.neq(other)
+    }
+
+    fn neq(&self, other: &Self) -> bool {
+        !self.eq(other)
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr b/src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr
new file mode 100644
index 00000000000..228bc3e35c2
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_gated.stderr
@@ -0,0 +1,11 @@
+error[E0658]: the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete definition of a trait, it's currently in experimental form and should be changed before being exposed outside of the std
+  --> $DIR/rustc_must_implement_one_of_gated.rs:1:1
+   |
+LL | #[rustc_must_implement_one_of(eq, neq)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs
new file mode 100644
index 00000000000..1089e5f9c4a
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs
@@ -0,0 +1,38 @@
+#![feature(rustc_attrs)]
+
+#[rustc_must_implement_one_of(a, b)]
+//~^ Function not found in this trait
+//~| Function not found in this trait
+trait Tr0 {}
+
+#[rustc_must_implement_one_of(a, b)]
+//~^ Function not found in this trait
+trait Tr1 {
+    fn a() {}
+}
+
+#[rustc_must_implement_one_of(a)]
+//~^ the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
+trait Tr2 {
+    fn a() {}
+}
+
+#[rustc_must_implement_one_of]
+//~^ malformed `rustc_must_implement_one_of` attribute input
+trait Tr3 {}
+
+#[rustc_must_implement_one_of(A, B)]
+trait Tr4 {
+    const A: u8 = 1; //~ Not a function
+
+    type B; //~ Not a function
+}
+
+#[rustc_must_implement_one_of(a, b)]
+trait Tr5 {
+    fn a(); //~ This function doesn't have a default implementation
+
+    fn b(); //~ This function doesn't have a default implementation
+}
+
+fn main() {}
diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr
new file mode 100644
index 00000000000..74a6dc8fec9
--- /dev/null
+++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr
@@ -0,0 +1,82 @@
+error: malformed `rustc_must_implement_one_of` attribute input
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:20:1
+   |
+LL | #[rustc_must_implement_one_of]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_must_implement_one_of(function1, function2, ...)]`
+
+error: Function not found in this trait
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:3:31
+   |
+LL | #[rustc_must_implement_one_of(a, b)]
+   |                               ^
+
+error: Function not found in this trait
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:3:34
+   |
+LL | #[rustc_must_implement_one_of(a, b)]
+   |                                  ^
+
+error: Function not found in this trait
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:8:34
+   |
+LL | #[rustc_must_implement_one_of(a, b)]
+   |                                  ^
+
+error: the `#[rustc_must_implement_one_of]` attribute must be used with at least 2 args
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:14:1
+   |
+LL | #[rustc_must_implement_one_of(a)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: Not a function
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:26:5
+   |
+LL |     const A: u8 = 1;
+   |     ^^^^^^^^^^^^^^^^
+   |
+note: required by this annotation
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:24:1
+   |
+LL | #[rustc_must_implement_one_of(A, B)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: All `#[rustc_must_implement_one_of]` arguments must be associated function names
+
+error: Not a function
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:28:5
+   |
+LL |     type B;
+   |     ^^^^^^^
+   |
+note: required by this annotation
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:24:1
+   |
+LL | #[rustc_must_implement_one_of(A, B)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: All `#[rustc_must_implement_one_of]` arguments must be associated function names
+
+error: This function doesn't have a default implementation
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:33:5
+   |
+LL |     fn a();
+   |     ^^^^^^^
+   |
+note: required by this annotation
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:31:1
+   |
+LL | #[rustc_must_implement_one_of(a, b)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: This function doesn't have a default implementation
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:35:5
+   |
+LL |     fn b();
+   |     ^^^^^^^
+   |
+note: required by this annotation
+  --> $DIR/rustc_must_implement_one_of_misuse.rs:31:1
+   |
+LL | #[rustc_must_implement_one_of(a, b)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
+