Skip to content

Commit 3a223a9

Browse files
committed
Support registering attributes and attribute tools using crate-level attributes
1 parent 5a50275 commit 3a223a9

File tree

14 files changed

+210
-20
lines changed

14 files changed

+210
-20
lines changed

src/librustc/hir/def.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub enum NonMacroAttrKind {
4040
Tool,
4141
/// Single-segment custom attribute registered by a derive macro (`#[serde(default)]`).
4242
DeriveHelper,
43+
/// Single-segment custom attribute registered with `#[register_attr]`.
44+
Registered,
4345
/// Single-segment custom attribute registered by a legacy plugin (`register_attribute`).
4446
LegacyPluginHelper,
4547
/// Single-segment custom attribute not registered in any way (`#[my_attr]`).
@@ -329,6 +331,7 @@ impl NonMacroAttrKind {
329331
NonMacroAttrKind::Builtin => "built-in attribute",
330332
NonMacroAttrKind::Tool => "tool attribute",
331333
NonMacroAttrKind::DeriveHelper => "derive helper attribute",
334+
NonMacroAttrKind::Registered => "explicitly registered attribute",
332335
NonMacroAttrKind::LegacyPluginHelper => "legacy plugin helper attribute",
333336
NonMacroAttrKind::Custom => "custom attribute",
334337
}

src/librustc_resolve/diagnostics.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use syntax_pos::hygiene::MacroKind;
1919
use syntax_pos::{BytePos, Span, MultiSpan};
2020

2121
use crate::resolve_imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
22-
use crate::{path_names_to_string, KNOWN_TOOLS};
22+
use crate::path_names_to_string;
2323
use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot};
2424
use crate::{PathResult, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Segment};
2525

@@ -400,6 +400,14 @@ impl<'a> Resolver<'a> {
400400
Scope::Module(module) => {
401401
this.add_module_candidates(module, &mut suggestions, filter_fn);
402402
}
403+
Scope::RegisteredAttrs => {
404+
let res = Res::NonMacroAttr(NonMacroAttrKind::Registered);
405+
if filter_fn(res) {
406+
suggestions.extend(this.registered_attrs.iter().map(|ident| {
407+
TypoSuggestion::from_res(ident.name, res)
408+
}));
409+
}
410+
}
403411
Scope::MacroUsePrelude => {
404412
suggestions.extend(this.macro_use_prelude.iter().filter_map(|(name, binding)| {
405413
let res = binding.res();
@@ -439,8 +447,8 @@ impl<'a> Resolver<'a> {
439447
}
440448
Scope::ToolPrelude => {
441449
let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);
442-
suggestions.extend(KNOWN_TOOLS.iter().map(|name| {
443-
TypoSuggestion::from_res(*name, res)
450+
suggestions.extend(this.registered_tools.iter().map(|ident| {
451+
TypoSuggestion::from_res(ident.name, res)
444452
}));
445453
}
446454
Scope::StdLibPrelude => {

src/librustc_resolve/lib.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ mod check_unused;
7474
mod build_reduced_graph;
7575
mod resolve_imports;
7676

77-
const KNOWN_TOOLS: &[Name] = &[sym::clippy, sym::rustfmt];
78-
7977
enum Weak {
8078
Yes,
8179
No,
@@ -102,6 +100,7 @@ enum Scope<'a> {
102100
MacroRules(LegacyScope<'a>),
103101
CrateRoot,
104102
Module(Module<'a>),
103+
RegisteredAttrs,
105104
MacroUsePrelude,
106105
BuiltinAttrs,
107106
LegacyPluginHelpers,
@@ -916,6 +915,8 @@ pub struct Resolver<'a> {
916915
crate_loader: CrateLoader<'a>,
917916
macro_names: FxHashSet<Ident>,
918917
builtin_macros: FxHashMap<Name, SyntaxExtension>,
918+
registered_attrs: FxHashSet<Ident>,
919+
registered_tools: FxHashSet<Ident>,
919920
macro_use_prelude: FxHashMap<Name, &'a NameBinding<'a>>,
920921
all_macros: FxHashMap<Name, Res>,
921922
macro_map: FxHashMap<DefId, Lrc<SyntaxExtension>>,
@@ -1132,6 +1133,9 @@ impl<'a> Resolver<'a> {
11321133
}
11331134
}
11341135

1136+
let (registered_attrs, registered_tools) =
1137+
macros::registered_attrs_and_tools(session, &krate.attrs);
1138+
11351139
let mut invocation_parent_scopes = FxHashMap::default();
11361140
invocation_parent_scopes.insert(ExpnId::root(), ParentScope::module(graph_root));
11371141

@@ -1201,6 +1205,8 @@ impl<'a> Resolver<'a> {
12011205
crate_loader: CrateLoader::new(session, metadata_loader, crate_name),
12021206
macro_names: FxHashSet::default(),
12031207
builtin_macros: Default::default(),
1208+
registered_attrs,
1209+
registered_tools,
12041210
macro_use_prelude: FxHashMap::default(),
12051211
all_macros: FxHashMap::default(),
12061212
macro_map: FxHashMap::default(),
@@ -1469,6 +1475,7 @@ impl<'a> Resolver<'a> {
14691475
Scope::MacroRules(..) => true,
14701476
Scope::CrateRoot => true,
14711477
Scope::Module(..) => true,
1478+
Scope::RegisteredAttrs => true,
14721479
Scope::MacroUsePrelude => use_prelude || rust_2015,
14731480
Scope::BuiltinAttrs => true,
14741481
Scope::LegacyPluginHelpers => use_prelude || rust_2015,
@@ -1513,11 +1520,12 @@ impl<'a> Resolver<'a> {
15131520
match ns {
15141521
TypeNS => Scope::ExternPrelude,
15151522
ValueNS => Scope::StdLibPrelude,
1516-
MacroNS => Scope::MacroUsePrelude,
1523+
MacroNS => Scope::RegisteredAttrs,
15171524
}
15181525
}
15191526
}
15201527
}
1528+
Scope::RegisteredAttrs => Scope::MacroUsePrelude,
15211529
Scope::MacroUsePrelude => Scope::StdLibPrelude,
15221530
Scope::BuiltinAttrs => Scope::LegacyPluginHelpers,
15231531
Scope::LegacyPluginHelpers => break, // nowhere else to search
@@ -1673,11 +1681,11 @@ impl<'a> Resolver<'a> {
16731681
if let Some(binding) = self.extern_prelude_get(ident, !record_used) {
16741682
return Some(LexicalScopeBinding::Item(binding));
16751683
}
1676-
}
1677-
if ns == TypeNS && KNOWN_TOOLS.contains(&ident.name) {
1678-
let binding = (Res::ToolMod, ty::Visibility::Public,
1679-
DUMMY_SP, ExpnId::root()).to_name_binding(self.arenas);
1680-
return Some(LexicalScopeBinding::Item(binding));
1684+
if let Some(ident) = self.registered_tools.get(&ident) {
1685+
let binding = (Res::ToolMod, ty::Visibility::Public,
1686+
ident.span, ExpnId::root()).to_name_binding(self.arenas);
1687+
return Some(LexicalScopeBinding::Item(binding));
1688+
}
16811689
}
16821690
if let Some(prelude) = self.prelude {
16831691
if let Ok(binding) = self.resolve_ident_in_module_unadjusted(

src/librustc_resolve/macros.rs

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
44
use crate::{AmbiguityError, AmbiguityKind, AmbiguityErrorMisc, Determinacy};
55
use crate::{CrateLint, Resolver, ResolutionError, Scope, ScopeSet, ParentScope, Weak};
6-
use crate::{ModuleKind, NameBinding, PathResult, Segment, ToNameBinding};
7-
use crate::{ModuleOrUniformRoot, KNOWN_TOOLS};
6+
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
87
use crate::Namespace::*;
98
use crate::resolve_imports::ImportResolver;
109
use rustc::hir::def::{self, DefKind, NonMacroAttrKind};
1110
use rustc::hir::def_id;
1211
use rustc::middle::stability;
12+
use rustc::session::Session;
13+
use rustc::util::nodemap::FxHashSet;
1314
use rustc::{ty, lint, span_bug};
1415
use syntax::ast::{self, NodeId, Ident};
15-
use syntax::attr::StabilityLevel;
16+
use syntax::attr::{self, StabilityLevel};
1617
use syntax::edition::Edition;
1718
use syntax::feature_gate::{emit_feature_err, is_builtin_attr_name};
1819
use syntax::feature_gate::GateIssue;
@@ -93,6 +94,45 @@ fn fast_print_path(path: &ast::Path) -> Symbol {
9394
}
9495
}
9596

97+
fn registered_idents(
98+
sess: &Session,
99+
attrs: &[ast::Attribute],
100+
attr_name: Symbol,
101+
descr: &str,
102+
) -> FxHashSet<Ident> {
103+
let mut registered = FxHashSet::default();
104+
for attr in attr::filter_by_name(attrs, attr_name) {
105+
for nested_meta in attr.meta_item_list().unwrap_or_default() {
106+
match nested_meta.ident() {
107+
Some(ident) => if let Some(old_ident) = registered.replace(ident) {
108+
let msg = format!("{} `{}` was already registered", descr, ident);
109+
sess.struct_span_err(ident.span, &msg)
110+
.span_label(old_ident.span, "already registered here").emit();
111+
}
112+
None => {
113+
let msg = format!("`{}` only accepts identifiers", attr_name);
114+
let span = nested_meta.span();
115+
sess.struct_span_err(span, &msg).span_label(span, "not an identifier").emit();
116+
}
117+
}
118+
}
119+
}
120+
registered
121+
}
122+
123+
crate fn registered_attrs_and_tools(
124+
sess: &Session,
125+
attrs: &[ast::Attribute],
126+
) -> (FxHashSet<Ident>, FxHashSet<Ident>) {
127+
let registered_attrs = registered_idents(sess, attrs, sym::register_attr, "attribute");
128+
let mut registered_tools = registered_idents(sess, attrs, sym::register_tool, "tool");
129+
// We implicitly add `rustfmt` and `clippy` to known tools,
130+
// but it's not an error to register them explicitly.
131+
let predefined_tools = [sym::clippy, sym::rustfmt];
132+
registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span));
133+
(registered_attrs, registered_tools)
134+
}
135+
96136
impl<'a> base::Resolver for Resolver<'a> {
97137
fn next_node_id(&mut self) -> NodeId {
98138
self.session.next_node_id()
@@ -531,6 +571,15 @@ impl<'a> Resolver<'a> {
531571
Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
532572
}
533573
}
574+
Scope::RegisteredAttrs => match this.registered_attrs.get(&ident).cloned() {
575+
Some(ident) => {
576+
let binding = (Res::NonMacroAttr(NonMacroAttrKind::Registered),
577+
ty::Visibility::Public, ident.span, ExpnId::root())
578+
.to_name_binding(this.arenas);
579+
Ok((binding, Flags::PRELUDE))
580+
}
581+
None => Err(Determinacy::Determined)
582+
}
534583
Scope::MacroUsePrelude => match this.macro_use_prelude.get(&ident.name).cloned() {
535584
Some(binding) => Ok((binding, Flags::PRELUDE | Flags::MISC_FROM_PRELUDE)),
536585
None => Err(Determinacy::determined(
@@ -560,12 +609,14 @@ impl<'a> Resolver<'a> {
560609
this.graph_root.unexpanded_invocations.borrow().is_empty()
561610
)),
562611
}
563-
Scope::ToolPrelude => if KNOWN_TOOLS.contains(&ident.name) {
564-
let binding = (Res::ToolMod, ty::Visibility::Public, DUMMY_SP, ExpnId::root())
565-
.to_name_binding(this.arenas);
566-
Ok((binding, Flags::PRELUDE))
567-
} else {
568-
Err(Determinacy::Determined)
612+
Scope::ToolPrelude => match this.registered_tools.get(&ident).cloned() {
613+
Some(ident) => {
614+
let binding = (Res::ToolMod,
615+
ty::Visibility::Public, ident.span, ExpnId::root())
616+
.to_name_binding(this.arenas);
617+
Ok((binding, Flags::PRELUDE))
618+
}
619+
None => Err(Determinacy::Determined)
569620
}
570621
Scope::StdLibPrelude => {
571622
let mut result = Err(Determinacy::Determined);

src/libsyntax/feature_gate/active.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,12 @@ declare_features! (
526526
/// Allows using the `efiapi` ABI.
527527
(active, abi_efiapi, "1.40.0", Some(65815), None),
528528

529+
/// Allows using the `#[register_attr]` attribute.
530+
(active, register_attr, "1.41.0", Some(29642), None),
531+
532+
/// Allows using the `#[register_attr]` attribute.
533+
(active, register_tool, "1.41.0", Some(44690), None),
534+
529535
// -------------------------------------------------------------------------
530536
// feature-group-end: actual feature gates
531537
// -------------------------------------------------------------------------

src/libsyntax/feature_gate/builtin_attrs.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
329329

330330
gated!(ffi_returns_twice, Whitelisted, template!(Word), experimental!(ffi_returns_twice)),
331331
gated!(track_caller, Whitelisted, template!(Word), experimental!(track_caller)),
332+
gated!(
333+
register_attr, Whitelisted, template!(List: "attr1, attr2, ..."),
334+
experimental!(register_attr),
335+
),
336+
gated!(
337+
register_tool, Whitelisted, template!(List: "tool1, tool2, ..."),
338+
experimental!(register_tool),
339+
),
332340

333341
// ==========================================================================
334342
// Internal attributes: Stability, deprecation, and unsafe:

src/libsyntax_pos/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ symbols! {
545545
recursion_limit,
546546
reexport_test_harness_main,
547547
reflect,
548+
register_attr,
549+
register_tool,
548550
relaxed_adts,
549551
repr,
550552
repr128,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(register_attr)]
2+
#![feature(register_tool)]
3+
4+
#![register_attr] //~ ERROR malformed `register_attr` attribute input
5+
#![register_tool] //~ ERROR malformed `register_tool` attribute input
6+
7+
#![register_attr(a::b)] //~ ERROR `register_attr` only accepts identifiers
8+
#![register_tool(a::b)] //~ ERROR `register_tool` only accepts identifiers
9+
10+
#![register_attr(attr, attr)] //~ ERROR attribute `attr` was already registered
11+
#![register_tool(tool, tool)] //~ ERROR tool `tool` was already registered
12+
13+
fn main() {}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error: `register_attr` only accepts identifiers
2+
--> $DIR/register-attr-tool-fail.rs:7:18
3+
|
4+
LL | #![register_attr(a::b)]
5+
| ^^^^ not an identifier
6+
7+
error: attribute `attr` was already registered
8+
--> $DIR/register-attr-tool-fail.rs:10:24
9+
|
10+
LL | #![register_attr(attr, attr)]
11+
| ---- ^^^^
12+
| |
13+
| already registered here
14+
15+
error: `register_tool` only accepts identifiers
16+
--> $DIR/register-attr-tool-fail.rs:8:18
17+
|
18+
LL | #![register_tool(a::b)]
19+
| ^^^^ not an identifier
20+
21+
error: tool `tool` was already registered
22+
--> $DIR/register-attr-tool-fail.rs:11:24
23+
|
24+
LL | #![register_tool(tool, tool)]
25+
| ---- ^^^^
26+
| |
27+
| already registered here
28+
29+
error: malformed `register_attr` attribute input
30+
--> $DIR/register-attr-tool-fail.rs:4:1
31+
|
32+
LL | #![register_attr]
33+
| ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[register_attr(attr1, attr2, ...)]`
34+
35+
error: malformed `register_tool` attribute input
36+
--> $DIR/register-attr-tool-fail.rs:5:1
37+
|
38+
LL | #![register_tool]
39+
| ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[register_tool(tool1, tool2, ...)]`
40+
41+
error: aborting due to 6 previous errors
42+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// check-pass
2+
// compile-flags: --cfg foo
3+
4+
#![feature(register_attr)]
5+
#![feature(register_tool)]
6+
7+
#![register_attr(attr)]
8+
#![register_tool(tool)]
9+
#![register_tool(rustfmt, clippy)] // OK
10+
#![cfg_attr(foo, register_attr(conditional_attr))]
11+
#![cfg_attr(foo, register_tool(conditional_tool))]
12+
13+
#[attr]
14+
#[tool::attr]
15+
#[rustfmt::attr]
16+
#[clippy::attr]
17+
#[conditional_attr]
18+
#[conditional_tool::attr]
19+
fn main() {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#![register_attr(attr)] //~ ERROR the `#[register_attr]` attribute is an experimental feature
2+
3+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: the `#[register_attr]` attribute is an experimental feature
2+
--> $DIR/feature-gate-register_attr.rs:1:1
3+
|
4+
LL | #![register_attr(attr)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: for more information, see https://github.com/rust-lang/rust/issues/29642
8+
= help: add `#![feature(register_attr)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#![register_tool(tool)] //~ ERROR the `#[register_tool]` attribute is an experimental feature
2+
3+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0658]: the `#[register_tool]` attribute is an experimental feature
2+
--> $DIR/feature-gate-register_tool.rs:1:1
3+
|
4+
LL | #![register_tool(tool)]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: for more information, see https://github.com/rust-lang/rust/issues/44690
8+
= help: add `#![feature(register_tool)]` to the crate attributes to enable
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)