about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/hir/def.rs23
-rw-r--r--src/librustc_plugin/registry.rs1
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs3
-rw-r--r--src/librustc_resolve/diagnostics.rs14
-rw-r--r--src/librustc_resolve/lib.rs27
-rw-r--r--src/librustc_resolve/macros.rs138
-rw-r--r--src/libsyntax/feature_gate/active.rs9
-rw-r--r--src/libsyntax/feature_gate/builtin_attrs.rs8
-rw-r--r--src/libsyntax/feature_gate/removed.rs3
-rw-r--r--src/libsyntax_pos/symbol.rs2
-rw-r--r--src/test/ui-fulldeps/issue-15778-pass.rs10
-rw-r--r--src/test/ui-fulldeps/issue-15778-pass.stderr2
-rw-r--r--src/test/ui/attributes/register-attr-tool-fail.rs13
-rw-r--r--src/test/ui/attributes/register-attr-tool-fail.stderr42
-rw-r--r--src/test/ui/attributes/register-attr-tool-import.rs14
-rw-r--r--src/test/ui/attributes/register-attr-tool-import.stderr26
-rw-r--r--src/test/ui/attributes/register-attr-tool-prelude.rs14
-rw-r--r--src/test/ui/attributes/register-attr-tool-prelude.stderr15
-rw-r--r--src/test/ui/attributes/register-attr-tool-unused.rs10
-rw-r--r--src/test/ui/attributes/register-attr-tool-unused.stderr33
-rw-r--r--src/test/ui/attributes/register-attr-tool.rs19
-rw-r--r--src/test/ui/feature-gates/feature-gate-custom_attribute2.rs2
-rw-r--r--src/test/ui/feature-gates/feature-gate-custom_attribute2.stderr34
-rw-r--r--src/test/ui/feature-gates/feature-gate-register_attr.rs3
-rw-r--r--src/test/ui/feature-gates/feature-gate-register_attr.stderr12
-rw-r--r--src/test/ui/feature-gates/feature-gate-register_tool.rs3
-rw-r--r--src/test/ui/feature-gates/feature-gate-register_tool.stderr12
-rw-r--r--src/test/ui/proc-macro/expand-to-unstable-2.rs4
-rw-r--r--src/test/ui/proc-macro/expand-to-unstable-2.stderr2
-rw-r--r--src/test/ui/proc-macro/issue-41211.rs6
-rw-r--r--src/test/ui/proc-macro/issue-41211.stderr19
-rw-r--r--src/test/ui/span/issue-36530.rs4
-rw-r--r--src/test/ui/span/issue-36530.stderr2
33 files changed, 411 insertions, 118 deletions
diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs
index d4d7af92fe3..025494e3fd7 100644
--- a/src/librustc/hir/def.rs
+++ b/src/librustc/hir/def.rs
@@ -40,10 +40,10 @@ 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]`).
-    Custom,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, HashStable)]
@@ -329,8 +329,24 @@ 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",
+        }
+    }
+
+    pub fn article(self) -> &'static str {
+        match self {
+            NonMacroAttrKind::Registered => "an",
+            _ => "a",
+        }
+    }
+
+    /// Users of some attributes cannot mark them as used, so they are considered always used.
+    pub fn is_used(self) -> bool {
+        match self {
+            NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper => true,
+            NonMacroAttrKind::Builtin | NonMacroAttrKind::Registered |
+            NonMacroAttrKind::LegacyPluginHelper => false,
         }
     }
 }
@@ -389,6 +405,7 @@ impl<Id> Res<Id> {
     pub fn article(&self) -> &'static str {
         match *self {
             Res::Def(kind, _) => kind.article(),
+            Res::NonMacroAttr(kind) => kind.article(),
             Res::Err => "an",
             _ => "a",
         }
diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs
index 2e23b8c870c..3f4b87a97c7 100644
--- a/src/librustc_plugin/registry.rs
+++ b/src/librustc_plugin/registry.rs
@@ -101,7 +101,6 @@ impl<'a> Registry<'a> {
 
     /// Register an attribute with an attribute type.
     ///
-    /// Registered attributes will bypass the `custom_attribute` feature gate.
     /// `Whitelisted` attributes will additionally not trigger the `unused_attribute`
     /// lint. `CrateLevel` attributes will not be allowed on anything other than a crate.
     pub fn register_attribute(&mut self, name: Symbol, ty: AttributeType) {
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 1ce356d4891..0fcac5aab62 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -141,8 +141,7 @@ impl<'a> Resolver<'a> {
     crate fn get_macro(&mut self, res: Res) -> Option<Lrc<SyntaxExtension>> {
         match res {
             Res::Def(DefKind::Macro(..), def_id) => self.get_macro_by_def_id(def_id),
-            Res::NonMacroAttr(attr_kind) =>
-                Some(self.non_macro_attr(attr_kind == NonMacroAttrKind::Tool)),
+            Res::NonMacroAttr(attr_kind) => Some(self.non_macro_attr(attr_kind.is_used())),
             _ => None,
         }
     }
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 e94b544582e..88c675a6ce5 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,
@@ -621,7 +620,6 @@ enum AmbiguityKind {
     Import,
     BuiltinAttr,
     DeriveHelper,
-    LegacyHelperVsPrelude,
     LegacyVsModern,
     GlobVsOuter,
     GlobVsGlob,
@@ -638,8 +636,6 @@ impl AmbiguityKind {
                 "built-in attribute vs any other name",
             AmbiguityKind::DeriveHelper =>
                 "derive helper attribute vs any other name",
-            AmbiguityKind::LegacyHelperVsPrelude =>
-                "legacy plugin helper attribute vs name from prelude",
             AmbiguityKind::LegacyVsModern =>
                 "`macro_rules` vs non-`macro_rules` from other module",
             AmbiguityKind::GlobVsOuter =>
@@ -916,6 +912,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>>,
@@ -1138,6 +1136,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));
 
@@ -1207,6 +1208,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(),
@@ -1484,6 +1487,7 @@ impl<'a> Resolver<'a> {
                 Scope::MacroRules(..) => true,
                 Scope::CrateRoot => true,
                 Scope::Module(..) => true,
+                Scope::RegisteredAttrs => use_prelude,
                 Scope::MacroUsePrelude => use_prelude || rust_2015,
                 Scope::BuiltinAttrs => true,
                 Scope::LegacyPluginHelpers => use_prelude || rust_2015,
@@ -1528,11 +1532,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
@@ -1688,11 +1693,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 cc811d3b59a..2d125a459c8 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,46 @@ fn fast_print_path(path: &ast::Path) -> Symbol {
     }
 }
 
+/// The code common between processing `#![register_tool]` and `#![register_attr]`.
+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.next_node_id()
@@ -416,10 +457,9 @@ impl<'a> Resolver<'a> {
             struct Flags: u8 {
                 const MACRO_RULES        = 1 << 0;
                 const MODULE             = 1 << 1;
-                const PRELUDE            = 1 << 2;
-                const MISC_SUGGEST_CRATE = 1 << 3;
-                const MISC_SUGGEST_SELF  = 1 << 4;
-                const MISC_FROM_PRELUDE  = 1 << 5;
+                const MISC_SUGGEST_CRATE = 1 << 2;
+                const MISC_SUGGEST_SELF  = 1 << 3;
+                const MISC_FROM_PRELUDE  = 1 << 4;
             }
         }
 
@@ -453,6 +493,10 @@ impl<'a> Resolver<'a> {
         // Go through all the scopes and try to resolve the name.
         let break_result = self.visit_scopes(scope_set, parent_scope, orig_ident,
                                              |this, scope, use_prelude, ident| {
+            let ok = |res, span, arenas| Ok((
+                (res, ty::Visibility::Public, span, ExpnId::root()).to_name_binding(arenas),
+                Flags::empty(),
+            ));
             let result = match scope {
                 Scope::DeriveHelpers => {
                     let mut result = Err(Determinacy::Determined);
@@ -461,10 +505,8 @@ impl<'a> Resolver<'a> {
                         match this.resolve_macro_path(derive, Some(MacroKind::Derive),
                                                       parent_scope, true, force) {
                             Ok((Some(ext), _)) => if ext.helper_attrs.contains(&ident.name) {
-                                let binding = (Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper),
-                                               ty::Visibility::Public, derive.span, ExpnId::root())
-                                               .to_name_binding(this.arenas);
-                                result = Ok((binding, Flags::empty()));
+                                let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
+                                result = ok(res, derive.span, this.arenas);
                                 break;
                             }
                             Ok(_) | Err(Determinacy::Determined) => {}
@@ -531,41 +573,39 @@ impl<'a> Resolver<'a> {
                         Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
                     }
                 }
+                Scope::RegisteredAttrs => match this.registered_attrs.get(&ident).cloned() {
+                    Some(ident) => ok(
+                        Res::NonMacroAttr(NonMacroAttrKind::Registered), ident.span, this.arenas
+                    ),
+                    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)),
+                    Some(binding) => Ok((binding, Flags::MISC_FROM_PRELUDE)),
                     None => Err(Determinacy::determined(
                         this.graph_root.unexpanded_invocations.borrow().is_empty()
                     ))
                 }
                 Scope::BuiltinAttrs => if is_builtin_attr_name(ident.name) {
-                    let binding = (Res::NonMacroAttr(NonMacroAttrKind::Builtin),
-                                   ty::Visibility::Public, DUMMY_SP, ExpnId::root())
-                                   .to_name_binding(this.arenas);
-                    Ok((binding, Flags::PRELUDE))
+                    ok(Res::NonMacroAttr(NonMacroAttrKind::Builtin), DUMMY_SP, this.arenas)
                 } else {
                     Err(Determinacy::Determined)
                 }
                 Scope::LegacyPluginHelpers => if this.session.plugin_attributes.borrow().iter()
                                                      .any(|(name, _)| ident.name == *name) {
-                    let binding = (Res::NonMacroAttr(NonMacroAttrKind::LegacyPluginHelper),
-                                   ty::Visibility::Public, DUMMY_SP, ExpnId::root())
-                                   .to_name_binding(this.arenas);
-                    Ok((binding, Flags::PRELUDE))
+                    let res = Res::NonMacroAttr(NonMacroAttrKind::LegacyPluginHelper);
+                    ok(res, DUMMY_SP, this.arenas)
                 } else {
                     Err(Determinacy::Determined)
                 }
                 Scope::ExternPrelude => match this.extern_prelude_get(ident, !record_used) {
-                    Some(binding) => Ok((binding, Flags::PRELUDE)),
+                    Some(binding) => Ok((binding, Flags::empty())),
                     None => Err(Determinacy::determined(
                         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) => ok(Res::ToolMod, ident.span, this.arenas),
+                    None => Err(Determinacy::Determined)
                 }
                 Scope::StdLibPrelude => {
                     let mut result = Err(Determinacy::Determined);
@@ -579,7 +619,7 @@ impl<'a> Resolver<'a> {
                             path_span,
                         ) {
                             if use_prelude || this.is_builtin_macro(binding.res()) {
-                                result = Ok((binding, Flags::PRELUDE | Flags::MISC_FROM_PRELUDE));
+                                result = Ok((binding, Flags::MISC_FROM_PRELUDE));
                             }
                         }
                     }
@@ -587,11 +627,7 @@ impl<'a> Resolver<'a> {
                 }
                 Scope::BuiltinTypes => match this.primitive_type_table.primitive_types
                                                  .get(&ident.name).cloned() {
-                    Some(prim_ty) => {
-                        let binding = (Res::PrimTy(prim_ty), ty::Visibility::Public,
-                                       DUMMY_SP, ExpnId::root()).to_name_binding(this.arenas);
-                        Ok((binding, Flags::PRELUDE))
-                    }
+                    Some(prim_ty) => ok(Res::PrimTy(prim_ty), DUMMY_SP, this.arenas),
                     None => Err(Determinacy::Determined)
                 }
             };
@@ -608,8 +644,6 @@ impl<'a> Resolver<'a> {
                         if res != innermost_res {
                             let builtin = Res::NonMacroAttr(NonMacroAttrKind::Builtin);
                             let derive_helper = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
-                            let legacy_helper =
-                                Res::NonMacroAttr(NonMacroAttrKind::LegacyPluginHelper);
 
                             let ambiguity_error_kind = if is_import {
                                 Some(AmbiguityKind::Import)
@@ -617,11 +651,6 @@ impl<'a> Resolver<'a> {
                                 Some(AmbiguityKind::BuiltinAttr)
                             } else if innermost_res == derive_helper || res == derive_helper {
                                 Some(AmbiguityKind::DeriveHelper)
-                            } else if innermost_res == legacy_helper &&
-                                      flags.contains(Flags::PRELUDE) ||
-                                      res == legacy_helper &&
-                                      innermost_flags.contains(Flags::PRELUDE) {
-                                Some(AmbiguityKind::LegacyHelperVsPrelude)
                             } else if innermost_flags.contains(Flags::MACRO_RULES) &&
                                       flags.contains(Flags::MODULE) &&
                                       !this.disambiguate_legacy_vs_modern(innermost_binding,
@@ -681,20 +710,7 @@ impl<'a> Resolver<'a> {
             return Ok(binding);
         }
 
-        let determinacy = Determinacy::determined(determinacy == Determinacy::Determined || force);
-        if determinacy == Determinacy::Determined && macro_kind == Some(MacroKind::Attr) &&
-           self.session.features_untracked().custom_attribute {
-            // For single-segment attributes interpret determinate "no resolution" as a custom
-            // attribute. (Lexical resolution implies the first segment and attr kind should imply
-            // the last segment, so we are certainly working with a single-segment attribute here.)
-            assert!(ns == MacroNS);
-            let binding = (Res::NonMacroAttr(NonMacroAttrKind::Custom),
-                           ty::Visibility::Public, orig_ident.span, ExpnId::root())
-                           .to_name_binding(self.arenas);
-            Ok(binding)
-        } else {
-            Err(determinacy)
-        }
+        Err(Determinacy::determined(determinacy == Determinacy::Determined || force))
     }
 
     crate fn finalize_macro_resolutions(&mut self) {
@@ -705,16 +721,7 @@ impl<'a> Resolver<'a> {
                     // Make sure compilation does not succeed if preferred macro resolution
                     // has changed after the macro had been expanded. In theory all such
                     // situations should be reported as ambiguity errors, so this is a bug.
-                    if initial_res == Res::NonMacroAttr(NonMacroAttrKind::Custom) {
-                        // Yeah, legacy custom attributes are implemented using forced resolution
-                        // (which is a best effort error recovery tool, basically), so we can't
-                        // promise their resolution won't change later.
-                        let msg = format!("inconsistent resolution for a macro: first {}, then {}",
-                                          initial_res.descr(), res.descr());
-                        this.session.span_err(span, &msg);
-                    } else {
-                        span_bug!(span, "inconsistent resolution for a macro");
-                    }
+                    span_bug!(span, "inconsistent resolution for a macro");
                 }
             } else {
                 // It's possible that the macro was unresolved (indeterminate) and silently
@@ -826,7 +833,8 @@ impl<'a> Resolver<'a> {
                                          res: Option<Res>, span: Span) {
         if let Some(Res::NonMacroAttr(kind)) = res {
             if kind != NonMacroAttrKind::Tool && binding.map_or(true, |b| b.is_import()) {
-                let msg = format!("cannot use a {} through an import", kind.descr());
+                let msg =
+                    format!("cannot use {} {} through an import", kind.article(), kind.descr());
                 let mut err = self.session.struct_span_err(span, &msg);
                 if let Some(binding) = binding {
                     err.span_note(binding.span, &format!("the {} imported here", kind.descr()));
diff --git a/src/libsyntax/feature_gate/active.rs b/src/libsyntax/feature_gate/active.rs
index 1d198fca56b..d59d0f0e28e 100644
--- a/src/libsyntax/feature_gate/active.rs
+++ b/src/libsyntax/feature_gate/active.rs
@@ -265,9 +265,6 @@ declare_features! (
     /// Allows the use of SIMD types in functions declared in `extern` blocks.
     (active, simd_ffi, "1.0.0", Some(27731), None),
 
-    /// Allows using custom attributes (RFC 572).
-    (active, custom_attribute, "1.0.0", Some(29642), None),
-
     /// Allows using non lexical lifetimes (RFC 2094).
     (active, nll, "1.0.0", Some(43234), None),
 
@@ -526,6 +523,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(66080), None),
+
+    /// Allows using the `#[register_attr]` attribute.
+    (active, register_tool, "1.41.0", Some(66079), 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..608cc2a09cb 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, CrateLevel, template!(List: "attr1, attr2, ..."),
+        experimental!(register_attr),
+    ),
+    gated!(
+        register_tool, CrateLevel, template!(List: "tool1, tool2, ..."),
+        experimental!(register_tool),
+    ),
 
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
diff --git a/src/libsyntax/feature_gate/removed.rs b/src/libsyntax/feature_gate/removed.rs
index c7b931a6f70..f0aa74c65df 100644
--- a/src/libsyntax/feature_gate/removed.rs
+++ b/src/libsyntax/feature_gate/removed.rs
@@ -67,6 +67,9 @@ declare_features! (
      Some("merged into `#![feature(slice_patterns)]`")),
     (removed, macro_reexport, "1.0.0", Some(29638), None,
      Some("subsumed by `pub use`")),
+    /// Allows using custom attributes (RFC 572).
+    (removed, custom_attribute, "1.0.0", Some(29642), None,
+     Some("removed in favor of `#![register_tool]` and `#![register_attr]`")),
     (removed, pushpop_unsafe, "1.2.0", None, None, None),
     (removed, needs_allocator, "1.4.0", Some(27389), None,
      Some("subsumed by `#![feature(allocator_internals)]`")),
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index f07d56e7ea2..c41f413970f 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -546,6 +546,8 @@ symbols! {
         recursion_limit,
         reexport_test_harness_main,
         reflect,
+        register_attr,
+        register_tool,
         relaxed_adts,
         repr,
         repr128,
diff --git a/src/test/ui-fulldeps/issue-15778-pass.rs b/src/test/ui-fulldeps/issue-15778-pass.rs
index b93630d56b0..55f19ab4ee3 100644
--- a/src/test/ui-fulldeps/issue-15778-pass.rs
+++ b/src/test/ui-fulldeps/issue-15778-pass.rs
@@ -3,7 +3,15 @@
 // ignore-stage1
 // compile-flags: -D crate-not-okay
 
-#![feature(plugin, custom_attribute, custom_inner_attributes, rustc_attrs)]
+#![feature(plugin, register_attr, custom_inner_attributes, rustc_attrs)]
+
+#![register_attr(
+    rustc_crate_okay,
+    rustc_crate_blue,
+    rustc_crate_red,
+    rustc_crate_grey,
+    rustc_crate_green,
+)]
 
 #![plugin(lint_for_crate_rpass)]
 #![rustc_crate_okay]
diff --git a/src/test/ui-fulldeps/issue-15778-pass.stderr b/src/test/ui-fulldeps/issue-15778-pass.stderr
index 0c30d2cdcbf..48b42958489 100644
--- a/src/test/ui-fulldeps/issue-15778-pass.stderr
+++ b/src/test/ui-fulldeps/issue-15778-pass.stderr
@@ -1,5 +1,5 @@
 warning: use of deprecated attribute `plugin`: compiler plugins are deprecated. See https://github.com/rust-lang/rust/pull/64675
-  --> $DIR/issue-15778-pass.rs:8:1
+  --> $DIR/issue-15778-pass.rs:16:1
    |
 LL | #![plugin(lint_for_crate_rpass)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: may be removed in a future compiler version
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-import.rs b/src/test/ui/attributes/register-attr-tool-import.rs
new file mode 100644
index 00000000000..3d0cf9154fb
--- /dev/null
+++ b/src/test/ui/attributes/register-attr-tool-import.rs
@@ -0,0 +1,14 @@
+// edition:2018
+
+#![feature(register_attr)]
+#![feature(register_tool)]
+
+#![register_attr(attr)]
+#![register_tool(tool)]
+
+use attr as renamed_attr; // OK
+use tool as renamed_tool; // OK
+
+#[renamed_attr] //~ ERROR cannot use an explicitly registered attribute through an import
+#[renamed_tool::attr] //~ ERROR cannot use a tool module through an import
+fn main() {}
diff --git a/src/test/ui/attributes/register-attr-tool-import.stderr b/src/test/ui/attributes/register-attr-tool-import.stderr
new file mode 100644
index 00000000000..6f280c8e0d9
--- /dev/null
+++ b/src/test/ui/attributes/register-attr-tool-import.stderr
@@ -0,0 +1,26 @@
+error: cannot use an explicitly registered attribute through an import
+  --> $DIR/register-attr-tool-import.rs:12:3
+   |
+LL | #[renamed_attr]
+   |   ^^^^^^^^^^^^
+   |
+note: the explicitly registered attribute imported here
+  --> $DIR/register-attr-tool-import.rs:9:5
+   |
+LL | use attr as renamed_attr; // OK
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: cannot use a tool module through an import
+  --> $DIR/register-attr-tool-import.rs:13:3
+   |
+LL | #[renamed_tool::attr]
+   |   ^^^^^^^^^^^^
+   |
+note: the tool module imported here
+  --> $DIR/register-attr-tool-import.rs:10:5
+   |
+LL | use tool as renamed_tool; // OK
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/attributes/register-attr-tool-prelude.rs b/src/test/ui/attributes/register-attr-tool-prelude.rs
new file mode 100644
index 00000000000..a491773f5eb
--- /dev/null
+++ b/src/test/ui/attributes/register-attr-tool-prelude.rs
@@ -0,0 +1,14 @@
+#![feature(register_attr)]
+#![feature(register_tool)]
+
+#![register_attr(attr)]
+#![register_tool(tool)]
+
+#[no_implicit_prelude]
+mod m {
+    #[attr] //~ ERROR cannot find attribute `attr` in this scope
+    #[tool::attr] //~ ERROR failed to resolve: use of undeclared type or module `tool`
+    fn check() {}
+}
+
+fn main() {}
diff --git a/src/test/ui/attributes/register-attr-tool-prelude.stderr b/src/test/ui/attributes/register-attr-tool-prelude.stderr
new file mode 100644
index 00000000000..66a4eeb6aa4
--- /dev/null
+++ b/src/test/ui/attributes/register-attr-tool-prelude.stderr
@@ -0,0 +1,15 @@
+error[E0433]: failed to resolve: use of undeclared type or module `tool`
+  --> $DIR/register-attr-tool-prelude.rs:10:7
+   |
+LL |     #[tool::attr]
+   |       ^^^^ use of undeclared type or module `tool`
+
+error: cannot find attribute `attr` in this scope
+  --> $DIR/register-attr-tool-prelude.rs:9:7
+   |
+LL |     #[attr]
+   |       ^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/src/test/ui/attributes/register-attr-tool-unused.rs b/src/test/ui/attributes/register-attr-tool-unused.rs
new file mode 100644
index 00000000000..546e372f5e3
--- /dev/null
+++ b/src/test/ui/attributes/register-attr-tool-unused.rs
@@ -0,0 +1,10 @@
+#![deny(unused)]
+
+#![feature(register_attr)]
+#![feature(register_tool)]
+
+#[register_attr(attr)] //~ ERROR crate-level attribute should be an inner attribute
+                       //~| ERROR unused attribute
+#[register_tool(tool)] //~ ERROR crate-level attribute should be an inner attribute
+                       //~| ERROR unused attribute
+fn main() {}
diff --git a/src/test/ui/attributes/register-attr-tool-unused.stderr b/src/test/ui/attributes/register-attr-tool-unused.stderr
new file mode 100644
index 00000000000..0756c572c35
--- /dev/null
+++ b/src/test/ui/attributes/register-attr-tool-unused.stderr
@@ -0,0 +1,33 @@
+error: unused attribute
+  --> $DIR/register-attr-tool-unused.rs:6:1
+   |
+LL | #[register_attr(attr)]
+   | ^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: lint level defined here
+  --> $DIR/register-attr-tool-unused.rs:1:9
+   |
+LL | #![deny(unused)]
+   |         ^^^^^^
+   = note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]`
+
+error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
+  --> $DIR/register-attr-tool-unused.rs:6:1
+   |
+LL | #[register_attr(attr)]
+   | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unused attribute
+  --> $DIR/register-attr-tool-unused.rs:8:1
+   |
+LL | #[register_tool(tool)]
+   | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
+  --> $DIR/register-attr-tool-unused.rs:8:1
+   |
+LL | #[register_tool(tool)]
+   | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 4 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-custom_attribute2.rs b/src/test/ui/feature-gates/feature-gate-custom_attribute2.rs
index e4c80141aa2..724e53debeb 100644
--- a/src/test/ui/feature-gates/feature-gate-custom_attribute2.rs
+++ b/src/test/ui/feature-gates/feature-gate-custom_attribute2.rs
@@ -1,8 +1,6 @@
 // This test ensures that attributes on formals in generic parameter
 // lists are included when we are checking for unstable attributes.
 
-// gate-test-custom_attribute
-
 struct StLt<#[lt_struct] 'a>(&'a u32);
 //~^ ERROR cannot find attribute `lt_struct` in this scope
 struct StTy<#[ty_struct] I>(I);
diff --git a/src/test/ui/feature-gates/feature-gate-custom_attribute2.stderr b/src/test/ui/feature-gates/feature-gate-custom_attribute2.stderr
index bc89caddb44..b33710ce049 100644
--- a/src/test/ui/feature-gates/feature-gate-custom_attribute2.stderr
+++ b/src/test/ui/feature-gates/feature-gate-custom_attribute2.stderr
@@ -1,101 +1,101 @@
 error: cannot find attribute `lt_hof` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:53:21
+  --> $DIR/feature-gate-custom_attribute2.rs:51:21
    |
 LL |     where Q: for <#[lt_hof] 'i> Fn(&'i [u32]) -> &'i u32
    |                     ^^^^^^
 
 error: cannot find attribute `ty_meth` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:48:15
+  --> $DIR/feature-gate-custom_attribute2.rs:46:15
    |
 LL |     fn m_ty<#[ty_meth] P>(_: P) { }
    |               ^^^^^^^
 
 error: cannot find attribute `lt_meth` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:46:15
+  --> $DIR/feature-gate-custom_attribute2.rs:44:15
    |
 LL |     fn m_lt<#[lt_meth] 'h>(_: &'h [u32]) -> &'h u32 { loop { } }
    |               ^^^^^^^
 
 error: cannot find attribute `ty_fn` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:42:11
+  --> $DIR/feature-gate-custom_attribute2.rs:40:11
    |
 LL | fn f_ty<#[ty_fn] O>(_: O) { }
    |           ^^^^^
 
 error: cannot find attribute `lt_fn` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:40:11
+  --> $DIR/feature-gate-custom_attribute2.rs:38:11
    |
 LL | fn f_lt<#[lt_fn] 'g>(_: &'g [u32]) -> &'g u32 { loop { } }
    |           ^^^^^
 
 error: cannot find attribute `ty_impl_for` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:35:8
+  --> $DIR/feature-gate-custom_attribute2.rs:33:8
    |
 LL | impl<#[ty_impl_for] N> TrTy<N> for StTy<N> {
    |        ^^^^^^^^^^^
 
 error: cannot find attribute `lt_impl_for` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:31:8
+  --> $DIR/feature-gate-custom_attribute2.rs:29:8
    |
 LL | impl<#[lt_impl_for] 'f> TrLt<'f> for StLt<'f> {
    |        ^^^^^^^^^^^
 
 error: cannot find attribute `ty_inherent` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:28:8
+  --> $DIR/feature-gate-custom_attribute2.rs:26:8
    |
 LL | impl<#[ty_inherent] M> StTy<M> { }
    |        ^^^^^^^^^^^
 
 error: cannot find attribute `lt_inherent` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:26:8
+  --> $DIR/feature-gate-custom_attribute2.rs:24:8
    |
 LL | impl<#[lt_inherent] 'e> StLt<'e> { }
    |        ^^^^^^^^^^^
 
 error: cannot find attribute `ty_type` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:23:13
+  --> $DIR/feature-gate-custom_attribute2.rs:21:13
    |
 LL | type TyTy<#[ty_type] L> = (L, );
    |             ^^^^^^^
 
 error: cannot find attribute `lt_type` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:21:13
+  --> $DIR/feature-gate-custom_attribute2.rs:19:13
    |
 LL | type TyLt<#[lt_type] 'd> = &'d u32;
    |             ^^^^^^^
 
 error: cannot find attribute `ty_trait` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:18:14
+  --> $DIR/feature-gate-custom_attribute2.rs:16:14
    |
 LL | trait TrTy<#[ty_trait] K> { fn foo(&self, _: K); }
    |              ^^^^^^^^
 
 error: cannot find attribute `lt_trait` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:16:14
+  --> $DIR/feature-gate-custom_attribute2.rs:14:14
    |
 LL | trait TrLt<#[lt_trait] 'c> { fn foo(&self, _: &'c [u32]) -> &'c u32; }
    |              ^^^^^^^^
 
 error: cannot find attribute `ty_enum` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:13:13
+  --> $DIR/feature-gate-custom_attribute2.rs:11:13
    |
 LL | enum EnTy<#[ty_enum] J> { A(J), B }
    |             ^^^^^^^
 
 error: cannot find attribute `lt_enum` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:11:13
+  --> $DIR/feature-gate-custom_attribute2.rs:9:13
    |
 LL | enum EnLt<#[lt_enum] 'b> { A(&'b u32), B }
    |             ^^^^^^^
 
 error: cannot find attribute `ty_struct` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:8:15
+  --> $DIR/feature-gate-custom_attribute2.rs:6:15
    |
 LL | struct StTy<#[ty_struct] I>(I);
    |               ^^^^^^^^^
 
 error: cannot find attribute `lt_struct` in this scope
-  --> $DIR/feature-gate-custom_attribute2.rs:6:15
+  --> $DIR/feature-gate-custom_attribute2.rs:4:15
    |
 LL | struct StLt<#[lt_struct] 'a>(&'a u32);
    |               ^^^^^^^^^
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..3965d481d9b
--- /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/66080
+   = 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..177342aed90
--- /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/66079
+   = 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`.
diff --git a/src/test/ui/proc-macro/expand-to-unstable-2.rs b/src/test/ui/proc-macro/expand-to-unstable-2.rs
index da7c89fdd46..4160e5418b7 100644
--- a/src/test/ui/proc-macro/expand-to-unstable-2.rs
+++ b/src/test/ui/proc-macro/expand-to-unstable-2.rs
@@ -1,6 +1,8 @@
 // aux-build:derive-unstable-2.rs
 
-#![feature(custom_attribute)]
+#![feature(register_attr)]
+
+#![register_attr(rustc_foo)]
 
 #[macro_use]
 extern crate derive_unstable_2;
diff --git a/src/test/ui/proc-macro/expand-to-unstable-2.stderr b/src/test/ui/proc-macro/expand-to-unstable-2.stderr
index 01e6a4a8ab9..5b6184afacd 100644
--- a/src/test/ui/proc-macro/expand-to-unstable-2.stderr
+++ b/src/test/ui/proc-macro/expand-to-unstable-2.stderr
@@ -1,5 +1,5 @@
 error[E0658]: attributes starting with `rustc` are reserved for use by the `rustc` compiler
-  --> $DIR/expand-to-unstable-2.rs:8:10
+  --> $DIR/expand-to-unstable-2.rs:10:10
    |
 LL | #[derive(Unstable)]
    |          ^^^^^^^^
diff --git a/src/test/ui/proc-macro/issue-41211.rs b/src/test/ui/proc-macro/issue-41211.rs
index 491b89b2f55..072a63baf3a 100644
--- a/src/test/ui/proc-macro/issue-41211.rs
+++ b/src/test/ui/proc-macro/issue-41211.rs
@@ -3,11 +3,13 @@
 // FIXME: https://github.com/rust-lang/rust/issues/41430
 // This is a temporary regression test for the ICE reported in #41211
 
-#![feature(custom_attribute)]
 #![feature(custom_inner_attributes)]
+#![feature(register_attr)]
+
+#![register_attr(identity_attr)]
 
 #![identity_attr]
-//~^ ERROR inconsistent resolution for a macro: first custom attribute, then attribute macro
+//~^ ERROR `identity_attr` is ambiguous
 extern crate test_macros;
 use test_macros::identity_attr;
 
diff --git a/src/test/ui/proc-macro/issue-41211.stderr b/src/test/ui/proc-macro/issue-41211.stderr
index f01cba0c930..22ad4aa147b 100644
--- a/src/test/ui/proc-macro/issue-41211.stderr
+++ b/src/test/ui/proc-macro/issue-41211.stderr
@@ -1,8 +1,21 @@
-error: inconsistent resolution for a macro: first custom attribute, then attribute macro
-  --> $DIR/issue-41211.rs:9:4
+error[E0659]: `identity_attr` is ambiguous (macro-expanded name vs less macro-expanded name from outer scope during import/macro resolution)
+  --> $DIR/issue-41211.rs:11:4
    |
 LL | #![identity_attr]
-   |    ^^^^^^^^^^^^^
+   |    ^^^^^^^^^^^^^ ambiguous name
+   |
+note: `identity_attr` could refer to the attribute macro imported here
+  --> $DIR/issue-41211.rs:14:5
+   |
+LL | use test_macros::identity_attr;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = help: use `crate::identity_attr` to refer to this attribute macro unambiguously
+note: `identity_attr` could also refer to the explicitly registered attribute defined here
+  --> $DIR/issue-41211.rs:9:18
+   |
+LL | #![register_attr(identity_attr)]
+   |                  ^^^^^^^^^^^^^
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0659`.
diff --git a/src/test/ui/span/issue-36530.rs b/src/test/ui/span/issue-36530.rs
index 14b2c8644e0..4776740d8de 100644
--- a/src/test/ui/span/issue-36530.rs
+++ b/src/test/ui/span/issue-36530.rs
@@ -1,6 +1,8 @@
 // gate-test-custom_inner_attributes
 
-#![feature(custom_attribute)]
+#![feature(register_attr)]
+
+#![register_attr(foo)]
 
 #[foo]
 mod foo {
diff --git a/src/test/ui/span/issue-36530.stderr b/src/test/ui/span/issue-36530.stderr
index c6b7895e65a..2d3972917df 100644
--- a/src/test/ui/span/issue-36530.stderr
+++ b/src/test/ui/span/issue-36530.stderr
@@ -1,5 +1,5 @@
 error[E0658]: non-builtin inner attributes are unstable
-  --> $DIR/issue-36530.rs:7:5
+  --> $DIR/issue-36530.rs:9:5
    |
 LL |     #![foo]
    |     ^^^^^^^