diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index ddfca3a4cfb72..646060bc8be77 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 a3518b2b478ad..e33f8e27871b2 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 24b44964e4fd2..5ad737776da09 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 165a4c707bb6d..81bd687e26321 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 b79e0c2bd3b26..c479912b4ef81 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 9b92bb7698aaa..7f65e5dfaa2bf 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>, + /// 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>, extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>, binding_parent_modules: FxHashMap>, Module<'a>>, @@ -914,6 +918,7 @@ pub struct Resolver<'a> { non_macro_attrs: [Lrc; 2], macro_defs: FxHashMap, local_macro_def_scopes: FxHashMap>, + ast_transform_scopes: FxHashMap>, unused_macros: NodeMap, 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 7224bd74230b3..054b17fec7849 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; @@ -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, + ) -> 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 fd222a132a3f8..ca189e71800b3 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 109ba041016c6..5b2515d20cbab 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, + ) -> 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 6772bbce21b50..8c1632dc7808d 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 dbfd8fe98f389..ccdc5bd81a04b 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 e772eaf834964..31d32d23a645e 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 { - 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 8ca376341fcdb..c577b1e33cfeb 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, ) -> (ast::Crate, Option) { - 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 5fd87d3a0e5c6..be5aca73f5cb1 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 4a6ea0ebf85e5..b93c11fad3823 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, } struct TestCtxt<'a> { - span_diagnostic: &'a errors::Handler, - path: Vec, ext_cx: ExtCtxt<'a>, + def_site: Span, test_cases: Vec, reexport_test_harness_main: Option, test_runner: Option, - // top-level re-export submodule, filled out after folding is finished - toplevel_reexport: Option, } // 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, - - // submodule name, gensym'd identifier for re-exports - tested_submods: Vec<(Ident, Ident)>, + tests: Vec, } impl<'a> MutVisitor for TestHarnessGenerator<'a> { @@ -77,49 +72,47 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { } fn flat_map_item(&mut self, i: P) -> SmallVec<[P; 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, - tested_submods: Vec<(Ident, Ident)>) - -> (P, 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, krate: &mut ast::Crate, - sd: &errors::Handler, features: &Features, test_runner: Option) { - // 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 { - // 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 { // 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 { 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 { 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 { +/// &[&test1, &test2] +fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P { 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{ - 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 733f6f0449065..f0e7344c1b986 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 3d8bfc77a8950..9a296f17aaf4a 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 3a4dc1f5a096b..ecc31cfeb3d1f 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 0000000000000..babba293d03bd --- /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 0000000000000..51e7bed6580b3 --- /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 613ccc0b24234..7b53f0c536ad9 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 b6a817e6b1d30..0000000000000 --- 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 0fe02a9a34d18..ea06f6c1acaf9 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 a499e1362ec0b..7ee8078b2c5d2 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 da1d7549d0750..4f7e000265eb0 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 similarity index 100% rename from src/test/ui/test-shadowing/auxiliary/test_macro.rs rename to 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 0000000000000..fcbe9f49e5564 --- /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 similarity index 56% rename from src/test/ui/inaccessible-test-modules.rs rename to src/test/ui/test-attrs/inaccessible-test-modules.rs index 7095ec290f8f2..f5b3479379480 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 0000000000000..a94ea1e79bc51 --- /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 similarity index 100% rename from src/test/ui/test-allow-fail-attr.rs rename to 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 similarity index 100% rename from src/test/ui/test-attr-non-associated-functions.rs rename to 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 similarity index 100% rename from src/test/ui/test-attr-non-associated-functions.stderr rename to 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 similarity index 100% rename from src/test/ui/test-shadowing/test-cant-be-shadowed.rs rename to 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 similarity index 100% rename from src/test/ui/test-fn-signature-verification-for-explicit-return-type.rs rename to 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 similarity index 100% rename from src/test/ui/test-main-not-dead-attr.rs rename to 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 similarity index 100% rename from src/test/ui/test-main-not-dead.rs rename to 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 similarity index 100% rename from src/test/ui/test-on-macro.rs rename to 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 similarity index 100% rename from src/test/ui/test-on-macro.stderr rename to 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 similarity index 100% rename from src/test/ui/test-runner-hides-buried-main.rs rename to 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 similarity index 100% rename from src/test/ui/test-runner-hides-main.rs rename to 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 similarity index 100% rename from src/test/ui/test-runner-hides-start.rs rename to 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 similarity index 100% rename from src/test/ui/test-should-fail-good-message.rs rename to 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 similarity index 100% rename from src/test/ui/test-should-panic-attr.rs rename to 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 similarity index 100% rename from src/test/ui/test-should-panic-attr.stderr rename to 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 similarity index 100% rename from src/test/ui/test-vs-cfg-test.rs rename to 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 similarity index 100% rename from src/test/ui/test-warns-dead-code.rs rename to 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 similarity index 100% rename from src/test/ui/test-warns-dead-code.stderr rename to src/test/ui/test-attrs/test-warns-dead-code.stderr