diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 3b5e06c2a88fc..b5934f4e36e89 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -284,6 +284,9 @@ pub enum AttributeKind { /// Represents `#[no_mangle]` NoMangle(Span), + /// Represents `#[non_exhaustive]` + NonExhaustive(Span), + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 145cfba8e424b..02e95ddcb6fdb 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -36,6 +36,7 @@ impl AttributeKind { Naked(..) => No, NoImplicitPrelude(..) => No, NoMangle(..) => No, + NonExhaustive(..) => Yes, Optimize(..) => No, PassByValue(..) => Yes, PubTransparent(..) => Yes, diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0898b44863a3d..f5ac3890a46a7 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 lint_helpers; pub(crate) mod loop_match; pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; +pub(crate) mod non_exhaustive; pub(crate) mod repr; pub(crate) mod rustc_internal; pub(crate) mod semantics; diff --git a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs new file mode 100644 index 0000000000000..94f6a65c74ecd --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs @@ -0,0 +1,13 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_span::{Span, Symbol, sym}; + +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; + +pub(crate) struct NonExhaustiveParser; + +impl NoArgsAttributeParser for NonExhaustiveParser { + const PATH: &[Symbol] = &[sym::non_exhaustive]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::NonExhaustive; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5ddb686a8c09c..265e1bb6a8cba 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -27,6 +27,7 @@ use crate::attributes::lint_helpers::{AsPtrParser, PassByValueParser, PubTranspa use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; +use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, @@ -144,6 +145,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index aad9b08b2a3a5..271104c20c62b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -779,9 +779,11 @@ fn lower_variant<'tcx>( fields, parent_did.to_def_id(), recovered, - adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive) - || variant_did - .is_some_and(|variant_did| tcx.has_attr(variant_did, sym::non_exhaustive)), + adt_kind == AdtKind::Struct + && find_attr!(tcx.get_all_attrs(parent_did), AttributeKind::NonExhaustive(..)) + || variant_did.is_some_and(|variant_did| { + find_attr!(tcx.get_all_attrs(variant_did), AttributeKind::NonExhaustive(..)) + }), ) } diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 6d5a3abf665b9..44165b06f1c33 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -4,6 +4,7 @@ use std::ops::Range; use std::str; use rustc_abi::{FIRST_VARIANT, ReprOptions, VariantIdx}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; @@ -278,7 +279,9 @@ impl AdtDefData { debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr); let mut flags = AdtFlags::NO_ADT_FLAGS; - if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) { + if kind == AdtKind::Enum + && find_attr!(tcx.get_all_attrs(did), AttributeKind::NonExhaustive(..)) + { debug!("found non-exhaustive variant list for {:?}", did); flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE; } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 47249b38df5f2..27355a422d1fe 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -303,6 +303,7 @@ fn emit_malformed_attribute( | sym::rustc_allow_const_fn_unstable | sym::naked | sym::no_mangle + | sym::non_exhaustive | sym::must_use | sym::track_caller | sym::link_name diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c0ef81166ea0a..c5ced4064140e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -194,6 +194,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::TrackCaller(attr_span)) => { self.check_track_caller(hir_id, *attr_span, attrs, span, target) } + Attribute::Parsed(AttributeKind::NonExhaustive(attr_span)) => { + self.check_non_exhaustive(hir_id, *attr_span, span, target, item) + } Attribute::Parsed( AttributeKind::RustcLayoutScalarValidRangeStart(_num, attr_span) | AttributeKind::RustcLayoutScalarValidRangeEnd(_num, attr_span), @@ -237,7 +240,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::no_sanitize, ..] => { self.check_no_sanitize(attr, span, target) } - [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item), [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), [sym::thread_local, ..] => self.check_thread_local(attr, span, target), [sym::doc, ..] => self.check_doc_attrs( @@ -779,7 +781,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_non_exhaustive( &self, hir_id: HirId, - attr: &Attribute, + attr_span: Span, span: Span, target: Target, item: Option>, @@ -794,7 +796,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && fields.iter().any(|f| f.default.is_some()) { self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues { - attr_span: attr.span(), + attr_span, defn_span: span, }); } @@ -805,13 +807,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // erroneously allowed it and some crates used it accidentally, to be compatible // with crates depending on them, we can't throw an error here. Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "non_exhaustive"); + self.inline_attr_str_error_with_macro_def(hir_id, attr_span, "non_exhaustive"); } _ => { - self.dcx().emit_err(errors::NonExhaustiveWrongLocation { - attr_span: attr.span(), - defn_span: span, - }); + self.dcx() + .emit_err(errors::NonExhaustiveWrongLocation { attr_span, defn_span: span }); } } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 18078b760c3e7..c99bc747fd21d 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -5,7 +5,7 @@ use rustc_ast::{ self as ast, CRATE_NODE_ID, Crate, ItemKind, MetaItemInner, MetaItemKind, ModKind, NodeId, Path, }; use rustc_ast_pretty::pprust; -use rustc_attr_data_structures::{self as attr, Stability}; +use rustc_attr_data_structures::{self as attr, AttributeKind, Stability, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; @@ -1998,9 +1998,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Otherwise, point out if the struct has any private fields. if let Some(def_id) = res.opt_def_id() && !def_id.is_local() - && let Some(attr) = self.tcx.get_attr(def_id, sym::non_exhaustive) + && let Some(attr_span) = find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::NonExhaustive(span) => *span) { - non_exhaustive = Some(attr.span()); + non_exhaustive = Some(attr_span); } else if let Some(span) = ctor_fields_span { let label = errors::ConstructorPrivateIfAnyFieldPrivate { span }; err.subdiagnostic(label); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index cc8f4bbb42cc2..de920469fdca6 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -621,7 +621,7 @@ impl Item { } pub(crate) fn is_non_exhaustive(&self) -> bool { - self.attrs.other_attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + find_attr!(&self.attrs.other_attrs, AttributeKind::NonExhaustive(..)) } /// Returns a documentation-level item type from the item. @@ -776,6 +776,8 @@ impl Item { } else if let hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) = attr { Some(format!("#[export_name = \"{name}\"]")) + } else if let hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) = attr { + Some("#[non_exhaustive]".to_string()) } else if is_json { match attr { // rustdoc-json stores this in `Item::deprecation`, so we diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs index 86d9038ec45d3..7dda3e0fdb91c 100644 --- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs +++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs @@ -4,7 +4,9 @@ use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_attr_data_structures::AttributeKind; +use rustc_attr_data_structures::find_attr; + declare_clippy_lint! { /// ### What it does @@ -85,7 +87,7 @@ impl LateLintPass<'_> for ExhaustiveItems { }; if cx.effective_visibilities.is_exported(item.owner_id.def_id) && let attrs = cx.tcx.hir_attrs(item.hir_id()) - && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + && !find_attr!(attrs, AttributeKind::NonExhaustive(..)) && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { span_lint_and_then(cx, lint, item.span, msg, |diag| { diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 3562b1ff5ccec..51696b388800f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -4,7 +4,6 @@ use clippy_utils::is_doc_hidden; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_indent; use itertools::Itertools; -use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -12,7 +11,9 @@ use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::Span; +use rustc_attr_data_structures::find_attr; +use rustc_attr_data_structures::AttributeKind; declare_clippy_lint! { /// ### What it does @@ -93,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { .then_some((v.def_id, v.span)) }); if let Ok((id, span)) = iter.exactly_one() - && !attr::contains_name(cx.tcx.hir_attrs(item.hir_id()), sym::non_exhaustive) + && !find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::NonExhaustive(..)) { self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); } @@ -113,10 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { item.span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if let Some(non_exhaustive) = - attr::find_by_name(cx.tcx.hir_attrs(item.hir_id()), sym::non_exhaustive) + if let Some(non_exhaustive_span) = + find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::NonExhaustive(span) => *span) { - diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive"); + diag.span_note(non_exhaustive_span, "the struct is already non-exhaustive"); } else { let indent = snippet_indent(cx, item.span).unwrap_or_default(); diag.span_suggestion_verbose( diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 8a0ff5323c98d..4c7a589e185b9 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -7,9 +7,10 @@ use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; use rustc_span::{Span, Symbol}; use std::str::FromStr; - +use rustc_attr_data_structures::find_attr; use crate::source::SpanRangeExt; use crate::{sym, tokenize_with_text}; +use rustc_attr_data_structures::AttributeKind; /// Deprecation status of attributes known by Clippy. pub enum DeprecationStatus { @@ -165,13 +166,13 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool { pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { adt.is_variant_list_non_exhaustive() - || tcx.has_attr(adt.did(), sym::non_exhaustive) + || find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..)) || adt.variants().iter().any(|variant_def| { - variant_def.is_field_list_non_exhaustive() || tcx.has_attr(variant_def.def_id, sym::non_exhaustive) + variant_def.is_field_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..)) }) || adt .all_fields() - .any(|field_def| tcx.has_attr(field_def.did, sym::non_exhaustive)) + .any(|field_def| find_attr!(tcx.get_all_attrs(field_def.did), AttributeKind::NonExhaustive(..))) } /// Checks if the given span contains a `#[cfg(..)]` attribute diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index bdebe155aba0a..32b0ddf87ba4f 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -206,12 +206,6 @@ error: malformed `automatically_derived` attribute input LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[automatically_derived]` -error: malformed `non_exhaustive` attribute input - --> $DIR/malformed-attrs.rs:196:1 - | -LL | #[non_exhaustive = 1] - | ^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[non_exhaustive]` - error: malformed `thread_local` attribute input --> $DIR/malformed-attrs.rs:202:1 | @@ -552,6 +546,15 @@ LL | #[rustc_layout_scalar_valid_range_end] | expected this to be a list | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` +error[E0565]: malformed `non_exhaustive` attribute input + --> $DIR/malformed-attrs.rs:196:1 + | +LL | #[non_exhaustive = 1] + | ^^^^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[non_exhaustive]` + error: attribute should be applied to `const fn` --> $DIR/malformed-attrs.rs:34:1 | diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index d9ceca2f29bd0..eff478d04aee8 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -65,18 +65,6 @@ LL | #[should_panic] | ^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:66:1 - | -LL | #[non_exhaustive] - | ^^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:65:1 - | -LL | #[non_exhaustive] - | ^^^^^^^^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:70:1 | @@ -190,6 +178,18 @@ LL | #[must_use] | ^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:66:1 + | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:65:1 + | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ + error: unused attribute --> $DIR/unused-attr-duplicate.rs:74:1 | diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr index 136cd763b05c1..1ac017aa08b9d 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/invalid-attribute.stderr @@ -1,8 +1,11 @@ -error: malformed `non_exhaustive` attribute input +error[E0565]: malformed `non_exhaustive` attribute input --> $DIR/invalid-attribute.rs:1:1 | LL | #[non_exhaustive(anything)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[non_exhaustive]` + | ^^^^^^^^^^^^^^^^----------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[non_exhaustive]` error[E0701]: attribute should be applied to a struct or enum --> $DIR/invalid-attribute.rs:5:1 @@ -27,4 +30,5 @@ LL | | } error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0701`. +Some errors have detailed explanations: E0565, E0701. +For more information about an error, try `rustc --explain E0565`.