From 30107e89e69816fd3dcfa904fc97b5a95db9ed87 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 9 Apr 2026 18:22:12 +0200 Subject: [PATCH] Revert #154808 because it is based on #152369 This reverts commit 0c94559d488c86ef1bf757420a053ab96937f403, reversing changes made to 33528612babe2a44618b88949bcb17ee16baf6fa. --- .../rustc_attr_parsing/src/validate_attr.rs | 61 +- compiler/rustc_expand/src/errors.rs | 18 + compiler/rustc_expand/src/expand.rs | 21 +- compiler/rustc_expand/src/module.rs | 17 +- compiler/rustc_feature/src/builtin_attrs.rs | 1236 ++++++++++++++--- compiler/rustc_feature/src/lib.rs | 7 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 4 + compiler/rustc_middle/src/ty/mod.rs | 9 +- compiler/rustc_passes/src/check_attr.rs | 124 +- compiler/rustc_passes/src/errors.rs | 24 + 10 files changed, 1252 insertions(+), 269 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index f5ff312f3ade..eb3c4796f02d 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -1,14 +1,15 @@ //! Meta-syntax validation logic of attributes for post-expansion. use std::convert::identity; +use std::slice; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, }; -use rustc_errors::{Applicability, PResult}; -use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP}; +use rustc_errors::{Applicability, FatalError, PResult}; +use rustc_feature::{AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::AttrPath; use rustc_hir::lints::AttributeLintKind; use rustc_parse::parse_in; @@ -17,23 +18,43 @@ use rustc_session::parse::ParseSess; use rustc_span::{Span, Symbol, sym}; -use crate::session_diagnostics as errors; +use crate::{AttributeParser, Late, session_diagnostics as errors}; pub fn check_attr(psess: &ParseSess, attr: &Attribute) { - // Built-in attributes are parsed in their respective attribute parsers, so can be ignored here - if attr.is_doc_comment() - || attr.name().is_some_and(|name| BUILTIN_ATTRIBUTE_MAP.contains_key(&name)) + if attr.is_doc_comment() || attr.has_name(sym::cfg_trace) || attr.has_name(sym::cfg_attr_trace) { return; } - let attr_item = attr.get_normal_item(); - if let AttrArgs::Eq { .. } = attr_item.args.unparsed_ref().unwrap() { - // All key-value attributes are restricted to meta-item syntax. - match parse_meta(psess, attr) { - Ok(_) => {} - Err(err) => { - err.emit(); + let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)); + + // Check input tokens for built-in and key-value attributes. + match builtin_attr_info { + // `rustc_dummy` doesn't have any restrictions specific to built-in attributes. + Some(BuiltinAttribute { name, template, .. }) => { + if AttributeParser::::is_parsed_attribute(slice::from_ref(&name)) { + return; + } + match parse_meta(psess, attr) { + // Don't check safety again, we just did that + Ok(meta) => { + check_builtin_meta_item(psess, &meta, attr.style, *name, *template, false) + } + Err(err) => { + err.emit(); + } + } + } + _ => { + let attr_item = attr.get_normal_item(); + if let AttrArgs::Eq { .. } = attr_item.args.unparsed_ref().unwrap() { + // All key-value attributes are restricted to meta-item syntax. + match parse_meta(psess, attr) { + Ok(_) => {} + Err(err) => { + err.emit(); + } + } } } } @@ -148,7 +169,7 @@ pub fn check_builtin_meta_item( } } -pub fn emit_malformed_attribute( +fn emit_malformed_attribute( psess: &ParseSess, style: ast::AttrStyle, span: Span, @@ -210,3 +231,15 @@ pub fn emit_malformed_attribute( err.emit(); } } + +pub fn emit_fatal_malformed_builtin_attribute( + psess: &ParseSess, + attr: &Attribute, + name: Symbol, +) -> ! { + let template = BUILTIN_ATTRIBUTE_MAP.get(&name).expect("builtin attr defined").template; + emit_malformed_attribute(psess, attr.style, attr.span, name, template); + // This is fatal, otherwise it will likely cause a cascade of other errors + // (and an error here is expected to be very rare). + FatalError.raise() +} diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index cee333e0a59f..6c5732f497f8 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -603,3 +603,21 @@ pub(crate) struct TrailingMacro { pub is_trailing: bool, pub name: Ident, } + +#[derive(Diagnostic)] +#[diag("unused attribute `{$attr_name}`")] +pub(crate) struct UnusedBuiltinAttribute { + #[note( + "the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}`" + )] + pub invoc_span: Span, + pub attr_name: Symbol, + pub macro_name: String, + #[suggestion( + "remove the attribute", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] + pub attr_span: Span, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 5e7c15ba1d7b..91d6611d719c 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -15,8 +15,8 @@ use rustc_ast_pretty::pprust; use rustc_attr_parsing::parser::AllowExprMetavar; use rustc_attr_parsing::{ - AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg, - validate_attr, + AttributeParser, CFG_TEMPLATE, Early, EvalConfigResult, ShouldEmit, eval_config_entry, + parse_cfg, validate_attr, }; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -30,7 +30,7 @@ RecoverColon, RecoverComma, Recovery, token_descr, }; use rustc_session::Session; -use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; +use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; use rustc_span::hygiene::SyntaxContext; use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym}; @@ -2274,6 +2274,21 @@ fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { self.cx.current_expansion.lint_node_id, crate::errors::MacroCallUnusedDocComment { span: attr.span }, ); + } else if rustc_attr_parsing::is_builtin_attr(attr) + && !AttributeParser::::is_parsed_attribute(&attr.path()) + { + let attr_name = attr.name().unwrap(); + self.cx.sess.psess.buffer_lint( + UNUSED_ATTRIBUTES, + attr.span, + self.cx.current_expansion.lint_node_id, + crate::errors::UnusedBuiltinAttribute { + attr_name, + macro_name: pprust::path_to_string(&call.path), + invoc_span: call.path.span, + attr_span: attr.span, + }, + ); } } } diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 803803ec3f6c..79ab3cab22ce 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -2,14 +2,12 @@ use std::path::{self, Path, PathBuf}; use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans}; -use rustc_attr_parsing::validate_attr::emit_malformed_attribute; +use rustc_attr_parsing::validate_attr; use rustc_errors::{Diag, ErrorGuaranteed}; -use rustc_feature::template; use rustc_parse::lexer::StripTokens; use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::Session; use rustc_session::parse::ParseSess; -use rustc_span::fatal_error::FatalError; use rustc_span::{Ident, Span, sym}; use thin_vec::ThinVec; @@ -186,7 +184,6 @@ pub(crate) fn mod_file_path_from_attr( attrs: &[Attribute], dir_path: &Path, ) -> Option { - // FIXME(154781) use a parsed attribute here // Extract path string from first `#[path = "path_string"]` attribute. let first_path = attrs.iter().find(|at| at.has_name(sym::path))?; let Some(path_sym) = first_path.value_str() else { @@ -198,17 +195,7 @@ pub(crate) fn mod_file_path_from_attr( // Usually bad forms are checked during semantic analysis via // `TyCtxt::check_mod_attrs`), but by the time that runs the macro // is expanded, and it doesn't give an error. - emit_malformed_attribute( - &sess.psess, - first_path.style, - first_path.span, - sym::path, - template!( - NameValueStr: "file", - "https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute" - ), - ); - FatalError.raise() + validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, first_path, sym::path); }; let path_str = path_sym.as_str(); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index b8b9226cc602..acbcba90fbcc 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -2,9 +2,12 @@ use std::sync::LazyLock; +use AttributeDuplicates::*; use AttributeGate::*; +use AttributeType::*; use rustc_data_structures::fx::FxHashMap; use rustc_hir::AttrStyle; +use rustc_hir::attrs::EncodeCrossCrate; use rustc_span::edition::Edition; use rustc_span::{Symbol, sym}; @@ -70,10 +73,21 @@ pub fn find_gated_cfg(pred: impl Fn(Symbol) -> bool) -> Option<&'static GatedCfg // move that documentation into the relevant place in the other docs, and // remove the chapter on the flag. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum AttributeType { + /// Normal, builtin attribute that is consumed + /// by the compiler before the unused_attribute check + Normal, + + /// Builtin attribute that is only allowed at the crate level + CrateLevel, +} + #[derive(Copy, Clone, PartialEq, Debug)] pub enum AttributeSafety { /// Normal attribute that does not need `#[unsafe(...)]` Normal, + /// Unsafe attribute that requires safety obligations to be discharged. /// /// An error is emitted when `#[unsafe(...)]` is omitted, except when the attribute's edition @@ -167,6 +181,57 @@ pub fn suggestions( } } +/// How to handle multiple duplicate attributes on the same item. +#[derive(Clone, Copy, Default)] +pub enum AttributeDuplicates { + /// Duplicates of this attribute are allowed. + /// + /// This should only be used with attributes where duplicates have semantic + /// meaning, or some kind of "additive" behavior. For example, `#[warn(..)]` + /// can be specified multiple times, and it combines all the entries. Or use + /// this if there is validation done elsewhere. + #[default] + DuplicatesOk, + /// Duplicates after the first attribute will be an unused_attribute warning. + /// + /// This is usually used for "word" attributes, where they are used as a + /// boolean marker, like `#[used]`. It is not necessarily wrong that there + /// are duplicates, but the others should probably be removed. + WarnFollowing, + /// Same as `WarnFollowing`, but only issues warnings for word-style attributes. + /// + /// This is only for special cases, for example multiple `#[macro_use]` can + /// be warned, but multiple `#[macro_use(...)]` should not because the list + /// form has different meaning from the word form. + WarnFollowingWordOnly, + /// Duplicates after the first attribute will be an error. + /// + /// This should be used where duplicates would be ignored, but carry extra + /// meaning that could cause confusion. For example, `#[stable(since="1.0")] + /// #[stable(since="2.0")]`, which version should be used for `stable`? + ErrorFollowing, + /// Duplicates preceding the last instance of the attribute will be an error. + /// + /// This is the same as `ErrorFollowing`, except the last attribute is the + /// one that is "used". This is typically used in cases like codegen + /// attributes which usually only honor the last attribute. + ErrorPreceding, + /// Duplicates after the first attribute will be an unused_attribute warning + /// with a note that this will be an error in the future. + /// + /// This should be used for attributes that should be `ErrorFollowing`, but + /// because older versions of rustc silently accepted (and ignored) the + /// attributes, this is used to transition. + FutureWarnFollowing, + /// Duplicates preceding the last instance of the attribute will be a + /// warning, with a note that this will be an error in the future. + /// + /// This is the same as `FutureWarnFollowing`, except the last attribute is + /// the one that is "used". Ideally these can eventually migrate to + /// `ErrorPreceding`. + FutureWarnPreceding, +} + /// A convenience macro for constructing attribute templates. /// E.g., `template!(Word, List: "description")` means that the attribute /// supports forms `#[attr]` and `#[attr(description)]`. @@ -203,31 +268,50 @@ macro_rules! template { } macro_rules! ungated { - (unsafe($edition:ident) $attr:ident $(,)?) => { + (unsafe($edition:ident) $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, safety: AttributeSafety::Unsafe { unsafe_since: Some(Edition::$edition) }, + template: $tpl, gate: Ungated, + duplicates: $duplicates, } }; - (unsafe $attr:ident $(,)?) => { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, safety: AttributeSafety::Unsafe { unsafe_since: None }, + template: $tpl, gate: Ungated, + duplicates: $duplicates, } }; - ($attr:ident $(,)?) => { - BuiltinAttribute { name: sym::$attr, safety: AttributeSafety::Normal, gate: Ungated } + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr $(,)?) => { + BuiltinAttribute { + name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, + safety: AttributeSafety::Normal, + template: $tpl, + gate: Ungated, + duplicates: $duplicates, + } }; } macro_rules! gated { - (unsafe $attr:ident, $gate:ident, $message:expr $(,)?) => { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, safety: AttributeSafety::Unsafe { unsafe_since: None }, - + template: $tpl, + duplicates: $duplicates, gate: Gated { feature: sym::$gate, message: $message, @@ -236,11 +320,14 @@ macro_rules! gated { }, } }; - (unsafe $attr:ident, $message:expr $(,)?) => { + (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, safety: AttributeSafety::Unsafe { unsafe_since: None }, - + template: $tpl, + duplicates: $duplicates, gate: Gated { feature: sym::$attr, message: $message, @@ -249,11 +336,14 @@ macro_rules! gated { }, } }; - ($attr:ident, $gate:ident, $message:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, safety: AttributeSafety::Normal, - + template: $tpl, + duplicates: $duplicates, gate: Gated { feature: sym::$gate, message: $message, @@ -262,11 +352,14 @@ macro_rules! gated { }, } }; - ($attr:ident, $message:expr $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $message:expr $(,)?) => { BuiltinAttribute { name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, safety: AttributeSafety::Normal, - + template: $tpl, + duplicates: $duplicates, gate: Gated { feature: sym::$attr, message: $message, @@ -278,8 +371,13 @@ macro_rules! gated { } macro_rules! rustc_attr { - (TEST, $attr:ident $(,)?) => { - rustc_attr!( $attr, + (TEST, $attr:ident, $typ:expr, $tpl:expr, $duplicate:expr, $encode_cross_crate:expr $(,)?) => { + rustc_attr!( + $attr, + $typ, + $tpl, + $duplicate, + $encode_cross_crate, concat!( "the `#[", stringify!($attr), @@ -287,10 +385,14 @@ macro_rules! rustc_attr { ), ) }; - ($attr:ident $(, $notes:expr)* $(,)?) => { + ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $($notes:expr),* $(,)?) => { BuiltinAttribute { name: sym::$attr, + encode_cross_crate: $encode_cross_crate, + type_: $typ, safety: AttributeSafety::Normal, + template: $tpl, + duplicates: $duplicates, gate: Gated { feature: sym::rustc_attrs, message: "use of an internal attribute", @@ -314,7 +416,15 @@ macro_rules! experimental { pub struct BuiltinAttribute { pub name: Symbol, + /// Whether this attribute is encode cross crate. + /// + /// If so, it is encoded in the crate metadata. + /// Otherwise, it can only be used in the local crate. + pub encode_cross_crate: EncodeCrossCrate, + pub type_: AttributeType, pub safety: AttributeSafety, + pub template: AttributeTemplate, + pub duplicates: AttributeDuplicates, pub gate: AttributeGate, } @@ -326,100 +436,379 @@ pub struct BuiltinAttribute { // ========================================================================== // Conditional compilation: - ungated!(cfg), - ungated!(cfg_attr), + ungated!( + cfg, Normal, + template!( + List: &["predicate"], + "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute" + ), + DuplicatesOk, EncodeCrossCrate::No + ), + ungated!( + cfg_attr, Normal, + template!( + List: &["predicate, attr1, attr2, ..."], + "https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute" + ), + DuplicatesOk, EncodeCrossCrate::No + ), // Testing: - ungated!(ignore), - ungated!(should_panic), + ungated!( + ignore, Normal, + template!( + Word, + NameValueStr: "reason", + "https://doc.rust-lang.org/reference/attributes/testing.html#the-ignore-attribute" + ), + WarnFollowing, EncodeCrossCrate::No, + ), + ungated!( + should_panic, Normal, + template!( + Word, + List: &[r#"expected = "reason""#], + NameValueStr: "reason", + "https://doc.rust-lang.org/reference/attributes/testing.html#the-should_panic-attribute" + ), + FutureWarnFollowing, EncodeCrossCrate::No, + ), // Macros: - ungated!(automatically_derived), - ungated!(macro_use), - ungated!(macro_escape), // Deprecated synonym for `macro_use`. - ungated!(macro_export), - ungated!(proc_macro), - ungated!(proc_macro_derive), - ungated!(proc_macro_attribute), + ungated!( + automatically_derived, Normal, + template!( + Word, + "https://doc.rust-lang.org/reference/attributes/derive.html#the-automatically_derived-attribute" + ), + WarnFollowing, EncodeCrossCrate::Yes + ), + ungated!( + macro_use, Normal, + template!( + Word, + List: &["name1, name2, ..."], + "https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute" + ), + WarnFollowingWordOnly, EncodeCrossCrate::No, + ), + ungated!(macro_escape, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Deprecated synonym for `macro_use`. + ungated!( + macro_export, Normal, + template!( + Word, + List: &["local_inner_macros"], + "https://doc.rust-lang.org/reference/macros-by-example.html#path-based-scope" + ), + WarnFollowing, EncodeCrossCrate::Yes + ), + ungated!( + proc_macro, Normal, + template!( + Word, + "https://doc.rust-lang.org/reference/procedural-macros.html#function-like-procedural-macros"), + ErrorFollowing, EncodeCrossCrate::No + ), + ungated!( + proc_macro_derive, Normal, + template!( + List: &["TraitName", "TraitName, attributes(name1, name2, ...)"], + "https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros" + ), + ErrorFollowing, EncodeCrossCrate::No, + ), + ungated!( + proc_macro_attribute, Normal, + template!(Word, "https://doc.rust-lang.org/reference/procedural-macros.html#attribute-macros"), + ErrorFollowing, EncodeCrossCrate::No + ), // Lints: - ungated!(warn), - ungated!(allow), - ungated!(expect), - ungated!(forbid), - ungated!(deny), - ungated!(must_use), - gated!(must_not_suspend, experimental!(must_not_suspend)), - ungated!(deprecated), + ungated!( + warn, Normal, + template!( + List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" + ), + DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + allow, Normal, + template!( + List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" + ), + DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + expect, Normal, + template!( + List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" + ), + DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + forbid, Normal, + template!( + List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" + ), + DuplicatesOk, EncodeCrossCrate::No + ), + ungated!( + deny, Normal, + template!( + List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" + ), + DuplicatesOk, EncodeCrossCrate::No + ), + ungated!( + must_use, Normal, + template!( + Word, + NameValueStr: "reason", + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute" + ), + FutureWarnFollowing, EncodeCrossCrate::Yes + ), + gated!( + must_not_suspend, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, + EncodeCrossCrate::Yes, experimental!(must_not_suspend) + ), + ungated!( + deprecated, Normal, + template!( + Word, + List: &[r#"/*opt*/ since = "version", /*opt*/ note = "reason""#], + NameValueStr: "reason", + "https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-deprecated-attribute" + ), + ErrorFollowing, EncodeCrossCrate::Yes + ), // Crate properties: - ungated!(crate_name), - ungated!(crate_type), + ungated!( + crate_name, CrateLevel, + template!( + NameValueStr: "name", + "https://doc.rust-lang.org/reference/crates-and-source-files.html#the-crate_name-attribute" + ), + FutureWarnFollowing, EncodeCrossCrate::No, + ), + ungated!( + crate_type, CrateLevel, + template!( + NameValueStr: ["bin", "lib", "dylib", "cdylib", "rlib", "staticlib", "sdylib", "proc-macro"], + "https://doc.rust-lang.org/reference/linkage.html" + ), + DuplicatesOk, EncodeCrossCrate::No, + ), // ABI, linking, symbols, and FFI - ungated!(link), - ungated!(link_name), - ungated!(no_link), - ungated!(repr), + ungated!( + link, Normal, + template!(List: &[ + r#"name = "...""#, + r#"name = "...", kind = "dylib|static|...""#, + r#"name = "...", wasm_import_module = "...""#, + r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#, + r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#, + ], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute"), + DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + link_name, Normal, + template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"), + FutureWarnPreceding, EncodeCrossCrate::Yes + ), + ungated!( + no_link, Normal, + template!(Word, "https://doc.rust-lang.org/reference/items/extern-crates.html#the-no_link-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), + ungated!( + repr, Normal, + template!( + List: &["C", "Rust", "transparent", "align(...)", "packed(...)", ""], + "https://doc.rust-lang.org/reference/type-layout.html#representations" + ), + DuplicatesOk, EncodeCrossCrate::No + ), // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity - gated!(rustc_align,fn_align, experimental!(rustc_align)), - gated!(rustc_align_static,static_align, experimental!(rustc_align_static)), - ungated!(unsafe(Edition2024) export_name), - ungated!(unsafe(Edition2024) link_section), - ungated!(unsafe(Edition2024) no_mangle), - ungated!(used), - ungated!(link_ordinal), - ungated!(unsafe naked), + gated!(rustc_align, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(rustc_align)), + gated!(rustc_align_static, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, static_align, experimental!(rustc_align_static)), + ungated!( + unsafe(Edition2024) export_name, Normal, + template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute"), + FutureWarnPreceding, EncodeCrossCrate::No + ), + ungated!( + unsafe(Edition2024) link_section, Normal, + template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"), + FutureWarnPreceding, EncodeCrossCrate::No + ), + ungated!( + unsafe(Edition2024) no_mangle, Normal, + template!(Word, "https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), + ungated!( + used, Normal, + template!(Word, List: &["compiler", "linker"], "https://doc.rust-lang.org/reference/abi.html#the-used-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), + ungated!( + link_ordinal, Normal, + template!(List: &["ordinal"], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"), + ErrorPreceding, EncodeCrossCrate::Yes + ), + ungated!( + unsafe naked, Normal, + template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-naked-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), // See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details. - rustc_attr!(rustc_pass_indirectly_in_non_rustic_abis, "types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic ABIs"), + rustc_attr!( + rustc_pass_indirectly_in_non_rustic_abis, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::No, + "types marked with `#[rustc_pass_indirectly_in_non_rustic_abis]` are always passed indirectly by non-Rustic ABIs" + ), // Limits: - ungated!(recursion_limit), - ungated!(type_length_limit), + ungated!( + recursion_limit, CrateLevel, + template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"), + FutureWarnFollowing, EncodeCrossCrate::No + ), + ungated!( + type_length_limit, CrateLevel, + template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-type_length_limit-attribute"), + FutureWarnFollowing, EncodeCrossCrate::No + ), gated!( - move_size_limit, large_assignments, experimental!(move_size_limit) + move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing, + EncodeCrossCrate::No, large_assignments, experimental!(move_size_limit) ), // Entry point: - ungated!(no_main), + ungated!( + no_main, CrateLevel, + template!(Word, "https://doc.rust-lang.org/reference/crates-and-source-files.html#the-no_main-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), // Modules, prelude, and resolution: - ungated!(path), - ungated!(no_std), - ungated!(no_implicit_prelude), - ungated!(non_exhaustive), + ungated!( + path, Normal, + template!(NameValueStr: "file", "https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute"), + FutureWarnFollowing, EncodeCrossCrate::No + ), + ungated!( + no_std, CrateLevel, + template!(Word, "https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), + ungated!( + no_implicit_prelude, Normal, + template!(Word, "https://doc.rust-lang.org/reference/names/preludes.html#the-no_implicit_prelude-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), + ungated!( + non_exhaustive, Normal, + template!(Word, "https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute"), + WarnFollowing, EncodeCrossCrate::Yes + ), // Runtime - ungated!(windows_subsystem), - ungated!(// RFC 2070 - panic_handler + ungated!( + windows_subsystem, CrateLevel, + template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"), + FutureWarnFollowing, EncodeCrossCrate::No + ), + ungated!( // RFC 2070 + panic_handler, Normal, + template!(Word, "https://doc.rust-lang.org/reference/panic.html#the-panic_handler-attribute"), + WarnFollowing, EncodeCrossCrate::Yes ), // Code generation: - ungated!(inline), - ungated!(cold), - ungated!(no_builtins), - ungated!(target_feature), - ungated!(track_caller), - ungated!(instruction_set), - gated!( - unsafe force_target_feature, - effective_target_features, experimental!(force_target_feature) + ungated!( + inline, Normal, + template!( + Word, + List: &["always", "never"], + "https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute" + ), + FutureWarnFollowing, EncodeCrossCrate::No + ), + ungated!( + cold, Normal, + template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-cold-attribute"), + WarnFollowing, EncodeCrossCrate::No + ), + ungated!( + no_builtins, CrateLevel, + template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-no_builtins-attribute"), + WarnFollowing, EncodeCrossCrate::Yes + ), + ungated!( + target_feature, Normal, + template!(List: &[r#"enable = "name""#], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-target_feature-attribute"), + DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + track_caller, Normal, + template!(Word, "https://doc.rust-lang.org/reference/attributes/codegen.html#the-track_caller-attribute"), + WarnFollowing, EncodeCrossCrate::Yes + ), + ungated!( + instruction_set, Normal, + template!(List: &["set"], "https://doc.rust-lang.org/reference/attributes/codegen.html#the-instruction_set-attribute"), + ErrorPreceding, EncodeCrossCrate::No ), gated!( - sanitize, - sanitize, experimental!(sanitize) + unsafe force_target_feature, Normal, template!(List: &[r#"enable = "name""#]), + DuplicatesOk, EncodeCrossCrate::No, effective_target_features, experimental!(force_target_feature) ), gated!( - coverage, + sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kernel_hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding, + EncodeCrossCrate::No, sanitize, experimental!(sanitize), + ), + gated!( + coverage, Normal, template!(OneOf: &[sym::off, sym::on]), + ErrorPreceding, EncodeCrossCrate::No, coverage_attribute, experimental!(coverage) ), - ungated!(doc), + ungated!( + doc, Normal, + template!( + List: &["hidden", "inline"], + NameValueStr: "string", + "https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html" + ), + DuplicatesOk, EncodeCrossCrate::Yes + ), // Debugging - ungated!(debugger_visualizer), - ungated!(collapse_debuginfo), + ungated!( + debugger_visualizer, Normal, + template!( + List: &[r#"natvis_file = "...", gdb_script_file = "...""#], + "https://doc.rust-lang.org/reference/attributes/debugger.html#the-debugger_visualizer-attribute" + ), + DuplicatesOk, EncodeCrossCrate::No + ), + ungated!( + collapse_debuginfo, Normal, + template!( + List: &["no", "external", "yes"], + "https://doc.rust-lang.org/reference/attributes/debugger.html#the-collapse_debuginfo-attribute" + ), + ErrorFollowing, EncodeCrossCrate::Yes + ), // ========================================================================== // Unstable attributes: @@ -427,61 +816,71 @@ pub struct BuiltinAttribute { // Linking: gated!( - export_stable, experimental!(export_stable) + export_stable, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, experimental!(export_stable) ), // Testing: gated!( - test_runner, custom_test_frameworks, - "custom test frameworks are an unstable feature" + test_runner, CrateLevel, template!(List: &["path"]), ErrorFollowing, + EncodeCrossCrate::Yes, custom_test_frameworks, + "custom test frameworks are an unstable feature", ), gated!( - reexport_test_harness_main, custom_test_frameworks, - "custom test frameworks are an unstable feature" + reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing, + EncodeCrossCrate::No, custom_test_frameworks, + "custom test frameworks are an unstable feature", ), // RFC #1268 gated!( - marker,marker_trait_attr, experimental!(marker) + marker, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, + marker_trait_attr, experimental!(marker) ), gated!( - thread_local,"`#[thread_local]` is an experimental feature, and does not currently handle destructors" + thread_local, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, + "`#[thread_local]` is an experimental feature, and does not currently handle destructors", ), gated!( - no_core, experimental!(no_core) + no_core, CrateLevel, template!(Word), WarnFollowing, + EncodeCrossCrate::No, experimental!(no_core) ), // RFC 2412 gated!( - optimize, - optimize_attribute, experimental!(optimize) + optimize, Normal, template!(List: &["none", "size", "speed"]), ErrorPreceding, + EncodeCrossCrate::No, optimize_attribute, experimental!(optimize) ), gated!( - unsafe ffi_pure, experimental!(ffi_pure) + unsafe ffi_pure, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, experimental!(ffi_pure) ), gated!( - unsafe ffi_const, experimental!(ffi_const) + unsafe ffi_const, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, experimental!(ffi_const) ), gated!( - register_tool, experimental!(register_tool) + register_tool, CrateLevel, template!(List: &["tool1, tool2, ..."]), DuplicatesOk, + EncodeCrossCrate::No, experimental!(register_tool), ), // `#[cfi_encoding = ""]` gated!( - cfi_encoding, - experimental!(cfi_encoding) + cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding, + EncodeCrossCrate::Yes, experimental!(cfi_encoding) ), // `#[coroutine]` attribute to be applied to closures to make them coroutines instead gated!( - coroutine,coroutines, experimental!(coroutine) + coroutine, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::No, coroutines, experimental!(coroutine) ), // RFC 3543 // `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` gated!( - patchable_function_entry, - experimental!(patchable_function_entry) + patchable_function_entry, Normal, template!(List: &["prefix_nops = m, entry_nops = n"]), ErrorPreceding, + EncodeCrossCrate::Yes, experimental!(patchable_function_entry) ), // The `#[loop_match]` and `#[const_continue]` attributes are part of the @@ -489,10 +888,12 @@ pub struct BuiltinAttribute { // // - https://github.com/rust-lang/rust/issues/132306 gated!( - const_continue,loop_match, experimental!(const_continue) + const_continue, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::No, loop_match, experimental!(const_continue) ), gated!( - loop_match,loop_match, experimental!(loop_match) + loop_match, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::No, loop_match, experimental!(loop_match) ), // The `#[pin_v2]` attribute is part of the `pin_ergonomics` experiment @@ -500,40 +901,74 @@ pub struct BuiltinAttribute { // // - https://github.com/rust-lang/rust/issues/130494 gated!( - pin_v2,pin_ergonomics, experimental!(pin_v2), + pin_v2, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_v2), ), // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== - ungated!(feature), + ungated!( + feature, CrateLevel, + template!(List: &["name1, name2, ..."]), DuplicatesOk, EncodeCrossCrate::No, + ), // DuplicatesOk since it has its own validation - ungated!(stable), - ungated!(unstable), - ungated!(unstable_feature_bound), - ungated!(rustc_const_unstable), - ungated!(rustc_const_stable), - ungated!(rustc_default_body_unstable), + ungated!( + stable, Normal, + template!(List: &[r#"feature = "name", since = "version""#]), DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + unstable, Normal, + template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]), DuplicatesOk, + EncodeCrossCrate::Yes + ), + ungated!( + unstable_feature_bound, Normal, template!(Word, List: &["feat1, feat2, ..."]), + DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + rustc_const_unstable, Normal, template!(List: &[r#"feature = "name""#]), + DuplicatesOk, EncodeCrossCrate::Yes + ), + ungated!( + rustc_const_stable, Normal, + template!(List: &[r#"feature = "name""#]), DuplicatesOk, EncodeCrossCrate::No, + ), + ungated!( + rustc_default_body_unstable, Normal, + template!(List: &[r#"feature = "name", reason = "...", issue = "N""#]), + DuplicatesOk, EncodeCrossCrate::No + ), gated!( - allow_internal_unstable, + allow_internal_unstable, Normal, template!(Word, List: &["feat1, feat2, ..."]), + DuplicatesOk, EncodeCrossCrate::Yes, "allow_internal_unstable side-steps feature gating and stability checks", ), gated!( - allow_internal_unsafe, "allow_internal_unsafe side-steps the unsafe_code lint", + allow_internal_unsafe, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, "allow_internal_unsafe side-steps the unsafe_code lint", ), gated!( - rustc_eii_foreign_item, - eii_internals, + rustc_eii_foreign_item, Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::Yes, eii_internals, "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation", ), - rustc_attr!(rustc_allowed_through_unstable_modules, + rustc_attr!( + rustc_allowed_through_unstable_modules, Normal, template!(NameValueStr: "deprecation message"), + WarnFollowing, EncodeCrossCrate::No, "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \ through unstable paths" ), - rustc_attr!(rustc_deprecated_safe_2024,"`#[rustc_deprecated_safe_2024]` is used to declare functions unsafe across the edition 2024 boundary", + rustc_attr!( + rustc_deprecated_safe_2024, Normal, template!(List: &[r#"audit_that = "...""#]), + ErrorFollowing, EncodeCrossCrate::Yes, + "`#[rustc_deprecated_safe_2024]` is used to declare functions unsafe across the edition 2024 boundary", ), - rustc_attr!(rustc_pub_transparent,"used internally to mark types with a `transparent` representation when it is guaranteed by the documentation", + rustc_attr!( + rustc_pub_transparent, Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::Yes, + "used internally to mark types with a `transparent` representation when it is guaranteed by the documentation", ), @@ -541,13 +976,25 @@ pub struct BuiltinAttribute { // Internal attributes: Type system related: // ========================================================================== - gated!(fundamental, experimental!(fundamental)), + gated!(fundamental, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, experimental!(fundamental)), gated!( - may_dangle, dropck_eyepatch, - "`may_dangle` has unstable semantics and may be removed in the future" + may_dangle, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, dropck_eyepatch, + "`may_dangle` has unstable semantics and may be removed in the future", ), - rustc_attr!(rustc_never_type_options, + rustc_attr!( + rustc_never_type_options, + Normal, + template!(List: &[ + "", + r#"fallback = "unit""#, + r#"fallback = "niko""#, + r#"fallback = "never""#, + r#"fallback = "no""#, + ]), + ErrorFollowing, + EncodeCrossCrate::No, "`rustc_never_type_options` is used to experiment with never type fallback and work on \ never type stabilization" ), @@ -556,33 +1003,57 @@ pub struct BuiltinAttribute { // Internal attributes: Runtime related: // ========================================================================== - rustc_attr!(rustc_allocator), - rustc_attr!(rustc_nounwind), - rustc_attr!(rustc_reallocator), - rustc_attr!(rustc_deallocator), - rustc_attr!(rustc_allocator_zeroed), - rustc_attr!(rustc_allocator_zeroed_variant), - gated!( - default_lib_allocator, allocator_internals, experimental!(default_lib_allocator), + rustc_attr!( + rustc_allocator, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_nounwind, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_reallocator, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_deallocator, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_allocator_zeroed_variant, Normal, template!(NameValueStr: "function"), ErrorPreceding, + EncodeCrossCrate::Yes, ), gated!( - needs_allocator, allocator_internals, experimental!(needs_allocator), + default_lib_allocator, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, allocator_internals, experimental!(default_lib_allocator), ), gated!( - panic_runtime, experimental!(panic_runtime) + needs_allocator, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, allocator_internals, experimental!(needs_allocator), ), gated!( - needs_panic_runtime, experimental!(needs_panic_runtime) + panic_runtime, CrateLevel, template!(Word), WarnFollowing, + EncodeCrossCrate::No, experimental!(panic_runtime) ), gated!( - compiler_builtins, + needs_panic_runtime, CrateLevel, template!(Word), WarnFollowing, + EncodeCrossCrate::No, experimental!(needs_panic_runtime) + ), + gated!( + compiler_builtins, CrateLevel, template!(Word), WarnFollowing, + EncodeCrossCrate::No, "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \ - which contains compiler-rt intrinsics and will never be stable" + which contains compiler-rt intrinsics and will never be stable", ), gated!( - profiler_runtime, + profiler_runtime, CrateLevel, template!(Word), WarnFollowing, + EncodeCrossCrate::No, "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \ - which contains the profiler runtime and will never be stable" + which contains the profiler runtime and will never be stable", ), // ========================================================================== @@ -590,123 +1061,277 @@ pub struct BuiltinAttribute { // ========================================================================== gated!( - linkage, - "the `linkage` attribute is experimental and not portable across platforms" + linkage, Normal, template!(NameValueStr: [ + "available_externally", + "common", + "extern_weak", + "external", + "internal", + "linkonce", + "linkonce_odr", + "weak", + "weak_odr", + ], "https://doc.rust-lang.org/reference/linkage.html"), + ErrorPreceding, EncodeCrossCrate::No, + "the `linkage` attribute is experimental and not portable across platforms", + ), + rustc_attr!( + rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_objc_class, Normal, template!(NameValueStr: "ClassName"), ErrorPreceding, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_objc_selector, Normal, template!(NameValueStr: "methodName"), ErrorPreceding, + EncodeCrossCrate::No, ), - rustc_attr!(rustc_std_internal_symbol), - rustc_attr!(rustc_objc_class), - rustc_attr!(rustc_objc_selector), // ========================================================================== // Internal attributes, Macro related: // ========================================================================== - rustc_attr!(rustc_builtin_macro), - rustc_attr!(rustc_proc_macro_decls), - rustc_attr!(rustc_macro_transparency, - "used internally for testing macro hygiene" + rustc_attr!( + rustc_builtin_macro, Normal, + template!(Word, List: &["name", "name, /*opt*/ attributes(name1, name2, ...)"]), ErrorFollowing, + EncodeCrossCrate::Yes, + ), + rustc_attr!( + rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_macro_transparency, Normal, + template!(NameValueStr: ["transparent", "semiopaque", "opaque"]), ErrorFollowing, + EncodeCrossCrate::Yes, "used internally for testing macro hygiene", + ), + rustc_attr!( + rustc_autodiff, Normal, + template!(Word, List: &[r#""...""#]), DuplicatesOk, + EncodeCrossCrate::Yes, + ), + rustc_attr!( + rustc_offload_kernel, Normal, + template!(Word), DuplicatesOk, + EncodeCrossCrate::Yes, ), - rustc_attr!(rustc_autodiff), - rustc_attr!(rustc_offload_kernel), // Traces that are left when `cfg` and `cfg_attr` attributes are expanded. // The attributes are not gated, to avoid stability errors, but they cannot be used in stable // or unstable code directly because `sym::cfg_(attr_)trace` are not valid identifiers, they // can only be generated by the compiler. - ungated!(cfg_trace + ungated!( + cfg_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk, + EncodeCrossCrate::Yes ), - ungated!(cfg_attr_trace + ungated!( + cfg_attr_trace, Normal, template!(Word /* irrelevant */), DuplicatesOk, + EncodeCrossCrate::No ), // ========================================================================== // Internal attributes, Diagnostics related: // ========================================================================== - rustc_attr!(rustc_on_unimplemented,"see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute" + rustc_attr!( + rustc_on_unimplemented, Normal, + template!( + List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#], + NameValueStr: "message" + ), + ErrorFollowing, EncodeCrossCrate::Yes, + "see `#[diagnostic::on_unimplemented]` for the stable equivalent of this attribute" + ), + rustc_attr!( + rustc_confusables, Normal, + template!(List: &[r#""name1", "name2", ..."#]), + ErrorFollowing, EncodeCrossCrate::Yes, ), - rustc_attr!(rustc_confusables), // Enumerates "identity-like" conversion methods to suggest on type mismatch. - rustc_attr!(rustc_conversion_suggestion), + rustc_attr!( + rustc_conversion_suggestion, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, + ), // Prevents field reads in the marked trait or method to be considered // during dead code analysis. - rustc_attr!(rustc_trivial_field_reads), + rustc_attr!( + rustc_trivial_field_reads, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, + ), // Used by the `rustc::potential_query_instability` lint to warn methods which // might not be stable during incremental compilation. - rustc_attr!(rustc_lint_query_instability), + rustc_attr!( + rustc_lint_query_instability, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, + ), // Used by the `rustc::untracked_query_information` lint to warn methods which // might not be stable during incremental compilation. - rustc_attr!(rustc_lint_untracked_query_information), + rustc_attr!( + rustc_lint_untracked_query_information, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, + ), // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions` // types (as well as any others in future). - rustc_attr!(rustc_lint_opt_ty), + rustc_attr!( + rustc_lint_opt_ty, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes, + ), // Used by the `rustc::bad_opt_access` lint on fields // types (as well as any others in future). - rustc_attr!(rustc_lint_opt_deny_field_access), + rustc_attr!( + rustc_lint_opt_deny_field_access, Normal, template!(List: &["message"]), + WarnFollowing, EncodeCrossCrate::Yes, + ), // ========================================================================== // Internal attributes, Const related: // ========================================================================== - rustc_attr!(rustc_promotable), - rustc_attr!(rustc_legacy_const_generics), + rustc_attr!( + rustc_promotable, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, ), + rustc_attr!( + rustc_legacy_const_generics, Normal, template!(List: &["N"]), ErrorFollowing, + EncodeCrossCrate::Yes, + ), // Do not const-check this function's body. It will always get replaced during CTFE via `hook_special_const_fn`. - rustc_attr!(rustc_do_not_const_check, "`#[rustc_do_not_const_check]` skips const-check for this function's body"), - rustc_attr!(rustc_const_stable_indirect, - "this is an internal implementation detail"), - rustc_attr!(rustc_intrinsic_const_stable_indirect, - "this is an internal implementation detail"), - rustc_attr!(rustc_allow_const_fn_unstable, - "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" + rustc_attr!( + rustc_do_not_const_check, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, "`#[rustc_do_not_const_check]` skips const-check for this function's body", + ), + rustc_attr!( + rustc_const_stable_indirect, Normal, + template!(Word), + WarnFollowing, + EncodeCrossCrate::No, + "this is an internal implementation detail", + ), + rustc_attr!( + rustc_intrinsic_const_stable_indirect, Normal, + template!(Word), WarnFollowing, EncodeCrossCrate::No, "this is an internal implementation detail", + ), + rustc_attr!( + rustc_allow_const_fn_unstable, Normal, + template!(Word, List: &["feat1, feat2, ..."]), DuplicatesOk, EncodeCrossCrate::No, + "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" ), // ========================================================================== // Internal attributes, Layout related: // ========================================================================== - rustc_attr!(rustc_layout_scalar_valid_range_start, "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \ - niche optimizations in the standard library"), - rustc_attr!(rustc_layout_scalar_valid_range_end, "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ - niche optimizations in the standard library"), - rustc_attr!(rustc_simd_monomorphize_lane_limit, "the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \ - for better error messages"), - rustc_attr!(rustc_nonnull_optimization_guaranteed, + rustc_attr!( + rustc_layout_scalar_valid_range_start, Normal, template!(List: &["value"]), ErrorFollowing, + EncodeCrossCrate::Yes, + "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \ + niche optimizations in the standard library", + ), + rustc_attr!( + rustc_layout_scalar_valid_range_end, Normal, template!(List: &["value"]), ErrorFollowing, + EncodeCrossCrate::Yes, + "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \ + niche optimizations in the standard library", + ), + rustc_attr!( + rustc_simd_monomorphize_lane_limit, Normal, template!(NameValueStr: "N"), ErrorFollowing, + EncodeCrossCrate::Yes, + "the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \ + for better error messages", + ), + rustc_attr!( + rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes, "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to document \ guaranteed niche optimizations in the standard library", "the compiler does not even check whether the type indeed is being non-null-optimized; \ - it is your responsibility to ensure that the attribute is only used on types that are optimized"), + it is your responsibility to ensure that the attribute is only used on types that are optimized", + ), // ========================================================================== // Internal attributes, Misc: // ========================================================================== gated!( - lang,lang_items, - "lang items are subject to change" + lang, Normal, template!(NameValueStr: "name"), DuplicatesOk, EncodeCrossCrate::No, lang_items, + "lang items are subject to change", ), - rustc_attr!(rustc_as_ptr, "`#[rustc_as_ptr]` is used to mark functions returning pointers to their inner allocations"), - rustc_attr!(rustc_should_not_be_called_on_const_items, "`#[rustc_should_not_be_called_on_const_items]` is used to mark methods that don't make sense to be called on interior mutable consts"), - rustc_attr!(rustc_pass_by_value, "`#[rustc_pass_by_value]` is used to mark types that must be passed by value instead of reference"), - rustc_attr!(rustc_never_returns_null_ptr, "`#[rustc_never_returns_null_ptr]` is used to mark functions returning non-null pointers"), - rustc_attr!(rustc_no_implicit_autorefs, "`#[rustc_no_implicit_autorefs]` is used to mark functions for which an autoref to the dereference of a raw pointer should not be used as an argument"), - rustc_attr!(rustc_coherence_is_core, "`#![rustc_coherence_is_core]` allows inherent methods on builtin types, only intended to be used in `core`"), - rustc_attr!(rustc_coinductive, "`#[rustc_coinductive]` changes a trait to be coinductive, allowing cycles in the trait solver"), - rustc_attr!(rustc_allow_incoherent_impl, "`#[rustc_allow_incoherent_impl]` has to be added to all impl items of an incoherent inherent impl"), - rustc_attr!(rustc_preserve_ub_checks, "`#![rustc_preserve_ub_checks]` prevents the designated crate from evaluating whether UB checks are enabled when optimizing MIR"), - rustc_attr!(rustc_deny_explicit_impl, + rustc_attr!( + rustc_as_ptr, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, + "`#[rustc_as_ptr]` is used to mark functions returning pointers to their inner allocations" + ), + rustc_attr!( + rustc_should_not_be_called_on_const_items, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, + "`#[rustc_should_not_be_called_on_const_items]` is used to mark methods that don't make sense to be called on interior mutable consts" + ), + rustc_attr!( + rustc_pass_by_value, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, + "`#[rustc_pass_by_value]` is used to mark types that must be passed by value instead of reference" + ), + rustc_attr!( + rustc_never_returns_null_ptr, Normal, template!(Word), ErrorFollowing, + EncodeCrossCrate::Yes, + "`#[rustc_never_returns_null_ptr]` is used to mark functions returning non-null pointers" + ), + rustc_attr!( + rustc_no_implicit_autorefs, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, + "`#[rustc_no_implicit_autorefs]` is used to mark functions for which an autoref to the dereference of a raw pointer should not be used as an argument" + ), + rustc_attr!( + rustc_coherence_is_core, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, + "`#![rustc_coherence_is_core]` allows inherent methods on builtin types, only intended to be used in `core`" + ), + rustc_attr!( + rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, + "`#[rustc_coinductive]` changes a trait to be coinductive, allowing cycles in the trait solver" + ), + rustc_attr!( + rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::No, + "`#[rustc_allow_incoherent_impl]` has to be added to all impl items of an incoherent inherent impl" + ), + rustc_attr!( + rustc_preserve_ub_checks, AttributeType::CrateLevel, template!(Word), ErrorFollowing, EncodeCrossCrate::No, + "`#![rustc_preserve_ub_checks]` prevents the designated crate from evaluating whether UB checks are enabled when optimizing MIR", + ), + rustc_attr!( + rustc_deny_explicit_impl, + AttributeType::Normal, + template!(Word), + ErrorFollowing, + EncodeCrossCrate::No, "`#[rustc_deny_explicit_impl]` enforces that a trait can have no user-provided impls" ), - rustc_attr!(rustc_dyn_incompatible_trait, + rustc_attr!( + rustc_dyn_incompatible_trait, + AttributeType::Normal, + template!(Word), + ErrorFollowing, + EncodeCrossCrate::No, "`#[rustc_dyn_incompatible_trait]` marks a trait as dyn-incompatible, \ even if it otherwise satisfies the requirements to be dyn-compatible." ), - rustc_attr!(rustc_has_incoherent_inherent_impls, "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ + rustc_attr!( + rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::Yes, + "`#[rustc_has_incoherent_inherent_impls]` allows the addition of incoherent inherent impls for \ the given type by annotating all impl items with `#[rustc_allow_incoherent_impl]`" ), - rustc_attr!(rustc_non_const_trait_method, "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \ + rustc_attr!( + rustc_non_const_trait_method, AttributeType::Normal, template!(Word), + ErrorFollowing, EncodeCrossCrate::No, + "`#[rustc_non_const_trait_method]` should only used by the standard library to mark trait methods \ as non-const to allow large traits an easier transition to const" ), BuiltinAttribute { name: sym::rustc_diagnostic_item, + // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`. + encode_cross_crate: EncodeCrossCrate::Yes, + type_: Normal, safety: AttributeSafety::Normal, + template: template!(NameValueStr: "name"), + duplicates: ErrorFollowing, gate: Gated { feature: sym::rustc_attrs, message: "use of an internal attribute", @@ -717,87 +1342,236 @@ pub struct BuiltinAttribute { }, gated!( // Used in resolve: - prelude_import, "`#[prelude_import]` is for use by rustc only", + prelude_import, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::No, "`#[prelude_import]` is for use by rustc only", ), gated!( - rustc_paren_sugar,unboxed_closures, "unboxed_closures are still evolving", + rustc_paren_sugar, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, + unboxed_closures, "unboxed_closures are still evolving", ), - rustc_attr!(rustc_inherit_overflow_checks,"the `#[rustc_inherit_overflow_checks]` attribute is just used to control \ + rustc_attr!( + rustc_inherit_overflow_checks, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, + "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \ overflow checking behavior of several functions in the standard library that are inlined \ - across crates" + across crates", ), - rustc_attr!(rustc_reservation_impl,"the `#[rustc_reservation_impl]` attribute is internally used \ + rustc_attr!( + rustc_reservation_impl, Normal, + template!(NameValueStr: "reservation message"), ErrorFollowing, EncodeCrossCrate::Yes, + "the `#[rustc_reservation_impl]` attribute is internally used \ for reserving `impl From for T` as part of the effort to stabilize `!`" ), - rustc_attr!(rustc_test_marker, "the `#[rustc_test_marker]` attribute is used internally to track tests"), - rustc_attr!(rustc_unsafe_specialization_marker, + rustc_attr!( + rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing, + EncodeCrossCrate::No, "the `#[rustc_test_marker]` attribute is used internally to track tests", + ), + rustc_attr!( + rustc_unsafe_specialization_marker, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_unsafe_specialization_marker]` attribute is used to check specializations" ), - rustc_attr!(rustc_specialization_trait, + rustc_attr!( + rustc_specialization_trait, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No, "the `#[rustc_specialization_trait]` attribute is used to check specializations" ), - rustc_attr!(rustc_main,"the `#[rustc_main]` attribute is used internally to specify test entry point function"), - rustc_attr!(rustc_skip_during_method_dispatch, "the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \ + rustc_attr!( + rustc_main, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, + "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"]), 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 \ editions < 2021 (array) or editions < 2024 (boxed_slice)" ), - rustc_attr!(rustc_must_implement_one_of,"the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \ + rustc_attr!( + rustc_must_implement_one_of, Normal, template!(List: &["function1, function2, ..."]), + ErrorFollowing, EncodeCrossCrate::No, + "the `#[rustc_must_implement_one_of]` attribute is used to change minimal complete \ definition of a trait. Its syntax and semantics are highly experimental and will be \ subject to change before stabilization", ), - rustc_attr!(rustc_doc_primitive,"the `#[rustc_doc_primitive]` attribute is used by the standard library \ + rustc_attr!( + rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing, + EncodeCrossCrate::Yes, "the `#[rustc_doc_primitive]` attribute is used by the standard library \ to provide a way to generate documentation for primitive types", ), gated!( - rustc_intrinsic,intrinsics, - "the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items"), - rustc_attr!(rustc_no_mir_inline,"`#[rustc_no_mir_inline]` prevents the MIR inliner from inlining a function while not affecting codegen" + rustc_intrinsic, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes, intrinsics, + "the `#[rustc_intrinsic]` attribute is used to declare intrinsics as function items", + ), + rustc_attr!( + rustc_no_mir_inline, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, + "`#[rustc_no_mir_inline]` prevents the MIR inliner from inlining a function while not affecting codegen" + ), + rustc_attr!( + rustc_force_inline, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing, EncodeCrossCrate::Yes, + "`#[rustc_force_inline]` forces a free function to be inlined" + ), + rustc_attr!( + rustc_scalable_vector, Normal, template!(List: &["count"]), WarnFollowing, EncodeCrossCrate::Yes, + "`#[rustc_scalable_vector]` defines a scalable vector type" ), - rustc_attr!(rustc_force_inline,"`#[rustc_force_inline]` forces a free function to be inlined"), - rustc_attr!(rustc_scalable_vector,"`#[rustc_scalable_vector]` defines a scalable vector type"), // ========================================================================== // Internal attributes, Testing: // ========================================================================== - rustc_attr!(TEST, rustc_effective_visibility), - rustc_attr!(TEST, rustc_dump_inferred_outlives), - rustc_attr!(TEST, rustc_capture_analysis), - rustc_attr!(TEST, rustc_insignificant_dtor), - rustc_attr!(TEST, rustc_no_implicit_bounds), - rustc_attr!(TEST, rustc_strict_coherence), - rustc_attr!(TEST, rustc_dump_variances), - rustc_attr!(TEST, rustc_dump_variances_of_opaques), - rustc_attr!(TEST, rustc_hidden_type_of_opaques), - rustc_attr!(TEST, rustc_layout), - rustc_attr!(TEST, rustc_abi), - rustc_attr!(TEST, rustc_regions), - rustc_attr!(TEST, rustc_delayed_bug_from_inside_query), - rustc_attr!(TEST, rustc_dump_user_args), - rustc_attr!(TEST, rustc_evaluate_where_clauses), - rustc_attr!(TEST, rustc_if_this_changed), - rustc_attr!(TEST, rustc_then_this_would_need), - rustc_attr!(TEST, rustc_clean), - rustc_attr!(TEST, rustc_partition_reused), - rustc_attr!(TEST, rustc_partition_codegened), - rustc_attr!(TEST, rustc_expected_cgu_reuse), - rustc_attr!(TEST, rustc_symbol_name), - rustc_attr!(TEST, rustc_def_path), - rustc_attr!(TEST, rustc_mir), - gated!(custom_mir,"the `#[custom_mir]` attribute is just used for the Rust test suite"), - rustc_attr!(TEST, rustc_dump_item_bounds), - rustc_attr!(TEST, rustc_dump_predicates), - rustc_attr!(TEST, rustc_dump_def_parents), - rustc_attr!(TEST, rustc_dump_object_lifetime_defaults), - rustc_attr!(TEST, rustc_dump_vtable), - rustc_attr!(TEST, rustc_dummy), - rustc_attr!(TEST, pattern_complexity_limit), + rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes), + rustc_attr!( + TEST, rustc_dump_inferred_outlives, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_capture_analysis, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_insignificant_dtor, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_no_implicit_bounds, CrateLevel, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_strict_coherence, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_dump_variances, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_dump_variances_of_opaques, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_hidden_type_of_opaques, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_layout, Normal, template!(List: &["field1, field2, ..."]), + WarnFollowing, EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_abi, Normal, template!(List: &["field1, field2, ..."]), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_regions, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_delayed_bug_from_inside_query, Normal, + template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_dump_user_args, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_evaluate_where_clauses, Normal, template!(Word), WarnFollowing, + EncodeCrossCrate::Yes + ), + rustc_attr!( + TEST, rustc_if_this_changed, Normal, template!(Word, List: &["DepNode"]), DuplicatesOk, + EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_then_this_would_need, Normal, template!(List: &["DepNode"]), DuplicatesOk, + EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_clean, Normal, + template!(List: &[r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#]), + DuplicatesOk, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_partition_reused, Normal, + template!(List: &[r#"cfg = "...", module = "...""#]), DuplicatesOk, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_partition_codegened, Normal, + template!(List: &[r#"cfg = "...", module = "...""#]), DuplicatesOk, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_expected_cgu_reuse, Normal, + template!(List: &[r#"cfg = "...", module = "...", kind = "...""#]), DuplicatesOk, + EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_symbol_name, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_def_path, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_mir, Normal, template!(List: &["arg1, arg2, ..."]), + DuplicatesOk, EncodeCrossCrate::Yes + ), + gated!( + custom_mir, Normal, template!(List: &[r#"dialect = "...", phase = "...""#]), + ErrorFollowing, EncodeCrossCrate::No, + "the `#[custom_mir]` attribute is just used for the Rust test suite", + ), + rustc_attr!( + TEST, rustc_dump_item_bounds, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_dump_predicates, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_dump_def_parents, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_dump_object_lifetime_defaults, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_dump_vtable, Normal, template!(Word), + WarnFollowing, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), + DuplicatesOk, EncodeCrossCrate::No + ), + rustc_attr!( + TEST, pattern_complexity_limit, CrateLevel, template!(NameValueStr: "N"), + ErrorFollowing, EncodeCrossCrate::No, + ), ]; pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } +/// Whether this builtin attribute is encoded cross crate. +/// This means it can be used cross crate. +pub fn encode_cross_crate(name: Symbol) -> bool { + if let Some(attr) = BUILTIN_ATTRIBUTE_MAP.get(&name) { + attr.encode_cross_crate == EncodeCrossCrate::Yes + } else { + true + } +} + +pub fn is_valid_for_get_attr(name: Symbol) -> bool { + BUILTIN_ATTRIBUTE_MAP.get(&name).is_some_and(|attr| match attr.duplicates { + WarnFollowing | ErrorFollowing | ErrorPreceding | FutureWarnFollowing + | FutureWarnPreceding => true, + DuplicatesOk | WarnFollowingWordOnly => false, + }) +} + pub static BUILTIN_ATTRIBUTE_MAP: LazyLock> = LazyLock::new(|| { let mut map = FxHashMap::default(); diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index 40a637bfa0b8..9d046bdef1cf 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -129,9 +129,10 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option) -> bool && p.encode_cross_crate() == EncodeCrossCrate::No { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. + } else if let Some(name) = attr.name() + && !rustc_feature::encode_cross_crate(name) + { + // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. } else if let hir::Attribute::Parsed(AttributeKind::DocComment { .. }) = attr { // We keep all doc comments reachable to rustdoc because they might be "imported" into // downstream crates if they use `#[doc(inline)]` to copy an item's documentation into diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 36a474688f07..dbf7d643a42c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1725,8 +1725,13 @@ pub fn get_attrs_by_path( #[deprecated = "Though there are valid usecases for this method, especially when your attribute is not a parsed attribute, usually you want to call rustc_hir::find_attr! instead."] pub fn get_attr(self, did: impl Into, attr: Symbol) -> Option<&'tcx hir::Attribute> { - #[allow(deprecated)] - self.get_attrs(did, attr).next() + if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) { + let did: DefId = did.into(); + bug!("get_attr: unexpected called with DefId `{:?}`, attr `{:?}`", did, attr); + } else { + #[allow(deprecated)] + self.get_attrs(did, attr).next() + } } /// Determines whether an item is annotated with an attribute. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3e3810b7f125..12b583d8fee1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -6,15 +6,17 @@ //! item. use std::cell::Cell; +use std::collections::hash_map::Entry; use std::slice; use rustc_abi::ExternAbi; use rustc_ast::ast; use rustc_attr_parsing::{AttributeParser, Late}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordMap; use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, msg}; -use rustc_feature::BUILTIN_ATTRIBUTE_MAP; +use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::{ AttributeKind, CrateType, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, @@ -135,6 +137,7 @@ fn check_attributes( target: Target, item: Option>, ) { + let mut seen = FxHashMap::default(); let attrs = self.tcx.hir_attrs(hir_id); for attr in attrs { match attr { @@ -401,6 +404,64 @@ fn check_attributes( } } + if hir_id != CRATE_HIR_ID { + match attr { + Attribute::Parsed(_) => { /* Already validated. */ } + Attribute::Unparsed(attr) => { + // FIXME(jdonszelmann): remove once all crate-level attrs are parsed and caught by + // the above + if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = + attr.path + .segments + .first() + .and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)) + { + match attr.style { + ast::AttrStyle::Outer => { + let attr_span = attr.span; + let bang_position = self + .tcx + .sess + .source_map() + .span_until_char(attr_span, '[') + .shrink_to_hi(); + + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr { + suggestion: errors::OuterCrateLevelAttrSuggestion { + bang_position, + }, + }, + ) + } + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + } + } + } + } + } + + if let Attribute::Unparsed(unparsed_attr) = attr + && let Some(BuiltinAttribute { duplicates, .. }) = + attr.name().and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name)) + { + check_duplicates( + self.tcx, + unparsed_attr.span, + attr, + hir_id, + *duplicates, + &mut seen, + ); + } self.check_unused_attribute(hir_id, attr) } @@ -1933,6 +1994,67 @@ pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { check_mod_attrs, ..*providers }; } +// FIXME(jdonszelmann): remove, check during parsing +fn check_duplicates( + tcx: TyCtxt<'_>, + attr_span: Span, + attr: &Attribute, + hir_id: HirId, + duplicates: AttributeDuplicates, + seen: &mut FxHashMap, +) { + use AttributeDuplicates::*; + if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() { + return; + } + let attr_name = attr.name().unwrap(); + match duplicates { + DuplicatesOk => {} + WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => { + match seen.entry(attr_name) { + Entry::Occupied(mut entry) => { + let (this, other) = if matches!(duplicates, FutureWarnPreceding) { + let to_remove = entry.insert(attr_span); + (to_remove, attr_span) + } else { + (attr_span, *entry.get()) + }; + tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + this, + errors::UnusedDuplicate { + this, + other, + warning: matches!( + duplicates, + FutureWarnFollowing | FutureWarnPreceding + ), + }, + ); + } + Entry::Vacant(entry) => { + entry.insert(attr_span); + } + } + } + ErrorFollowing | ErrorPreceding => match seen.entry(attr_name) { + Entry::Occupied(mut entry) => { + let (this, other) = if matches!(duplicates, ErrorPreceding) { + let to_remove = entry.insert(attr_span); + (to_remove, attr_span) + } else { + (attr_span, *entry.get()) + }; + tcx.dcx().emit_err(errors::UnusedMultiple { this, other, name: attr_name }); + } + Entry::Vacant(entry) => { + entry.insert(attr_span); + } + }, + } +} + fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool { matches!(&self_ty.kind, hir::TyKind::Tup([_])) || if let hir::TyKind::FnPtr(fn_ptr_ty) = &self_ty.kind { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 46b96ff1da35..628d0b0c961a 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -326,6 +326,30 @@ pub(crate) struct InvalidMayDangle { pub attr_span: Span, } +#[derive(Diagnostic)] +#[diag("unused attribute")] +pub(crate) struct UnusedDuplicate { + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + pub this: Span, + #[note("attribute also specified here")] + pub other: Span, + #[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" + )] + pub warning: bool, +} + +#[derive(Diagnostic)] +#[diag("multiple `{$name}` attributes")] +pub(crate) struct UnusedMultiple { + #[primary_span] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + pub this: Span, + #[note("attribute also specified here")] + pub other: Span, + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag("this `#[deprecated]` annotation has no effect")] pub(crate) struct DeprecatedAnnotationHasNoEffect {