diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 176af5cdd192e..2d2994c02cd61 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -347,3 +347,50 @@ impl CombineAttributeParser for FeatureParser { res } } + +pub(crate) struct RegisterToolParser; + +impl CombineAttributeParser for RegisterToolParser { + const PATH: &[Symbol] = &[sym::register_tool]; + type Item = Ident; + const CONVERT: ConvertFn = AttributeKind::RegisterTool; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const TEMPLATE: AttributeTemplate = template!(List: &["tool1, tool2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let ArgParser::List(list) = args else { + cx.expected_list(cx.attr_span, args); + return Vec::new(); + }; + + if list.is_empty() { + cx.warn_empty_attribute(cx.attr_span); + } + + let mut res = Vec::new(); + + for elem in list.mixed() { + let Some(elem) = elem.meta_item() else { + cx.expected_identifier(elem.span()); + continue; + }; + if let Err(arg_span) = elem.args().no_args() { + cx.expected_no_args(arg_span); + continue; + } + + let path = elem.path(); + let Some(ident) = path.word() else { + cx.expected_identifier(path.span()); + continue; + }; + + res.push(ident); + } + + res + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 3e5d9dc8fb6f5..cfb9c802a421f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -160,6 +160,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index fc46102f6a30f..38a11b427c1f6 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -707,8 +707,9 @@ fn print_crate_info( }; let crate_name = passes::get_crate_name(sess, attrs); let lint_store = crate::unerased_lint_store(sess); - let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs); let features = rustc_expand::config::features(sess, attrs, crate_name); + let registered_tools = + rustc_resolve::registered_tools_ast(sess.dcx(), attrs, sess, &features); let lint_levels = rustc_lint::LintLevelsBuilder::crate_root( sess, &features, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index e33d943fa5461..fe91ba4b3c247 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1139,6 +1139,9 @@ pub enum AttributeKind { /// Represents `#[reexport_test_harness_main]` ReexportTestHarnessMain(Symbol), + /// Represents `#[register_tool]` + RegisterTool(ThinVec, Span), + /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { reprs: ThinVec<(ReprAttr, Span)>, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 3088d4bc32858..9e1302282c5ba 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -92,6 +92,7 @@ impl AttributeKind { ProfilerRuntime => No, RecursionLimit { .. } => No, ReexportTestHarnessMain(..) => No, + RegisterTool(..) => No, Repr { .. } => No, RustcAbi { .. } => No, RustcAllocator => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index af76ea183c476..4b569b49b9347 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -289,6 +289,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::ProfilerRuntime | AttributeKind::RecursionLimit { .. } | AttributeKind::ReexportTestHarnessMain(..) + | AttributeKind::RegisterTool(..) // handled below this loop and elsewhere | AttributeKind::Repr { .. } | AttributeKind::RustcAbi { .. } @@ -407,8 +408,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_layout | sym::rustc_autodiff // crate-level attrs, are checked below - | sym::feature - | sym::register_tool, + | sym::feature, .. ] => {} [name, rest@..] => { diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 1ca5c17856262..45c1dee9bf52b 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1201,15 +1201,6 @@ pub(crate) struct ToolWasAlreadyRegistered { pub(crate) old_ident_span: Span, } -#[derive(Diagnostic)] -#[diag("`{$tool}` only accepts identifiers")] -pub(crate) struct ToolOnlyAcceptsIdentifiers { - #[primary_span] - #[label("not an identifier")] - pub(crate) span: Span, - pub(crate) tool: Symbol, -} - #[derive(Subdiagnostic)] pub(crate) enum DefinedHere { #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 99cd485241546..551d89ee6022a 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,8 +4,9 @@ use std::mem; use std::sync::Arc; -use rustc_ast::{self as ast, Crate, NodeId, attr}; +use rustc_ast::{self as ast, Crate, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; use rustc_expand::base::{ Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension, @@ -15,12 +16,14 @@ use rustc_expand::compile_declarative_macro; use rustc_expand::expand::{ AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion, }; -use rustc_hir::StabilityLevel; -use rustc_hir::attrs::{CfgEntry, StrippedCfgItem}; +use rustc_feature::Features; +use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; use rustc_hir::def::{self, DefKind, MacroKinds, Namespace, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::{Attribute, StabilityLevel}; use rustc_middle::middle::stability; use rustc_middle::ty::{RegisteredTools, TyCtxt}; +use rustc_session::Session; use rustc_session::lint::builtin::{ LEGACY_DERIVE_HELPERS, OUT_OF_SCOPE_MACRO_CALLS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNUSED_MACRO_RULES, UNUSED_MACROS, @@ -122,35 +125,38 @@ fn fast_print_path(path: &ast::Path) -> Symbol { pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools { let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow(); - registered_tools_ast(tcx.dcx(), pre_configured_attrs) + registered_tools_ast(tcx.dcx(), pre_configured_attrs, tcx.sess, tcx.features()) } pub fn registered_tools_ast( dcx: DiagCtxtHandle<'_>, pre_configured_attrs: &[ast::Attribute], + sess: &Session, + features: &Features, ) -> RegisteredTools { let mut registered_tools = RegisteredTools::default(); - for attr in attr::filter_by_name(pre_configured_attrs, sym::register_tool) { - for meta_item_inner in attr.meta_item_list().unwrap_or_default() { - match meta_item_inner.ident() { - Some(ident) => { - if let Some(old_ident) = registered_tools.replace(ident) { - dcx.emit_err(errors::ToolWasAlreadyRegistered { - span: ident.span, - tool: ident, - old_ident_span: old_ident.span, - }); - } - } - None => { - dcx.emit_err(errors::ToolOnlyAcceptsIdentifiers { - span: meta_item_inner.span(), - tool: sym::register_tool, - }); - } + + if let Some(Attribute::Parsed(AttributeKind::RegisterTool(tools, _))) = + AttributeParser::parse_limited( + sess, + pre_configured_attrs, + sym::register_tool, + DUMMY_SP, + DUMMY_NODE_ID, + Some(features), + ) + { + for tool in tools { + if let Some(old_tool) = registered_tools.replace(tool) { + dcx.emit_err(errors::ToolWasAlreadyRegistered { + span: tool.span, + tool, + old_ident_span: old_tool.span, + }); } } } + // 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 = diff --git a/tests/ui/tool-attributes/invalid-tool.rs b/tests/ui/tool-attributes/invalid-tool.rs index 125333231d217..aec31cc7f667c 100644 --- a/tests/ui/tool-attributes/invalid-tool.rs +++ b/tests/ui/tool-attributes/invalid-tool.rs @@ -1,6 +1,6 @@ #![feature(register_tool)] #![register_tool(1)] -//~^ ERROR `register_tool` only accepts identifiers +//~^ ERROR malformed `register_tool` attribute input fn main() {} diff --git a/tests/ui/tool-attributes/invalid-tool.stderr b/tests/ui/tool-attributes/invalid-tool.stderr index deafa6d167c20..4f82e9ef5437f 100644 --- a/tests/ui/tool-attributes/invalid-tool.stderr +++ b/tests/ui/tool-attributes/invalid-tool.stderr @@ -1,8 +1,12 @@ -error: `register_tool` only accepts identifiers - --> $DIR/invalid-tool.rs:3:18 +error[E0539]: malformed `register_tool` attribute input + --> $DIR/invalid-tool.rs:3:1 | LL | #![register_tool(1)] - | ^ not an identifier + | ^^^^^^^^^^^^^^^^^-^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#![register_tool(tool1, tool2, ...)]` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/tool-attributes/nested-disallowed.rs b/tests/ui/tool-attributes/nested-disallowed.rs index 8e78042776106..87d0bf48b6c8f 100644 --- a/tests/ui/tool-attributes/nested-disallowed.rs +++ b/tests/ui/tool-attributes/nested-disallowed.rs @@ -1,4 +1,4 @@ #![feature(register_tool)] -#![register_tool(foo::bar)] //~ ERROR only accepts identifiers +#![register_tool(foo::bar)] //~ ERROR malformed `register_tool` attribute input fn main() {} diff --git a/tests/ui/tool-attributes/nested-disallowed.stderr b/tests/ui/tool-attributes/nested-disallowed.stderr index 1af73fc2f1995..e59ebd979b4c6 100644 --- a/tests/ui/tool-attributes/nested-disallowed.stderr +++ b/tests/ui/tool-attributes/nested-disallowed.stderr @@ -1,8 +1,12 @@ -error: `register_tool` only accepts identifiers - --> $DIR/nested-disallowed.rs:2:18 +error[E0539]: malformed `register_tool` attribute input + --> $DIR/nested-disallowed.rs:2:1 | LL | #![register_tool(foo::bar)] - | ^^^^^^^^ not an identifier + | ^^^^^^^^^^^^^^^^^--------^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#![register_tool(tool1, tool2, ...)]` error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`.