Rollup merge of #152988 - JonathanBrouwer:register-tool, r=jdonszelmann

Port `#[register_tool]` to the new attribute system

For https://github.com/rust-lang/rust/issues/131229#issuecomment-2971351163
Rebase of https://github.com/rust-lang/rust/pull/146702

r? @jdonszelmann
This commit is contained in:
Jonathan Brouwer
2026-02-24 14:41:56 +01:00
committed by GitHub
12 changed files with 100 additions and 42 deletions
@@ -347,3 +347,50 @@ fn extend(
res
}
}
pub(crate) struct RegisterToolParser;
impl<S: Stage> CombineAttributeParser<S> for RegisterToolParser {
const PATH: &[Symbol] = &[sym::register_tool];
type Item = Ident;
const CONVERT: ConvertFn<Self::Item> = 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<Item = Self::Item> {
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
}
}
@@ -164,6 +164,7 @@ mod late {
Combine<FeatureParser>,
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
Combine<RegisterToolParser>,
Combine<ReprParser>,
Combine<RustcCleanParser>,
Combine<RustcLayoutParser>,
+2 -1
View File
@@ -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,
@@ -1154,6 +1154,9 @@ pub enum AttributeKind {
/// Represents `#[reexport_test_harness_main]`
ReexportTestHarnessMain(Symbol),
/// Represents `#[register_tool]`
RegisterTool(ThinVec<Ident>, Span),
/// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
Repr {
reprs: ThinVec<(ReprAttr, Span)>,
@@ -94,6 +94,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate {
ProfilerRuntime => No,
RecursionLimit { .. } => No,
ReexportTestHarnessMain(..) => No,
RegisterTool(..) => No,
Repr { .. } => No,
RustcAbi { .. } => No,
RustcAllocator => No,
+2 -2
View File
@@ -292,6 +292,7 @@ fn check_attributes(
| AttributeKind::ProfilerRuntime
| AttributeKind::RecursionLimit { .. }
| AttributeKind::ReexportTestHarnessMain(..)
| AttributeKind::RegisterTool(..)
// handled below this loop and elsewhere
| AttributeKind::Repr { .. }
| AttributeKind::RustcAbi { .. }
@@ -404,8 +405,7 @@ fn check_attributes(
| sym::rustc_layout
| sym::rustc_autodiff
// crate-level attrs, are checked below
| sym::feature
| sym::register_tool,
| sym::feature,
..
] => {}
[name, rest@..] => {
-9
View File
@@ -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")]
+28 -22
View File
@@ -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::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 =
+1 -1
View File
@@ -1,6 +1,6 @@
#![feature(register_tool)]
#![register_tool(1)]
//~^ ERROR `register_tool` only accepts identifiers
//~^ ERROR malformed `register_tool` attribute input
fn main() {}
+7 -3
View File
@@ -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`.
@@ -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() {}
@@ -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`.