about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/ich/impls_syntax.rs8
-rw-r--r--src/librustc/lint/mod.rs46
-rw-r--r--src/librustc_interface/passes.rs27
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs3
-rw-r--r--src/librustc_resolve/diagnostics.rs8
-rw-r--r--src/librustc_resolve/lib.rs22
-rw-r--r--src/librustc_resolve/macros.rs43
-rw-r--r--src/librustc_resolve/resolve_imports.rs11
-rw-r--r--src/libsyntax/ext/base.rs20
-rw-r--r--src/libsyntax/print/pprust.rs12
-rw-r--r--src/libsyntax_ext/plugin_macro_defs.rs4
-rw-r--r--src/libsyntax_ext/proc_macro_harness.rs69
-rw-r--r--src/libsyntax_ext/standard_library_imports.rs110
-rw-r--r--src/libsyntax_ext/test.rs19
-rw-r--r--src/libsyntax_ext/test_harness.rs243
-rw-r--r--src/libsyntax_pos/hygiene.rs28
-rw-r--r--src/libsyntax_pos/lib.rs20
-rw-r--r--src/libsyntax_pos/symbol.rs27
-rw-r--r--src/test/ui/hygiene/auxiliary/not-libstd.rs1
-rw-r--r--src/test/ui/hygiene/prelude-import-hygiene.rs29
-rw-r--r--src/test/ui/imports/gensymed.rs4
-rw-r--r--src/test/ui/inaccessible-test-modules.stderr21
-rw-r--r--src/test/ui/proc-macro/dollar-crate-issue-57089.stdout32
-rw-r--r--src/test/ui/proc-macro/dollar-crate-issue-62325.stdout44
-rw-r--r--src/test/ui/proc-macro/dollar-crate.stdout96
-rw-r--r--src/test/ui/test-attrs/auxiliary/test_macro.rs (renamed from src/test/ui/test-shadowing/auxiliary/test_macro.rs)0
-rw-r--r--src/test/ui/test-attrs/decl-macro-test.rs22
-rw-r--r--src/test/ui/test-attrs/inaccessible-test-modules.rs (renamed from src/test/ui/inaccessible-test-modules.rs)4
-rw-r--r--src/test/ui/test-attrs/inaccessible-test-modules.stderr21
-rw-r--r--src/test/ui/test-attrs/test-allow-fail-attr.rs (renamed from src/test/ui/test-allow-fail-attr.rs)0
-rw-r--r--src/test/ui/test-attrs/test-attr-non-associated-functions.rs (renamed from src/test/ui/test-attr-non-associated-functions.rs)0
-rw-r--r--src/test/ui/test-attrs/test-attr-non-associated-functions.stderr (renamed from src/test/ui/test-attr-non-associated-functions.stderr)0
-rw-r--r--src/test/ui/test-attrs/test-cant-be-shadowed.rs (renamed from src/test/ui/test-shadowing/test-cant-be-shadowed.rs)0
-rw-r--r--src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs (renamed from src/test/ui/test-fn-signature-verification-for-explicit-return-type.rs)0
-rw-r--r--src/test/ui/test-attrs/test-main-not-dead-attr.rs (renamed from src/test/ui/test-main-not-dead-attr.rs)0
-rw-r--r--src/test/ui/test-attrs/test-main-not-dead.rs (renamed from src/test/ui/test-main-not-dead.rs)0
-rw-r--r--src/test/ui/test-attrs/test-on-macro.rs (renamed from src/test/ui/test-on-macro.rs)0
-rw-r--r--src/test/ui/test-attrs/test-on-macro.stderr (renamed from src/test/ui/test-on-macro.stderr)0
-rw-r--r--src/test/ui/test-attrs/test-runner-hides-buried-main.rs (renamed from src/test/ui/test-runner-hides-buried-main.rs)0
-rw-r--r--src/test/ui/test-attrs/test-runner-hides-main.rs (renamed from src/test/ui/test-runner-hides-main.rs)0
-rw-r--r--src/test/ui/test-attrs/test-runner-hides-start.rs (renamed from src/test/ui/test-runner-hides-start.rs)0
-rw-r--r--src/test/ui/test-attrs/test-should-fail-good-message.rs (renamed from src/test/ui/test-should-fail-good-message.rs)0
-rw-r--r--src/test/ui/test-attrs/test-should-panic-attr.rs (renamed from src/test/ui/test-should-panic-attr.rs)0
-rw-r--r--src/test/ui/test-attrs/test-should-panic-attr.stderr (renamed from src/test/ui/test-should-panic-attr.stderr)0
-rw-r--r--src/test/ui/test-attrs/test-vs-cfg-test.rs (renamed from src/test/ui/test-vs-cfg-test.rs)0
-rw-r--r--src/test/ui/test-attrs/test-warns-dead-code.rs (renamed from src/test/ui/test-warns-dead-code.rs)0
-rw-r--r--src/test/ui/test-attrs/test-warns-dead-code.stderr (renamed from src/test/ui/test-warns-dead-code.stderr)0
47 files changed, 555 insertions, 439 deletions
diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs
index ddfca3a4cfb..646060bc8be 100644
--- a/src/librustc/ich/impls_syntax.rs
+++ b/src/librustc/ich/impls_syntax.rs
@@ -390,9 +390,17 @@ impl_stable_hash_for!(struct ::syntax_pos::hygiene::ExpnData {
 impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnKind {
     Root,
     Macro(kind, descr),
+    AstPass(kind),
     Desugaring(kind)
 });
 
+impl_stable_hash_for!(enum ::syntax_pos::hygiene::AstPass {
+    StdImports,
+    TestHarness,
+    ProcMacroHarness,
+    PluginMacroDefs,
+});
+
 impl_stable_hash_for!(enum ::syntax_pos::hygiene::DesugaringKind {
     CondTemporary,
     Async,
diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs
index a3518b2b478..e33f8e27871 100644
--- a/src/librustc/lint/mod.rs
+++ b/src/librustc/lint/mod.rs
@@ -646,6 +646,30 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
         (Level::Forbid, None) => sess.struct_err(msg),
     };
 
+    // Check for future incompatibility lints and issue a stronger warning.
+    let lints = sess.lint_store.borrow();
+    let lint_id = LintId::of(lint);
+    let future_incompatible = lints.future_incompatible(lint_id);
+
+    // If this code originates in a foreign macro, aka something that this crate
+    // did not itself author, then it's likely that there's nothing this crate
+    // can do about it. We probably want to skip the lint entirely.
+    if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
+        // Any suggestions made here are likely to be incorrect, so anything we
+        // emit shouldn't be automatically fixed by rustfix.
+        err.allow_suggestions(false);
+
+        // If this is a future incompatible lint it'll become a hard error, so
+        // we have to emit *something*. Also allow lints to whitelist themselves
+        // on a case-by-case basis for emission in a foreign macro.
+        if future_incompatible.is_none() && !lint.report_in_external_macro {
+            err.cancel();
+            // Don't continue further, since we don't want to have
+            // `diag_span_note_once` called for a diagnostic that isn't emitted.
+            return err;
+        }
+    }
+
     let name = lint.name_lower();
     match src {
         LintSource::Default => {
@@ -695,10 +719,6 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
 
     err.code(DiagnosticId::Lint(name));
 
-    // Check for future incompatibility lints and issue a stronger warning.
-    let lints = sess.lint_store.borrow();
-    let lint_id = LintId::of(lint);
-    let future_incompatible = lints.future_incompatible(lint_id);
     if let Some(future_incompatible) = future_incompatible {
         const STANDARD_MESSAGE: &str =
             "this was previously accepted by the compiler but is being phased out; \
@@ -723,22 +743,6 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
         err.note(&citation);
     }
 
-    // If this code originates in a foreign macro, aka something that this crate
-    // did not itself author, then it's likely that there's nothing this crate
-    // can do about it. We probably want to skip the lint entirely.
-    if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
-        // Any suggestions made here are likely to be incorrect, so anything we
-        // emit shouldn't be automatically fixed by rustfix.
-        err.allow_suggestions(false);
-
-        // If this is a future incompatible lint it'll become a hard error, so
-        // we have to emit *something*. Also allow lints to whitelist themselves
-        // on a case-by-case basis for emission in a foreign macro.
-        if future_incompatible.is_none() && !lint.report_in_external_macro {
-            err.cancel()
-        }
-    }
-
     return err
 }
 
@@ -868,7 +872,7 @@ pub fn in_external_macro(sess: &Session, span: Span) -> bool {
     let expn_data = span.ctxt().outer_expn_data();
     match expn_data.kind {
         ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) => false,
-        ExpnKind::Desugaring(_) => true, // well, it's "external"
+        ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external"
         ExpnKind::Macro(MacroKind::Bang, _) => {
             if expn_data.def_site.is_dummy() {
                 // dummy span for the def_site means it's an external macro
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 24b44964e4f..5ad737776da 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -233,7 +233,7 @@ pub fn register_plugins<'a>(
         syntax::attr::inject(krate, &sess.parse_sess, &sess.opts.debugging_opts.crate_attr)
     });
 
-    let (mut krate, features) = syntax::config::features(
+    let (krate, features) = syntax::config::features(
         krate,
         &sess.parse_sess,
         sess.edition(),
@@ -268,16 +268,6 @@ pub fn register_plugins<'a>(
         middle::recursion_limit::update_limits(sess, &krate);
     });
 
-    krate = time(sess, "crate injection", || {
-        let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| &**s);
-        let (krate, name) =
-            syntax_ext::standard_library_imports::inject(krate, alt_std_name, sess.edition());
-        if let Some(name) = name {
-            sess.parse_sess.injected_crate_name.set(name);
-        }
-        krate
-    });
-
     let registrars = time(sess, "plugin loading", || {
         plugin::load::load_plugins(
             sess,
@@ -370,6 +360,21 @@ fn configure_and_expand_inner<'a>(
         &resolver_arenas,
     );
     syntax_ext::register_builtin_macros(&mut resolver, sess.edition());
+
+    krate = time(sess, "crate injection", || {
+        let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
+        let (krate, name) = syntax_ext::standard_library_imports::inject(
+            krate,
+            &mut resolver,
+            &sess.parse_sess,
+            alt_std_name,
+        );
+        if let Some(name) = name {
+            sess.parse_sess.injected_crate_name.set(name);
+        }
+        krate
+    });
+
     syntax_ext::plugin_macro_defs::inject(
         &mut krate, &mut resolver, plugin_info.syntax_exts, sess.edition()
     );
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 165a4c707bb..81bd687e263 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -126,7 +126,8 @@ impl<'a> Resolver<'a> {
     crate fn macro_def_scope(&mut self, expn_id: ExpnId) -> Module<'a> {
         let def_id = match self.macro_defs.get(&expn_id) {
             Some(def_id) => *def_id,
-            None => return self.graph_root,
+            None => return self.ast_transform_scopes.get(&expn_id)
+                .unwrap_or(&self.graph_root),
         };
         if let Some(id) = self.definitions.as_local_node_id(def_id) {
             self.local_macro_def_scopes[&id]
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index b79e0c2bd3b..c479912b4ef 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -604,6 +604,14 @@ impl<'a> Resolver<'a> {
         if lookup_ident.span.rust_2018() {
             let extern_prelude_names = self.extern_prelude.clone();
             for (ident, _) in extern_prelude_names.into_iter() {
+                if ident.span.from_expansion() {
+                    // Idents are adjusted to the root context before being
+                    // resolved in the extern prelude, so reporting this to the
+                    // user is no help. This skips the injected
+                    // `extern crate std` in the 2018 edition, which would
+                    // otherwise cause duplicate suggestions.
+                    continue;
+                }
                 if let Some(crate_id) = self.crate_loader.maybe_process_path_extern(ident.name,
                                                                                     ident.span) {
                     let crate_root = self.get_module(DefId {
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 9b92bb7698a..7f65e5dfaa2 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -880,6 +880,10 @@ pub struct Resolver<'a> {
     /// There will be an anonymous module created around `g` with the ID of the
     /// entry block for `f`.
     block_map: NodeMap<Module<'a>>,
+    /// A fake module that contains no definition and no prelude. Used so that
+    /// some AST passes can generate identifiers that only resolve to local or
+    /// language items.
+    empty_module: Module<'a>,
     module_map: FxHashMap<DefId, Module<'a>>,
     extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>,
     binding_parent_modules: FxHashMap<PtrKey<'a, NameBinding<'a>>, Module<'a>>,
@@ -914,6 +918,7 @@ pub struct Resolver<'a> {
     non_macro_attrs: [Lrc<SyntaxExtension>; 2],
     macro_defs: FxHashMap<ExpnId, DefId>,
     local_macro_def_scopes: FxHashMap<NodeId, Module<'a>>,
+    ast_transform_scopes: FxHashMap<ExpnId, Module<'a>>,
     unused_macros: NodeMap<Span>,
     proc_macro_stubs: NodeSet,
     /// Traces collected during macro resolution and validated when it's complete.
@@ -1081,6 +1086,21 @@ impl<'a> Resolver<'a> {
             no_implicit_prelude: attr::contains_name(&krate.attrs, sym::no_implicit_prelude),
             ..ModuleData::new(None, root_module_kind, root_def_id, ExpnId::root(), krate.span)
         });
+        let empty_module_kind = ModuleKind::Def(
+            DefKind::Mod,
+            root_def_id,
+            kw::Invalid,
+        );
+        let empty_module = arenas.alloc_module(ModuleData {
+            no_implicit_prelude: true,
+            ..ModuleData::new(
+                Some(graph_root),
+                empty_module_kind,
+                root_def_id,
+                ExpnId::root(),
+                DUMMY_SP,
+            )
+        });
         let mut module_map = FxHashMap::default();
         module_map.insert(DefId::local(CRATE_DEF_INDEX), graph_root);
 
@@ -1140,10 +1160,12 @@ impl<'a> Resolver<'a> {
             label_res_map: Default::default(),
             export_map: FxHashMap::default(),
             trait_map: Default::default(),
+            empty_module,
             module_map,
             block_map: Default::default(),
             extern_module_map: FxHashMap::default(),
             binding_parent_modules: FxHashMap::default(),
+            ast_transform_scopes: FxHashMap::default(),
 
             glob_map: Default::default(),
 
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 7224bd74230..054b17fec78 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -8,6 +8,7 @@ use crate::{ModuleOrUniformRoot, KNOWN_TOOLS};
 use crate::Namespace::*;
 use crate::resolve_imports::ImportResolver;
 use rustc::hir::def::{self, DefKind, NonMacroAttrKind};
+use rustc::hir::def_id;
 use rustc::middle::stability;
 use rustc::{ty, lint, span_bug};
 use syntax::ast::{self, NodeId, Ident};
@@ -25,6 +26,7 @@ use syntax_pos::{Span, DUMMY_SP};
 
 use std::{mem, ptr};
 use rustc_data_structures::sync::Lrc;
+use syntax_pos::hygiene::AstPass;
 
 type Res = def::Res<NodeId>;
 
@@ -95,16 +97,6 @@ impl<'a> base::Resolver for Resolver<'a> {
         self.session.next_node_id()
     }
 
-    fn get_module_scope(&mut self, id: NodeId) -> ExpnId {
-        let expn_id = ExpnId::fresh(Some(ExpnData::default(
-            ExpnKind::Macro(MacroKind::Attr, sym::test_case), DUMMY_SP, self.session.edition()
-        )));
-        let module = self.module_map[&self.definitions.local_def_id(id)];
-        self.invocation_parent_scopes.insert(expn_id, ParentScope::module(module));
-        self.definitions.set_invocation_parent(expn_id, module.def_id().unwrap().index);
-        expn_id
-    }
-
     fn resolve_dollar_crates(&mut self) {
         hygiene::update_dollar_crate_names(|ctxt| {
             let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt));
@@ -136,6 +128,37 @@ impl<'a> base::Resolver for Resolver<'a> {
         }
     }
 
+    // Create a new Expansion with a definition site of the provided module, or
+    // a fake empty `#[no_implicit_prelude]` module if no module is provided.
+    fn expansion_for_ast_pass(
+        &mut self,
+        call_site: Span,
+        pass: AstPass,
+        features: &[Symbol],
+        parent_module_id: Option<NodeId>,
+    ) -> ExpnId {
+        let expn_id = ExpnId::fresh(Some(ExpnData::allow_unstable(
+            ExpnKind::AstPass(pass),
+            call_site,
+            self.session.edition(),
+            features.into(),
+        )));
+
+        let parent_scope = if let Some(module_id) = parent_module_id {
+            let parent_def_id = self.definitions.local_def_id(module_id);
+            self.definitions.add_parent_module_of_macro_def(expn_id, parent_def_id);
+            self.module_map[&parent_def_id]
+        } else {
+            self.definitions.add_parent_module_of_macro_def(
+                expn_id,
+                def_id::DefId::local(def_id::CRATE_DEF_INDEX),
+            );
+            self.empty_module
+        };
+        self.ast_transform_scopes.insert(expn_id, parent_scope);
+        expn_id
+    }
+
     fn resolve_imports(&mut self) {
         ImportResolver { r: self }.resolve_imports()
     }
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index fd222a132a3..ca189e71800 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -1307,12 +1307,11 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
                 None => continue,
             };
 
-            // Filter away ambiguous and gensymed imports. Gensymed imports
-            // (e.g. implicitly injected `std`) cannot be properly encoded in metadata,
-            // so they can cause name conflict errors downstream.
-            let is_good_import = binding.is_import() && !binding.is_ambiguity() &&
-                                 // Note that as_str() de-gensyms the Symbol
-                                 !(ident.is_gensymed() && ident.name.as_str() != "_");
+            // Filter away ambiguous imports and anything that has def-site
+            // hygiene.
+            // FIXME: Implement actual cross-crate hygiene.
+            let is_good_import = binding.is_import() && !binding.is_ambiguity()
+                && !ident.span.modern().from_expansion();
             if is_good_import || binding.is_macro_def() {
                 let res = binding.res();
                 if res != Res::Err {
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 109ba041016..5b2515d20cb 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -3,7 +3,7 @@ use crate::attr::{self, HasAttrs, Stability, Deprecation};
 use crate::source_map::SourceMap;
 use crate::edition::Edition;
 use crate::ext::expand::{self, AstFragment, Invocation};
-use crate::ext::hygiene::{ExpnId, Transparency};
+use crate::ext::hygiene::ExpnId;
 use crate::mut_visit::{self, MutVisitor};
 use crate::parse::{self, parser, ParseSess, DirectoryOwnership};
 use crate::parse::token;
@@ -16,7 +16,7 @@ use crate::visit::Visitor;
 use errors::{DiagnosticBuilder, DiagnosticId};
 use smallvec::{smallvec, SmallVec};
 use syntax_pos::{FileName, Span, MultiSpan, DUMMY_SP};
-use syntax_pos::hygiene::{ExpnData, ExpnKind};
+use syntax_pos::hygiene::{AstPass, ExpnData, ExpnKind};
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{self, Lrc};
@@ -732,13 +732,19 @@ bitflags::bitflags! {
 pub trait Resolver {
     fn next_node_id(&mut self) -> NodeId;
 
-    fn get_module_scope(&mut self, id: NodeId) -> ExpnId;
-
     fn resolve_dollar_crates(&mut self);
     fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment,
                                             extra_placeholders: &[NodeId]);
     fn register_builtin_macro(&mut self, ident: ast::Ident, ext: SyntaxExtension);
 
+    fn expansion_for_ast_pass(
+        &mut self,
+        call_site: Span,
+        pass: AstPass,
+        features: &[Symbol],
+        parent_module_id: Option<NodeId>,
+    ) -> ExpnId;
+
     fn resolve_imports(&mut self);
 
     fn resolve_macro_invocation(
@@ -822,20 +828,20 @@ impl<'a> ExtCtxt<'a> {
     /// Equivalent of `Span::def_site` from the proc macro API,
     /// except that the location is taken from the span passed as an argument.
     pub fn with_def_site_ctxt(&self, span: Span) -> Span {
-        span.with_ctxt_from_mark(self.current_expansion.id, Transparency::Opaque)
+        span.with_def_site_ctxt(self.current_expansion.id)
     }
 
     /// Equivalent of `Span::call_site` from the proc macro API,
     /// except that the location is taken from the span passed as an argument.
     pub fn with_call_site_ctxt(&self, span: Span) -> Span {
-        span.with_ctxt_from_mark(self.current_expansion.id, Transparency::Transparent)
+        span.with_call_site_ctxt(self.current_expansion.id)
     }
 
     /// Span with a context reproducing `macro_rules` hygiene (hygienic locals, unhygienic items).
     /// FIXME: This should be eventually replaced either with `with_def_site_ctxt` (preferably),
     /// or with `with_call_site_ctxt` (where necessary).
     pub fn with_legacy_ctxt(&self, span: Span) -> Span {
-        span.with_ctxt_from_mark(self.current_expansion.id, Transparency::SemiTransparent)
+        span.with_legacy_ctxt(self.current_expansion.id)
     }
 
     /// Returns span for the macro which originally caused the current expansion to happen.
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 6772bbce21b..8c1632dc780 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -128,10 +128,14 @@ pub fn print_crate<'a>(cm: &'a SourceMap,
         let fake_attr = attr::mk_attr_inner(list);
         s.print_attribute(&fake_attr);
 
-        // #![no_std]
-        let no_std_meta = attr::mk_word_item(ast::Ident::with_dummy_span(sym::no_std));
-        let fake_attr = attr::mk_attr_inner(no_std_meta);
-        s.print_attribute(&fake_attr);
+        // Currently on Rust 2018 we don't have `extern crate std;` at the crate
+        // root, so this is not needed, and actually breaks things.
+        if sess.edition == syntax_pos::edition::Edition::Edition2015 {
+            // #![no_std]
+            let no_std_meta = attr::mk_word_item(ast::Ident::with_dummy_span(sym::no_std));
+            let fake_attr = attr::mk_attr_inner(no_std_meta);
+            s.print_attribute(&fake_attr);
+        }
     }
 
     s.print_mod(&krate.module, &krate.attrs);
diff --git a/src/libsyntax_ext/plugin_macro_defs.rs b/src/libsyntax_ext/plugin_macro_defs.rs
index dbfd8fe98f3..ccdc5bd81a0 100644
--- a/src/libsyntax_ext/plugin_macro_defs.rs
+++ b/src/libsyntax_ext/plugin_macro_defs.rs
@@ -11,7 +11,7 @@ use syntax::source_map::respan;
 use syntax::symbol::sym;
 use syntax::tokenstream::*;
 use syntax_pos::{Span, DUMMY_SP};
-use syntax_pos::hygiene::{ExpnData, ExpnKind, MacroKind};
+use syntax_pos::hygiene::{ExpnData, ExpnKind, AstPass};
 
 use std::mem;
 
@@ -44,7 +44,7 @@ pub fn inject(
     if !named_exts.is_empty() {
         let mut extra_items = Vec::new();
         let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-            ExpnKind::Macro(MacroKind::Attr, sym::plugin), DUMMY_SP, edition,
+            ExpnKind::AstPass(AstPass::PluginMacroDefs), DUMMY_SP, edition,
             [sym::rustc_attrs][..].into(),
         ));
         for (name, ext) in named_exts {
diff --git a/src/libsyntax_ext/proc_macro_harness.rs b/src/libsyntax_ext/proc_macro_harness.rs
index e772eaf8349..31d32d23a64 100644
--- a/src/libsyntax_ext/proc_macro_harness.rs
+++ b/src/libsyntax_ext/proc_macro_harness.rs
@@ -3,8 +3,7 @@ use std::mem;
 use smallvec::smallvec;
 use syntax::ast::{self, Ident};
 use syntax::attr;
-use syntax::source_map::{ExpnData, ExpnKind, respan};
-use syntax::ext::base::{ExtCtxt, MacroKind};
+use syntax::ext::base::ExtCtxt;
 use syntax::ext::expand::{AstFragment, ExpansionConfig};
 use syntax::ext::proc_macro::is_proc_macro_attr;
 use syntax::parse::ParseSess;
@@ -12,6 +11,7 @@ use syntax::ptr::P;
 use syntax::symbol::{kw, sym};
 use syntax::visit::{self, Visitor};
 use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::hygiene::AstPass;
 
 struct ProcMacroDerive {
     trait_name: ast::Name,
@@ -308,8 +308,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
 
 // Creates a new module which looks like:
 //
-//      #[doc(hidden)]
-//      mod $gensym {
+//      const _: () = {
 //          extern crate proc_macro;
 //
 //          use proc_macro::bridge::client::ProcMacro;
@@ -327,32 +326,30 @@ fn mk_decls(
     custom_attrs: &[ProcMacroDef],
     custom_macros: &[ProcMacroDef],
 ) -> P<ast::Item> {
-    let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-        ExpnKind::Macro(MacroKind::Attr, sym::proc_macro), DUMMY_SP, cx.parse_sess.edition,
-        [sym::rustc_attrs, sym::proc_macro_internals][..].into(),
-    ));
-
-    let hidden = cx.meta_list_item_word(span, sym::hidden);
-    let doc = cx.meta_list(span, sym::doc, vec![hidden]);
-    let doc_hidden = cx.attribute(doc);
-
-    let proc_macro = Ident::with_dummy_span(sym::proc_macro);
+    let expn_id = cx.resolver.expansion_for_ast_pass(
+        DUMMY_SP,
+        AstPass::ProcMacroHarness,
+        &[sym::rustc_attrs, sym::proc_macro_internals],
+        None,
+    );
+    let span = DUMMY_SP.with_def_site_ctxt(expn_id);
+
+    let proc_macro = Ident::new(sym::proc_macro, span);
     let krate = cx.item(span,
                         proc_macro,
                         Vec::new(),
                         ast::ItemKind::ExternCrate(None));
 
-    let bridge = Ident::from_str("bridge");
-    let client = Ident::from_str("client");
-    let proc_macro_ty = Ident::from_str("ProcMacro");
-    let custom_derive = Ident::from_str("custom_derive");
-    let attr = Ident::from_str("attr");
-    let bang = Ident::from_str("bang");
-    let crate_kw = Ident::with_dummy_span(kw::Crate);
+    let bridge = Ident::from_str_and_span("bridge", span);
+    let client = Ident::from_str_and_span("client", span);
+    let proc_macro_ty = Ident::from_str_and_span("ProcMacro", span);
+    let custom_derive = Ident::from_str_and_span("custom_derive", span);
+    let attr = Ident::from_str_and_span("attr", span);
+    let bang = Ident::from_str_and_span("bang", span);
 
     let decls = {
         let local_path = |sp: Span, name| {
-            cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![crate_kw, name]))
+            cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![name]))
         };
         let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![
             proc_macro, bridge, client, proc_macro_ty, method,
@@ -381,7 +378,7 @@ fn mk_decls(
 
     let decls_static = cx.item_static(
         span,
-        Ident::from_str("_DECLS"),
+        Ident::from_str_and_span("_DECLS", span),
         cx.ty_rptr(span,
             cx.ty(span, ast::TyKind::Slice(
                 cx.ty_path(cx.path(span,
@@ -392,22 +389,22 @@ fn mk_decls(
     ).map(|mut i| {
         let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);
         i.attrs.push(cx.attribute(attr));
-        i.vis = respan(span, ast::VisibilityKind::Public);
         i
     });
 
-    let module = cx.item_mod(
+    let block = cx.expr_block(cx.block(
         span,
-        span,
-        ast::Ident::from_str("decls").gensym(),
-        vec![doc_hidden],
-        vec![krate, decls_static],
-    ).map(|mut i| {
-        i.vis = respan(span, ast::VisibilityKind::Public);
-        i
-    });
+        vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)],
+    ));
 
-    // Integrate the new module into existing module structures.
-    let module = AstFragment::Items(smallvec![module]);
-    cx.monotonic_expander().fully_expand_fragment(module).make_items().pop().unwrap()
+    let anon_constant = cx.item_const(
+        span,
+        ast::Ident::new(kw::Underscore, span),
+        cx.ty(span, ast::TyKind::Tup(Vec::new())),
+        block,
+    );
+
+    // Integrate the new item into existing module structures.
+    let items = AstFragment::Items(smallvec![anon_constant]);
+    cx.monotonic_expander().fully_expand_fragment(items).make_items().pop().unwrap()
 }
diff --git a/src/libsyntax_ext/standard_library_imports.rs b/src/libsyntax_ext/standard_library_imports.rs
index 8ca376341fc..c577b1e33cf 100644
--- a/src/libsyntax_ext/standard_library_imports.rs
+++ b/src/libsyntax_ext/standard_library_imports.rs
@@ -1,86 +1,86 @@
 use syntax::{ast, attr};
 use syntax::edition::Edition;
-use syntax::ext::hygiene::MacroKind;
+use syntax::ext::expand::ExpansionConfig;
+use syntax::ext::hygiene::AstPass;
+use syntax::ext::base::{ExtCtxt, Resolver};
+use syntax::parse::ParseSess;
 use syntax::ptr::P;
-use syntax::source_map::{ExpnData, ExpnKind, dummy_spanned, respan};
 use syntax::symbol::{Ident, Symbol, kw, sym};
 use syntax_pos::DUMMY_SP;
 
-use std::iter;
-
 pub fn inject(
-    mut krate: ast::Crate, alt_std_name: Option<&str>, edition: Edition
+    mut krate: ast::Crate,
+    resolver: &mut dyn Resolver,
+    sess: &ParseSess,
+    alt_std_name: Option<Symbol>,
 ) -> (ast::Crate, Option<Symbol>) {
-    let rust_2018 = edition >= Edition::Edition2018;
+    let rust_2018 = sess.edition >= Edition::Edition2018;
 
     // the first name in this list is the crate name of the crate with the prelude
-    let names: &[&str] = if attr::contains_name(&krate.attrs, sym::no_core) {
+    let names: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) {
         return (krate, None);
     } else if attr::contains_name(&krate.attrs, sym::no_std) {
         if attr::contains_name(&krate.attrs, sym::compiler_builtins) {
-            &["core"]
+            &[sym::core]
         } else {
-            &["core", "compiler_builtins"]
+            &[sym::core, sym::compiler_builtins]
         }
     } else {
-        &["std"]
+        &[sym::std]
     };
 
+    let expn_id = resolver.expansion_for_ast_pass(
+        DUMMY_SP,
+        AstPass::StdImports,
+        &[sym::prelude_import],
+        None,
+    );
+    let span = DUMMY_SP.with_def_site_ctxt(expn_id);
+    let call_site = DUMMY_SP.with_call_site_ctxt(expn_id);
+
+    let ecfg = ExpansionConfig::default("std_lib_injection".to_string());
+    let cx = ExtCtxt::new(sess, ecfg, resolver);
+
+
     // .rev() to preserve ordering above in combination with insert(0, ...)
-    let alt_std_name = alt_std_name.map(Symbol::intern);
-    for orig_name_str in names.iter().rev() {
-        // HACK(eddyb) gensym the injected crates on the Rust 2018 edition,
-        // so they don't accidentally interfere with the new import paths.
-        let orig_name_sym = Symbol::intern(orig_name_str);
-        let orig_name_ident = Ident::with_dummy_span(orig_name_sym);
-        let (rename, orig_name) = if rust_2018 {
-            (orig_name_ident.gensym(), Some(orig_name_sym))
+    for &name in names.iter().rev() {
+        let ident = if rust_2018 {
+            Ident::new(name, span)
         } else {
-            (orig_name_ident, None)
+            Ident::new(name, call_site)
         };
-        krate.module.items.insert(0, P(ast::Item {
-            attrs: vec![attr::mk_attr_outer(
-                attr::mk_word_item(ast::Ident::with_dummy_span(sym::macro_use))
-            )],
-            vis: dummy_spanned(ast::VisibilityKind::Inherited),
-            node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)),
-            ident: rename,
-            id: ast::DUMMY_NODE_ID,
-            span: DUMMY_SP,
-            tokens: None,
-        }));
+        krate.module.items.insert(0, cx.item(
+            span,
+            ident,
+            vec![cx.attribute(cx.meta_word(span, sym::macro_use))],
+            ast::ItemKind::ExternCrate(alt_std_name),
+        ));
     }
 
-    // the crates have been injected, the assumption is that the first one is the one with
-    // the prelude.
+    // The crates have been injected, the assumption is that the first one is
+    // the one with the prelude.
     let name = names[0];
 
-    let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-        ExpnKind::Macro(MacroKind::Attr, sym::std_inject), DUMMY_SP, edition,
-        [sym::prelude_import][..].into(),
-    ));
+    let import_path = if rust_2018 {
+        [name, sym::prelude, sym::v1].iter()
+            .map(|symbol| ast::Ident::new(*symbol, span)).collect()
+    } else {
+        [kw::PathRoot, name, sym::prelude, sym::v1].iter()
+            .map(|symbol| ast::Ident::new(*symbol, span)).collect()
+    };
 
-    krate.module.items.insert(0, P(ast::Item {
-        attrs: vec![attr::mk_attr_outer(
-            attr::mk_word_item(ast::Ident::new(sym::prelude_import, span)))],
-        vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited),
-        node: ast::ItemKind::Use(P(ast::UseTree {
-            prefix: ast::Path {
-                segments: iter::once(ast::Ident::with_dummy_span(kw::PathRoot))
-                    .chain(
-                        [name, "prelude", "v1"].iter().cloned()
-                            .map(ast::Ident::from_str)
-                    ).map(ast::PathSegment::from_ident).collect(),
-                span,
-            },
+    let use_item = cx.item(
+        span,
+        ast::Ident::invalid(),
+        vec![cx.attribute(cx.meta_word(span, sym::prelude_import))],
+        ast::ItemKind::Use(P(ast::UseTree {
+            prefix: cx.path(span, import_path),
             kind: ast::UseTreeKind::Glob,
             span,
         })),
-        id: ast::DUMMY_NODE_ID,
-        ident: ast::Ident::invalid(),
-        span,
-        tokens: None,
-    }));
+    );
+
+    krate.module.items.insert(0, use_item);
 
-    (krate, Some(Symbol::intern(name)))
+    (krate, Some(name))
 }
diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs
index 5fd87d3a0e5..be5aca73f5c 100644
--- a/src/libsyntax_ext/test.rs
+++ b/src/libsyntax_ext/test.rs
@@ -28,11 +28,11 @@ pub fn expand_test_case(
 
     if !ecx.ecfg.should_test { return vec![]; }
 
-    let sp = ecx.with_legacy_ctxt(attr_sp);
+    let sp = ecx.with_def_site_ctxt(attr_sp);
     let mut item = anno_item.expect_item();
     item = item.map(|mut item| {
         item.vis = respan(item.vis.span, ast::VisibilityKind::Public);
-        item.ident = item.ident.gensym();
+        item.ident.span = item.ident.span.with_ctxt(sp.ctxt());
         item.attrs.push(
             ecx.attribute(ecx.meta_word(sp, sym::rustc_test_marker))
         );
@@ -92,10 +92,9 @@ pub fn expand_test_or_bench(
         return vec![Annotatable::Item(item)];
     }
 
-    let (sp, attr_sp) = (cx.with_legacy_ctxt(item.span), cx.with_legacy_ctxt(attr_sp));
+    let (sp, attr_sp) = (cx.with_def_site_ctxt(item.span), cx.with_def_site_ctxt(attr_sp));
 
-    // Gensym "test" so we can extern crate without conflicting with any local names
-    let test_id = cx.ident_of("test").gensym();
+    let test_id = ast::Ident::new(sym::test, attr_sp);
 
     // creates test::$name
     let test_path = |name| {
@@ -112,7 +111,7 @@ pub fn expand_test_or_bench(
 
     let test_fn = if is_bench {
         // A simple ident for a lambda
-        let b = cx.ident_of("b");
+        let b = ast::Ident::from_str_and_span("b", attr_sp);
 
         cx.expr_call(sp, cx.expr_path(test_path("StaticBenchFn")), vec![
             // |b| self::test::assert_test_result(
@@ -143,7 +142,7 @@ pub fn expand_test_or_bench(
         ])
     };
 
-    let mut test_const = cx.item(sp, ast::Ident::new(item.ident.name, sp).gensym(),
+    let mut test_const = cx.item(sp, ast::Ident::new(item.ident.name, sp),
         vec![
             // #[cfg(test)]
             cx.attribute(cx.meta_list(attr_sp, sym::cfg, vec![
@@ -192,17 +191,17 @@ pub fn expand_test_or_bench(
         ));
     test_const = test_const.map(|mut tc| { tc.vis.node = ast::VisibilityKind::Public; tc});
 
-    // extern crate test as test_gensym
+    // extern crate test
     let test_extern = cx.item(sp,
         test_id,
         vec![],
-        ast::ItemKind::ExternCrate(Some(sym::test))
+        ast::ItemKind::ExternCrate(None)
     );
 
     log::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
 
     vec![
-        // Access to libtest under a gensymed name
+        // Access to libtest under a hygienic name
         Annotatable::Item(test_extern),
         // The generated test case
         Annotatable::Item(test_const),
diff --git a/src/libsyntax_ext/test_harness.rs b/src/libsyntax_ext/test_harness.rs
index 4a6ea0ebf85..b93c11fad38 100644
--- a/src/libsyntax_ext/test_harness.rs
+++ b/src/libsyntax_ext/test_harness.rs
@@ -5,32 +5,30 @@ use smallvec::{smallvec, SmallVec};
 use syntax::ast::{self, Ident};
 use syntax::attr;
 use syntax::entry::{self, EntryPointType};
-use syntax::ext::base::{ExtCtxt, MacroKind, Resolver};
+use syntax::ext::base::{ExtCtxt, Resolver};
 use syntax::ext::expand::{AstFragment, ExpansionConfig};
 use syntax::feature_gate::Features;
 use syntax::mut_visit::{*, ExpectOne};
 use syntax::parse::ParseSess;
 use syntax::ptr::P;
-use syntax::source_map::{ExpnData, ExpnKind, dummy_spanned};
-use syntax::symbol::{kw, sym, Symbol};
+use syntax::source_map::respan;
+use syntax::symbol::{sym, Symbol};
 use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::hygiene::{AstPass, SyntaxContext, Transparency};
 
 use std::{iter, mem};
 
 struct Test {
     span: Span,
-    path: Vec<Ident>,
+    ident: Ident,
 }
 
 struct TestCtxt<'a> {
-    span_diagnostic: &'a errors::Handler,
-    path: Vec<Ident>,
     ext_cx: ExtCtxt<'a>,
+    def_site: Span,
     test_cases: Vec<Test>,
     reexport_test_harness_main: Option<Symbol>,
     test_runner: Option<ast::Path>,
-    // top-level re-export submodule, filled out after folding is finished
-    toplevel_reexport: Option<Ident>,
 }
 
 // Traverse the crate, collecting all the test functions, eliding any
@@ -43,8 +41,8 @@ pub fn inject(
     span_diagnostic: &errors::Handler,
     features: &Features,
 ) {
-    // Check for #[reexport_test_harness_main = "some_name"] which
-    // creates a `use __test::main as some_name;`. This needs to be
+    // Check for #![reexport_test_harness_main = "some_name"] which gives the
+    // main test function the name `some_name` without hygiene. This needs to be
     // unconditional, so that the attribute is still marked as used in
     // non-test builds.
     let reexport_test_harness_main =
@@ -56,16 +54,13 @@ pub fn inject(
 
     if should_test {
         generate_test_harness(sess, resolver, reexport_test_harness_main,
-                              krate, span_diagnostic, features, test_runner)
+                              krate, features, test_runner)
     }
 }
 
 struct TestHarnessGenerator<'a> {
     cx: TestCtxt<'a>,
-    tests: Vec<Ident>,
-
-    // submodule name, gensym'd identifier for re-exports
-    tested_submods: Vec<(Ident, Ident)>,
+    tests: Vec<Test>,
 }
 
 impl<'a> MutVisitor for TestHarnessGenerator<'a> {
@@ -77,49 +72,47 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
     }
 
     fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
-        let ident = i.ident;
-        if ident.name != kw::Invalid {
-            self.cx.path.push(ident);
-        }
-        debug!("current path: {}", path_name_i(&self.cx.path));
-
         let mut item = i.into_inner();
         if is_test_case(&item) {
             debug!("this is a test item");
 
             let test = Test {
                 span: item.span,
-                path: self.cx.path.clone(),
+                ident: item.ident,
             };
-            self.cx.test_cases.push(test);
-            self.tests.push(item.ident);
+            self.tests.push(test);
         }
 
         // We don't want to recurse into anything other than mods, since
         // mods or tests inside of functions will break things
         if let ast::ItemKind::Mod(mut module) = item.node {
             let tests = mem::take(&mut self.tests);
-            let tested_submods = mem::take(&mut self.tested_submods);
             noop_visit_mod(&mut module, self);
-            let tests = mem::replace(&mut self.tests, tests);
-            let tested_submods = mem::replace(&mut self.tested_submods, tested_submods);
+            let mut tests = mem::replace(&mut self.tests, tests);
 
-            if !tests.is_empty() || !tested_submods.is_empty() {
-                let (it, sym) = mk_reexport_mod(&mut self.cx, item.id, tests, tested_submods);
-                module.items.push(it);
-
-                if !self.cx.path.is_empty() {
-                    self.tested_submods.push((self.cx.path[self.cx.path.len()-1], sym));
+            if !tests.is_empty() {
+                let parent = if item.id == ast::DUMMY_NODE_ID {
+                    ast::CRATE_NODE_ID
                 } else {
-                    debug!("pushing nothing, sym: {:?}", sym);
-                    self.cx.toplevel_reexport = Some(sym);
+                    item.id
+                };
+                // Create an identifier that will hygienically resolve the test
+                // case name, even in another module.
+                let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass(
+                    module.inner,
+                    AstPass::TestHarness,
+                    &[],
+                    Some(parent),
+                );
+                for test in &mut tests {
+                    // See the comment on `mk_main` for why we're using
+                    // `apply_mark` directly.
+                    test.ident.span = test.ident.span.apply_mark(expn_id, Transparency::Opaque);
                 }
+                self.cx.test_cases.extend(tests);
             }
             item.node = ast::ItemKind::Mod(module);
         }
-        if ident.name != kw::Invalid {
-            self.cx.path.pop();
-        }
         smallvec![P(item)]
     }
 
@@ -133,6 +126,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
 struct EntryPointCleaner {
     // Current depth in the ast
     depth: usize,
+    def_site: Span,
 }
 
 impl MutVisitor for EntryPointCleaner {
@@ -149,8 +143,10 @@ impl MutVisitor for EntryPointCleaner {
             EntryPointType::MainAttr |
             EntryPointType::Start =>
                 item.map(|ast::Item {id, ident, attrs, node, vis, span, tokens}| {
-                    let allow_ident = Ident::with_dummy_span(sym::allow);
-                    let dc_nested = attr::mk_nested_word_item(Ident::from_str("dead_code"));
+                    let allow_ident = Ident::new(sym::allow, self.def_site);
+                    let dc_nested = attr::mk_nested_word_item(
+                        Ident::from_str_and_span("dead_code", self.def_site),
+                    );
                     let allow_dead_code_item = attr::mk_list_item(allow_ident, vec![dc_nested]);
                     let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
 
@@ -181,124 +177,99 @@ impl MutVisitor for EntryPointCleaner {
     }
 }
 
-/// Creates an item (specifically a module) that "pub use"s the tests passed in.
-/// Each tested submodule will contain a similar reexport module that we will export
-/// under the name of the original module. That is, `submod::__test_reexports` is
-/// reexported like so `pub use submod::__test_reexports as submod`.
-fn mk_reexport_mod(cx: &mut TestCtxt<'_>,
-                   parent: ast::NodeId,
-                   tests: Vec<Ident>,
-                   tested_submods: Vec<(Ident, Ident)>)
-                   -> (P<ast::Item>, Ident) {
-    let super_ = Ident::with_dummy_span(kw::Super);
-
-    let items = tests.into_iter().map(|r| {
-        cx.ext_cx.item_use_simple(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public),
-                                  cx.ext_cx.path(DUMMY_SP, vec![super_, r]))
-    }).chain(tested_submods.into_iter().map(|(r, sym)| {
-        let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]);
-        cx.ext_cx.item_use_simple_(DUMMY_SP, dummy_spanned(ast::VisibilityKind::Public),
-                                   Some(r), path)
-    })).collect();
-
-    let reexport_mod = ast::Mod {
-        inline: true,
-        inner: DUMMY_SP,
-        items,
-    };
-
-    let name = Ident::from_str("__test_reexports").gensym();
-    let parent = if parent == ast::DUMMY_NODE_ID { ast::CRATE_NODE_ID } else { parent };
-    cx.ext_cx.current_expansion.id = cx.ext_cx.resolver.get_module_scope(parent);
-    let module = P(ast::Item {
-        ident: name,
-        attrs: Vec::new(),
-        id: ast::DUMMY_NODE_ID,
-        node: ast::ItemKind::Mod(reexport_mod),
-        vis: dummy_spanned(ast::VisibilityKind::Public),
-        span: DUMMY_SP,
-        tokens: None,
-    });
-
-    // Integrate the new module into existing module structures.
-    let module = AstFragment::Items(smallvec![module]);
-    let module =
-        cx.ext_cx.monotonic_expander().fully_expand_fragment(module).make_items().pop().unwrap();
-
-    (module, name)
-}
-
 /// Crawl over the crate, inserting test reexports and the test main function
 fn generate_test_harness(sess: &ParseSess,
                          resolver: &mut dyn Resolver,
                          reexport_test_harness_main: Option<Symbol>,
                          krate: &mut ast::Crate,
-                         sd: &errors::Handler,
                          features: &Features,
                          test_runner: Option<ast::Path>) {
-    // Remove the entry points
-    let mut cleaner = EntryPointCleaner { depth: 0 };
-    cleaner.visit_crate(krate);
-
     let mut econfig = ExpansionConfig::default("test".to_string());
     econfig.features = Some(features);
 
+    let ext_cx = ExtCtxt::new(sess, econfig, resolver);
+
+    let expn_id = ext_cx.resolver.expansion_for_ast_pass(
+        DUMMY_SP,
+        AstPass::TestHarness,
+        &[sym::main, sym::test, sym::rustc_attrs],
+        None,
+    );
+    let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
+
+    // Remove the entry points
+    let mut cleaner = EntryPointCleaner { depth: 0, def_site };
+    cleaner.visit_crate(krate);
+
     let cx = TestCtxt {
-        span_diagnostic: sd,
-        ext_cx: ExtCtxt::new(sess, econfig, resolver),
-        path: Vec::new(),
+        ext_cx,
+        def_site,
         test_cases: Vec::new(),
         reexport_test_harness_main,
-        toplevel_reexport: None,
         test_runner
     };
 
     TestHarnessGenerator {
         cx,
         tests: Vec::new(),
-        tested_submods: Vec::new(),
     }.visit_crate(krate);
 }
 
 /// Creates a function item for use as the main function of a test build.
 /// This function will call the `test_runner` as specified by the crate attribute
+///
+/// By default this expands to
+///
+/// #[main]
+/// pub fn main() {
+///     extern crate test;
+///     test::test_main_static(&[
+///         &test_const1,
+///         &test_const2,
+///         &test_const3,
+///     ]);
+/// }
+///
+/// Most of the Ident have the usual def-site hygiene for the AST pass. The
+/// exception is the `test_const`s. These have a syntax context that has two
+/// opaque marks: one from the expansion of `test` or `test_case`, and one
+/// generated  in `TestHarnessGenerator::flat_map_item`. When resolving this
+/// identifier after failing to find a matching identifier in the root module
+/// we remove the outer mark, and try resolving at its def-site, which will
+/// then resolve to `test_const`.
+///
+/// The expansion here can be controlled by two attributes:
+///
+/// `reexport_test_harness_main` provides a different name for the `main`
+/// function and `test_runner` provides a path that replaces
+/// `test::test_main_static`.
 fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
-    // Writing this out by hand:
-    //        pub fn main() {
-    //            #![main]
-    //            test::test_main_static(&[..tests]);
-    //        }
-    let sp = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
-        ExpnKind::Macro(MacroKind::Attr, sym::test_case), DUMMY_SP, cx.ext_cx.parse_sess.edition,
-        [sym::main, sym::test, sym::rustc_attrs][..].into(),
-    ));
+    let sp = cx.def_site;
     let ecx = &cx.ext_cx;
-    let test_id = Ident::with_dummy_span(sym::test);
+    let test_id = Ident::new(sym::test, sp);
 
     // test::test_main_static(...)
     let mut test_runner = cx.test_runner.clone().unwrap_or(
-        ecx.path(sp, vec![
-            test_id, ecx.ident_of("test_main_static")
-        ]));
+        ecx.path(sp, vec![test_id, Ident::from_str_and_span("test_main_static", sp)]));
 
     test_runner.span = sp;
 
     let test_main_path_expr = ecx.expr_path(test_runner);
     let call_test_main = ecx.expr_call(sp, test_main_path_expr,
-                                       vec![mk_tests_slice(cx)]);
+                                       vec![mk_tests_slice(cx, sp)]);
     let call_test_main = ecx.stmt_expr(call_test_main);
 
-    // #![main]
-    let main_meta = ecx.meta_word(sp, sym::main);
-    let main_attr = ecx.attribute(main_meta);
-
-    // extern crate test as test_gensym
+    // extern crate test
     let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp,
         test_id,
         vec![],
         ast::ItemKind::ExternCrate(None)
     ));
 
+    // #[main]
+    let main_meta = ecx.meta_word(sp, sym::main);
+    let main_attr = ecx.attribute(main_meta);
+
     // pub fn main() { ... }
     let main_ret_ty = ecx.ty(sp, ast::TyKind::Tup(vec![]));
 
@@ -316,8 +287,8 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
 
     // Honor the reexport_test_harness_main attribute
     let main_id = match cx.reexport_test_harness_main {
-        Some(sym) => Ident::new(sym, sp),
-        None => Ident::from_str_and_span("main", sp).gensym(),
+        Some(sym) => Ident::new(sym, sp.with_ctxt(SyntaxContext::root())),
+        None => Ident::from_str_and_span("main", sp),
     };
 
     let main = P(ast::Item {
@@ -325,7 +296,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
         attrs: vec![main_attr],
         id: ast::DUMMY_NODE_ID,
         node: main,
-        vis: dummy_spanned(ast::VisibilityKind::Public),
+        vis: respan(sp, ast::VisibilityKind::Public),
         span: sp,
         tokens: None,
     });
@@ -335,44 +306,20 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
     cx.ext_cx.monotonic_expander().fully_expand_fragment(main).make_items().pop().unwrap()
 }
 
-fn path_name_i(idents: &[Ident]) -> String {
-    let mut path_name = "".to_string();
-    let mut idents_iter = idents.iter().peekable();
-    while let Some(ident) = idents_iter.next() {
-        path_name.push_str(&ident.as_str());
-        if idents_iter.peek().is_some() {
-            path_name.push_str("::")
-        }
-    }
-    path_name
-}
-
 /// Creates a slice containing every test like so:
-/// &[path::to::test1, path::to::test2]
-fn mk_tests_slice(cx: &TestCtxt<'_>) -> P<ast::Expr> {
+/// &[&test1, &test2]
+fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
     debug!("building test vector from {} tests", cx.test_cases.len());
     let ref ecx = cx.ext_cx;
 
-    ecx.expr_vec_slice(DUMMY_SP,
+
+    ecx.expr_vec_slice(sp,
         cx.test_cases.iter().map(|test| {
             ecx.expr_addr_of(test.span,
-                ecx.expr_path(ecx.path(test.span, visible_path(cx, &test.path))))
+                ecx.expr_path(ecx.path(test.span, vec![test.ident])))
         }).collect())
 }
 
-/// Creates a path from the top-level __test module to the test via __test_reexports
-fn visible_path(cx: &TestCtxt<'_>, path: &[Ident]) -> Vec<Ident>{
-    let mut visible_path = vec![];
-    match cx.toplevel_reexport {
-        Some(id) => visible_path.push(id),
-        None => {
-            cx.span_diagnostic.bug("expected to find top-level re-export name, but found None");
-        }
-    }
-    visible_path.extend_from_slice(path);
-    visible_path
-}
-
 fn is_test_case(i: &ast::Item) -> bool {
     attr::contains_name(&i.attrs, sym::rustc_test_marker)
 }
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index 733f6f04490..f0e7344c1b9 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -360,7 +360,7 @@ impl SyntaxContext {
     }
 
     /// Extend a syntax context with a given expansion and transparency.
-    pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
+    crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
         HygieneData::with(|data| data.apply_mark(self, expn_id, transparency))
     }
 
@@ -550,7 +550,7 @@ impl Span {
     /// The returned span belongs to the created expansion and has the new properties,
     /// but its location is inherited from the current span.
     pub fn fresh_expansion(self, expn_data: ExpnData) -> Span {
-        self.fresh_expansion_with_transparency(expn_data, Transparency::SemiTransparent)
+        self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent)
     }
 
     pub fn fresh_expansion_with_transparency(
@@ -639,8 +639,9 @@ pub enum ExpnKind {
     /// No expansion, aka root expansion. Only `ExpnId::root()` has this kind.
     Root,
     /// Expansion produced by a macro.
-    /// FIXME: Some code injected by the compiler before HIR lowering also gets this kind.
     Macro(MacroKind, Symbol),
+    /// Transform done by the compiler on the AST.
+    AstPass(AstPass),
     /// Desugaring done by the compiler during HIR lowering.
     Desugaring(DesugaringKind)
 }
@@ -650,6 +651,7 @@ impl ExpnKind {
         match *self {
             ExpnKind::Root => kw::PathRoot,
             ExpnKind::Macro(_, descr) => descr,
+            ExpnKind::AstPass(kind) => Symbol::intern(kind.descr()),
             ExpnKind::Desugaring(kind) => Symbol::intern(kind.descr()),
         }
     }
@@ -683,6 +685,26 @@ impl MacroKind {
     }
 }
 
+/// The kind of AST transform.
+#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
+pub enum AstPass {
+    StdImports,
+    TestHarness,
+    ProcMacroHarness,
+    PluginMacroDefs,
+}
+
+impl AstPass {
+    fn descr(self) -> &'static str {
+        match self {
+            AstPass::StdImports => "standard library imports",
+            AstPass::TestHarness => "test harness",
+            AstPass::ProcMacroHarness => "proc macro harness",
+            AstPass::PluginMacroDefs => "plugin macro definitions",
+        }
+    }
+}
+
 /// The kind of compiler desugaring.
 #[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
 pub enum DesugaringKind {
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 3d8bfc77a89..9a296f17aaf 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -442,6 +442,7 @@ impl Span {
                 let (pre, post) = match expn_data.kind {
                     ExpnKind::Root => break,
                     ExpnKind::Desugaring(..) => ("desugaring of ", ""),
+                    ExpnKind::AstPass(..) => ("", ""),
                     ExpnKind::Macro(macro_kind, _) => match macro_kind {
                         MacroKind::Bang => ("", "!"),
                         MacroKind::Attr => ("#[", "]"),
@@ -513,6 +514,25 @@ impl Span {
                   span.ctxt)
     }
 
+    /// Equivalent of `Span::def_site` from the proc macro API,
+    /// except that the location is taken from the `self` span.
+    pub fn with_def_site_ctxt(self, expn_id: ExpnId) -> Span {
+        self.with_ctxt_from_mark(expn_id, Transparency::Opaque)
+    }
+
+    /// Equivalent of `Span::call_site` from the proc macro API,
+    /// except that the location is taken from the `self` span.
+    pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span {
+        self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
+    }
+
+    /// Span with a context reproducing `macro_rules` hygiene (hygienic locals, unhygienic items).
+    /// FIXME: This should be eventually replaced either with `with_def_site_ctxt` (preferably),
+    /// or with `with_call_site_ctxt` (where necessary).
+    pub fn with_legacy_ctxt(&self, expn_id: ExpnId) -> Span {
+        self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent)
+    }
+
     /// Produces a span with the same location as `self` and context produced by a macro with the
     /// given ID and transparency, assuming that macro was defined directly and not produced by
     /// some other macro (which is the case for built-in and procedural macros).
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index 3a4dc1f5a09..ecc31cfeb3d 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -801,21 +801,15 @@ impl Ident {
         Ident::new(self.name, self.span.modern_and_legacy())
     }
 
-    /// Transforms an identifier into one with the same name, but gensymed.
-    pub fn gensym(self) -> Ident {
-        let name = with_interner(|interner| interner.gensymed(self.name));
-        Ident::new(name, self.span)
-    }
-
     /// Transforms an underscore identifier into one with the same name, but
     /// gensymed. Leaves non-underscore identifiers unchanged.
     pub fn gensym_if_underscore(self) -> Ident {
-        if self.name == kw::Underscore { self.gensym() } else { self }
-    }
-
-    // WARNING: this function is deprecated and will be removed in the future.
-    pub fn is_gensymed(self) -> bool {
-        with_interner(|interner| interner.is_gensymed(self.name))
+        if self.name == kw::Underscore {
+            let name = with_interner(|interner| interner.gensymed(self.name));
+            Ident::new(name, self.span)
+        } else {
+            self
+        }
     }
 
     /// Convert the name to a `LocalInternedString`. This is a slowish
@@ -892,9 +886,12 @@ impl UseSpecializedDecodable for Ident {
 ///
 /// Examples:
 /// ```
-/// assert_eq!(Ident::from_str("x"), Ident::from_str("x"))
-/// assert_ne!(Ident::from_str("x").gensym(), Ident::from_str("x"))
-/// assert_ne!(Ident::from_str("x").gensym(), Ident::from_str("x").gensym())
+/// assert_eq!(Ident::from_str("_"), Ident::from_str("_"))
+/// assert_ne!(Ident::from_str("_").gensym_if_underscore(), Ident::from_str("_"))
+/// assert_ne!(
+///     Ident::from_str("_").gensym_if_underscore(),
+///     Ident::from_str("_").gensym_if_underscore(),
+/// )
 /// ```
 /// Internally, a symbol is implemented as an index, and all operations
 /// (including hashing, equality, and ordering) operate on that index. The use
diff --git a/src/test/ui/hygiene/auxiliary/not-libstd.rs b/src/test/ui/hygiene/auxiliary/not-libstd.rs
new file mode 100644
index 00000000000..babba293d03
--- /dev/null
+++ b/src/test/ui/hygiene/auxiliary/not-libstd.rs
@@ -0,0 +1 @@
+pub fn not_in_lib_std() {}
diff --git a/src/test/ui/hygiene/prelude-import-hygiene.rs b/src/test/ui/hygiene/prelude-import-hygiene.rs
new file mode 100644
index 00000000000..51e7bed6580
--- /dev/null
+++ b/src/test/ui/hygiene/prelude-import-hygiene.rs
@@ -0,0 +1,29 @@
+// Make sure that attribute used when injecting the prelude are resolved
+// hygienically.
+
+// check-pass
+// aux-build:not-libstd.rs
+
+//revisions: rust2015 rust2018
+//[rust2018] edition:2018
+
+// The prelude import shouldn't see these as candidates for when it's trying to
+// use the built-in macros.
+extern crate core;
+use core::prelude::v1::test as prelude_import;
+use core::prelude::v1::test as macro_use;
+
+// Should not be used for the prelude import - not a concern in the 2015 edition
+// because `std` is already declared in the crate root.
+#[cfg(rust2018)]
+extern crate not_libstd as std;
+
+#[cfg(rust2018)]
+mod x {
+    // The extern crate item should override `std` in the extern prelude.
+    fn f() {
+        std::not_in_lib_std();
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/imports/gensymed.rs b/src/test/ui/imports/gensymed.rs
index 613ccc0b242..7b53f0c536a 100644
--- a/src/test/ui/imports/gensymed.rs
+++ b/src/test/ui/imports/gensymed.rs
@@ -1,7 +1,9 @@
-// build-pass (FIXME(62277): could be check-pass?)
+// check-pass
 // edition:2018
 // aux-build:gensymed.rs
 
 extern crate gensymed;
 
+use gensymed::*;
+
 fn main() {}
diff --git a/src/test/ui/inaccessible-test-modules.stderr b/src/test/ui/inaccessible-test-modules.stderr
deleted file mode 100644
index b6a817e6b1d..00000000000
--- a/src/test/ui/inaccessible-test-modules.stderr
+++ /dev/null
@@ -1,21 +0,0 @@
-error[E0432]: unresolved import `__test`
-  --> $DIR/inaccessible-test-modules.rs:5:5
-   |
-LL | use __test as x;
-   |     ------^^^^^
-   |     |
-   |     no `__test` in the root
-   |     help: a similar name exists in the module: `test`
-
-error[E0432]: unresolved import `__test_reexports`
-  --> $DIR/inaccessible-test-modules.rs:6:5
-   |
-LL | use __test_reexports as y;
-   |     ----------------^^^^^
-   |     |
-   |     no `__test_reexports` in the root
-   |     help: a similar name exists in the module: `__test_reexports`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0432`.
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
index 0fe02a9a34d..ea06f6c1aca 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
+++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
@@ -2,40 +2,40 @@ PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "M",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
@@ -43,39 +43,39 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
index a499e1362ec..7ee8078b2c5 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
+++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
@@ -3,55 +3,55 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A (identity ! ($crate :: S)) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "identity",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: '!',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
                         ident: "$crate",
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Joint,
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Alone,
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                     Ident {
                         ident: "S",
-                        span: #2 bytes(LO..HI),
+                        span: #3 bytes(LO..HI),
                     },
                 ],
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct B(identity!(::dollar_crate_external :: S));
@@ -59,54 +59,54 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct B (identity ! ($crate :: S)) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
     Ident {
         ident: "B",
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "identity",
-                span: #7 bytes(LO..HI),
+                span: #8 bytes(LO..HI),
             },
             Punct {
                 ch: '!',
                 spacing: Alone,
-                span: #7 bytes(LO..HI),
+                span: #8 bytes(LO..HI),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
                         ident: "$crate",
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Joint,
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                     Punct {
                         ch: ':',
                         spacing: Alone,
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                     Ident {
                         ident: "S",
-                        span: #7 bytes(LO..HI),
+                        span: #8 bytes(LO..HI),
                     },
                 ],
-                span: #7 bytes(LO..HI),
+                span: #8 bytes(LO..HI),
             },
         ],
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #7 bytes(LO..HI),
+        span: #8 bytes(LO..HI),
     },
 ]
diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout
index da1d7549d07..4f7e000265e 100644
--- a/src/test/ui/proc-macro/dollar-crate.stdout
+++ b/src/test/ui/proc-macro/dollar-crate.stdout
@@ -2,40 +2,40 @@ PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "M",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A(crate::S);
@@ -43,40 +43,40 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): struct D(crate::S);
@@ -84,80 +84,80 @@ PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ;
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Ident {
         ident: "D",
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #2 bytes(LO..HI),
+                span: #3 bytes(LO..HI),
             },
         ],
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #2 bytes(LO..HI),
+        span: #3 bytes(LO..HI),
     },
 ]
 PRINT-BANG INPUT (DISPLAY): struct M ($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Ident {
         ident: "M",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
         ],
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A(::dollar_crate_external::S);
@@ -165,40 +165,40 @@ PRINT-ATTR RE-COLLECTED (DISPLAY): struct A ($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Ident {
         ident: "A",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
         ],
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): struct D(::dollar_crate_external::S);
@@ -206,39 +206,39 @@ PRINT-DERIVE RE-COLLECTED (DISPLAY): struct D ($crate :: S) ;
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Ident {
         ident: "D",
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
             Ident {
                 ident: "S",
-                span: #9 bytes(LO..HI),
+                span: #10 bytes(LO..HI),
             },
         ],
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #9 bytes(LO..HI),
+        span: #10 bytes(LO..HI),
     },
 ]
diff --git a/src/test/ui/test-shadowing/auxiliary/test_macro.rs b/src/test/ui/test-attrs/auxiliary/test_macro.rs
index 60a98ab6a02..60a98ab6a02 100644
--- a/src/test/ui/test-shadowing/auxiliary/test_macro.rs
+++ b/src/test/ui/test-attrs/auxiliary/test_macro.rs
diff --git a/src/test/ui/test-attrs/decl-macro-test.rs b/src/test/ui/test-attrs/decl-macro-test.rs
new file mode 100644
index 00000000000..fcbe9f49e55
--- /dev/null
+++ b/src/test/ui/test-attrs/decl-macro-test.rs
@@ -0,0 +1,22 @@
+// Check that declarative macros can declare tests
+
+// check-pass
+// compile-flags: --test
+
+#![feature(decl_macro)]
+
+macro create_test() {
+    #[test]
+    fn test() {}
+}
+
+macro create_module_test() {
+    mod x {
+        #[test]
+        fn test() {}
+    }
+}
+
+create_test!();
+create_test!();
+create_module_test!();
diff --git a/src/test/ui/inaccessible-test-modules.rs b/src/test/ui/test-attrs/inaccessible-test-modules.rs
index 7095ec290f8..f5b34793794 100644
--- a/src/test/ui/inaccessible-test-modules.rs
+++ b/src/test/ui/test-attrs/inaccessible-test-modules.rs
@@ -2,8 +2,8 @@
 
 // the `--test` harness creates modules with these textual names, but
 // they should be inaccessible from normal code.
-use __test as x; //~ ERROR unresolved import `__test`
-use __test_reexports as y; //~ ERROR unresolved import `__test_reexports`
+use main as x; //~ ERROR unresolved import `main`
+use test as y; //~ ERROR unresolved import `test`
 
 #[test]
 fn baz() {}
diff --git a/src/test/ui/test-attrs/inaccessible-test-modules.stderr b/src/test/ui/test-attrs/inaccessible-test-modules.stderr
new file mode 100644
index 00000000000..a94ea1e79bc
--- /dev/null
+++ b/src/test/ui/test-attrs/inaccessible-test-modules.stderr
@@ -0,0 +1,21 @@
+error[E0432]: unresolved import `main`
+  --> $DIR/inaccessible-test-modules.rs:5:5
+   |
+LL | use main as x;
+   |     ----^^^^^
+   |     |
+   |     no `main` in the root
+   |     help: a similar name exists in the module: `main`
+
+error[E0432]: unresolved import `test`
+  --> $DIR/inaccessible-test-modules.rs:6:5
+   |
+LL | use test as y;
+   |     ----^^^^^
+   |     |
+   |     no `test` in the root
+   |     help: a similar name exists in the module: `test`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0432`.
diff --git a/src/test/ui/test-allow-fail-attr.rs b/src/test/ui/test-attrs/test-allow-fail-attr.rs
index 1a478460efc..1a478460efc 100644
--- a/src/test/ui/test-allow-fail-attr.rs
+++ b/src/test/ui/test-attrs/test-allow-fail-attr.rs
diff --git a/src/test/ui/test-attr-non-associated-functions.rs b/src/test/ui/test-attrs/test-attr-non-associated-functions.rs
index e475f5b4a75..e475f5b4a75 100644
--- a/src/test/ui/test-attr-non-associated-functions.rs
+++ b/src/test/ui/test-attrs/test-attr-non-associated-functions.rs
diff --git a/src/test/ui/test-attr-non-associated-functions.stderr b/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr
index cb3ae51823e..cb3ae51823e 100644
--- a/src/test/ui/test-attr-non-associated-functions.stderr
+++ b/src/test/ui/test-attrs/test-attr-non-associated-functions.stderr
diff --git a/src/test/ui/test-shadowing/test-cant-be-shadowed.rs b/src/test/ui/test-attrs/test-cant-be-shadowed.rs
index 831372d4506..831372d4506 100644
--- a/src/test/ui/test-shadowing/test-cant-be-shadowed.rs
+++ b/src/test/ui/test-attrs/test-cant-be-shadowed.rs
diff --git a/src/test/ui/test-fn-signature-verification-for-explicit-return-type.rs b/src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs
index ff62d84925f..ff62d84925f 100644
--- a/src/test/ui/test-fn-signature-verification-for-explicit-return-type.rs
+++ b/src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs
diff --git a/src/test/ui/test-main-not-dead-attr.rs b/src/test/ui/test-attrs/test-main-not-dead-attr.rs
index 628b1896ace..628b1896ace 100644
--- a/src/test/ui/test-main-not-dead-attr.rs
+++ b/src/test/ui/test-attrs/test-main-not-dead-attr.rs
diff --git a/src/test/ui/test-main-not-dead.rs b/src/test/ui/test-attrs/test-main-not-dead.rs
index 30a9c85e3d2..30a9c85e3d2 100644
--- a/src/test/ui/test-main-not-dead.rs
+++ b/src/test/ui/test-attrs/test-main-not-dead.rs
diff --git a/src/test/ui/test-on-macro.rs b/src/test/ui/test-attrs/test-on-macro.rs
index a238db6d716..a238db6d716 100644
--- a/src/test/ui/test-on-macro.rs
+++ b/src/test/ui/test-attrs/test-on-macro.rs
diff --git a/src/test/ui/test-on-macro.stderr b/src/test/ui/test-attrs/test-on-macro.stderr
index 256a41722fa..256a41722fa 100644
--- a/src/test/ui/test-on-macro.stderr
+++ b/src/test/ui/test-attrs/test-on-macro.stderr
diff --git a/src/test/ui/test-runner-hides-buried-main.rs b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs
index 917c09801e1..917c09801e1 100644
--- a/src/test/ui/test-runner-hides-buried-main.rs
+++ b/src/test/ui/test-attrs/test-runner-hides-buried-main.rs
diff --git a/src/test/ui/test-runner-hides-main.rs b/src/test/ui/test-attrs/test-runner-hides-main.rs
index 0de1d64f0fc..0de1d64f0fc 100644
--- a/src/test/ui/test-runner-hides-main.rs
+++ b/src/test/ui/test-attrs/test-runner-hides-main.rs
diff --git a/src/test/ui/test-runner-hides-start.rs b/src/test/ui/test-attrs/test-runner-hides-start.rs
index 56212bb6f4b..56212bb6f4b 100644
--- a/src/test/ui/test-runner-hides-start.rs
+++ b/src/test/ui/test-attrs/test-runner-hides-start.rs
diff --git a/src/test/ui/test-should-fail-good-message.rs b/src/test/ui/test-attrs/test-should-fail-good-message.rs
index 9fa759f9eb4..9fa759f9eb4 100644
--- a/src/test/ui/test-should-fail-good-message.rs
+++ b/src/test/ui/test-attrs/test-should-fail-good-message.rs
diff --git a/src/test/ui/test-should-panic-attr.rs b/src/test/ui/test-attrs/test-should-panic-attr.rs
index 9c38322fe96..9c38322fe96 100644
--- a/src/test/ui/test-should-panic-attr.rs
+++ b/src/test/ui/test-attrs/test-should-panic-attr.rs
diff --git a/src/test/ui/test-should-panic-attr.stderr b/src/test/ui/test-attrs/test-should-panic-attr.stderr
index 4b032eba5f8..4b032eba5f8 100644
--- a/src/test/ui/test-should-panic-attr.stderr
+++ b/src/test/ui/test-attrs/test-should-panic-attr.stderr
diff --git a/src/test/ui/test-vs-cfg-test.rs b/src/test/ui/test-attrs/test-vs-cfg-test.rs
index cd1cd33c284..cd1cd33c284 100644
--- a/src/test/ui/test-vs-cfg-test.rs
+++ b/src/test/ui/test-attrs/test-vs-cfg-test.rs
diff --git a/src/test/ui/test-warns-dead-code.rs b/src/test/ui/test-attrs/test-warns-dead-code.rs
index 936461f162d..936461f162d 100644
--- a/src/test/ui/test-warns-dead-code.rs
+++ b/src/test/ui/test-attrs/test-warns-dead-code.rs
diff --git a/src/test/ui/test-warns-dead-code.stderr b/src/test/ui/test-attrs/test-warns-dead-code.stderr
index 62e99225dd9..62e99225dd9 100644
--- a/src/test/ui/test-warns-dead-code.stderr
+++ b/src/test/ui/test-attrs/test-warns-dead-code.stderr