diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 9227b81f12fac..ae3a1a4138716 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 24c40c301fe08..0fa69c401546f 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 SingleAttributeParser for ColdParser { const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - 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 AttributeParser for NakedParser { const ATTRIBUTES: AcceptMapping = &[(&[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 SingleAttributeParser for NoMangleParser { const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - 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 4cfd9a82ce8d2..1c8fc5079dad3 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 SingleAttributeParser for AsPtrParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { - // FIXME: check that there's no args (this is currently checked elsewhere) + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + } Some(AttributeKind::AsPtr(cx.attr_span)) } } @@ -27,8 +29,10 @@ impl SingleAttributeParser for PubTransparentParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { - // FIXME: check that there's no args (this is currently checked elsewhere) + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + 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 738d8735b6924..ac7e90fd90256 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 071574a5612aa..54f50445fbdff 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -13,7 +13,10 @@ impl SingleAttributeParser for MayDangleParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + 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 6871ff4ec9f39..37104855623f4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -139,7 +139,10 @@ impl SingleAttributeParser for ConstStabilityIndirectParser { const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(_cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + } Some(AttributeKind::ConstStabilityIndirect) } } @@ -361,8 +364,8 @@ pub(crate) fn parse_unstability( }; } 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 0000000000000..83a98c53c7f74 --- /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 SingleAttributeParser for SkipDuringMethodDispatchParser { + const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + + const TEMPLATE: AttributeTemplate = template!(List: "array, boxed_slice"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + 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 457e073c488b0..43b2d07dbe100 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, Single, Single, + Single, Single, // 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 e02dc098127cf..aecaae947c99b 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 808e452799d3c..263b323e3eb1f 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 }, + 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 280b33f072343..3540c9b4bf0bd 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 176d955bf032d..c967e87bfd87c 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 8e6442353c3e9..47e29923ee59f 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 c2a58b4cd7d53..25b8417944351 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 684b1781b44ef..5861ae4a372b7 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 0000000000000..25b473d5a5851 --- /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 0000000000000..2f5d79684899f --- /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`.