about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-08-02 21:39:14 +0000
committerbors <bors@rust-lang.org>2018-08-02 21:39:14 +0000
commit40e4b6ee3dd70a05007915bd1c15c150a7b7899f (patch)
treeb9ca402630b5a091d7fb317c270af3b8d83683bf
parent40cb4478a3f550bf12d81cdcf08cc9ef3985ed41 (diff)
parentc3e54217e855a2492d9b707eb3fb7cdb6702d45a (diff)
downloadrust-40e4b6ee3dd70a05007915bd1c15c150a7b7899f.tar.gz
rust-40e4b6ee3dd70a05007915bd1c15c150a7b7899f.zip
Auto merge of #52841 - petrochenkov:premacro, r=alexcrichton
resolve: Implement prelude search for macro paths, implement tool attributes

When identifier is macro path is resolved in scopes (i.e. the first path segment - `foo` in `foo::mac!()` or `foo!()`), scopes are searched in the same order as for non-macro paths - items in modules, extern prelude, tool prelude (see later), standard library prelude, language prelude, but with some extra shadowing restrictions (names from globs and macro expansions cannot shadow names from outer scopes). See the comment in `fn resolve_lexical_macro_path_segment` for more details.

"Tool prelude" currently contains two "tool modules" `rustfmt` and `clippy`, and is searched immediately after extern prelude.
This makes the [possible long-term solution](https://github.com/rust-lang/rfcs/blob/master/text/2103-tool-attributes.md#long-term-solution) for tool attributes exactly equivalent to the existing extern prelude scheme, except that `--extern=my_crate` making crate names available in scope is replaced with something like `--tool=my_tool` making tool names available in scope.

The `tool_attributes` feature is still unstable and `#![feature(tool_attributes)]` now implicitly enables `#![feature(use_extern_macros)]`. `use_extern_macros` is a prerequisite for `tool_attributes`, so their stabilization will happen in the same order.
If `use_extern_macros` is not enabled, then tool attributes are treated as custom attributes (this is temporary, anyway).

Fixes https://github.com/rust-lang/rust/issues/52576
Fixes https://github.com/rust-lang/rust/issues/52512
Fixes https://github.com/rust-lang/rust/issues/51277
cc https://github.com/rust-lang/rust/issues/52269
-rw-r--r--src/librustc/hir/def.rs6
-rw-r--r--src/librustc/ich/impls_hir.rs2
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs3
-rw-r--r--src/librustc_resolve/check_unused.rs3
-rw-r--r--src/librustc_resolve/lib.rs30
-rw-r--r--src/librustc_resolve/macros.rs276
-rw-r--r--src/librustc_save_analysis/lib.rs2
-rw-r--r--src/libsyntax/attr/mod.rs15
-rw-r--r--src/libsyntax/ext/base.rs5
-rw-r--r--src/libsyntax/ext/expand.rs24
-rw-r--r--src/libsyntax/feature_gate.rs38
-rw-r--r--src/test/compile-fail/unknown-tool-name.rs6
-rw-r--r--src/test/run-pass-fulldeps/proc-macro/issue-42708.rs2
-rw-r--r--src/test/run-pass-fulldeps/proc-macro/issue-50061.rs2
-rw-r--r--src/test/ui-fulldeps/proc-macro/parent-source-spans.rs2
-rw-r--r--src/test/ui/auxiliary/macro-in-other-crate.rs5
-rw-r--r--src/test/ui/feature-gate-macros_in_extern.stderr6
-rw-r--r--src/test/ui/feature-gate-tool_attributes.rs4
-rw-r--r--src/test/ui/feature-gate-tool_attributes.stderr6
-rw-r--r--src/test/ui/issue-11692-1.rs2
-rw-r--r--src/test/ui/issue-11692-1.stderr6
-rw-r--r--src/test/ui/issue-11692-2.rs2
-rw-r--r--src/test/ui/issue-11692-2.stderr2
-rw-r--r--src/test/ui/issue-50187.rs2
-rw-r--r--src/test/ui/macro-path-prelude-fail-1.rs (renamed from src/test/compile-fail/unknown_tool_attributes-1.rs)12
-rw-r--r--src/test/ui/macro-path-prelude-fail-1.stderr15
-rw-r--r--src/test/ui/macro-path-prelude-fail-2.rs19
-rw-r--r--src/test/ui/macro-path-prelude-fail-2.stderr8
-rw-r--r--src/test/ui/macro-path-prelude-fail-3.rs18
-rw-r--r--src/test/ui/macro-path-prelude-fail-3.stderr14
-rw-r--r--src/test/ui/macro-path-prelude-pass.rs (renamed from src/test/compile-fail/feature-gate-tool_attributes.rs)14
-rw-r--r--src/test/ui/macro-path-prelude-shadowing.rs41
-rw-r--r--src/test/ui/macro-path-prelude-shadowing.stderr42
-rw-r--r--src/test/ui/tool-attributes-disabled-1.rs15
-rw-r--r--src/test/ui/tool-attributes-disabled-1.stderr11
-rw-r--r--src/test/ui/tool-attributes-disabled-2.rs19
-rw-r--r--src/test/ui/tool-attributes-misplaced-1.rs28
-rw-r--r--src/test/ui/tool-attributes-misplaced-1.stderr46
-rw-r--r--src/test/ui/tool-attributes-misplaced-2.rs18
-rw-r--r--src/test/ui/tool-attributes-misplaced-2.stderr14
-rw-r--r--src/test/ui/tool-attributes-shadowing.rs16
-rw-r--r--src/test/ui/tool-attributes-shadowing.stderr9
42 files changed, 675 insertions, 135 deletions
diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs
index 1c355e35fd6..cb5e1e600d3 100644
--- a/src/librustc/hir/def.rs
+++ b/src/librustc/hir/def.rs
@@ -49,6 +49,7 @@ pub enum Def {
     PrimTy(hir::PrimTy),
     TyParam(DefId),
     SelfTy(Option<DefId> /* trait */, Option<DefId> /* impl */),
+    ToolMod, // e.g. `rustfmt` in `#[rustfmt::skip]`
 
     // Value namespace
     Fn(DefId),
@@ -67,6 +68,7 @@ pub enum Def {
 
     // Macro namespace
     Macro(DefId, MacroKind),
+    NonMacroAttr, // e.g. `#[inline]` or `#[rustfmt::skip]`
 
     GlobalAsm(DefId),
 
@@ -259,6 +261,8 @@ impl Def {
             Def::Label(..)  |
             Def::PrimTy(..) |
             Def::SelfTy(..) |
+            Def::ToolMod |
+            Def::NonMacroAttr |
             Def::Err => {
                 bug!("attempted .def_id() on invalid def: {:?}", self)
             }
@@ -299,6 +303,8 @@ impl Def {
             Def::SelfTy(..) => "self type",
             Def::Macro(.., macro_kind) => macro_kind.descr(),
             Def::GlobalAsm(..) => "global asm",
+            Def::ToolMod => "tool module",
+            Def::NonMacroAttr => "non-macro attribute",
             Def::Err => "unresolved item",
         }
     }
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index 410d578d404..cac0d182d3a 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -1016,6 +1016,8 @@ impl_stable_hash_for!(enum hir::def::Def {
     Label(node_id),
     Macro(def_id, macro_kind),
     GlobalAsm(def_id),
+    ToolMod,
+    NonMacroAttr,
     Err
 });
 
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 4553a2ab577..ab4d15d0b90 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -629,7 +629,8 @@ impl<'a> Resolver<'a> {
     pub fn get_macro(&mut self, def: Def) -> Lrc<SyntaxExtension> {
         let def_id = match def {
             Def::Macro(def_id, ..) => def_id,
-            _ => panic!("Expected Def::Macro(..)"),
+            Def::NonMacroAttr => return Lrc::new(SyntaxExtension::NonMacroAttr),
+            _ => panic!("Expected Def::Macro(..) or Def::NonMacroAttr"),
         };
         if let Some(ext) = self.macro_map.get(&def_id) {
             return ext.clone();
diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs
index ec067a6477b..4c12591c832 100644
--- a/src/librustc_resolve/check_unused.rs
+++ b/src/librustc_resolve/check_unused.rs
@@ -131,8 +131,7 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) {
                  directive.vis.get() == ty::Visibility::Public ||
                  directive.span.is_dummy() => {
                 if let ImportDirectiveSubclass::MacroUse = directive.subclass {
-                    if resolver.session.features_untracked().use_extern_macros &&
-                        !directive.span.is_dummy() {
+                    if resolver.use_extern_macros && !directive.span.is_dummy() {
                         resolver.session.buffer_lint(
                             lint::builtin::MACRO_USE_EXTERN_CRATE,
                             directive.id,
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index ca42cf6dace..9c58d2c1f0b 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -86,6 +86,10 @@ mod check_unused;
 mod build_reduced_graph;
 mod resolve_imports;
 
+fn is_known_tool(name: Name) -> bool {
+    ["clippy", "rustfmt"].contains(&&*name.as_str())
+}
+
 /// A free importable items suggested in case of resolution failure.
 struct ImportSuggestion {
     path: Path,
@@ -200,15 +204,10 @@ fn resolve_struct_error<'sess, 'a>(resolver: &'sess Resolver,
                         err.span_label(typaram_span, "type variable from outer function");
                     }
                 },
-                Def::Mod(..) | Def::Struct(..) | Def::Union(..) | Def::Enum(..) | Def::Variant(..) |
-                Def::Trait(..) | Def::TyAlias(..) | Def::TyForeign(..) | Def::TraitAlias(..) |
-                Def::AssociatedTy(..) | Def::PrimTy(..) | Def::Fn(..) | Def::Const(..) |
-                Def::Static(..) | Def::StructCtor(..) | Def::VariantCtor(..) | Def::Method(..) |
-                Def::AssociatedConst(..) | Def::Local(..) | Def::Upvar(..) | Def::Label(..) |
-                Def::Existential(..) | Def::AssociatedExistential(..) |
-                Def::Macro(..) | Def::GlobalAsm(..) | Def::Err =>
+                _ => {
                     bug!("TypeParametersFromOuterFunction should only be used with Def::SelfTy or \
                          Def::TyParam")
+                }
             }
 
             // Try to retrieve the span of the function signature and generate a new message with
@@ -1711,9 +1710,7 @@ impl<'a> Resolver<'a> {
                 vis: ty::Visibility::Public,
             }),
 
-            // The `proc_macro` and `decl_macro` features imply `use_extern_macros`
-            use_extern_macros:
-                features.use_extern_macros || features.decl_macro,
+            use_extern_macros: features.use_extern_macros(),
 
             crate_loader,
             macro_names: FxHashSet(),
@@ -1846,6 +1843,7 @@ impl<'a> Resolver<'a> {
                                       path_span: Span)
                                       -> Option<LexicalScopeBinding<'a>> {
         let record_used = record_used_id.is_some();
+        assert!(ns == TypeNS  || ns == ValueNS);
         if ns == TypeNS {
             ident.span = if ident.name == keywords::SelfType.name() {
                 // FIXME(jseyfried) improve `Self` hygiene
@@ -1922,8 +1920,9 @@ impl<'a> Resolver<'a> {
                     return Some(LexicalScopeBinding::Item(binding))
                 }
                 _ if poisoned.is_some() => break,
-                Err(Undetermined) => return None,
-                Err(Determined) => {}
+                Err(Determined) => continue,
+                Err(Undetermined) =>
+                    span_bug!(ident.span, "undetermined resolution during main resolution pass"),
             }
         }
 
@@ -1945,6 +1944,11 @@ impl<'a> Resolver<'a> {
                                ident.span, Mark::root()).to_name_binding(self.arenas);
                 return Some(LexicalScopeBinding::Item(binding));
             }
+            if ns == TypeNS && is_known_tool(ident.name) {
+                let binding = (Def::ToolMod, ty::Visibility::Public,
+                               ident.span, Mark::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(prelude, ident, ns,
                                                                         false, false, path_span) {
@@ -3505,6 +3509,8 @@ impl<'a> Resolver<'a> {
                     let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(def);
                     if let Some(next_module) = binding.module() {
                         module = Some(next_module);
+                    } else if def == Def::ToolMod && i + 1 != path.len() {
+                        return PathResult::NonModule(PathResolution::new(Def::NonMacroAttr))
                     } else if def == Def::Err {
                         return PathResult::NonModule(err_path_resolution());
                     } else if opt_ns.is_some() && (is_last || maybe_assoc) {
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 1d42ad4e490..60a6bcf499d 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -8,9 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use {AmbiguityError, CrateLint, Resolver, ResolutionError, resolve_error};
-use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult};
-use Namespace::{self, MacroNS};
+use {AmbiguityError, CrateLint, Resolver, ResolutionError, is_known_tool, resolve_error};
+use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, ToNameBinding};
+use Namespace::{self, TypeNS, MacroNS};
 use build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport};
 use resolve_imports::ImportResolver;
 use rustc::hir::def_id::{DefId, BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, DefIndex,
@@ -27,7 +27,7 @@ use syntax::ext::expand::{self, AstFragment, AstFragmentKind, Invocation, Invoca
 use syntax::ext::hygiene::{self, Mark};
 use syntax::ext::placeholders::placeholder;
 use syntax::ext::tt::macro_rules;
-use syntax::feature_gate::{self, emit_feature_err, GateIssue};
+use syntax::feature_gate::{self, feature_err, emit_feature_err, is_builtin_attr_name, GateIssue};
 use syntax::fold::{self, Folder};
 use syntax::parse::parser::PathStyle;
 use syntax::parse::token::{self, Token};
@@ -326,6 +326,18 @@ impl<'a> base::Resolver for Resolver<'a> {
         if let Def::Macro(_, MacroKind::ProcMacroStub) = def {
             self.report_proc_macro_stub(invoc.span());
             return Err(Determinacy::Determined);
+        } else if let Def::NonMacroAttr = def {
+            if let InvocationKind::Attr { .. } = invoc.kind {
+                if !self.session.features_untracked().tool_attributes {
+                    feature_err(&self.session.parse_sess, "tool_attributes",
+                                invoc.span(), GateIssue::Language,
+                                "tool attributes are unstable").emit();
+                }
+                return Ok(Some(Lrc::new(SyntaxExtension::NonMacroAttr)));
+            } else {
+                self.report_non_macro_attr(invoc.path_span());
+                return Err(Determinacy::Determined);
+            }
         }
         let def_id = def.def_id();
 
@@ -348,6 +360,9 @@ impl<'a> base::Resolver for Resolver<'a> {
             if let Def::Macro(_, MacroKind::ProcMacroStub) = def {
                 self.report_proc_macro_stub(path.span);
                 return Err(Determinacy::Determined);
+            } else if let Def::NonMacroAttr = def {
+                self.report_non_macro_attr(path.span);
+                return Err(Determinacy::Determined);
             }
             self.unused_macros.remove(&def.def_id());
             Ok(self.get_macro(def))
@@ -378,6 +393,11 @@ impl<'a> Resolver<'a> {
                               "can't use a procedural macro from the same crate that defines it");
     }
 
+    fn report_non_macro_attr(&self, span: Span) {
+        self.session.span_err(span,
+                              "expected a macro, found non-macro attribute");
+    }
+
     fn resolve_invoc_to_def(&mut self, invoc: &mut Invocation, scope: Mark, force: bool)
                             -> Result<Def, Determinacy> {
         let (attr, traits, item) = match invoc.kind {
@@ -450,7 +470,15 @@ impl<'a> Resolver<'a> {
 
     fn resolve_macro_to_def(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool)
                             -> Result<Def, Determinacy> {
-        if kind != MacroKind::Bang && path.segments.len() > 1 {
+        let def = self.resolve_macro_to_def_inner(scope, path, kind, force);
+        if def != Err(Determinacy::Undetermined) {
+            // Do not report duplicated errors on every undetermined resolution.
+            path.segments.iter().find(|segment| segment.args.is_some()).map(|segment| {
+                self.session.span_err(segment.args.as_ref().unwrap().span(),
+                                      "generic arguments in macro path");
+            });
+        }
+        if kind != MacroKind::Bang && path.segments.len() > 1 && def != Ok(Def::NonMacroAttr) {
             if !self.session.features_untracked().proc_macro_path_invoc {
                 emit_feature_err(
                     &self.session.parse_sess,
@@ -462,15 +490,6 @@ impl<'a> Resolver<'a> {
                 );
             }
         }
-
-        let def = self.resolve_macro_to_def_inner(scope, path, kind, force);
-        if def != Err(Determinacy::Undetermined) {
-            // Do not report duplicated errors on every undetermined resolution.
-            path.segments.iter().find(|segment| segment.args.is_some()).map(|segment| {
-                self.session.span_err(segment.args.as_ref().unwrap().span(),
-                                      "generic arguments in macro path");
-            });
-        }
         def
     }
 
@@ -544,67 +563,226 @@ impl<'a> Resolver<'a> {
         result
     }
 
-    // Resolve the initial segment of a non-global macro path (e.g. `foo` in `foo::bar!();`)
+    // Resolve the initial segment of a non-global macro path
+    // (e.g. `foo` in `foo::bar!(); or `foo!();`).
+    // This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during
+    // expansion and import resolution (perhaps they can be merged in the future).
     pub fn resolve_lexical_macro_path_segment(&mut self,
                                               mut ident: Ident,
                                               ns: Namespace,
                                               record_used: bool,
                                               path_span: Span)
                                               -> Result<MacroBinding<'a>, Determinacy> {
+        // General principles:
+        // 1. Not controlled (user-defined) names should have higher priority than controlled names
+        //    built into the language or standard library. This way we can add new names into the
+        //    language or standard library without breaking user code.
+        // 2. "Closed set" below means new names can appear after the current resolution attempt.
+        // Places to search (in order of decreasing priority):
+        // (Type NS)
+        // 1. FIXME: Ribs (type parameters), there's no necessary infrastructure yet
+        //    (open set, not controlled).
+        // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
+        //    (open, not controlled).
+        // 3. Extern prelude (closed, not controlled).
+        // 4. Tool modules (closed, controlled right now, but not in the future).
+        // 5. Standard library prelude (de-facto closed, controlled).
+        // 6. Language prelude (closed, controlled).
+        // (Macro NS)
+        // 1. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
+        //    (open, not controlled).
+        // 2. Macro prelude (language, standard library, user-defined legacy plugins lumped into
+        //    one set) (open, the open part is from macro expansions, not controlled).
+        // 2a. User-defined prelude from macro-use
+        //    (open, the open part is from macro expansions, not controlled).
+        // 2b. Standard library prelude, currently just a macro-use (closed, controlled)
+        // 2c. Language prelude, perhaps including builtin attributes
+        //    (closed, controlled, except for legacy plugins).
+        // 3. Builtin attributes (closed, controlled).
+
+        assert!(ns == TypeNS  || ns == MacroNS);
         ident = ident.modern();
-        let mut module = Some(self.current_module);
-        let mut potential_illegal_shadower = Err(Determinacy::Determined);
-        let determinacy =
-            if record_used { Determinacy::Determined } else { Determinacy::Undetermined };
+
+        // Names from inner scope that can't shadow names from outer scopes, e.g.
+        // mod m { ... }
+        // {
+        //     use prefix::*; // if this imports another `m`, then it can't shadow the outer `m`
+        //                    // and we have and ambiguity error
+        //     m::mac!();
+        // }
+        // This includes names from globs and from macro expansions.
+        let mut potentially_ambiguous_result: Option<MacroBinding> = None;
+
+        enum WhereToResolve<'a> {
+            Module(Module<'a>),
+            MacroPrelude,
+            BuiltinAttrs,
+            ExternPrelude,
+            ToolPrelude,
+            StdLibPrelude,
+            PrimitiveTypes,
+        }
+
+        // Go through all the scopes and try to resolve the name.
+        let mut where_to_resolve = WhereToResolve::Module(self.current_module);
+        let mut use_prelude = !self.current_module.no_implicit_prelude;
         loop {
-            let orig_current_module = self.current_module;
-            let result = if let Some(module) = module {
-                self.current_module = module; // Lexical resolutions can never be a privacy error.
-                // Since expanded macros may not shadow the lexical scope and
-                // globs may not shadow global macros (both enforced below),
-                // we resolve with restricted shadowing (indicated by the penultimate argument).
-                self.resolve_ident_in_module_unadjusted(
-                    module, ident, ns, true, record_used, path_span,
-                ).map(MacroBinding::Modern)
-            } else {
-                self.macro_prelude.get(&ident.name).cloned().ok_or(determinacy)
-                    .map(MacroBinding::Global)
+            let result = match where_to_resolve {
+                WhereToResolve::Module(module) => {
+                    let orig_current_module = mem::replace(&mut self.current_module, module);
+                    let binding = self.resolve_ident_in_module_unadjusted(
+                            module, ident, ns, true, record_used, path_span,
+                    );
+                    self.current_module = orig_current_module;
+                    binding.map(MacroBinding::Modern)
+                }
+                WhereToResolve::MacroPrelude => {
+                    match self.macro_prelude.get(&ident.name).cloned() {
+                        Some(binding) => Ok(MacroBinding::Global(binding)),
+                        None => Err(Determinacy::Determined),
+                    }
+                }
+                WhereToResolve::BuiltinAttrs => {
+                    if is_builtin_attr_name(ident.name) {
+                        let binding = (Def::NonMacroAttr, ty::Visibility::Public,
+                                       ident.span, Mark::root()).to_name_binding(self.arenas);
+                        Ok(MacroBinding::Global(binding))
+                    } else {
+                        Err(Determinacy::Determined)
+                    }
+                }
+                WhereToResolve::ExternPrelude => {
+                    if use_prelude && self.extern_prelude.contains(&ident.name) {
+                        if !self.session.features_untracked().extern_prelude &&
+                           !self.ignore_extern_prelude_feature {
+                            feature_err(&self.session.parse_sess, "extern_prelude",
+                                        ident.span, GateIssue::Language,
+                                        "access to extern crates through prelude is experimental")
+                                        .emit();
+                        }
+
+                        let crate_id =
+                            self.crate_loader.process_path_extern(ident.name, ident.span);
+                        let crate_root =
+                            self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
+                        self.populate_module_if_necessary(crate_root);
+
+                        let binding = (crate_root, ty::Visibility::Public,
+                                       ident.span, Mark::root()).to_name_binding(self.arenas);
+                        Ok(MacroBinding::Global(binding))
+                    } else {
+                        Err(Determinacy::Determined)
+                    }
+                }
+                WhereToResolve::ToolPrelude => {
+                    if use_prelude && is_known_tool(ident.name) {
+                        let binding = (Def::ToolMod, ty::Visibility::Public,
+                                       ident.span, Mark::root()).to_name_binding(self.arenas);
+                        Ok(MacroBinding::Global(binding))
+                    } else {
+                        Err(Determinacy::Determined)
+                    }
+                }
+                WhereToResolve::StdLibPrelude => {
+                    let mut result = Err(Determinacy::Determined);
+                    if use_prelude {
+                        if let Some(prelude) = self.prelude {
+                            if let Ok(binding) =
+                                    self.resolve_ident_in_module_unadjusted(prelude, ident, ns,
+                                                                          false, false, path_span) {
+                                result = Ok(MacroBinding::Global(binding));
+                            }
+                        }
+                    }
+                    result
+                }
+                WhereToResolve::PrimitiveTypes => {
+                    if let Some(prim_ty) =
+                            self.primitive_type_table.primitive_types.get(&ident.name).cloned() {
+                        let binding = (Def::PrimTy(prim_ty), ty::Visibility::Public,
+                                       ident.span, Mark::root()).to_name_binding(self.arenas);
+                        Ok(MacroBinding::Global(binding))
+                    } else {
+                        Err(Determinacy::Determined)
+                    }
+                }
             };
-            self.current_module = orig_current_module;
 
-            match result.map(MacroBinding::binding) {
-                Ok(binding) => {
+            macro_rules! continue_search { () => {
+                where_to_resolve = match where_to_resolve {
+                    WhereToResolve::Module(module) => {
+                        match self.hygienic_lexical_parent(module, &mut ident.span) {
+                            Some(parent_module) => WhereToResolve::Module(parent_module),
+                            None => {
+                                use_prelude = !module.no_implicit_prelude;
+                                if ns == MacroNS {
+                                    WhereToResolve::MacroPrelude
+                                } else {
+                                    WhereToResolve::ExternPrelude
+                                }
+                            }
+                        }
+                    }
+                    WhereToResolve::MacroPrelude => WhereToResolve::BuiltinAttrs,
+                    WhereToResolve::BuiltinAttrs => break, // nowhere else to search
+                    WhereToResolve::ExternPrelude => WhereToResolve::ToolPrelude,
+                    WhereToResolve::ToolPrelude => WhereToResolve::StdLibPrelude,
+                    WhereToResolve::StdLibPrelude => WhereToResolve::PrimitiveTypes,
+                    WhereToResolve::PrimitiveTypes => break, // nowhere else to search
+                };
+
+                continue;
+            }}
+
+            match result {
+                Ok(result) => {
                     if !record_used {
-                        return result;
+                        return Ok(result);
                     }
-                    if let Ok(MacroBinding::Modern(shadower)) = potential_illegal_shadower {
-                        if shadower.def() != binding.def() {
-                            let name = ident.name;
+
+                    let binding = result.binding();
+
+                    // Found a solution that is ambiguous with a previously found solution.
+                    // Push an ambiguity error for later reporting and
+                    // return something for better recovery.
+                    if let Some(previous_result) = potentially_ambiguous_result {
+                        if binding.def() != previous_result.binding().def() {
                             self.ambiguity_errors.push(AmbiguityError {
                                 span: path_span,
-                                name,
-                                b1: shadower,
+                                name: ident.name,
+                                b1: previous_result.binding(),
                                 b2: binding,
                                 lexical: true,
                             });
-                            return potential_illegal_shadower;
+                            return Ok(previous_result);
                         }
                     }
+
+                    // Found a solution that's not an ambiguity yet, but is "suspicious" and
+                    // can participate in ambiguities later on.
+                    // Remember it and go search for other solutions in outer scopes.
                     if binding.is_glob_import() || binding.expansion != Mark::root() {
-                        potential_illegal_shadower = result;
-                    } else {
-                        return result;
+                        potentially_ambiguous_result = Some(result);
+
+                        continue_search!();
                     }
+
+                    // Found a solution that can't be ambiguous, great success.
+                    return Ok(result);
                 },
+                Err(Determinacy::Determined) => {
+                    continue_search!();
+                }
                 Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
-                Err(Determinacy::Determined) => {}
             }
+        }
 
-            module = match module {
-                Some(module) => self.hygienic_lexical_parent(module, &mut ident.span),
-                None => return potential_illegal_shadower,
-            }
+        // Previously found potentially ambiguous result turned out to not be ambiguous after all.
+        if let Some(previous_result) = potentially_ambiguous_result {
+            return Ok(previous_result);
         }
+
+        if record_used { Err(Determinacy::Determined) } else { Err(Determinacy::Undetermined) }
     }
 
     pub fn resolve_legacy_scope(&mut self,
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 761521c8807..240b33c3c94 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -811,6 +811,8 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> {
             HirDef::Label(..) |
             HirDef::Macro(..) |
             HirDef::GlobalAsm(..) |
+            HirDef::ToolMod |
+            HirDef::NonMacroAttr |
             HirDef::Err => None,
         }
     }
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 7fbc5d4c41a..b3b173db70b 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -65,17 +65,8 @@ pub fn is_known(attr: &Attribute) -> bool {
     })
 }
 
-const RUST_KNOWN_TOOL: &[&str] = &["clippy", "rustfmt"];
-const RUST_KNOWN_LINT_TOOL: &[&str] = &["clippy"];
-
-pub fn is_known_tool(attr: &Attribute) -> bool {
-    let tool_name =
-        attr.path.segments.iter().next().expect("empty path in attribute").ident.name;
-    RUST_KNOWN_TOOL.contains(&tool_name.as_str().as_ref())
-}
-
 pub fn is_known_lint_tool(m_item: Ident) -> bool {
-    RUST_KNOWN_LINT_TOOL.contains(&m_item.as_str().as_ref())
+    ["clippy"].contains(&m_item.as_str().as_ref())
 }
 
 impl NestedMetaItem {
@@ -221,10 +212,6 @@ impl Attribute {
     pub fn is_value_str(&self) -> bool {
         self.value_str().is_some()
     }
-
-    pub fn is_scoped(&self) -> bool {
-        self.path.segments.len() > 1
-    }
 }
 
 impl MetaItem {
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index b55c4f99206..8450daa3f7c 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -588,6 +588,9 @@ impl MacroKind {
 
 /// An enum representing the different kinds of syntax extensions.
 pub enum SyntaxExtension {
+    /// A trivial "extension" that does nothing, only keeps the attribute and marks it as known.
+    NonMacroAttr,
+
     /// A syntax extension that is attached to an item and creates new items
     /// based upon it.
     ///
@@ -667,6 +670,7 @@ impl SyntaxExtension {
             SyntaxExtension::IdentTT(..) |
             SyntaxExtension::ProcMacro { .. } =>
                 MacroKind::Bang,
+            SyntaxExtension::NonMacroAttr |
             SyntaxExtension::MultiDecorator(..) |
             SyntaxExtension::MultiModifier(..) |
             SyntaxExtension::AttrProcMacro(..) =>
@@ -696,6 +700,7 @@ impl SyntaxExtension {
             SyntaxExtension::AttrProcMacro(.., edition) |
             SyntaxExtension::ProcMacroDerive(.., edition) => edition,
             // Unstable legacy stuff
+            SyntaxExtension::NonMacroAttr |
             SyntaxExtension::IdentTT(..) |
             SyntaxExtension::MultiDecorator(..) |
             SyntaxExtension::MultiModifier(..) |
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 383813d73b1..8bd30e43476 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -37,7 +37,7 @@ use visit::{self, Visitor};
 use std::collections::HashMap;
 use std::fs::File;
 use std::io::Read;
-use std::mem;
+use std::{iter, mem};
 use std::rc::Rc;
 use std::path::PathBuf;
 
@@ -244,6 +244,15 @@ impl Invocation {
         }
     }
 
+    pub fn path_span(&self) -> Span {
+        match self.kind {
+            InvocationKind::Bang { ref mac, .. } => mac.node.path.span,
+            InvocationKind::Attr { attr: Some(ref attr), .. } => attr.path.span,
+            InvocationKind::Attr { attr: None, .. } => DUMMY_SP,
+            InvocationKind::Derive { ref path, .. } => path.span,
+        }
+    }
+
     pub fn attr_id(&self) -> Option<ast::AttrId> {
         match self.kind {
             InvocationKind::Attr { attr: Some(ref attr), .. } => Some(attr.id),
@@ -568,6 +577,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         });
 
         match *ext {
+            NonMacroAttr => {
+                attr::mark_known(&attr);
+                let item = item.map_attrs(|mut attrs| { attrs.push(attr); attrs });
+                Some(invoc.fragment_kind.expect_from_annotatables(iter::once(item)))
+            }
             MultiModifier(ref mac) => {
                 let meta = attr.parse_meta(self.cx.parse_sess)
                                .map_err(|mut e| { e.emit(); }).ok()?;
@@ -812,7 +826,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 }
             }
 
-            MultiDecorator(..) | MultiModifier(..) | AttrProcMacro(..) => {
+            MultiDecorator(..) | MultiModifier(..) |
+            AttrProcMacro(..) | SyntaxExtension::NonMacroAttr => {
                 self.cx.span_err(path.span,
                                  &format!("`{}` can only be used in attributes", path));
                 self.cx.trace_macros_diag();
@@ -1669,13 +1684,16 @@ impl<'feat> ExpansionConfig<'feat> {
         fn enable_allow_internal_unstable = allow_internal_unstable,
         fn enable_custom_derive = custom_derive,
         fn enable_format_args_nl = format_args_nl,
-        fn use_extern_macros_enabled = use_extern_macros,
         fn macros_in_extern_enabled = macros_in_extern,
         fn proc_macro_mod = proc_macro_mod,
         fn proc_macro_gen = proc_macro_gen,
         fn proc_macro_expr = proc_macro_expr,
         fn proc_macro_non_items = proc_macro_non_items,
     }
+
+    pub fn use_extern_macros_enabled(&self) -> bool {
+        self.features.map_or(false, |features| features.use_extern_macros())
+    }
 }
 
 // A Marker adds the given mark to the syntax context.
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 37a021a9529..77e3faa5b1f 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -80,6 +80,11 @@ macro_rules! declare_features {
             {
                 $(f(stringify!($feature), self.$feature);)+
             }
+
+            pub fn use_extern_macros(&self) -> bool {
+                // The `decl_macro` and `tool_attributes` features imply `use_extern_macros`.
+                self.use_extern_macros || self.decl_macro || self.tool_attributes
+            }
         }
     };
 
@@ -689,6 +694,10 @@ pub fn deprecated_attributes() -> Vec<&'static (&'static str, AttributeType, Att
     BUILTIN_ATTRIBUTES.iter().filter(|a| a.2.is_deprecated()).collect()
 }
 
+pub fn is_builtin_attr_name(name: ast::Name) -> bool {
+    BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| name == builtin_name)
+}
+
 pub fn is_builtin_attr(attr: &ast::Attribute) -> bool {
     BUILTIN_ATTRIBUTES.iter().any(|&(builtin_name, _, _)| attr.check_name(builtin_name)) ||
     attr.name().as_str().starts_with("rustc_")
@@ -1198,28 +1207,9 @@ impl<'a> Context<'a> {
             // before the plugin attributes are registered
             // so we skip this then
             if !is_macro {
-                if attr.is_scoped() {
-                    gate_feature!(self, tool_attributes, attr.span,
-                                  &format!("scoped attribute `{}` is experimental", attr.path));
-                    if attr::is_known_tool(attr) {
-                        attr::mark_used(attr);
-                    } else {
-                        span_err!(
-                            self.parse_sess.span_diagnostic,
-                            attr.span,
-                            E0694,
-                            "an unknown tool name found in scoped attribute: `{}`.",
-                            attr.path
-                        );
-                    }
-                } else {
-                    gate_feature!(self, custom_attribute, attr.span,
-                                  &format!("The attribute `{}` is currently \
-                                            unknown to the compiler and \
-                                            may have meaning \
-                                            added to it in the future",
-                                           attr.path));
-                }
+                let msg = format!("The attribute `{}` is currently unknown to the compiler and \
+                                   may have meaning added to it in the future", attr.path);
+                gate_feature!(self, custom_attribute, attr.span, &msg);
             }
         }
     }
@@ -1529,7 +1519,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             }
         }
 
-        if self.context.features.use_extern_macros && attr::is_known(attr) {
+        if self.context.features.use_extern_macros() && attr::is_known(attr) {
             return
         }
 
@@ -2004,7 +1994,7 @@ impl FeatureChecker {
     // the branching can be eliminated by modifying `set!()` to set these spans
     // only for the features that need to be checked for mutual exclusion.
     fn collect(&mut self, features: &Features, span: Span) {
-        if features.use_extern_macros {
+        if features.use_extern_macros() {
             // If self.use_extern_macros is None, set to Some(span)
             self.use_extern_macros = self.use_extern_macros.or(Some(span));
         }
diff --git a/src/test/compile-fail/unknown-tool-name.rs b/src/test/compile-fail/unknown-tool-name.rs
index c2192a21d90..c4d22e6d392 100644
--- a/src/test/compile-fail/unknown-tool-name.rs
+++ b/src/test/compile-fail/unknown-tool-name.rs
@@ -8,9 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(tool_attributes)]
+#![feature(use_extern_macros, proc_macro_path_invoc)]
 
-#![foo::bar] //~ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
-
-#[foo::bar] //~ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
+#[foo::bar] //~ ERROR failed to resolve. Use of undeclared type or module `foo`
 fn main() {}
diff --git a/src/test/run-pass-fulldeps/proc-macro/issue-42708.rs b/src/test/run-pass-fulldeps/proc-macro/issue-42708.rs
index df4a1d8994b..d4af99f97c5 100644
--- a/src/test/run-pass-fulldeps/proc-macro/issue-42708.rs
+++ b/src/test/run-pass-fulldeps/proc-macro/issue-42708.rs
@@ -11,7 +11,7 @@
 // aux-build:issue-42708.rs
 // ignore-stage1
 
-#![feature(decl_macro, use_extern_macros, proc_macro_path_invoc)]
+#![feature(decl_macro, proc_macro_path_invoc)]
 #![allow(unused)]
 
 extern crate issue_42708;
diff --git a/src/test/run-pass-fulldeps/proc-macro/issue-50061.rs b/src/test/run-pass-fulldeps/proc-macro/issue-50061.rs
index 15dff94c88c..53783e7fedb 100644
--- a/src/test/run-pass-fulldeps/proc-macro/issue-50061.rs
+++ b/src/test/run-pass-fulldeps/proc-macro/issue-50061.rs
@@ -11,7 +11,7 @@
 // aux-build:issue-50061.rs
 // ignore-stage1
 
-#![feature(use_extern_macros, proc_macro_path_invoc, decl_macro)]
+#![feature(proc_macro_path_invoc, decl_macro)]
 
 extern crate issue_50061;
 
diff --git a/src/test/ui-fulldeps/proc-macro/parent-source-spans.rs b/src/test/ui-fulldeps/proc-macro/parent-source-spans.rs
index a60841d848c..18f5b0f506c 100644
--- a/src/test/ui-fulldeps/proc-macro/parent-source-spans.rs
+++ b/src/test/ui-fulldeps/proc-macro/parent-source-spans.rs
@@ -11,7 +11,7 @@
 // aux-build:parent-source-spans.rs
 // ignore-stage1
 
-#![feature(use_extern_macros, decl_macro, proc_macro_non_items)]
+#![feature(decl_macro, proc_macro_non_items)]
 
 extern crate parent_source_spans;
 
diff --git a/src/test/ui/auxiliary/macro-in-other-crate.rs b/src/test/ui/auxiliary/macro-in-other-crate.rs
index 01282f2ad24..c787cedc2d0 100644
--- a/src/test/ui/auxiliary/macro-in-other-crate.rs
+++ b/src/test/ui/auxiliary/macro-in-other-crate.rs
@@ -12,3 +12,8 @@
 macro_rules! mac {
     ($ident:ident) => { let $ident = 42; }
 }
+
+#[macro_export]
+macro_rules! inline {
+    () => ()
+}
diff --git a/src/test/ui/feature-gate-macros_in_extern.stderr b/src/test/ui/feature-gate-macros_in_extern.stderr
index 748adc390d8..5d7e01fbbb7 100644
--- a/src/test/ui/feature-gate-macros_in_extern.stderr
+++ b/src/test/ui/feature-gate-macros_in_extern.stderr
@@ -1,4 +1,4 @@
-error[E0658]: macro invocations in `extern {}` blocks are experimental. (see issue #49476)
+error[E0658]: macro and proc-macro invocations in `extern {}` blocks are experimental. (see issue #49476)
   --> $DIR/feature-gate-macros_in_extern.rs:29:5
    |
 LL |     returns_isize!(rust_get_test_int);
@@ -6,7 +6,7 @@ LL |     returns_isize!(rust_get_test_int);
    |
    = help: add #![feature(macros_in_extern)] to the crate attributes to enable
 
-error[E0658]: macro invocations in `extern {}` blocks are experimental. (see issue #49476)
+error[E0658]: macro and proc-macro invocations in `extern {}` blocks are experimental. (see issue #49476)
   --> $DIR/feature-gate-macros_in_extern.rs:31:5
    |
 LL |     takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
@@ -14,7 +14,7 @@ LL |     takes_u32_returns_u32!(rust_dbg_extern_identity_u32);
    |
    = help: add #![feature(macros_in_extern)] to the crate attributes to enable
 
-error[E0658]: macro invocations in `extern {}` blocks are experimental. (see issue #49476)
+error[E0658]: macro and proc-macro invocations in `extern {}` blocks are experimental. (see issue #49476)
   --> $DIR/feature-gate-macros_in_extern.rs:33:5
    |
 LL |     emits_nothing!();
diff --git a/src/test/ui/feature-gate-tool_attributes.rs b/src/test/ui/feature-gate-tool_attributes.rs
index 2b7cf56d938..47c623574ec 100644
--- a/src/test/ui/feature-gate-tool_attributes.rs
+++ b/src/test/ui/feature-gate-tool_attributes.rs
@@ -8,8 +8,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(use_extern_macros)]
+
 fn main() {
-    #[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental
+    #[rustfmt::skip] //~ ERROR tool attributes are unstable
     let x = 3
         ;
 }
diff --git a/src/test/ui/feature-gate-tool_attributes.stderr b/src/test/ui/feature-gate-tool_attributes.stderr
index da89c4a5ef6..ebc266e004e 100644
--- a/src/test/ui/feature-gate-tool_attributes.stderr
+++ b/src/test/ui/feature-gate-tool_attributes.stderr
@@ -1,7 +1,7 @@
-error[E0658]: scoped attribute `rustfmt::skip` is experimental (see issue #44690)
-  --> $DIR/feature-gate-tool_attributes.rs:12:5
+error[E0658]: tool attributes are unstable (see issue #44690)
+  --> $DIR/feature-gate-tool_attributes.rs:14:5
    |
-LL |     #[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental
+LL |     #[rustfmt::skip] //~ ERROR tool attributes are unstable
    |     ^^^^^^^^^^^^^^^^
    |
    = help: add #![feature(tool_attributes)] to the crate attributes to enable
diff --git a/src/test/ui/issue-11692-1.rs b/src/test/ui/issue-11692-1.rs
index f577aad04e6..ff6009da72f 100644
--- a/src/test/ui/issue-11692-1.rs
+++ b/src/test/ui/issue-11692-1.rs
@@ -9,6 +9,6 @@
 // except according to those terms.
 
 fn main() {
-    print!(test!());
+    print!(testo!());
     //~^ ERROR: format argument must be a string literal
 }
diff --git a/src/test/ui/issue-11692-1.stderr b/src/test/ui/issue-11692-1.stderr
index bee73e9f568..61ff455d16c 100644
--- a/src/test/ui/issue-11692-1.stderr
+++ b/src/test/ui/issue-11692-1.stderr
@@ -1,11 +1,11 @@
 error: format argument must be a string literal
   --> $DIR/issue-11692-1.rs:12:12
    |
-LL |     print!(test!());
-   |            ^^^^^^^
+LL |     print!(testo!());
+   |            ^^^^^^^^
 help: you might be missing a string literal to format with
    |
-LL |     print!("{}", test!());
+LL |     print!("{}", testo!());
    |            ^^^^^
 
 error: aborting due to previous error
diff --git a/src/test/ui/issue-11692-2.rs b/src/test/ui/issue-11692-2.rs
index acac2d151fe..c595b0fb2c2 100644
--- a/src/test/ui/issue-11692-2.rs
+++ b/src/test/ui/issue-11692-2.rs
@@ -10,5 +10,5 @@
 
 fn main() {
     concat!(test!());
-    //~^ ERROR cannot find macro `test!` in this scope
+    //~^ ERROR expected a macro, found non-macro attribute
 }
diff --git a/src/test/ui/issue-11692-2.stderr b/src/test/ui/issue-11692-2.stderr
index 51d6041e922..3d080bd46dc 100644
--- a/src/test/ui/issue-11692-2.stderr
+++ b/src/test/ui/issue-11692-2.stderr
@@ -1,4 +1,4 @@
-error: cannot find macro `test!` in this scope
+error: expected a macro, found non-macro attribute
   --> $DIR/issue-11692-2.rs:12:13
    |
 LL |     concat!(test!());
diff --git a/src/test/ui/issue-50187.rs b/src/test/ui/issue-50187.rs
index 87acf106393..ccb2742841c 100644
--- a/src/test/ui/issue-50187.rs
+++ b/src/test/ui/issue-50187.rs
@@ -10,7 +10,7 @@
 
 // compile-pass
 
-#![feature(use_extern_macros, decl_macro)]
+#![feature(decl_macro)]
 
 mod type_ns {
     pub type A = u8;
diff --git a/src/test/compile-fail/unknown_tool_attributes-1.rs b/src/test/ui/macro-path-prelude-fail-1.rs
index ba38c297a11..b953805a7eb 100644
--- a/src/test/compile-fail/unknown_tool_attributes-1.rs
+++ b/src/test/ui/macro-path-prelude-fail-1.rs
@@ -8,11 +8,13 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// Make sure that 'custom_attributes' feature does not allow scoped attributes.
+#![feature(use_extern_macros, extern_prelude)]
 
-#![feature(custom_attributes)]
+mod m {
+    fn check() {
+        Vec::clone!(); //~ ERROR failed to resolve. Not a module `Vec`
+        u8::clone!(); //~ ERROR failed to resolve. Not a module `u8`
+    }
+}
 
-#[foo::bar]
-//~^ ERROR scoped attribute `foo::bar` is experimental (see issue #44690) [E0658]
-//~^^ ERROR an unknown tool name found in scoped attribute: `foo::bar`. [E0694]
 fn main() {}
diff --git a/src/test/ui/macro-path-prelude-fail-1.stderr b/src/test/ui/macro-path-prelude-fail-1.stderr
new file mode 100644
index 00000000000..fc74937d912
--- /dev/null
+++ b/src/test/ui/macro-path-prelude-fail-1.stderr
@@ -0,0 +1,15 @@
+error[E0433]: failed to resolve. Not a module `Vec`
+  --> $DIR/macro-path-prelude-fail-1.rs:15:9
+   |
+LL |         Vec::clone!(); //~ ERROR failed to resolve. Not a module `Vec`
+   |         ^^^ Not a module `Vec`
+
+error[E0433]: failed to resolve. Not a module `u8`
+  --> $DIR/macro-path-prelude-fail-1.rs:16:9
+   |
+LL |         u8::clone!(); //~ ERROR failed to resolve. Not a module `u8`
+   |         ^^ Not a module `u8`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/src/test/ui/macro-path-prelude-fail-2.rs b/src/test/ui/macro-path-prelude-fail-2.rs
new file mode 100644
index 00000000000..ec77e276bd4
--- /dev/null
+++ b/src/test/ui/macro-path-prelude-fail-2.rs
@@ -0,0 +1,19 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(use_extern_macros)]
+
+mod m {
+    fn check() {
+        Result::Ok!(); //~ ERROR fail to resolve non-ident macro path
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/macro-path-prelude-fail-2.stderr b/src/test/ui/macro-path-prelude-fail-2.stderr
new file mode 100644
index 00000000000..d23aed847a3
--- /dev/null
+++ b/src/test/ui/macro-path-prelude-fail-2.stderr
@@ -0,0 +1,8 @@
+error: fail to resolve non-ident macro path
+  --> $DIR/macro-path-prelude-fail-2.rs:15:9
+   |
+LL |         Result::Ok!(); //~ ERROR fail to resolve non-ident macro path
+   |         ^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/macro-path-prelude-fail-3.rs b/src/test/ui/macro-path-prelude-fail-3.rs
new file mode 100644
index 00000000000..4cf90019d40
--- /dev/null
+++ b/src/test/ui/macro-path-prelude-fail-3.rs
@@ -0,0 +1,18 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(use_extern_macros)]
+
+#[derive(inline)] //~ ERROR expected a macro, found non-macro attribute
+struct S;
+
+fn main() {
+    inline!(); //~ ERROR expected a macro, found non-macro attribute
+}
diff --git a/src/test/ui/macro-path-prelude-fail-3.stderr b/src/test/ui/macro-path-prelude-fail-3.stderr
new file mode 100644
index 00000000000..bd1015b7ee1
--- /dev/null
+++ b/src/test/ui/macro-path-prelude-fail-3.stderr
@@ -0,0 +1,14 @@
+error: expected a macro, found non-macro attribute
+  --> $DIR/macro-path-prelude-fail-3.rs:13:10
+   |
+LL | #[derive(inline)] //~ ERROR expected a macro, found non-macro attribute
+   |          ^^^^^^
+
+error: expected a macro, found non-macro attribute
+  --> $DIR/macro-path-prelude-fail-3.rs:17:5
+   |
+LL |     inline!(); //~ ERROR expected a macro, found non-macro attribute
+   |     ^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/compile-fail/feature-gate-tool_attributes.rs b/src/test/ui/macro-path-prelude-pass.rs
index 5a7536ca330..bc58754513b 100644
--- a/src/test/compile-fail/feature-gate-tool_attributes.rs
+++ b/src/test/ui/macro-path-prelude-pass.rs
@@ -8,8 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-fn main() {
-    #[rustfmt::skip] //~ ERROR scoped attribute `rustfmt::skip` is experimental
-    let x =
-        3;
+// compile-pass
+
+#![feature(use_extern_macros, extern_prelude)]
+
+mod m {
+    fn check() {
+        std::panic!(); // OK
+    }
 }
+
+fn main() {}
diff --git a/src/test/ui/macro-path-prelude-shadowing.rs b/src/test/ui/macro-path-prelude-shadowing.rs
new file mode 100644
index 00000000000..1aff7777ef7
--- /dev/null
+++ b/src/test/ui/macro-path-prelude-shadowing.rs
@@ -0,0 +1,41 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:macro-in-other-crate.rs
+
+#![feature(decl_macro, extern_prelude)]
+
+macro_rules! add_macro_expanded_things_to_macro_prelude {() => {
+    #[macro_use]
+    extern crate macro_in_other_crate;
+}}
+
+add_macro_expanded_things_to_macro_prelude!();
+
+mod m1 {
+    fn check() {
+        inline!(); //~ ERROR `inline` is ambiguous
+    }
+}
+
+mod m2 {
+    pub mod std {
+        pub macro panic() {}
+    }
+}
+
+mod m3 {
+    use m2::*; // glob-import user-defined `std`
+    fn check() {
+        std::panic!(); //~ ERROR `std` is ambiguous
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/macro-path-prelude-shadowing.stderr b/src/test/ui/macro-path-prelude-shadowing.stderr
new file mode 100644
index 00000000000..0e1b9a985a3
--- /dev/null
+++ b/src/test/ui/macro-path-prelude-shadowing.stderr
@@ -0,0 +1,42 @@
+error[E0659]: `inline` is ambiguous
+  --> $DIR/macro-path-prelude-shadowing.rs:24:9
+   |
+LL |         inline!(); //~ ERROR `inline` is ambiguous
+   |         ^^^^^^
+   |
+note: `inline` could refer to the name imported here
+  --> $DIR/macro-path-prelude-shadowing.rs:16:5
+   |
+LL |     #[macro_use]
+   |     ^^^^^^^^^^^^
+...
+LL | add_macro_expanded_things_to_macro_prelude!();
+   | ---------------------------------------------- in this macro invocation
+note: `inline` could also refer to the name defined here
+  --> $DIR/macro-path-prelude-shadowing.rs:24:9
+   |
+LL |         inline!(); //~ ERROR `inline` is ambiguous
+   |         ^^^^^^
+   = note: macro-expanded macro imports do not shadow
+
+error[E0659]: `std` is ambiguous
+  --> $DIR/macro-path-prelude-shadowing.rs:37:9
+   |
+LL |         std::panic!(); //~ ERROR `std` is ambiguous
+   |         ^^^^^^^^^^
+   |
+note: `std` could refer to the name imported here
+  --> $DIR/macro-path-prelude-shadowing.rs:35:9
+   |
+LL |     use m2::*; // glob-import user-defined `std`
+   |         ^^^^^
+note: `std` could also refer to the name defined here
+  --> $DIR/macro-path-prelude-shadowing.rs:37:9
+   |
+LL |         std::panic!(); //~ ERROR `std` is ambiguous
+   |         ^^^
+   = note: consider adding an explicit import of `std` to disambiguate
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0659`.
diff --git a/src/test/ui/tool-attributes-disabled-1.rs b/src/test/ui/tool-attributes-disabled-1.rs
new file mode 100644
index 00000000000..87d47b75607
--- /dev/null
+++ b/src/test/ui/tool-attributes-disabled-1.rs
@@ -0,0 +1,15 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// If macro modularization (`use_extern_macros`) is not enabled,
+// then tool attributes are treated as custom attributes.
+
+#[rustfmt::bar] //~ ERROR The attribute `rustfmt::bar` is currently unknown to the compiler
+fn main() {}
diff --git a/src/test/ui/tool-attributes-disabled-1.stderr b/src/test/ui/tool-attributes-disabled-1.stderr
new file mode 100644
index 00000000000..6302c06057a
--- /dev/null
+++ b/src/test/ui/tool-attributes-disabled-1.stderr
@@ -0,0 +1,11 @@
+error[E0658]: The attribute `rustfmt::bar` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642)
+  --> $DIR/tool-attributes-disabled-1.rs:14:1
+   |
+LL | #[rustfmt::bar] //~ ERROR The attribute `rustfmt::bar` is currently unknown to the compiler
+   | ^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(custom_attribute)] 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/tool-attributes-disabled-2.rs b/src/test/ui/tool-attributes-disabled-2.rs
new file mode 100644
index 00000000000..160dda05b1e
--- /dev/null
+++ b/src/test/ui/tool-attributes-disabled-2.rs
@@ -0,0 +1,19 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// If macro modularization (`use_extern_macros`) is not enabled,
+// then tool attributes are treated as custom attributes.
+
+// compile-pass
+
+#![feature(custom_attribute)]
+
+#[rustfmt::bar]
+fn main() {}
diff --git a/src/test/ui/tool-attributes-misplaced-1.rs b/src/test/ui/tool-attributes-misplaced-1.rs
new file mode 100644
index 00000000000..b3355352423
--- /dev/null
+++ b/src/test/ui/tool-attributes-misplaced-1.rs
@@ -0,0 +1,28 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(tool_attributes)]
+
+type A = rustfmt; //~ ERROR expected type, found tool module `rustfmt`
+type B = rustfmt::skip; //~ ERROR expected type, found non-macro attribute `rustfmt::skip`
+
+#[derive(rustfmt)] //~ ERROR cannot find derive macro `rustfmt` in this scope
+struct S;
+
+#[rustfmt] //~ ERROR cannot find attribute macro `rustfmt` in this scope
+fn check() {}
+
+#[rustfmt::skip] // OK
+fn main() {
+    rustfmt; //~ ERROR expected value, found tool module `rustfmt`
+    rustfmt!(); //~ ERROR cannot find macro `rustfmt!` in this scope
+
+    rustfmt::skip; //~ ERROR expected value, found non-macro attribute `rustfmt::skip`
+}
diff --git a/src/test/ui/tool-attributes-misplaced-1.stderr b/src/test/ui/tool-attributes-misplaced-1.stderr
new file mode 100644
index 00000000000..b9e61121406
--- /dev/null
+++ b/src/test/ui/tool-attributes-misplaced-1.stderr
@@ -0,0 +1,46 @@
+error: cannot find derive macro `rustfmt` in this scope
+  --> $DIR/tool-attributes-misplaced-1.rs:16:10
+   |
+LL | #[derive(rustfmt)] //~ ERROR cannot find derive macro `rustfmt` in this scope
+   |          ^^^^^^^
+
+error: cannot find attribute macro `rustfmt` in this scope
+  --> $DIR/tool-attributes-misplaced-1.rs:19:3
+   |
+LL | #[rustfmt] //~ ERROR cannot find attribute macro `rustfmt` in this scope
+   |   ^^^^^^^
+
+error: cannot find macro `rustfmt!` in this scope
+  --> $DIR/tool-attributes-misplaced-1.rs:25:5
+   |
+LL |     rustfmt!(); //~ ERROR cannot find macro `rustfmt!` in this scope
+   |     ^^^^^^^
+
+error[E0573]: expected type, found tool module `rustfmt`
+  --> $DIR/tool-attributes-misplaced-1.rs:13:10
+   |
+LL | type A = rustfmt; //~ ERROR expected type, found tool module `rustfmt`
+   |          ^^^^^^^ not a type
+
+error[E0573]: expected type, found non-macro attribute `rustfmt::skip`
+  --> $DIR/tool-attributes-misplaced-1.rs:14:10
+   |
+LL | type B = rustfmt::skip; //~ ERROR expected type, found non-macro attribute `rustfmt::skip`
+   |          ^^^^^^^^^^^^^ not a type
+
+error[E0423]: expected value, found tool module `rustfmt`
+  --> $DIR/tool-attributes-misplaced-1.rs:24:5
+   |
+LL |     rustfmt; //~ ERROR expected value, found tool module `rustfmt`
+   |     ^^^^^^^ not a value
+
+error[E0423]: expected value, found non-macro attribute `rustfmt::skip`
+  --> $DIR/tool-attributes-misplaced-1.rs:27:5
+   |
+LL |     rustfmt::skip; //~ ERROR expected value, found non-macro attribute `rustfmt::skip`
+   |     ^^^^^^^^^^^^^ not a value
+
+error: aborting due to 7 previous errors
+
+Some errors occurred: E0423, E0573.
+For more information about an error, try `rustc --explain E0423`.
diff --git a/src/test/ui/tool-attributes-misplaced-2.rs b/src/test/ui/tool-attributes-misplaced-2.rs
new file mode 100644
index 00000000000..3bb0e3dc343
--- /dev/null
+++ b/src/test/ui/tool-attributes-misplaced-2.rs
@@ -0,0 +1,18 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(tool_attributes)]
+
+#[derive(rustfmt::skip)] //~ ERROR expected a macro, found non-macro attribute
+struct S;
+
+fn main() {
+    rustfmt::skip!(); //~ ERROR expected a macro, found non-macro attribute
+}
diff --git a/src/test/ui/tool-attributes-misplaced-2.stderr b/src/test/ui/tool-attributes-misplaced-2.stderr
new file mode 100644
index 00000000000..66452267e94
--- /dev/null
+++ b/src/test/ui/tool-attributes-misplaced-2.stderr
@@ -0,0 +1,14 @@
+error: expected a macro, found non-macro attribute
+  --> $DIR/tool-attributes-misplaced-2.rs:13:10
+   |
+LL | #[derive(rustfmt::skip)] //~ ERROR expected a macro, found non-macro attribute
+   |          ^^^^^^^^^^^^^
+
+error: expected a macro, found non-macro attribute
+  --> $DIR/tool-attributes-misplaced-2.rs:17:5
+   |
+LL |     rustfmt::skip!(); //~ ERROR expected a macro, found non-macro attribute
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/tool-attributes-shadowing.rs b/src/test/ui/tool-attributes-shadowing.rs
new file mode 100644
index 00000000000..7913c9f40b5
--- /dev/null
+++ b/src/test/ui/tool-attributes-shadowing.rs
@@ -0,0 +1,16 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(tool_attributes, proc_macro_path_invoc)]
+
+mod rustfmt {}
+
+#[rustfmt::skip] //~ ERROR failed to resolve. Could not find `skip` in `rustfmt`
+fn main() {}
diff --git a/src/test/ui/tool-attributes-shadowing.stderr b/src/test/ui/tool-attributes-shadowing.stderr
new file mode 100644
index 00000000000..f668d677f7a
--- /dev/null
+++ b/src/test/ui/tool-attributes-shadowing.stderr
@@ -0,0 +1,9 @@
+error[E0433]: failed to resolve. Could not find `skip` in `rustfmt`
+  --> $DIR/tool-attributes-shadowing.rs:15:12
+   |
+LL | #[rustfmt::skip] //~ ERROR failed to resolve. Could not find `skip` in `rustfmt`
+   |            ^^^^ Could not find `skip` in `rustfmt`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0433`.