diff options
| author | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-11-03 20:28:20 +0300 |
|---|---|---|
| committer | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-11-09 17:50:51 +0300 |
| commit | 3a223a917378f439fd9107e26ee7355f5f92c62d (patch) | |
| tree | 973b335a5debf2e4be2791df7af0f85d666bd133 | |
| parent | 5a5027519a4a634baa6cde5b698b907d27fbe6b3 (diff) | |
| download | rust-3a223a917378f439fd9107e26ee7355f5f92c62d.tar.gz rust-3a223a917378f439fd9107e26ee7355f5f92c62d.zip | |
Support registering attributes and attribute tools using crate-level attributes
| -rw-r--r-- | src/librustc/hir/def.rs | 3 | ||||
| -rw-r--r-- | src/librustc_resolve/diagnostics.rs | 14 | ||||
| -rw-r--r-- | src/librustc_resolve/lib.rs | 24 | ||||
| -rw-r--r-- | src/librustc_resolve/macros.rs | 69 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate/active.rs | 6 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate/builtin_attrs.rs | 8 | ||||
| -rw-r--r-- | src/libsyntax_pos/symbol.rs | 2 | ||||
| -rw-r--r-- | src/test/ui/attributes/register-attr-tool-fail.rs | 13 | ||||
| -rw-r--r-- | src/test/ui/attributes/register-attr-tool-fail.stderr | 42 | ||||
| -rw-r--r-- | src/test/ui/attributes/register-attr-tool.rs | 19 | ||||
| -rw-r--r-- | src/test/ui/feature-gates/feature-gate-register_attr.rs | 3 | ||||
| -rw-r--r-- | src/test/ui/feature-gates/feature-gate-register_attr.stderr | 12 | ||||
| -rw-r--r-- | src/test/ui/feature-gates/feature-gate-register_tool.rs | 3 | ||||
| -rw-r--r-- | src/test/ui/feature-gates/feature-gate-register_tool.stderr | 12 |
14 files changed, 210 insertions, 20 deletions
diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index d4d7af92fe3..486d6dfb095 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -40,6 +40,8 @@ pub enum NonMacroAttrKind { Tool, /// Single-segment custom attribute registered by a derive macro (`#[serde(default)]`). DeriveHelper, + /// Single-segment custom attribute registered with `#[register_attr]`. + Registered, /// Single-segment custom attribute registered by a legacy plugin (`register_attribute`). LegacyPluginHelper, /// Single-segment custom attribute not registered in any way (`#[my_attr]`). @@ -329,6 +331,7 @@ impl NonMacroAttrKind { NonMacroAttrKind::Builtin => "built-in attribute", NonMacroAttrKind::Tool => "tool attribute", NonMacroAttrKind::DeriveHelper => "derive helper attribute", + NonMacroAttrKind::Registered => "explicitly registered attribute", NonMacroAttrKind::LegacyPluginHelper => "legacy plugin helper attribute", NonMacroAttrKind::Custom => "custom attribute", } diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index 3d68b72a655..771519b86c1 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -19,7 +19,7 @@ use syntax_pos::hygiene::MacroKind; use syntax_pos::{BytePos, Span, MultiSpan}; use crate::resolve_imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver}; -use crate::{path_names_to_string, KNOWN_TOOLS}; +use crate::path_names_to_string; use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot}; use crate::{PathResult, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Segment}; @@ -400,6 +400,14 @@ impl<'a> Resolver<'a> { Scope::Module(module) => { this.add_module_candidates(module, &mut suggestions, filter_fn); } + Scope::RegisteredAttrs => { + let res = Res::NonMacroAttr(NonMacroAttrKind::Registered); + if filter_fn(res) { + suggestions.extend(this.registered_attrs.iter().map(|ident| { + TypoSuggestion::from_res(ident.name, res) + })); + } + } Scope::MacroUsePrelude => { suggestions.extend(this.macro_use_prelude.iter().filter_map(|(name, binding)| { let res = binding.res(); @@ -439,8 +447,8 @@ impl<'a> Resolver<'a> { } Scope::ToolPrelude => { let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); - suggestions.extend(KNOWN_TOOLS.iter().map(|name| { - TypoSuggestion::from_res(*name, res) + suggestions.extend(this.registered_tools.iter().map(|ident| { + TypoSuggestion::from_res(ident.name, res) })); } Scope::StdLibPrelude => { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5c996bffb9a..6aefacef92c 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -74,8 +74,6 @@ mod check_unused; mod build_reduced_graph; mod resolve_imports; -const KNOWN_TOOLS: &[Name] = &[sym::clippy, sym::rustfmt]; - enum Weak { Yes, No, @@ -102,6 +100,7 @@ enum Scope<'a> { MacroRules(LegacyScope<'a>), CrateRoot, Module(Module<'a>), + RegisteredAttrs, MacroUsePrelude, BuiltinAttrs, LegacyPluginHelpers, @@ -916,6 +915,8 @@ pub struct Resolver<'a> { crate_loader: CrateLoader<'a>, macro_names: FxHashSet<Ident>, builtin_macros: FxHashMap<Name, SyntaxExtension>, + registered_attrs: FxHashSet<Ident>, + registered_tools: FxHashSet<Ident>, macro_use_prelude: FxHashMap<Name, &'a NameBinding<'a>>, all_macros: FxHashMap<Name, Res>, macro_map: FxHashMap<DefId, Lrc<SyntaxExtension>>, @@ -1132,6 +1133,9 @@ impl<'a> Resolver<'a> { } } + let (registered_attrs, registered_tools) = + macros::registered_attrs_and_tools(session, &krate.attrs); + let mut invocation_parent_scopes = FxHashMap::default(); invocation_parent_scopes.insert(ExpnId::root(), ParentScope::module(graph_root)); @@ -1201,6 +1205,8 @@ impl<'a> Resolver<'a> { crate_loader: CrateLoader::new(session, metadata_loader, crate_name), macro_names: FxHashSet::default(), builtin_macros: Default::default(), + registered_attrs, + registered_tools, macro_use_prelude: FxHashMap::default(), all_macros: FxHashMap::default(), macro_map: FxHashMap::default(), @@ -1469,6 +1475,7 @@ impl<'a> Resolver<'a> { Scope::MacroRules(..) => true, Scope::CrateRoot => true, Scope::Module(..) => true, + Scope::RegisteredAttrs => true, Scope::MacroUsePrelude => use_prelude || rust_2015, Scope::BuiltinAttrs => true, Scope::LegacyPluginHelpers => use_prelude || rust_2015, @@ -1513,11 +1520,12 @@ impl<'a> Resolver<'a> { match ns { TypeNS => Scope::ExternPrelude, ValueNS => Scope::StdLibPrelude, - MacroNS => Scope::MacroUsePrelude, + MacroNS => Scope::RegisteredAttrs, } } } } + Scope::RegisteredAttrs => Scope::MacroUsePrelude, Scope::MacroUsePrelude => Scope::StdLibPrelude, Scope::BuiltinAttrs => Scope::LegacyPluginHelpers, Scope::LegacyPluginHelpers => break, // nowhere else to search @@ -1673,11 +1681,11 @@ impl<'a> Resolver<'a> { if let Some(binding) = self.extern_prelude_get(ident, !record_used) { return Some(LexicalScopeBinding::Item(binding)); } - } - if ns == TypeNS && KNOWN_TOOLS.contains(&ident.name) { - let binding = (Res::ToolMod, ty::Visibility::Public, - DUMMY_SP, ExpnId::root()).to_name_binding(self.arenas); - return Some(LexicalScopeBinding::Item(binding)); + if let Some(ident) = self.registered_tools.get(&ident) { + let binding = (Res::ToolMod, ty::Visibility::Public, + ident.span, ExpnId::root()).to_name_binding(self.arenas); + return Some(LexicalScopeBinding::Item(binding)); + } } if let Some(prelude) = self.prelude { if let Ok(binding) = self.resolve_ident_in_module_unadjusted( diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 3d5a7f26eda..9d8d035ab70 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -3,16 +3,17 @@ use crate::{AmbiguityError, AmbiguityKind, AmbiguityErrorMisc, Determinacy}; use crate::{CrateLint, Resolver, ResolutionError, Scope, ScopeSet, ParentScope, Weak}; -use crate::{ModuleKind, NameBinding, PathResult, Segment, ToNameBinding}; -use crate::{ModuleOrUniformRoot, KNOWN_TOOLS}; +use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding}; use crate::Namespace::*; use crate::resolve_imports::ImportResolver; use rustc::hir::def::{self, DefKind, NonMacroAttrKind}; use rustc::hir::def_id; use rustc::middle::stability; +use rustc::session::Session; +use rustc::util::nodemap::FxHashSet; use rustc::{ty, lint, span_bug}; use syntax::ast::{self, NodeId, Ident}; -use syntax::attr::StabilityLevel; +use syntax::attr::{self, StabilityLevel}; use syntax::edition::Edition; use syntax::feature_gate::{emit_feature_err, is_builtin_attr_name}; use syntax::feature_gate::GateIssue; @@ -93,6 +94,45 @@ fn fast_print_path(path: &ast::Path) -> Symbol { } } +fn registered_idents( + sess: &Session, + attrs: &[ast::Attribute], + attr_name: Symbol, + descr: &str, +) -> FxHashSet<Ident> { + let mut registered = FxHashSet::default(); + for attr in attr::filter_by_name(attrs, attr_name) { + for nested_meta in attr.meta_item_list().unwrap_or_default() { + match nested_meta.ident() { + Some(ident) => if let Some(old_ident) = registered.replace(ident) { + let msg = format!("{} `{}` was already registered", descr, ident); + sess.struct_span_err(ident.span, &msg) + .span_label(old_ident.span, "already registered here").emit(); + } + None => { + let msg = format!("`{}` only accepts identifiers", attr_name); + let span = nested_meta.span(); + sess.struct_span_err(span, &msg).span_label(span, "not an identifier").emit(); + } + } + } + } + registered +} + +crate fn registered_attrs_and_tools( + sess: &Session, + attrs: &[ast::Attribute], +) -> (FxHashSet<Ident>, FxHashSet<Ident>) { + let registered_attrs = registered_idents(sess, attrs, sym::register_attr, "attribute"); + let mut registered_tools = registered_idents(sess, attrs, sym::register_tool, "tool"); + // We implicitly add `rustfmt` and `clippy` to known tools, + // but it's not an error to register them explicitly. + let predefined_tools = [sym::clippy, sym::rustfmt]; + registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span)); + (registered_attrs, registered_tools) +} + impl<'a> base::Resolver for Resolver<'a> { fn next_node_id(&mut self) -> NodeId { self.session.next_node_id() @@ -531,6 +571,15 @@ impl<'a> Resolver<'a> { Err((Determinacy::Determined, _)) => Err(Determinacy::Determined), } } + Scope::RegisteredAttrs => match this.registered_attrs.get(&ident).cloned() { + Some(ident) => { + let binding = (Res::NonMacroAttr(NonMacroAttrKind::Registered), + ty::Visibility::Public, ident.span, ExpnId::root()) + .to_name_binding(this.arenas); + Ok((binding, Flags::PRELUDE)) + } + None => Err(Determinacy::Determined) + } Scope::MacroUsePrelude => match this.macro_use_prelude.get(&ident.name).cloned() { Some(binding) => Ok((binding, Flags::PRELUDE | Flags::MISC_FROM_PRELUDE)), None => Err(Determinacy::determined( @@ -560,12 +609,14 @@ impl<'a> Resolver<'a> { this.graph_root.unexpanded_invocations.borrow().is_empty() )), } - Scope::ToolPrelude => if KNOWN_TOOLS.contains(&ident.name) { - let binding = (Res::ToolMod, ty::Visibility::Public, DUMMY_SP, ExpnId::root()) - .to_name_binding(this.arenas); - Ok((binding, Flags::PRELUDE)) - } else { - Err(Determinacy::Determined) + Scope::ToolPrelude => match this.registered_tools.get(&ident).cloned() { + Some(ident) => { + let binding = (Res::ToolMod, + ty::Visibility::Public, ident.span, ExpnId::root()) + .to_name_binding(this.arenas); + Ok((binding, Flags::PRELUDE)) + } + None => Err(Determinacy::Determined) } Scope::StdLibPrelude => { let mut result = Err(Determinacy::Determined); diff --git a/src/libsyntax/feature_gate/active.rs b/src/libsyntax/feature_gate/active.rs index 1d198fca56b..087a754b27e 100644 --- a/src/libsyntax/feature_gate/active.rs +++ b/src/libsyntax/feature_gate/active.rs @@ -526,6 +526,12 @@ declare_features! ( /// Allows using the `efiapi` ABI. (active, abi_efiapi, "1.40.0", Some(65815), None), + /// Allows using the `#[register_attr]` attribute. + (active, register_attr, "1.41.0", Some(29642), None), + + /// Allows using the `#[register_attr]` attribute. + (active, register_tool, "1.41.0", Some(44690), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/libsyntax/feature_gate/builtin_attrs.rs b/src/libsyntax/feature_gate/builtin_attrs.rs index b32a887c6b2..3e77b4cf495 100644 --- a/src/libsyntax/feature_gate/builtin_attrs.rs +++ b/src/libsyntax/feature_gate/builtin_attrs.rs @@ -329,6 +329,14 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ gated!(ffi_returns_twice, Whitelisted, template!(Word), experimental!(ffi_returns_twice)), gated!(track_caller, Whitelisted, template!(Word), experimental!(track_caller)), + gated!( + register_attr, Whitelisted, template!(List: "attr1, attr2, ..."), + experimental!(register_attr), + ), + gated!( + register_tool, Whitelisted, template!(List: "tool1, tool2, ..."), + experimental!(register_tool), + ), // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index ae82ffd6383..4df9530ac79 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -545,6 +545,8 @@ symbols! { recursion_limit, reexport_test_harness_main, reflect, + register_attr, + register_tool, relaxed_adts, repr, repr128, diff --git a/src/test/ui/attributes/register-attr-tool-fail.rs b/src/test/ui/attributes/register-attr-tool-fail.rs new file mode 100644 index 00000000000..84736be844b --- /dev/null +++ b/src/test/ui/attributes/register-attr-tool-fail.rs @@ -0,0 +1,13 @@ +#![feature(register_attr)] +#![feature(register_tool)] + +#![register_attr] //~ ERROR malformed `register_attr` attribute input +#![register_tool] //~ ERROR malformed `register_tool` attribute input + +#![register_attr(a::b)] //~ ERROR `register_attr` only accepts identifiers +#![register_tool(a::b)] //~ ERROR `register_tool` only accepts identifiers + +#![register_attr(attr, attr)] //~ ERROR attribute `attr` was already registered +#![register_tool(tool, tool)] //~ ERROR tool `tool` was already registered + +fn main() {} diff --git a/src/test/ui/attributes/register-attr-tool-fail.stderr b/src/test/ui/attributes/register-attr-tool-fail.stderr new file mode 100644 index 00000000000..77acfcd87cf --- /dev/null +++ b/src/test/ui/attributes/register-attr-tool-fail.stderr @@ -0,0 +1,42 @@ +error: `register_attr` only accepts identifiers + --> $DIR/register-attr-tool-fail.rs:7:18 + | +LL | #![register_attr(a::b)] + | ^^^^ not an identifier + +error: attribute `attr` was already registered + --> $DIR/register-attr-tool-fail.rs:10:24 + | +LL | #![register_attr(attr, attr)] + | ---- ^^^^ + | | + | already registered here + +error: `register_tool` only accepts identifiers + --> $DIR/register-attr-tool-fail.rs:8:18 + | +LL | #![register_tool(a::b)] + | ^^^^ not an identifier + +error: tool `tool` was already registered + --> $DIR/register-attr-tool-fail.rs:11:24 + | +LL | #![register_tool(tool, tool)] + | ---- ^^^^ + | | + | already registered here + +error: malformed `register_attr` attribute input + --> $DIR/register-attr-tool-fail.rs:4:1 + | +LL | #![register_attr] + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[register_attr(attr1, attr2, ...)]` + +error: malformed `register_tool` attribute input + --> $DIR/register-attr-tool-fail.rs:5:1 + | +LL | #![register_tool] + | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[register_tool(tool1, tool2, ...)]` + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/attributes/register-attr-tool.rs b/src/test/ui/attributes/register-attr-tool.rs new file mode 100644 index 00000000000..ee9da74d4fb --- /dev/null +++ b/src/test/ui/attributes/register-attr-tool.rs @@ -0,0 +1,19 @@ +// check-pass +// compile-flags: --cfg foo + +#![feature(register_attr)] +#![feature(register_tool)] + +#![register_attr(attr)] +#![register_tool(tool)] +#![register_tool(rustfmt, clippy)] // OK +#![cfg_attr(foo, register_attr(conditional_attr))] +#![cfg_attr(foo, register_tool(conditional_tool))] + +#[attr] +#[tool::attr] +#[rustfmt::attr] +#[clippy::attr] +#[conditional_attr] +#[conditional_tool::attr] +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-register_attr.rs b/src/test/ui/feature-gates/feature-gate-register_attr.rs new file mode 100644 index 00000000000..36dce2aa7b9 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-register_attr.rs @@ -0,0 +1,3 @@ +#![register_attr(attr)] //~ ERROR the `#[register_attr]` attribute is an experimental feature + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-register_attr.stderr b/src/test/ui/feature-gates/feature-gate-register_attr.stderr new file mode 100644 index 00000000000..b097f578bf2 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-register_attr.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[register_attr]` attribute is an experimental feature + --> $DIR/feature-gate-register_attr.rs:1:1 + | +LL | #![register_attr(attr)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/29642 + = help: add `#![feature(register_attr)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gates/feature-gate-register_tool.rs b/src/test/ui/feature-gates/feature-gate-register_tool.rs new file mode 100644 index 00000000000..e599593283b --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-register_tool.rs @@ -0,0 +1,3 @@ +#![register_tool(tool)] //~ ERROR the `#[register_tool]` attribute is an experimental feature + +fn main() {} diff --git a/src/test/ui/feature-gates/feature-gate-register_tool.stderr b/src/test/ui/feature-gates/feature-gate-register_tool.stderr new file mode 100644 index 00000000000..85a86d2daf8 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-register_tool.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[register_tool]` attribute is an experimental feature + --> $DIR/feature-gate-register_tool.rs:1:1 + | +LL | #![register_tool(tool)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/44690 + = help: add `#![feature(register_tool)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. |
