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> {
-        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 };
-        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)
-            };
-            self.current_module = orig_current_module;
+        // 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).
 
-            match result.map(MacroBinding::binding) {
-                Ok(binding) => {
-                    if !record_used {
-                        return result;
+        assert!(ns == TypeNS  || ns == MacroNS);
+        ident = ident.modern();
+
+        // 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 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),
                     }
-                    if let Ok(MacroBinding::Modern(shadower)) = potential_illegal_shadower {
-                        if shadower.def() != binding.def() {
-                            let name = ident.name;
+                }
+                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)
+                    }
+                }
+            };
+
+            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 Ok(result);
+                    }
+
+                    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);
                         }
                     }
-                    if binding.is_glob_import() || binding.expansion != Mark::root() {
-                        potential_illegal_shadower = result;
-                    } else {
-                        return result;
-                    }
-                },
-                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,
+                    // 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() {
+                        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),
             }
         }
+
+        // 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 137b94230a3..938fd2767c9 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -89,17 +89,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 {
@@ -245,10 +236,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 9f8909e1626..1fd77045a45 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -36,7 +36,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;
 
@@ -243,6 +243,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),
@@ -566,6 +575,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()?;
@@ -810,7 +824,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();
@@ -1612,13 +1627,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 40fb2c69e57..747cf3f1654 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
similarity index 62%
rename from src/test/compile-fail/unknown_tool_attributes-1.rs
rename to 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
similarity index 77%
rename from src/test/compile-fail/feature-gate-tool_attributes.rs
rename to 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`.