diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 9ebf4c1793dde..431a57736755a 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -238,6 +238,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 1b03525a5ce84..ba6f82e2a66c9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -48,11 +48,7 @@ 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)); - return None; - }; - + cx.expect_no_args(args).ok()?; Some(AttributeKind::Cold(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 d4c846de56eb4..001e49e5dedd2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -14,8 +14,8 @@ 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 { + cx.expect_no_args(args).ok()?; Some(AttributeKind::AsPtr(cx.attr_span)) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 78a696afa2738..f6267394ca500 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -34,6 +34,7 @@ pub(crate) mod deprecation; pub(crate) mod inline; pub(crate) mod lint_helpers; pub(crate) mod repr; +pub(crate) mod resolution; pub(crate) mod stability; pub(crate) mod transparency; pub(crate) mod util; diff --git a/compiler/rustc_attr_parsing/src/attributes/resolution.rs b/compiler/rustc_attr_parsing/src/attributes/resolution.rs new file mode 100644 index 0000000000000..023bfd311ae11 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/resolution.rs @@ -0,0 +1,52 @@ +use core::mem; + +use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{ErrorGuaranteed, 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; + }; + let _: Result<(), ErrorGuaranteed> = cx.expect_no_args(arg.args()); + 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/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6871ff4ec9f39..c792796903420 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -139,7 +139,8 @@ 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 { + cx.expect_no_args(args).ok()?; Some(AttributeKind::ConstStabilityIndirect) } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 8311fccacd894..453685710fd5e 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -21,6 +21,7 @@ use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::lint_helpers::AsPtrParser; use crate::attributes::repr::{AlignParser, ReprParser}; +use crate::attributes::resolution::SkipDuringMethodDispatchParser; use crate::attributes::stability::{ BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, }; @@ -112,6 +113,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, // tidy-alphabetical-end ]; @@ -235,14 +237,22 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { }) } - pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed { - self.emit_err(AttributeParseError { - span: args_span, - attr_span: self.attr_span, - template: self.template.clone(), - attribute: self.attr_path.clone(), - reason: AttributeParseErrorReason::ExpectedNoArgs, - }) + pub(crate) fn expect_no_args(&self, args: &ArgParser<'_>) -> Result<(), ErrorGuaranteed> { + if let Some(span) = match args { + ArgParser::NoArgs => None, + ArgParser::List(args) => Some(args.span), + ArgParser::NameValue(args) => Some(args.eq_span.to(args.value_span)), + } { + Err(self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedNoArgs, + })) + } else { + Ok(()) + } } /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for @@ -290,6 +300,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/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 29f2e44a98a0b..cce1c078909cd 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -476,6 +476,7 @@ pub(crate) struct UnrecognizedReprHint { pub(crate) enum AttributeParseErrorReason { ExpectedNoArgs, ExpectedStringLiteral { byte_string: Option }, + ExpectedAtLeastOneArgument, ExpectedSingleArgument, ExpectedList, UnexpectedLiteral, @@ -519,6 +520,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 5b1f1684d54c1..9b0f6f2558fd6 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 ed1737bee3306..f3f7b97855ec3 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -286,6 +286,9 @@ fn emit_malformed_attribute( if matches!( name, sym::inline + | sym::rustc_as_ptr + | sym::rustc_pub_transparent + | sym::rustc_const_stable_indirect | sym::rustc_force_inline | sym::rustc_confusables | sym::repr @@ -293,6 +296,7 @@ fn emit_malformed_attribute( | sym::deprecated | sym::optimize | sym::cold + | sym::rustc_skip_during_method_dispatch ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5fb9514e5caff..1b94fc4c8e834 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -117,6 +117,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { 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); } @@ -232,7 +238,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::must_use, ..] => self.check_must_use(hir_id, attr, target), @@ -1899,14 +1905,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 b961a0184a753..1fa9950cdac42 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`.