about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPavel Grigorenko <GrigorenkoPV@ya.ru>2025-06-18 22:27:35 +0300
committerPavel Grigorenko <GrigorenkoPV@ya.ru>2025-06-23 22:48:20 +0300
commitaa80a2b62cde51fc9796a66dfed013a581bff706 (patch)
treefa2f84142c37f4762399cce580233940c5ff594e
parent42245d34d22ade32b3f276dcf74deb826841594c (diff)
downloadrust-aa80a2b62cde51fc9796a66dfed013a581bff706.tar.gz
rust-aa80a2b62cde51fc9796a66dfed013a581bff706.zip
Port `#[rustc_skip_during_method_dispatch]` to the new attribute system
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs3
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs14
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/semantics.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs9
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/traits.rs54
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/parser.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs4
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs22
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs7
-rw-r--r--compiler/rustc_passes/src/check_attr.rs15
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--tests/ui/attributes/rustc_skip_during_method_dispatch.rs38
-rw-r--r--tests/ui/attributes/rustc_skip_during_method_dispatch.stderr76
17 files changed, 246 insertions, 41 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 9227b81f12f..ae3a1a41387 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -259,6 +259,9 @@ pub enum AttributeKind {
     /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
     Repr(ThinVec<(ReprAttr, Span)>),
 
+    /// Represents `#[rustc_skip_during_method_dispatch]`.
+    SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
+
     /// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
     Stability {
         stability: Stability,
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 24c40c301fe..0fa69c40154 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -50,8 +50,8 @@ impl<S: Stage> SingleAttributeParser<S> for ColdParser {
     const TEMPLATE: AttributeTemplate = template!(Word);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
-        if !args.no_args() {
-            cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
             return None;
         }
 
@@ -67,8 +67,8 @@ pub(crate) struct NakedParser {
 impl<S: Stage> AttributeParser<S> for NakedParser {
     const ATTRIBUTES: AcceptMapping<Self, S> =
         &[(&[sym::naked], template!(Word), |this, cx, args| {
-            if !args.no_args() {
-                cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
+            if let Err(span) = args.no_args() {
+                cx.expected_no_args(span);
                 return;
             }
 
@@ -175,10 +175,10 @@ impl<S: Stage> SingleAttributeParser<S> for NoMangleParser {
     const TEMPLATE: AttributeTemplate = template!(Word);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
-        if !args.no_args() {
-            cx.expected_no_args(args.span().unwrap_or(cx.attr_span));
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
             return None;
-        };
+        }
 
         Some(AttributeKind::NoMangle(cx.attr_span))
     }
diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
index 4cfd9a82ce8..1c8fc5079da 100644
--- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
@@ -14,8 +14,10 @@ impl<S: Stage> SingleAttributeParser<S> for AsPtrParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
     const TEMPLATE: AttributeTemplate = template!(Word);
 
-    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
-        // FIXME: check that there's no args (this is currently checked elsewhere)
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
         Some(AttributeKind::AsPtr(cx.attr_span))
     }
 }
@@ -27,8 +29,10 @@ impl<S: Stage> SingleAttributeParser<S> for PubTransparentParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
     const TEMPLATE: AttributeTemplate = template!(Word);
 
-    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
-        // FIXME: check that there's no args (this is currently checked elsewhere)
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
         Some(AttributeKind::PubTransparent(cx.attr_span))
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index 738d8735b69..ac7e90fd902 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -36,6 +36,7 @@ pub(crate) mod must_use;
 pub(crate) mod repr;
 pub(crate) mod semantics;
 pub(crate) mod stability;
+pub(crate) mod traits;
 pub(crate) mod transparency;
 pub(crate) mod util;
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
index 071574a5612..54f50445fbd 100644
--- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
@@ -13,7 +13,10 @@ impl<S: Stage> SingleAttributeParser<S> for MayDangleParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
     const TEMPLATE: AttributeTemplate = template!(Word);
 
-    fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
         Some(AttributeKind::MayDangle(cx.attr_span))
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index 6871ff4ec9f..37104855623 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -139,7 +139,10 @@ impl<S: Stage> SingleAttributeParser<S> for ConstStabilityIndirectParser {
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Ignore;
     const TEMPLATE: AttributeTemplate = template!(Word);
 
-    fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option<AttributeKind> {
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        if let Err(span) = args.no_args() {
+            cx.expected_no_args(span);
+        }
         Some(AttributeKind::ConstStabilityIndirect)
     }
 }
@@ -361,8 +364,8 @@ pub(crate) fn parse_unstability<S: Stage>(
                 };
             }
             Some(sym::soft) => {
-                if !param.args().no_args() {
-                    cx.emit_err(session_diagnostics::SoftNoArgs { span: param.span() });
+                if let Err(span) = args.no_args() {
+                    cx.emit_err(session_diagnostics::SoftNoArgs { span });
                 }
                 is_soft = true;
             }
diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs
new file mode 100644
index 00000000000..83a98c53c7f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs
@@ -0,0 +1,54 @@
+use core::mem;
+
+use rustc_attr_data_structures::AttributeKind;
+use rustc_feature::{AttributeTemplate, template};
+use rustc_span::{Symbol, sym};
+
+use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
+pub(crate) struct SkipDuringMethodDispatchParser;
+
+impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
+    const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch];
+    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
+    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
+
+    const TEMPLATE: AttributeTemplate = template!(List: "array, boxed_slice");
+
+    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
+        let mut array = false;
+        let mut boxed_slice = false;
+        let Some(args) = args.list() else {
+            cx.expected_list(cx.attr_span);
+            return None;
+        };
+        if args.is_empty() {
+            cx.expected_at_least_one_argument(args.span);
+            return None;
+        }
+        for arg in args.mixed() {
+            let Some(arg) = arg.meta_item() else {
+                cx.unexpected_literal(arg.span());
+                continue;
+            };
+            if let Err(span) = arg.args().no_args() {
+                cx.expected_no_args(span);
+            }
+            let path = arg.path();
+            let (key, skip): (Symbol, &mut bool) = match path.word_sym() {
+                Some(key @ sym::array) => (key, &mut array),
+                Some(key @ sym::boxed_slice) => (key, &mut boxed_slice),
+                _ => {
+                    cx.expected_specific_argument(path.span(), vec!["array", "boxed_slice"]);
+                    continue;
+                }
+            };
+            if mem::replace(skip, true) {
+                cx.duplicate_key(arg.span(), key);
+            }
+        }
+        Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span })
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 457e073c488..43b2d07dbe1 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -26,6 +26,7 @@ use crate::attributes::semantics::MayDangleParser;
 use crate::attributes::stability::{
     BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
 };
+use crate::attributes::traits::SkipDuringMethodDispatchParser;
 use crate::attributes::transparency::TransparencyParser;
 use crate::attributes::{AttributeParser as _, Combine, Single};
 use crate::parser::{ArgParser, MetaItemParser, PathParser};
@@ -119,6 +120,7 @@ attribute_parsers!(
         Single<OptimizeParser>,
         Single<PubTransparentParser>,
         Single<RustcForceInlineParser>,
+        Single<SkipDuringMethodDispatchParser>,
         Single<TransparencyParser>,
         // tidy-alphabetical-end
     ];
@@ -325,6 +327,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
+    pub(crate) fn expected_at_least_one_argument(&self, span: Span) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedAtLeastOneArgument,
+        })
+    }
+
     pub(crate) fn expected_specific_argument(
         &self,
         span: Span,
diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs
index e02dc098127..aecaae947c9 100644
--- a/compiler/rustc_attr_parsing/src/parser.rs
+++ b/compiler/rustc_attr_parsing/src/parser.rs
@@ -169,9 +169,15 @@ impl<'a> ArgParser<'a> {
         }
     }
 
-    /// Asserts that there are no arguments
-    pub fn no_args(&self) -> bool {
-        matches!(self, Self::NoArgs)
+    /// Assert that there were no args.
+    /// If there were, get a span to the arguments
+    /// (to pass to [`AcceptContext::expected_no_args`](crate::context::AcceptContext::expected_no_args)).
+    pub fn no_args(&self) -> Result<(), Span> {
+        match self {
+            Self::NoArgs => Ok(()),
+            Self::List(args) => Err(args.span),
+            Self::NameValue(args) => Err(args.eq_span.to(args.value_span)),
+        }
     }
 }
 
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 808e452799d..263b323e3eb 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -496,6 +496,7 @@ pub(crate) struct NakedFunctionIncompatibleAttribute {
 pub(crate) enum AttributeParseErrorReason {
     ExpectedNoArgs,
     ExpectedStringLiteral { byte_string: Option<Span> },
+    ExpectedAtLeastOneArgument,
     ExpectedSingleArgument,
     ExpectedList,
     UnexpectedLiteral,
@@ -539,6 +540,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                 diag.span_label(self.span, "expected a single argument here");
                 diag.code(E0805);
             }
+            AttributeParseErrorReason::ExpectedAtLeastOneArgument => {
+                diag.span_label(self.span, "expected at least 1 argument here");
+            }
             AttributeParseErrorReason::ExpectedList => {
                 diag.span_label(self.span, "expected this to be a list");
             }
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 280b33f0723..3540c9b4bf0 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -1083,7 +1083,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "the `#[rustc_main]` attribute is used internally to specify test entry point function",
     ),
     rustc_attr!(
-        rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), WarnFollowing,
+        rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), ErrorFollowing,
         EncodeCrossCrate::No,
         "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \
         from method dispatch when the receiver is of the following type, for compatibility in \
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 176d955bf03..c967e87bfd8 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -21,6 +21,7 @@ use std::ops::Bound;
 
 use rustc_abi::ExternAbi;
 use rustc_ast::Recovered;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::unord::UnordMap;
 use rustc_errors::{
@@ -1151,22 +1152,11 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
     let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
     let is_fundamental = tcx.has_attr(def_id, sym::fundamental);
 
-    // FIXME: We could probably do way better attribute validation here.
-    let mut skip_array_during_method_dispatch = false;
-    let mut skip_boxed_slice_during_method_dispatch = false;
-    for attr in tcx.get_attrs(def_id, sym::rustc_skip_during_method_dispatch) {
-        if let Some(lst) = attr.meta_item_list() {
-            for item in lst {
-                if let Some(ident) = item.ident() {
-                    match ident.as_str() {
-                        "array" => skip_array_during_method_dispatch = true,
-                        "boxed_slice" => skip_boxed_slice_during_method_dispatch = true,
-                        _ => (),
-                    }
-                }
-            }
-        }
-    }
+    let [skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch] = find_attr!(
+        tcx.get_all_attrs(def_id),
+        AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span:_ } => [*array, *boxed_slice]
+    )
+    .unwrap_or([false; 2]);
 
     let specialization_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
         ty::trait_def::TraitSpecializationKind::Marker
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 8e6442353c3..47e29923ee5 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -286,13 +286,20 @@ fn emit_malformed_attribute(
     if matches!(
         name,
         sym::inline
+            | sym::may_dangle
+            | sym::rustc_as_ptr
+            | sym::rustc_pub_transparent
+            | sym::rustc_const_stable_indirect
             | sym::rustc_force_inline
             | sym::rustc_confusables
+            | sym::rustc_skip_during_method_dispatch
             | sym::repr
             | sym::align
             | sym::deprecated
             | sym::optimize
             | sym::cold
+            | sym::naked
+            | sym::no_mangle
             | sym::must_use
     ) {
         return;
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c2a58b4cd7d..25b84179443 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -118,6 +118,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         for attr in attrs {
             let mut style = None;
             match attr {
+                Attribute::Parsed(AttributeKind::SkipDuringMethodDispatch {
+                    span: attr_span,
+                    ..
+                }) => {
+                    self.check_must_be_applied_to_trait(*attr_span, span, target);
+                }
                 Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => {
                     self.check_confusables(*first_span, target);
                 }
@@ -250,7 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         | [sym::rustc_must_implement_one_of, ..]
                         | [sym::rustc_deny_explicit_impl, ..]
                         | [sym::rustc_do_not_implement_via_object, ..]
-                        | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target),
+                        | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr.span(), span, target),
                         [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target),
                         [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target),
                         [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target),
@@ -1805,14 +1811,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if the attribute is applied to a trait.
-    fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) {
+    fn check_must_be_applied_to_trait(&self, attr_span: Span, defn_span: Span, target: Target) {
         match target {
             Target::Trait => {}
             _ => {
-                self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait {
-                    attr_span: attr.span(),
-                    defn_span: span,
-                });
+                self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { attr_span, defn_span });
             }
         }
     }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 684b1781b44..5861ae4a372 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -577,6 +577,7 @@ symbols! {
         box_new,
         box_patterns,
         box_syntax,
+        boxed_slice,
         bpf_target_feature,
         braced_empty_structs,
         branch,
diff --git a/tests/ui/attributes/rustc_skip_during_method_dispatch.rs b/tests/ui/attributes/rustc_skip_during_method_dispatch.rs
new file mode 100644
index 00000000000..25b473d5a58
--- /dev/null
+++ b/tests/ui/attributes/rustc_skip_during_method_dispatch.rs
@@ -0,0 +1,38 @@
+#![feature(rustc_attrs)]
+
+#[rustc_skip_during_method_dispatch]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input [E0539]
+trait NotAList {}
+
+#[rustc_skip_during_method_dispatch = "array"]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input [E0539]
+trait AlsoNotAList {}
+
+#[rustc_skip_during_method_dispatch()]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait Argless {}
+
+#[rustc_skip_during_method_dispatch(array, boxed_slice, array)]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait Duplicate {}
+
+#[rustc_skip_during_method_dispatch(slice)]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait Unexpected {}
+
+#[rustc_skip_during_method_dispatch(array = true)]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait KeyValue {}
+
+#[rustc_skip_during_method_dispatch("array")]
+//~^ ERROR: malformed `rustc_skip_during_method_dispatch` attribute input
+trait String {}
+
+#[rustc_skip_during_method_dispatch(array, boxed_slice)]
+trait OK {}
+
+#[rustc_skip_during_method_dispatch(array)]
+//~^ ERROR: attribute should be applied to a trait
+impl OK for () {}
+
+fn main() {}
diff --git a/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr
new file mode 100644
index 00000000000..2f5d7968489
--- /dev/null
+++ b/tests/ui/attributes/rustc_skip_during_method_dispatch.stderr
@@ -0,0 +1,76 @@
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:3:1
+   |
+LL | #[rustc_skip_during_method_dispatch]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | expected this to be a list
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:7:1
+   |
+LL | #[rustc_skip_during_method_dispatch = "array"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | expected this to be a list
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:11:1
+   |
+LL | #[rustc_skip_during_method_dispatch()]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--^
+   | |                                  |
+   | |                                  expected at least 1 argument here
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0538]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:15:1
+   |
+LL | #[rustc_skip_during_method_dispatch(array, boxed_slice, array)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^
+   | |                                                       |
+   | |                                                       found `array` used as a key more than once
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0539]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:19:1
+   |
+LL | #[rustc_skip_during_method_dispatch(slice)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^
+   | |                                   |
+   | |                                   valid arguments are `array` or `boxed_slice`
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0565]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:23:1
+   |
+LL | #[rustc_skip_during_method_dispatch(array = true)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^
+   | |                                         |
+   | |                                         didn't expect any arguments here
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error[E0565]: malformed `rustc_skip_during_method_dispatch` attribute input
+  --> $DIR/rustc_skip_during_method_dispatch.rs:27:1
+   |
+LL | #[rustc_skip_during_method_dispatch("array")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^
+   | |                                   |
+   | |                                   didn't expect a literal here
+   | help: must be of the form: `#[rustc_skip_during_method_dispatch(array, boxed_slice)]`
+
+error: attribute should be applied to a trait
+  --> $DIR/rustc_skip_during_method_dispatch.rs:34:1
+   |
+LL | #[rustc_skip_during_method_dispatch(array)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |
+LL | impl OK for () {}
+   | ----------------- not a trait
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0538, E0539, E0565.
+For more information about an error, try `rustc --explain E0538`.