diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index dc1acade85ed5..54f46d3f8e79c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -291,7 +291,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { attribute_parser: AttributeParser::new( tcx.sess, tcx.features(), - tcx.registered_tools(()), + tcx.registered_attr_tools(()), ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, ), delayed_lints: Vec::new(), diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 1fefb5d6ecf48..72508f79f5d43 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -654,7 +654,7 @@ fn maybe_stage_features(sess: &Session, features: &Features, krate: &ast::Crate) let mut errored = false; if let Some(Attribute::Parsed(AttributeKind::Feature(feature_idents, first_span))) = - AttributeParser::parse_limited(sess, &krate.attrs, &[sym::feature]) + AttributeParser::parse_limited_sym(sess, &krate.attrs, &[sym::feature]) { // `feature(...)` used on non-nightly. This is definitely an error. let mut err = diagnostics::FeatureOnNonNightly { diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index f48fbe6324834..c7cadf18a1d36 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -283,7 +283,7 @@ impl AttributeParser for NakedParser { let span = self.span?; - let Some(tools) = cx.tools else { + let Some(tools) = cx.attr_tools else { unreachable!("tools required while parsing attributes"); }; diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 03012941cb1c9..92c4cdf74a9e7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::FxIndexSet; use rustc_feature::AttributeStability; use rustc_hir::attrs::{CrateType, WindowsSubsystemKind}; use rustc_session::lint::builtin::UNKNOWN_CRATE_TYPES; @@ -5,7 +6,7 @@ use rustc_span::Symbol; use rustc_span::edit_distance::find_best_match_for_name_with_substrings; use super::prelude::*; -use crate::diagnostics::{UnknownCrateTypes, UnknownCrateTypesSuggestion}; +use crate::diagnostics::{ToolReserved, UnknownCrateTypes, UnknownCrateTypesSuggestion}; pub(crate) struct CrateNameParser; @@ -317,49 +318,102 @@ impl CombineAttributeParser for FeatureParser { } } -pub(crate) struct RegisterToolParser; +#[derive(Default)] +pub(crate) struct RegisterToolParser { + attr_tools: FxIndexSet, + lint_tools: FxIndexSet, +} -impl CombineAttributeParser for RegisterToolParser { - const PATH: &[Symbol] = &[sym::register_tool]; - type Item = Ident; - const CONVERT: ConvertFn = |tools, _span| AttributeKind::RegisterTool(tools); - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); - const TEMPLATE: AttributeTemplate = template!(List: &["tool1, tool2, ..."]); - const STABILITY: AttributeStability = unstable!(register_tool); +const PREDEFINED_TOOLS: &[Symbol] = + &[sym::clippy, sym::rustfmt, sym::diagnostic, sym::miri, sym::rust_analyzer]; + +fn parse_register_tool( + tools: &mut [&mut FxIndexSet], + cx: &mut AcceptContext<'_, '_>, + args: &ArgParser, +) { + let Some(list) = cx.expect_list(args, cx.attr_span) else { + return; + }; + + if list.is_empty() { + let attr_span = cx.attr_span; + cx.adcx().warn_empty_attribute(attr_span); + } - fn extend( - cx: &mut AcceptContext<'_, '_>, - args: &ArgParser, - ) -> impl IntoIterator { - let Some(list) = cx.expect_list(args, cx.attr_span) else { - return Vec::new(); + for elem in list.mixed() { + let Some(elem) = elem.meta_item() else { + cx.adcx().expected_identifier(elem.span()); + continue; + }; + let Some(()) = cx.expect_no_args(elem.args()) else { + continue; }; - if list.is_empty() { - let attr_span = cx.attr_span; - cx.adcx().warn_empty_attribute(attr_span); + let path = elem.path(); + let Some(ident) = path.word() else { + cx.adcx().expected_identifier(path.span()); + continue; + }; + + if PREDEFINED_TOOLS.iter().any(|&tool| tool == ident.name) { + cx.should_emit.emit_err(cx.dcx().create_err(ToolReserved { + span: ident.span, + tool: ident, + reason: "predefined", + })); + continue; } - let mut res = Vec::new(); + if ident.name == sym::rustc { + cx.should_emit.emit_err(cx.dcx().create_err(ToolReserved { + span: ident.span, + tool: ident, + reason: "reserved", + })); + continue; + } - for elem in list.mixed() { - let Some(elem) = elem.meta_item() else { - cx.adcx().expected_identifier(elem.span()); - continue; - }; - let Some(()) = cx.expect_no_args(elem.args()) else { - continue; - }; + for tools in tools.iter_mut() { + tools.insert(ident); + } + } +} - let path = elem.path(); - let Some(ident) = path.word() else { - cx.adcx().expected_identifier(path.span()); - continue; - }; +impl AttributeParser for RegisterToolParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::register_tool], + template!(List: &["tool1, tool2, ..."]), + unstable!(register_tool), + |this, cx, args| { + parse_register_tool(&mut [&mut this.attr_tools, &mut this.lint_tools], cx, args) + }, + ), + ( + &[sym::register_attribute_tool], + template!(List: &["tool1, tool2, ..."]), + unstable!(register_tool), + |this, cx, args| parse_register_tool(&mut [&mut this.attr_tools], cx, args), + ), + ( + &[sym::register_lint_tool], + template!(List: &["tool1, tool2, ..."]), + unstable!(register_tool), + |this, cx, args| parse_register_tool(&mut [&mut this.lint_tools], cx, args), + ), + ]; - res.push(ident); - } + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]); - res + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + if self.attr_tools.is_empty() && self.lint_tools.is_empty() { + None + } else { + Some(AttributeKind::RegisterTool { + attr_tools: self.attr_tools, + lint_tools: self.lint_tools, + }) + } } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1595d2f80ddc7..1c90fbd97f9a9 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -151,6 +151,7 @@ attribute_parsers!( OnUnimplementedParser, OnUnknownParser, OnUnmatchedArgsParser, + RegisterToolParser, RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, @@ -165,7 +166,6 @@ attribute_parsers!( Combine, Combine, Combine, - Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index 50667952b814d..a74a85098f876 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -1,7 +1,7 @@ use rustc_errors::{Applicability, DiagArgValue, E0232, E0264, MultiSpan}; use rustc_hir::AttrPath; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] #[diag("`{$name}` attribute cannot be used at crate level")] @@ -824,3 +824,12 @@ pub(crate) struct UnknownExternLangItem { pub span: Span, pub lang_item: Symbol, } + +#[derive(Diagnostic)] +#[diag("tool `{$tool}` is {$reason} and cannot be registered")] +pub(crate) struct ToolReserved { + #[primary_span] + pub(crate) span: Span, + pub(crate) tool: Ident, + pub(crate) reason: &'static str, +} diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index e8bef70be550a..37ccfd300e6d4 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -37,15 +37,15 @@ pub struct EmitAttribute( /// Context created once, for example as part of the ast lowering /// context, through which all attributes can be lowered. pub struct AttributeParser<'sess> { - pub(crate) tools: Option<&'sess RegisteredTools>, + pub(crate) attr_tools: Option<&'sess RegisteredTools>, pub(crate) features: Option<&'sess Features>, pub(crate) sess: &'sess Session, pub(crate) should_emit: ShouldEmit, - /// *Only* parse attributes with this symbol. + /// *Only* parse attributes that passes this filter. /// - /// Used in cases where we want the lowering infrastructure for parse just a single attribute. - parse_only: Option<&'static [Symbol]>, + /// Used in cases where we want the lowering infrastructure for parse just limited attributes. + parse_filter: Option<&'sess dyn Fn(&ast::Attribute) -> bool>, } impl<'sess> AttributeParser<'sess> { @@ -57,8 +57,8 @@ impl<'sess> AttributeParser<'sess> { /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would /// crash if you tried to do so through [`parse_limited`](Self::parse_limited). /// - /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with - /// that symbol are picked out of the list of instructions and parsed. Those are returned. + /// To make sure use is limited, supply a filter. Only attributes that passes the filter are + /// picked out of the list of instructions and parsed. Those are returned. /// /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed @@ -68,21 +68,29 @@ impl<'sess> AttributeParser<'sess> { pub fn parse_limited( sess: &'sess Session, attrs: &[ast::Attribute], - sym: &'static [Symbol], + parse_filter: &dyn Fn(&ast::Attribute) -> bool, ) -> Option { Self::parse_limited_should_emit( sess, attrs, - sym, + parse_filter, // Because we're not emitting warnings/errors, the target should not matter DUMMY_SP, - CRATE_NODE_ID, - Target::Crate, None, ShouldEmit::Nothing, ) } + /// This does the same as `parse_limited`, except that it takes a fixed symbol instead of a + /// filter. + pub fn parse_limited_sym( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: &'static [Symbol], + ) -> Option { + Self::parse_limited(sess, attrs, &|attr| attr.path_matches(sym)) + } + /// This does the same as `parse_limited`, except it has a `should_emit` parameter which allows it to emit errors. /// Usually you want `parse_limited`, which emits no errors. /// @@ -90,20 +98,18 @@ impl<'sess> AttributeParser<'sess> { pub fn parse_limited_should_emit( sess: &'sess Session, attrs: &[ast::Attribute], - sym: &'static [Symbol], + parse_filter: &dyn Fn(&ast::Attribute) -> bool, target_span: Span, - target_node_id: NodeId, - target: Target, features: Option<&'sess Features>, should_emit: ShouldEmit, ) -> Option { let mut parsed = Self::parse_limited_all( sess, attrs, - Some(sym), - target, + Some(parse_filter), + Target::Crate, target_span, - target_node_id, + CRATE_NODE_ID, features, should_emit, None, @@ -112,25 +118,45 @@ impl<'sess> AttributeParser<'sess> { parsed.pop() } + /// This does the same as `parse_limited_should_emit`, except that it takes a fixed symbol + /// instead of a filter. + pub fn parse_limited_sym_should_emit( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: &'static [Symbol], + target_span: Span, + features: Option<&'sess Features>, + should_emit: ShouldEmit, + ) -> Option { + Self::parse_limited_should_emit( + sess, + attrs, + &|attr| attr.path_matches(sym), + target_span, + features, + should_emit, + ) + } + /// This method allows you to parse a list of attributes *before* `rustc_ast_lowering`. /// This can be used for attributes that would be removed before `rustc_ast_lowering`, such as attributes on macro calls. /// /// Try to use this as little as possible. Attributes *should* be lowered during /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would /// crash if you tried to do so through [`parse_limited_all`](Self::parse_limited_all). - /// Therefore, if `parse_only` is None, then features *must* be provided. + /// Therefore, if `parse_filter` is None, then features *must* be provided. pub fn parse_limited_all( sess: &'sess Session, attrs: &[ast::Attribute], - parse_only: Option<&'static [Symbol]>, + parse_filter: Option<&dyn Fn(&ast::Attribute) -> bool>, target: Target, target_span: Span, target_node_id: NodeId, features: Option<&'sess Features>, should_emit: ShouldEmit, - tools: Option<&'sess RegisteredTools>, + attr_tools: Option<&'sess RegisteredTools>, ) -> Vec { - let mut p = Self { features, tools, parse_only, sess, should_emit }; + let mut p = AttributeParser { features, attr_tools, parse_filter, sess, should_emit }; p.parse_attribute_list( attrs, target_span, @@ -212,7 +238,7 @@ impl<'sess> AttributeParser<'sess> { parse_fn: fn(cx: &mut AcceptContext<'_, '_>, item: &I) -> T, template: &AttributeTemplate, ) -> T { - let mut parser = Self { features, tools: None, parse_only: None, sess, should_emit }; + let mut parser = Self { features, attr_tools: None, parse_filter: None, sess, should_emit }; let mut emit_lint = |lint_id: LintId, span: MultiSpan, kind: EmitAttribute| { sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0) }; @@ -252,10 +278,16 @@ impl<'sess> AttributeParser<'sess> { pub fn new( sess: &'sess Session, features: &'sess Features, - tools: &'sess RegisteredTools, + attr_tools: &'sess RegisteredTools, should_emit: ShouldEmit, ) -> Self { - Self { features: Some(features), tools: Some(tools), parse_only: None, sess, should_emit } + Self { + features: Some(features), + attr_tools: Some(attr_tools), + parse_filter: None, + sess, + should_emit, + } } pub(crate) fn sess(&self) -> &'sess Session { @@ -299,8 +331,8 @@ impl<'sess> AttributeParser<'sess> { for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. - if let Some(expected) = self.parse_only { - if !attr.path_matches(expected) { + if let Some(filter) = self.parse_filter { + if !filter(attr) { continue; } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 1577db640502c..5bd2023fff1a5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -493,7 +493,7 @@ impl<'a> TraitDef<'a> { match item { Annotatable::Item(item) => { let is_packed = matches!( - AttributeParser::parse_limited(cx.sess, &item.attrs, &[sym::repr]), + AttributeParser::parse_limited_sym(cx.sess, &item.attrs, &[sym::repr]), Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..))) ); diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 32865f234ef44..325df57711d83 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -100,7 +100,7 @@ impl<'a> CollectProcMacros<'a> { attr: &'a ast::Attribute, ) { let Some(rustc_hir::Attribute::Parsed(AttributeKind::ProcMacroDerive { .. })) = - AttributeParser::parse_limited( + AttributeParser::parse_limited_sym( self.session, slice::from_ref(attr), &[sym::proc_macro_derive], diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index fda0660c48403..fa5508560e032 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -480,7 +480,7 @@ fn should_ignore_message(i: &ast::Item) -> Option { fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic { if let Some(Attribute::Parsed(AttributeKind::ShouldPanic { reason, .. })) = - AttributeParser::parse_limited(cx.sess, &i.attrs, &[sym::should_panic]) + AttributeParser::parse_limited_sym(cx.sess, &i.attrs, &[sym::should_panic]) { ShouldPanic::Yes(reason) } else { diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index e08fb81d4de68..af0292a889620 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -392,7 +392,7 @@ fn get_test_name(i: &ast::Item) -> Option { } fn get_test_runner(sess: &Session, krate: &ast::Crate) -> Option { - match AttributeParser::parse_limited(sess, &krate.attrs, &[sym::test_runner]) { + match AttributeParser::parse_limited_sym(sess, &krate.attrs, &[sym::test_runner]) { Some(rustc_hir::Attribute::Parsed(AttributeKind::TestRunner(path))) => Some(path), _ => None, } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 31353d1eac0fb..bf93d32b26f8d 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -713,13 +713,13 @@ fn print_crate_info( let crate_name = passes::get_crate_name(sess, attrs); let lint_store = crate::unerased_lint_store(sess); let features = rustc_expand::config::features(sess, attrs, crate_name); - let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs, sess); + let registered_lint_tools = rustc_resolve::registered_lint_tools_ast(sess, attrs); let builder = rustc_lint::LintLevelsBuilder::crate_root( sess, &features, true, lint_store, - ®istered_tools, + ®istered_lint_tools, attrs, ); for lint in lint_store.get_lints() { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ee516991cfbfc..d9855130d5210 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1122,8 +1122,11 @@ pub trait ResolverExpand { cfg_span: Span, ); - /// Tools registered with `#![register_tool]` and used by tool attributes and lints. - fn registered_tools(&self) -> &RegisteredTools; + /// Tools registered with `#![register_tool]` or `#![register_attribute_tool]`. + fn registered_attr_tools(&self) -> &RegisteredTools; + + /// Tools registered with `#![register_tool]` or `#![register_lint_tool]`. + fn registered_lint_tools(&self) -> &RegisteredTools; /// Mark this invocation id as a glob delegation. fn register_glob_delegation(&mut self, invoc_id: LocalExpnId); @@ -1150,7 +1153,7 @@ pub trait LintStoreExpand { &self, sess: &Session, features: &Features, - registered_tools: &RegisteredTools, + registered_lint_tools: &RegisteredTools, node_id: NodeId, attrs: &[Attribute], items: &[Box], diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 1289f8ed63af6..fd078483d9041 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -51,7 +51,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) - let mut features = Features::default(); if let Some(hir::Attribute::Parsed(AttributeKind::Feature(feature_idents, _))) = - AttributeParser::parse_limited(sess, krate_attrs, &[sym::feature]) + AttributeParser::parse_limited_sym(sess, krate_attrs, &[sym::feature]) { for feature_ident in feature_idents { // If the enabled feature has been removed, issue an error. diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index c6bbeda36e930..bdc342eeb979b 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1371,7 +1371,7 @@ impl InvocationCollectorNode for Box { lint_store.pre_expansion_lint( ecx.sess, ecx.ecfg.features, - ecx.resolver.registered_tools(), + ecx.resolver.registered_lint_tools(), ecx.current_expansion.lint_node_id, &attrs, &items, @@ -2232,7 +2232,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.current_expansion.lint_node_id, Some(self.cx.ecfg.features), ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, - Some(self.cx.resolver.registered_tools()), + Some(self.cx.resolver.registered_attr_tools()), ); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6a9cfeff49339..01fee5f8111dc 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -185,6 +185,8 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::ffi_pure, sym::ffi_const, + sym::register_attribute_tool, + sym::register_lint_tool, sym::register_tool, // `#[cfi_encoding = ""]` sym::cfi_encoding, diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index ab24b0ed37d22..b5fac5988b69b 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -95,7 +95,7 @@ declare_features! ( (removed, crate_visibility_modifier, "1.63.0", Some(53120), Some("removed in favor of `pub(crate)`"), 97254), /// Allows using custom attributes (RFC 572). (removed, custom_attribute, "1.0.0", Some(29642), - Some("removed in favor of `#![register_tool]` and `#![register_attr]`"), 66070), + Some("removed in favor of `#![register_tool]`"), 66070), /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`. (removed, custom_derive, "1.32.0", Some(29644), Some("subsumed by `#[proc_macro_derive]`")), diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 17d00863d99d5..9021c7aba7633 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -9,7 +9,7 @@ use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::expand::typetree::TypeTree; use rustc_ast::token::DocFragmentKind; use rustc_ast::{AttrStyle, Path, ast}; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_hir::LangItem; use rustc_macros::{Decodable, Encodable, PrintAttribute, StableHash}; @@ -1313,8 +1313,11 @@ pub enum AttributeKind { /// Represents `#[reexport_test_harness_main]` ReexportTestHarnessMain(Symbol), - /// Represents `#[register_tool]` - RegisterTool(ThinVec), + /// Represents `#[register_attribute_tool]`, `#[register_lint_tool]` and `#[register_tool]` + RegisterTool { + attr_tools: FxIndexSet, + lint_tools: FxIndexSet, + }, /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index f5a6eeec07406..88c9114d92757 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -96,7 +96,7 @@ impl AttributeKind { ProfilerRuntime => No, RecursionLimit { .. } => No, ReexportTestHarnessMain(..) => No, - RegisterTool(..) => No, + RegisterTool { .. } => No, Repr { .. } => No, RustcAbi { .. } => No, RustcAlign { .. } => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index d826aa363f349..ac6ecf027fd5e 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -10,7 +10,7 @@ use rustc_ast::expand::autodiff_attrs::{DiffActivity, DiffMode}; use rustc_ast::token::{CommentKind, DocFragmentKind}; use rustc_ast::{AttrId, AttrStyle, IntTy, UintTy}; use rustc_ast_pretty::pp::Printer; -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; @@ -81,6 +81,26 @@ impl PrintAttribute for ThinVec { p.word("]"); } } + +impl PrintAttribute for FxIndexSet { + fn should_render(&self) -> bool { + self.is_empty() || self[0].should_render() + } + + fn print_attribute(&self, p: &mut Printer) { + let mut last_printed = false; + p.word("["); + for i in self { + if last_printed { + p.word_space(","); + } + i.print_attribute(p); + last_printed = i.should_render(); + } + p.word("]"); + } +} + impl PrintAttribute for FxIndexMap { fn should_render(&self) -> bool { self.is_empty() || self[0].should_render() diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ca6e48cb67fe5..b42e6ce7e1048 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; -use rustc_ast::{self as ast, CRATE_NODE_ID}; +use rustc_ast as ast; use rustc_attr_parsing::{AttributeParser, ShouldEmit}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::{CompiledModules, CrateInfo}; @@ -24,7 +24,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_hir::limit::Limit; -use rustc_hir::{Attribute, Target, find_attr}; +use rustc_hir::{Attribute, find_attr}; use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::EncodedMetadata; @@ -88,7 +88,7 @@ fn pre_expansion_lint<'a>( sess: &Session, features: &Features, lint_store: &LintStore, - registered_tools: &RegisteredTools, + registered_lint_tools: &RegisteredTools, check_node: EarlyCheckNode<'a>, node_name: Symbol, ) { @@ -99,7 +99,7 @@ fn pre_expansion_lint<'a>( features, true, lint_store, - registered_tools, + registered_lint_tools, None, check_node, ); @@ -115,14 +115,14 @@ impl LintStoreExpand for LintStoreExpandImpl<'_> { &self, sess: &Session, features: &Features, - registered_tools: &RegisteredTools, + registered_lint_tools: &RegisteredTools, node_id: ast::NodeId, attrs: &[ast::Attribute], items: &[Box], name: Symbol, ) { let check_node = EarlyCheckNode::LoadedMod(node_id, attrs, items); - pre_expansion_lint(sess, features, self.0, registered_tools, check_node, name); + pre_expansion_lint(sess, features, self.0, registered_lint_tools, check_node, name); } } @@ -145,7 +145,7 @@ fn configure_and_expand( sess, features, lint_store, - tcx.registered_tools(()), + tcx.registered_lint_tools(()), EarlyCheckNode::CrateRoot(&krate, pre_configured_attrs), crate_name, ); @@ -476,7 +476,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { tcx.features(), false, lint_store, - tcx.registered_tools(()), + tcx.registered_lint_tools(()), Some(lint_buffer), EarlyCheckNode::CrateRoot(&*krate, &*krate.attrs), ) @@ -792,7 +792,8 @@ fn resolver_for_lowering_raw<'tcx>( &'tcx ty::ResolverGlobalCtxt, ) { let arenas = Resolver::arenas(); - let _ = tcx.registered_tools(()); // Uses `crate_for_resolver`. + let _ = tcx.registered_attr_tools(()); // Uses `crate_for_resolver`. + let _ = tcx.registered_lint_tools(()); // Uses `crate_for_resolver`. let (krate, pre_configured_attrs) = tcx.crate_for_resolver(()).steal(); let mut resolver = Resolver::new( tcx, @@ -1373,13 +1374,11 @@ pub(crate) fn parse_crate_name( emit_errors: ShouldEmit, ) -> Option<(Symbol, Span)> { let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = - AttributeParser::parse_limited_should_emit( + AttributeParser::parse_limited_sym_should_emit( sess, attrs, &[sym::crate_name], DUMMY_SP, - rustc_ast::node_id::CRATE_NODE_ID, - Target::Crate, None, emit_errors, )? @@ -1423,13 +1422,11 @@ pub fn collect_crate_types( let mut base = session.opts.crate_types.clone(); if base.is_empty() { if let Some(Attribute::Parsed(AttributeKind::CrateType(crate_type))) = - AttributeParser::parse_limited_should_emit( + AttributeParser::parse_limited_sym_should_emit( session, attrs, &[sym::crate_type], crate_span, - CRATE_NODE_ID, - Target::Crate, None, ShouldEmit::EarlyFatal { also_emit_lints: false }, ) @@ -1480,13 +1477,11 @@ fn default_output_for_target(sess: &Session) -> CrateType { } fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { - let attr = AttributeParser::parse_limited_should_emit( + let attr = AttributeParser::parse_limited_sym_should_emit( sess, &krate_attrs, &[sym::recursion_limit], DUMMY_SP, - rustc_ast::node_id::CRATE_NODE_ID, - Target::Crate, None, // errors are fatal here, but lints aren't. // If things aren't fatal we continue, and will parse this again. diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 383023aa24457..6b322ee668e02 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -243,7 +243,7 @@ impl EarlyLintPass for UnsafeCode { ast::ItemKind::MacroDef(..) => { if let Some(hir::Attribute::Parsed(AttributeKind::AllowInternalUnsafe(span))) = - AttributeParser::parse_limited( + AttributeParser::parse_limited_sym( cx.builder.sess(), &it.attrs, &[sym::allow_internal_unsafe], diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index e992f1ed385e7..cbde4320079c1 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -346,13 +346,13 @@ impl LintStore { &self, lint_name: &str, tool_name: Option, - registered_tools: &RegisteredTools, + registered_lint_tools: &RegisteredTools, ) -> CheckLintNameResult<'_> { if let Some(tool_name) = tool_name { // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes. if tool_name != sym::rustc && tool_name != sym::rustdoc - && !registered_tools.contains(&Ident::with_dummy_span(tool_name)) + && !registered_lint_tools.contains(&Ident::with_dummy_span(tool_name)) { return CheckLintNameResult::NoTool; } @@ -572,7 +572,7 @@ impl<'a> EarlyContext<'a> { features: &'a Features, lint_added_lints: bool, lint_store: &'a LintStore, - registered_tools: &'a RegisteredTools, + registered_lint_tools: &'a RegisteredTools, buffered: LintBuffer, ) -> EarlyContext<'a> { EarlyContext { @@ -581,7 +581,7 @@ impl<'a> EarlyContext<'a> { features, lint_added_lints, lint_store, - registered_tools, + registered_lint_tools, ), buffered, } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 9c123f67abd5f..e7459eb86e4db 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -314,7 +314,7 @@ pub fn check_ast_node<'a>( features: &Features, pre_expansion: bool, lint_store: &LintStore, - registered_tools: &RegisteredTools, + registered_lint_tools: &RegisteredTools, lint_buffer: Option, check_node: EarlyCheckNode<'a>, ) { @@ -323,7 +323,7 @@ pub fn check_ast_node<'a>( features, !pre_expansion, lint_store, - registered_tools, + registered_lint_tools, lint_buffer.unwrap_or_default(), ); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 492131ca8b32e..5369c1665fe04 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -171,7 +171,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe }, lint_added_lints: false, store, - registered_tools: tcx.registered_tools(()), + registered_lint_tools: tcx.registered_lint_tools(()), }; if owner == hir::CRATE_OWNER_ID { @@ -389,7 +389,7 @@ pub struct LintLevelsBuilder<'s, P> { provider: P, lint_added_lints: bool, store: &'s LintStore, - registered_tools: &'s RegisteredTools, + registered_lint_tools: &'s RegisteredTools, } pub(crate) struct BuilderPush { @@ -402,7 +402,7 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { features: &'s Features, lint_added_lints: bool, store: &'s LintStore, - registered_tools: &'s RegisteredTools, + registered_lint_tools: &'s RegisteredTools, ) -> Self { let mut builder = LintLevelsBuilder { sess, @@ -410,7 +410,7 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE }, lint_added_lints, store, - registered_tools, + registered_lint_tools, }; builder.process_command_line(); assert_eq!(builder.provider.sets.list.len(), 1); @@ -422,10 +422,10 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { features: &'s Features, lint_added_lints: bool, store: &'s LintStore, - registered_tools: &'s RegisteredTools, + registered_lint_tools: &'s RegisteredTools, crate_attrs: &[ast::Attribute], ) -> Self { - let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools); + let mut builder = Self::new(sess, features, lint_added_lints, store, registered_lint_tools); builder.add(crate_attrs, true); builder } @@ -503,7 +503,8 @@ where .dcx() .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); } - match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) { + match self.store.check_lint_name(lint_name_only, tool_name, self.registered_lint_tools) + { CheckLintNameResult::Renamed(ref replace) => { let name = lint_name.as_str(); let suggestion = RenamedLintSuggestion::WithoutSpan { replace }; @@ -768,7 +769,7 @@ where let tool_name = tool_ident.map(|ident| ident.name); let name = pprust::path_to_string(&meta_item.path); let lint_result = - self.store.check_lint_name(&name, tool_name, self.registered_tools); + self.store.check_lint_name(&name, tool_name, self.registered_lint_tools); let (ids, name) = match lint_result { CheckLintNameResult::Ok(ids) => { @@ -837,7 +838,7 @@ where // NOTE: `new_name` already includes the tool name, so we don't // have to add it again. let CheckLintNameResult::Ok(ids) = - self.store.check_lint_name(replace, None, self.registered_tools) + self.store.check_lint_name(replace, None, self.registered_lint_tools) else { panic!("renamed lint does not exist: {replace}"); }; diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index e3653c55f53a4..30b04fcf7e742 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -160,7 +160,7 @@ impl NonCamelCaseTypes { impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { let has_repr_c = matches!( - AttributeParser::parse_limited(cx.sess(), &it.attrs, &[sym::repr]), + AttributeParser::parse_limited_sym(cx.sess(), &it.attrs, &[sym::repr]), Some(Attribute::Parsed(AttributeKind::Repr { reprs, ..})) if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprC) ); diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index f0557c3d3381a..22c43979e4bdd 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -150,10 +150,16 @@ rustc_queries! { desc { "triggering a delayed bug for testing incremental" } } - /// Collects the list of all tools registered using `#![register_tool]`. - query registered_tools(_: ()) -> &'tcx ty::RegisteredTools { + /// Collects the list of all tools registered using `#![register_tool]` or `#![register_attribute_tool]`. + query registered_attr_tools(_: ()) -> &'tcx ty::RegisteredTools { arena_cache - desc { "compute registered tools for crate" } + desc { "compute registered attribute tools for crate" } + } + + /// Collects the list of all tools registered using `#![register_tool]` or `#![register_lint_tool]`. + query registered_lint_tools(_: ()) -> &'tcx ty::RegisteredTools { + arena_cache + desc { "compute registered lint tools for crate" } } query early_lint_checks(_: ()) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..fdf0911392ab1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -306,7 +306,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::ProfilerRuntime => (), AttributeKind::RecursionLimit { .. } => (), AttributeKind::ReexportTestHarnessMain(..) => (), - AttributeKind::RegisterTool(..) => (), + AttributeKind::RegisterTool { .. } => (), // handled below this loop and elsewhere AttributeKind::Repr { .. } => (), AttributeKind::RustcAbi { .. } => (), diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 493abd74cbfb8..459e6da7d961b 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -16,7 +16,7 @@ use crate::diagnostics::DebugVisualizerUnreadable; impl DebuggerVisualizerCollector<'_> { fn check_for_debugger_visualizer(&mut self, attrs: &[ast::Attribute]) { if let Some(Attribute::Parsed(AttributeKind::DebuggerVisualizer(visualizers))) = - AttributeParser::parse_limited(&self.sess, attrs, &[sym::debugger_visualizer]) + AttributeParser::parse_limited_sym(&self.sess, attrs, &[sym::debugger_visualizer]) { for DebugVisualizer { span, visualizer_type, path } in visualizers { let file = match resolve_path(&self.sess, path.as_str(), span) { diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c35dc02fb1de1..b0f897470d2d2 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1139,7 +1139,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { let mut import_all = None; let mut single_imports = ThinVec::new(); if let Some(Attribute::Parsed(AttributeKind::MacroUse { span, arguments })) = - AttributeParser::parse_limited(self.r.tcx.sess, &item.attrs, &[sym::macro_use]) + AttributeParser::parse_limited_sym(self.r.tcx.sess, &item.attrs, &[sym::macro_use]) { if self.parent_scope.module.expect_local().parent.is_some() { self.r.dcx().emit_err(diagnostics::ExternCrateLoadingMacroNotAtCrateRoot { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index de3f8c380fc44..540c1949193a5 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -180,7 +180,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let mut parser = AttributeParser::new( &self.r.tcx.sess, self.r.features, - self.r.tcx().registered_tools(()), + self.r.tcx().registered_attr_tools(()), ShouldEmit::Nothing, ); let attrs = parser.parse_attribute_list( diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 2baf423e296d6..534cdbeb0194e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1215,16 +1215,6 @@ pub(crate) struct CannotFindBuiltinMacroWithName { pub(crate) ident: Ident, } -#[derive(Diagnostic)] -#[diag("tool `{$tool}` was already registered")] -pub(crate) struct ToolWasAlreadyRegistered { - #[primary_span] - pub(crate) span: Span, - pub(crate) tool: Ident, - #[label("already registered here")] - pub(crate) old_ident_span: Span, -} - #[derive(Subdiagnostic)] pub(crate) enum DefinedHere { #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] diff --git a/compiler/rustc_resolve/src/error_helper.rs b/compiler/rustc_resolve/src/error_helper.rs index 8ab193afe0eee..e9c9e9a8edaaf 100644 --- a/compiler/rustc_resolve/src/error_helper.rs +++ b/compiler/rustc_resolve/src/error_helper.rs @@ -1528,10 +1528,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { })); } Scope::ExternPreludeFlags => {} - Scope::ToolPrelude => { + Scope::ToolAttributePrelude => { let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); suggestions.extend( - this.registered_tools + this.registered_attr_tools .iter() .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), ); @@ -4148,7 +4148,7 @@ impl OnUnknownData { ) -> Option { if r.features.diagnostic_on_unknown() && let Some(Attribute::Parsed(AttributeKind::OnUnknown { directive, .. })) = - AttributeParser::parse_limited( + AttributeParser::parse_limited_sym( r.tcx.sess, attrs, &[sym::diagnostic, sym::on_unknown], diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 12dd05bfe6e60..3425f1608388a 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -157,7 +157,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::ExternPreludeItems | Scope::ExternPreludeFlags => { use_prelude || module_and_extern_prelude || extern_prelude } - Scope::ToolPrelude => use_prelude, + Scope::ToolAttributePrelude => use_prelude, Scope::StdLibPrelude => use_prelude || ns == MacroNS, Scope::BuiltinTypes => true, }; @@ -223,8 +223,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::BuiltinAttrs => break, // nowhere else to search Scope::ExternPreludeItems => Scope::ExternPreludeFlags, Scope::ExternPreludeFlags if module_and_extern_prelude || extern_prelude => break, - Scope::ExternPreludeFlags => Scope::ToolPrelude, - Scope::ToolPrelude => Scope::StdLibPrelude, + Scope::ExternPreludeFlags => Scope::ToolAttributePrelude, + Scope::ToolAttributePrelude => Scope::StdLibPrelude, Scope::StdLibPrelude => match ns { TypeNS => Scope::BuiltinTypes, ValueNS => break, // nowhere else to search @@ -736,7 +736,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => Err(Determinacy::Determined), } } - Scope::ToolPrelude => match self.registered_tool_decls.get(&ident) { + Scope::ToolAttributePrelude => match self.registered_attr_tool_decls.get(&ident) { Some(decl) => Ok(*decl), None => Err(Determinacy::Determined), }, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 911350665ed32..e23b2f04f43dc 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -36,7 +36,7 @@ use late::{ ForwardGenericParamBanReason, HasGenericParams, PathSource, PatternSource, UnnecessaryQualification, }; -pub use macros::registered_tools_ast; +pub use macros::registered_lint_tools_ast; use macros::{MacroRulesDecl, MacroRulesScope, MacroRulesScopeRef}; use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::node_id::NodeMap; @@ -134,8 +134,8 @@ enum Scope<'ra> { ExternPreludeItems, /// Extern prelude names introduced by `--extern` flags. ExternPreludeFlags, - /// Tool modules introduced with `#![register_tool]`. - ToolPrelude, + /// Tool modules introduced with `#![register_tool]` or `#![register_attribute_tool]`. + ToolAttributePrelude, /// Standard library prelude introduced with an internal `#[prelude_import]` import. StdLibPrelude, /// Built-in types. @@ -1392,10 +1392,11 @@ pub struct Resolver<'ra, 'tcx> { dummy_decl: Decl<'ra>, builtin_type_decls: FxHashMap>, builtin_attr_decls: FxHashMap>, - registered_tool_decls: FxHashMap>, + registered_attr_tool_decls: FxHashMap>, macro_names: FxHashSet = default::fx_hash_set(), builtin_macros: FxHashMap = default::fx_hash_map(), - registered_tools: &'tcx RegisteredTools, + registered_attr_tools: &'tcx RegisteredTools, + registered_lint_tools: &'tcx RegisteredTools, macro_use_prelude: FxIndexMap>, /// Eagerly populated map of all local macro definitions. local_macro_map: FxHashMap> = default::fx_hash_map(), @@ -1777,7 +1778,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { invocation_parents.insert(LocalExpnId::ROOT, InvocationParent::ROOT); let extern_prelude = build_extern_prelude(tcx, attrs); - let registered_tools = tcx.registered_tools(()); + let registered_attr_tools = tcx.registered_attr_tools(()); + let registered_lint_tools = tcx.registered_lint_tools(()); let edition = tcx.sess.edition(); let mut resolver = Resolver { @@ -1815,7 +1817,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (*builtin_attr, decl) }) .collect(), - registered_tool_decls: registered_tools + registered_attr_tool_decls: registered_attr_tools .iter() .map(|&ident| { let res = Res::ToolMod; @@ -1823,7 +1825,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (IdentKey::new(ident), decl) }) .collect(), - registered_tools, + registered_attr_tools, + registered_lint_tools, macro_use_prelude: Default::default(), extern_macro_map: Default::default(), dummy_ext_bang: arenas.alloc_macro(SyntaxExtension::dummy_bang(edition)), @@ -2104,7 +2107,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Scope::ExternPreludeItems | Scope::ExternPreludeFlags - | Scope::ToolPrelude + | Scope::ToolAttributePrelude | Scope::BuiltinTypes => {} _ => unreachable!(), } @@ -2790,7 +2793,8 @@ impl Finalize { } pub fn provide(providers: &mut Providers) { - providers.registered_tools = macros::registered_tools; + providers.registered_attr_tools = macros::registered_attr_tools; + providers.registered_lint_tools = macros::registered_lint_tools; } /// A wrapper around `&mut Resolver` that may be mutable or immutable, depending on a conditions. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 595c2fdf011dd..72bbfb4b8cd6a 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use rustc_ast::{self as ast, Crate, DelegationSuffixes, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; -use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; +use rustc_errors::{Applicability, StashKey}; use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind, @@ -121,37 +121,52 @@ fn fast_print_path(path: &ast::Path) -> Symbol { } } -pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { +const PREDEFINED_TOOLS: &[Symbol] = + &[sym::clippy, sym::rustfmt, sym::diagnostic, sym::miri, sym::rust_analyzer]; + +pub(crate) fn registered_attr_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { + let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow(); + + let mut registered_tools = + if let Some(Attribute::Parsed(AttributeKind::RegisterTool { attr_tools, .. })) = + AttributeParser::parse_limited(tcx.sess, pre_configured_attrs, &|attr| { + attr.path_matches(&[sym::register_tool]) + || attr.path_matches(&[sym::register_attribute_tool]) + }) + { + attr_tools + } else { + Default::default() + }; + + // Implicitly add predefined tools. + registered_tools.extend(PREDEFINED_TOOLS.iter().cloned().map(Ident::with_dummy_span)); + registered_tools +} + +pub(crate) fn registered_lint_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow(); - registered_tools_ast(tcx.dcx(), pre_configured_attrs, tcx.sess) + registered_lint_tools_ast(tcx.sess, pre_configured_attrs) } -pub fn registered_tools_ast( - dcx: DiagCtxtHandle<'_>, - pre_configured_attrs: &[ast::Attribute], +pub fn registered_lint_tools_ast( sess: &Session, + pre_configured_attrs: &[ast::Attribute], ) -> RegisteredTools { - let mut registered_tools = RegisteredTools::default(); - - if let Some(Attribute::Parsed(AttributeKind::RegisterTool(tools))) = - AttributeParser::parse_limited(sess, pre_configured_attrs, &[sym::register_tool]) - { - for tool in tools { - if let Some(old_tool) = registered_tools.replace(tool) { - dcx.emit_err(diagnostics::ToolWasAlreadyRegistered { - span: tool.span, - tool, - old_ident_span: old_tool.span, - }); - } - } - } + let mut registered_tools = + if let Some(Attribute::Parsed(AttributeKind::RegisterTool { lint_tools, .. })) = + AttributeParser::parse_limited(sess, pre_configured_attrs, &|attr| { + attr.path_matches(&[sym::register_tool]) + || attr.path_matches(&[sym::register_lint_tool]) + }) + { + lint_tools + } else { + Default::default() + }; - // We implicitly add `rustfmt`, `clippy`, `diagnostic`, `miri` and `rust_analyzer` to known - // tools, but it's not an error to register them explicitly. - let predefined_tools = - [sym::clippy, sym::rustfmt, sym::diagnostic, sym::miri, sym::rust_analyzer]; - registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span)); + // Implicitly add predefined tools. + registered_tools.extend(PREDEFINED_TOOLS.iter().cloned().map(Ident::with_dummy_span)); registered_tools } @@ -518,8 +533,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { }); } - fn registered_tools(&self) -> &RegisteredTools { - self.registered_tools + fn registered_attr_tools(&self) -> &RegisteredTools { + self.registered_attr_tools + } + + fn registered_lint_tools(&self) -> &RegisteredTools { + self.registered_lint_tools } fn register_glob_delegation(&mut self, invoc_id: LocalExpnId) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index db2804a3f83bf..64be7aa237fc8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1690,6 +1690,8 @@ symbols! { reg_ptr, reg_upper, register_attr, + register_attribute_tool, + register_lint_tool, register_tool, relaxed_adts, relaxed_struct_unsize, diff --git a/tests/ui/tool-attributes/cfg-register.rs b/tests/ui/tool-attributes/cfg-register.rs new file mode 100644 index 0000000000000..c835e6bd54740 --- /dev/null +++ b/tests/ui/tool-attributes/cfg-register.rs @@ -0,0 +1,12 @@ +//@ check-pass +// +//@revisions: tool split_attr_lint + +#![feature(register_tool)] +#![cfg_attr(tool, register_tool(foo, bar))] +#![cfg_attr(split_attr_lint, register_attribute_tool(foo))] +#![cfg_attr(split_attr_lint, register_lint_tool(bar))] + +#[foo::a] +#[allow(bar::b)] +fn main() {} diff --git a/tests/ui/tool-attributes/duplicate-tool.rs b/tests/ui/tool-attributes/duplicate-tool.rs new file mode 100644 index 0000000000000..603292d1eb3cd --- /dev/null +++ b/tests/ui/tool-attributes/duplicate-tool.rs @@ -0,0 +1,15 @@ +//@ check-pass +#![feature(register_tool)] +// Register a tool multiple times is okay. +#![register_tool(foo)] +#![register_tool(foo)] +#![register_tool(bar)] +#![register_attribute_tool(bar)] +#![register_tool(baz)] +#![register_lint_tool(baz)] +#![register_attribute_tool(qux)] +#![register_attribute_tool(qux)] +#![register_lint_tool(quux)] +#![register_lint_tool(quux)] + +fn main() {} diff --git a/tests/ui/tool-attributes/predefined-tool.rs b/tests/ui/tool-attributes/predefined-tool.rs new file mode 100644 index 0000000000000..0b003b46ae3d5 --- /dev/null +++ b/tests/ui/tool-attributes/predefined-tool.rs @@ -0,0 +1,10 @@ +#![feature(register_tool)] +// Register predefined tool or "rustc" is error. +#![register_tool(clippy)] //~ ERROR predefined +#![register_attribute_tool(miri)] //~ ERROR predefined +#![register_lint_tool(rustfmt)] //~ ERROR predefined +#![register_tool(diagnostic)] //~ ERROR predefined +#![register_attribute_tool(rust_analyzer)] //~ ERROR predefined +#![register_tool(rustc)] //~ ERROR reserved + +fn main() {} diff --git a/tests/ui/tool-attributes/predefined-tool.stderr b/tests/ui/tool-attributes/predefined-tool.stderr new file mode 100644 index 0000000000000..6615e9d23c235 --- /dev/null +++ b/tests/ui/tool-attributes/predefined-tool.stderr @@ -0,0 +1,38 @@ +error: tool `clippy` is predefined and cannot be registered + --> $DIR/predefined-tool.rs:3:18 + | +LL | #![register_tool(clippy)] + | ^^^^^^ + +error: tool `miri` is predefined and cannot be registered + --> $DIR/predefined-tool.rs:4:28 + | +LL | #![register_attribute_tool(miri)] + | ^^^^ + +error: tool `rustfmt` is predefined and cannot be registered + --> $DIR/predefined-tool.rs:5:23 + | +LL | #![register_lint_tool(rustfmt)] + | ^^^^^^^ + +error: tool `diagnostic` is predefined and cannot be registered + --> $DIR/predefined-tool.rs:6:18 + | +LL | #![register_tool(diagnostic)] + | ^^^^^^^^^^ + +error: tool `rust_analyzer` is predefined and cannot be registered + --> $DIR/predefined-tool.rs:7:28 + | +LL | #![register_attribute_tool(rust_analyzer)] + | ^^^^^^^^^^^^^ + +error: tool `rustc` is reserved and cannot be registered + --> $DIR/predefined-tool.rs:8:18 + | +LL | #![register_tool(rustc)] + | ^^^^^ + +error: aborting due to 6 previous errors +