From 0133941f47b039d652f5d9f2617b7d3884298515 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 25 Aug 2019 19:59:51 +0100 Subject: [PATCH 01/10] Add an ExpnKind for AST passes --- src/librustc/ich/impls_syntax.rs | 8 ++++++++ src/librustc/lint/mod.rs | 2 +- src/libsyntax_pos/hygiene.rs | 24 +++++++++++++++++++++++- src/libsyntax_pos/lib.rs | 1 + 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 05e2c7854b49c..532485b5d77bb 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -411,9 +411,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 2b58627cdea56..1b133819a73bf 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -888,7 +888,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/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 733f6f0449065..237a02f2f667a 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -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..d800109cf8531 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 => ("#[", "]"), From 4082cd95a8de6c8244e9b44908f9859e75acdeea Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 25 Aug 2019 20:58:03 +0100 Subject: [PATCH 02/10] Allow ast passes to create hygienic spans --- src/librustc_resolve/build_reduced_graph.rs | 3 +- src/librustc_resolve/lib.rs | 22 ++++++++++++ src/librustc_resolve/macros.rs | 39 ++++++++++++++++++++- src/libsyntax/ext/base.rs | 10 +++++- src/libsyntax_pos/hygiene.rs | 2 +- 5 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 9a794ade729c2..5a3b768301ce0 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/lib.rs b/src/librustc_resolve/lib.rs index 875ae449d94e0..b12bf4521e098 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -879,6 +879,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>>, @@ -913,6 +917,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. @@ -1080,6 +1085,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); @@ -1139,10 +1159,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 719167eb057b2..fe7eae91945f7 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}; @@ -16,7 +17,7 @@ use syntax::edition::Edition; use syntax::ext::base::{self, Indeterminate, SpecialDerives}; use syntax::ext::base::{MacroKind, SyntaxExtension}; use syntax::ext::expand::{AstFragment, Invocation, InvocationKind}; -use syntax::ext::hygiene::{self, ExpnId, ExpnData, ExpnKind}; +use syntax::ext::hygiene::{self, ExpnId, ExpnData, ExpnKind, Transparency}; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{emit_feature_err, is_builtin_attr_name}; use syntax::feature_gate::GateIssue; @@ -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; @@ -136,6 +138,41 @@ impl<'a> base::Resolver for Resolver<'a> { } } + // Create a Span with modern hygiene with a definition site of the provided + // module, or a fake empty `#[no_implicit_prelude]` module if no module is + // provided. + fn span_for_ast_pass( + &mut self, + base_span: Span, + pass: AstPass, + features: &[Symbol], + parent_module_id: Option, + ) -> Span { + let span = base_span.fresh_expansion_with_transparency( + ExpnData::allow_unstable( + ExpnKind::AstPass(pass), + base_span, + self.session.edition(), + features.into(), + ), + Transparency::Opaque, + ); + let expn_id = span.ctxt().outer_expn(); + 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); + span + } + fn resolve_imports(&mut self) { ImportResolver { r: self }.resolve_imports() } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index a63c4181d5e03..3d274af99083c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -15,7 +15,7 @@ use crate::tokenstream::{self, TokenStream, TokenTree}; 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}; @@ -660,6 +660,14 @@ pub trait Resolver { extra_placeholders: &[NodeId]); fn register_builtin_macro(&mut self, ident: ast::Ident, ext: SyntaxExtension); + fn span_for_ast_pass( + &mut self, + span: Span, + pass: AstPass, + features: &[Symbol], + parent_module_id: Option, + ) -> Span; + fn resolve_imports(&mut self); fn resolve_macro_invocation( diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 237a02f2f667a..99afe57ef5497 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -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( From 6fcdb36ccb60cca5c26ac804770a47fc01e0c2dd Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 25 Aug 2019 21:03:24 +0100 Subject: [PATCH 03/10] Make use of hygiene in AST passes --- src/librustc_interface/passes.rs | 27 +-- src/librustc_resolve/macros.rs | 10 - src/libsyntax/ext/base.rs | 2 - src/libsyntax_ext/plugin_macro_defs.rs | 4 +- src/libsyntax_ext/proc_macro_harness.rs | 86 +++++---- src/libsyntax_ext/standard_library_imports.rs | 130 +++++++++---- src/libsyntax_ext/test.rs | 19 +- src/libsyntax_ext/test_harness.rs | 175 +++++------------- src/test/ui/hygiene/auxiliary/not-libstd.rs | 1 + src/test/ui/hygiene/prelude-import-hygiene.rs | 29 +++ src/test/ui/imports/gensymed.rs | 4 +- src/test/ui/inaccessible-test-modules.stderr | 21 --- src/test/ui/test-attrs/decl-macro-test.rs | 22 +++ .../inaccessible-test-modules.rs | 4 +- .../inaccessible-test-modules.stderr | 21 +++ 15 files changed, 298 insertions(+), 257 deletions(-) create mode 100644 src/test/ui/hygiene/auxiliary/not-libstd.rs create mode 100644 src/test/ui/hygiene/prelude-import-hygiene.rs delete mode 100644 src/test/ui/inaccessible-test-modules.stderr create mode 100644 src/test/ui/test-attrs/decl-macro-test.rs rename src/test/ui/{ => test-attrs}/inaccessible-test-modules.rs (56%) create mode 100644 src/test/ui/test-attrs/inaccessible-test-modules.stderr diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 8b0b5a5b7a2bd..649cc8a2fb515 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, + alt_std_name, + sess.edition(), + ); + 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/macros.rs b/src/librustc_resolve/macros.rs index fe7eae91945f7..20d281f1e997a 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -97,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)); diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 3d274af99083c..962447e8cf07f 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -653,8 +653,6 @@ 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]); 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..1cdaa1190fae3 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,29 @@ 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 span = cx.resolver.span_for_ast_pass( + DUMMY_SP, + AstPass::ProcMacroHarness, + &[sym::rustc_attrs, sym::proc_macro_internals], + None, + ); + + 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 +377,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 +388,44 @@ 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( - span, + let block = P(ast::Expr { + id: ast::DUMMY_NODE_ID, + attrs: syntax::ThinVec::new(), + node: ast::ExprKind::Block(P(ast::Block { + id: ast::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + stmts: vec![ + ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Item(krate), + span, + }, + ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Item(decls_static), + span, + } + ], + span, + }), None), 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 }); + let anon_constant = cx.item_const( + span, + ast::Ident::new(kw::Underscore, span), + P(ast::Ty { + id: ast::DUMMY_NODE_ID, + node: ast::TyKind::Tup(Vec::new()), + span, + }), + block, + ); + // 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 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..61e423266fa05 100644 --- a/src/libsyntax_ext/standard_library_imports.rs +++ b/src/libsyntax_ext/standard_library_imports.rs @@ -1,52 +1,56 @@ use syntax::{ast, attr}; use syntax::edition::Edition; -use syntax::ext::hygiene::MacroKind; +use syntax::ext::hygiene::AstPass; +use syntax::ext::base::Resolver; use syntax::ptr::P; -use syntax::source_map::{ExpnData, ExpnKind, dummy_spanned, respan}; +use syntax::source_map::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, + alt_std_name: Option, + edition: Edition, ) -> (ast::Crate, Option) { let rust_2018 = 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 span = resolver.span_for_ast_pass( + DUMMY_SP, + AstPass::StdImports, + &[sym::prelude_import], + None, + ); + // .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); + for &orig_name_sym in names.iter().rev() { let (rename, orig_name) = if rust_2018 { - (orig_name_ident.gensym(), Some(orig_name_sym)) + (Ident::new(kw::Underscore, span), Some(orig_name_sym)) } else { - (orig_name_ident, None) + (Ident::with_dummy_span(orig_name_sym), None) }; 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)) + attr::mk_word_item(ast::Ident::new(sym::macro_use, span)) )], - vis: dummy_spanned(ast::VisibilityKind::Inherited), + vis: respan(span, ast::VisibilityKind::Inherited), node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)), ident: rename, id: ast::DUMMY_NODE_ID, - span: DUMMY_SP, + span, tokens: None, })); } @@ -55,24 +59,22 @@ pub fn inject( // 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 segments = if rust_2018 { + [name, sym::prelude, sym::v1].iter() + .map(|symbol| ast::PathSegment::from_ident(ast::Ident::new(*symbol, span))) + .collect() + } else { + [kw::PathRoot, name, sym::prelude, sym::v1].iter() + .map(|symbol| ast::PathSegment::from_ident(ast::Ident::with_dummy_span(*symbol))) + .collect() + }; - krate.module.items.insert(0, P(ast::Item { + let use_item = 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, - }, + prefix: ast::Path { segments, span }, kind: ast::UseTreeKind::Glob, span, })), @@ -80,7 +82,65 @@ pub fn inject( ident: ast::Ident::invalid(), span, tokens: None, - })); + }); + + let prelude_import_item = if rust_2018 { + let hygienic_extern_crate = P(ast::Item { + attrs: vec![], + vis: respan(span, ast::VisibilityKind::Inherited), + node: ast::ItemKind::ExternCrate(alt_std_name), + ident: ast::Ident::new(name, span), + id: ast::DUMMY_NODE_ID, + span, + tokens: None, + }); + + // Use an anonymous const to hide `extern crate std as hygienic_std` + // FIXME: Once inter-crate hygiene exists, this can just be `use_item`. + P(ast::Item { + attrs: Vec::new(), + vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), + node: ast::ItemKind::Const( + P(ast::Ty { + id: ast::DUMMY_NODE_ID, + node: ast::TyKind::Tup(Vec::new()), + span, + }), + P(ast::Expr { + id: ast::DUMMY_NODE_ID, + attrs: syntax::ThinVec::new(), + node: ast::ExprKind::Block(P(ast::Block { + id: ast::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Default, + stmts: vec![ + ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Item(use_item), + span, + }, + ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Item(hygienic_extern_crate), + span, + } + ], + span, + }), None), + span, + }) + ), + id: ast::DUMMY_NODE_ID, + ident: ast::Ident::new(kw::Underscore, span), + span, + tokens: None, + }) + } else { + // Have `extern crate std` at the root, so don't need to create a named + // extern crate item. + use_item + }; + + krate.module.items.insert(0, prelude_import_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..6eb132979df8f 100644 --- a/src/libsyntax_ext/test_harness.rs +++ b/src/libsyntax_ext/test_harness.rs @@ -5,32 +5,29 @@ 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>, 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 +40,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 +53,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 +71,46 @@ 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 sp = self.cx.ext_cx.resolver.span_for_ast_pass( + module.inner, + AstPass::TestHarness, + &[], + Some(parent), + ); + let expn = sp.ctxt().outer_expn(); + for test in &mut tests { + test.ident.span = test.ident.span.apply_mark(expn, 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)] } @@ -181,59 +172,11 @@ 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 @@ -244,19 +187,15 @@ fn generate_test_harness(sess: &ParseSess, econfig.features = Some(features); let cx = TestCtxt { - span_diagnostic: sd, ext_cx: ExtCtxt::new(sess, econfig, resolver), - path: Vec::new(), 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); } @@ -268,12 +207,14 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { // #![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.ext_cx.resolver.span_for_ast_pass( + DUMMY_SP, + AstPass::TestHarness, + &[sym::main, sym::test, sym::rustc_attrs], + None, + ); 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( @@ -285,14 +226,14 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { 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![], @@ -316,8 +257,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 +266,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 +276,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/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/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`. From 2a82aec36ae86a1913473cb7e830a77b481641ea Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 25 Aug 2019 21:21:07 +0100 Subject: [PATCH 04/10] Move tests for unit tests to their own directory --- .../ui/{test-shadowing => test-attrs}/auxiliary/test_macro.rs | 0 src/test/ui/{ => test-attrs}/test-allow-fail-attr.rs | 0 .../ui/{ => test-attrs}/test-attr-non-associated-functions.rs | 0 .../ui/{ => test-attrs}/test-attr-non-associated-functions.stderr | 0 .../ui/{test-shadowing => test-attrs}/test-cant-be-shadowed.rs | 0 .../test-fn-signature-verification-for-explicit-return-type.rs | 0 src/test/ui/{ => test-attrs}/test-main-not-dead-attr.rs | 0 src/test/ui/{ => test-attrs}/test-main-not-dead.rs | 0 src/test/ui/{ => test-attrs}/test-on-macro.rs | 0 src/test/ui/{ => test-attrs}/test-on-macro.stderr | 0 src/test/ui/{ => test-attrs}/test-runner-hides-buried-main.rs | 0 src/test/ui/{ => test-attrs}/test-runner-hides-main.rs | 0 src/test/ui/{ => test-attrs}/test-runner-hides-start.rs | 0 src/test/ui/{ => test-attrs}/test-should-fail-good-message.rs | 0 src/test/ui/{ => test-attrs}/test-should-panic-attr.rs | 0 src/test/ui/{ => test-attrs}/test-should-panic-attr.stderr | 0 src/test/ui/{ => test-attrs}/test-vs-cfg-test.rs | 0 src/test/ui/{ => test-attrs}/test-warns-dead-code.rs | 0 src/test/ui/{ => test-attrs}/test-warns-dead-code.stderr | 0 19 files changed, 0 insertions(+), 0 deletions(-) rename src/test/ui/{test-shadowing => test-attrs}/auxiliary/test_macro.rs (100%) rename src/test/ui/{ => test-attrs}/test-allow-fail-attr.rs (100%) rename src/test/ui/{ => test-attrs}/test-attr-non-associated-functions.rs (100%) rename src/test/ui/{ => test-attrs}/test-attr-non-associated-functions.stderr (100%) rename src/test/ui/{test-shadowing => test-attrs}/test-cant-be-shadowed.rs (100%) rename src/test/ui/{ => test-attrs}/test-fn-signature-verification-for-explicit-return-type.rs (100%) rename src/test/ui/{ => test-attrs}/test-main-not-dead-attr.rs (100%) rename src/test/ui/{ => test-attrs}/test-main-not-dead.rs (100%) rename src/test/ui/{ => test-attrs}/test-on-macro.rs (100%) rename src/test/ui/{ => test-attrs}/test-on-macro.stderr (100%) rename src/test/ui/{ => test-attrs}/test-runner-hides-buried-main.rs (100%) rename src/test/ui/{ => test-attrs}/test-runner-hides-main.rs (100%) rename src/test/ui/{ => test-attrs}/test-runner-hides-start.rs (100%) rename src/test/ui/{ => test-attrs}/test-should-fail-good-message.rs (100%) rename src/test/ui/{ => test-attrs}/test-should-panic-attr.rs (100%) rename src/test/ui/{ => test-attrs}/test-should-panic-attr.stderr (100%) rename src/test/ui/{ => test-attrs}/test-vs-cfg-test.rs (100%) rename src/test/ui/{ => test-attrs}/test-warns-dead-code.rs (100%) rename src/test/ui/{ => test-attrs}/test-warns-dead-code.stderr (100%) 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-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 From beb2f5b8ff89f3e973e085ce338a59e5203fecb0 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 25 Aug 2019 21:31:42 +0100 Subject: [PATCH 05/10] Remove `Ident::{gensym, is_gensymed}` `gensym_if_underscore` still exists. The symbol interner can still create arbitray gensyms, this is just not exposed publicly. --- src/librustc_resolve/resolve_imports.rs | 8 ++------ src/libsyntax_pos/symbol.rs | 27 +++++++++++-------------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index fd222a132a3f8..4e33ea018a074 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -1307,12 +1307,8 @@ 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. + let is_good_import = binding.is_import() && !binding.is_ambiguity(); if is_good_import || binding.is_macro_def() { let res = binding.res(); if res != Res::Err { diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 0b8f16bbc3b99..ce20e4407fe88 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -798,21 +798,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 + } } pub fn as_str(self) -> LocalInternedString { @@ -865,9 +859,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 From 846df20578189260ed3f4c9c824d324b6c9b7eac Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 25 Aug 2019 22:36:13 +0100 Subject: [PATCH 06/10] Fix 2018 edition expanded pretty printing --- src/libsyntax/print/pprust.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 4dc00af486013..c18c8c18ae1f9 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); From 0b86782058c27ba694ec81ebe7108dceb0968a2b Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Tue, 27 Aug 2019 21:13:20 +0100 Subject: [PATCH 07/10] Don't call `diag_span_note_once` for suppressed lints --- src/librustc/lint/mod.rs | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 1b133819a73bf..fb7f14febf32e 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -666,6 +666,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 => { @@ -715,10 +739,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; \ @@ -743,22 +763,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 } From c8cf9f5a025bb475804b5a90f54aca310b952526 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 28 Aug 2019 12:41:29 +0300 Subject: [PATCH 08/10] Add `with_{def_site,call_site,legacy}_ctxt,` methods to `Span` Use these to create call-site spans for AST passes when needed. --- src/librustc_resolve/macros.rs | 27 +++--- src/libsyntax/ext/base.rs | 14 +-- src/libsyntax_ext/proc_macro_harness.rs | 3 +- src/libsyntax_ext/standard_library_imports.rs | 8 +- src/libsyntax_ext/test_harness.rs | 10 +- src/libsyntax_pos/hygiene.rs | 2 +- src/libsyntax_pos/lib.rs | 19 ++++ .../dollar-crate-issue-57089.stdout | 32 +++---- .../dollar-crate-issue-62325.stdout | 44 ++++----- src/test/ui/proc-macro/dollar-crate.stdout | 96 +++++++++---------- 10 files changed, 138 insertions(+), 117 deletions(-) diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 20d281f1e997a..02022c98c3502 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -17,7 +17,7 @@ use syntax::edition::Edition; use syntax::ext::base::{self, Indeterminate, SpecialDerives}; use syntax::ext::base::{MacroKind, SyntaxExtension}; use syntax::ext::expand::{AstFragment, Invocation, InvocationKind}; -use syntax::ext::hygiene::{self, ExpnId, ExpnData, ExpnKind, Transparency}; +use syntax::ext::hygiene::{self, ExpnId, ExpnData, ExpnKind}; use syntax::ext::tt::macro_rules; use syntax::feature_gate::{emit_feature_err, is_builtin_attr_name}; use syntax::feature_gate::GateIssue; @@ -131,23 +131,20 @@ impl<'a> base::Resolver for Resolver<'a> { // Create a Span with modern hygiene with a definition site of the provided // module, or a fake empty `#[no_implicit_prelude]` module if no module is // provided. - fn span_for_ast_pass( + fn expansion_for_ast_pass( &mut self, - base_span: Span, + call_site: Span, pass: AstPass, features: &[Symbol], parent_module_id: Option, - ) -> Span { - let span = base_span.fresh_expansion_with_transparency( - ExpnData::allow_unstable( - ExpnKind::AstPass(pass), - base_span, - self.session.edition(), - features.into(), - ), - Transparency::Opaque, - ); - let expn_id = span.ctxt().outer_expn(); + ) -> 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); @@ -160,7 +157,7 @@ impl<'a> base::Resolver for Resolver<'a> { self.empty_module }; self.ast_transform_scopes.insert(expn_id, parent_scope); - span + expn_id } fn resolve_imports(&mut self) { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 962447e8cf07f..4c146611deab6 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -3,7 +3,7 @@ use crate::attr::{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, DirectoryOwnership}; use crate::parse::token; @@ -658,13 +658,13 @@ pub trait Resolver { extra_placeholders: &[NodeId]); fn register_builtin_macro(&mut self, ident: ast::Ident, ext: SyntaxExtension); - fn span_for_ast_pass( + fn expansion_for_ast_pass( &mut self, - span: Span, + call_site: Span, pass: AstPass, features: &[Symbol], parent_module_id: Option, - ) -> Span; + ) -> ExpnId; fn resolve_imports(&mut self); @@ -750,20 +750,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_ext/proc_macro_harness.rs b/src/libsyntax_ext/proc_macro_harness.rs index 1cdaa1190fae3..8a4b78a3efa31 100644 --- a/src/libsyntax_ext/proc_macro_harness.rs +++ b/src/libsyntax_ext/proc_macro_harness.rs @@ -326,12 +326,13 @@ fn mk_decls( custom_attrs: &[ProcMacroDef], custom_macros: &[ProcMacroDef], ) -> P { - let span = cx.resolver.span_for_ast_pass( + 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, diff --git a/src/libsyntax_ext/standard_library_imports.rs b/src/libsyntax_ext/standard_library_imports.rs index 61e423266fa05..e5dded9ea5319 100644 --- a/src/libsyntax_ext/standard_library_imports.rs +++ b/src/libsyntax_ext/standard_library_imports.rs @@ -28,19 +28,21 @@ pub fn inject( &[sym::std] }; - let span = resolver.span_for_ast_pass( + 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); // .rev() to preserve ordering above in combination with insert(0, ...) for &orig_name_sym in names.iter().rev() { let (rename, orig_name) = if rust_2018 { (Ident::new(kw::Underscore, span), Some(orig_name_sym)) } else { - (Ident::with_dummy_span(orig_name_sym), None) + (Ident::new(orig_name_sym, call_site), None) }; krate.module.items.insert(0, P(ast::Item { attrs: vec![attr::mk_attr_outer( @@ -65,7 +67,7 @@ pub fn inject( .collect() } else { [kw::PathRoot, name, sym::prelude, sym::v1].iter() - .map(|symbol| ast::PathSegment::from_ident(ast::Ident::with_dummy_span(*symbol))) + .map(|symbol| ast::PathSegment::from_ident(ast::Ident::new(*symbol, call_site))) .collect() }; diff --git a/src/libsyntax_ext/test_harness.rs b/src/libsyntax_ext/test_harness.rs index 6eb132979df8f..eedd7fbee3806 100644 --- a/src/libsyntax_ext/test_harness.rs +++ b/src/libsyntax_ext/test_harness.rs @@ -97,15 +97,16 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { }; // Create an identifier that will hygienically resolve the test // case name, even in another module. - let sp = self.cx.ext_cx.resolver.span_for_ast_pass( + let expn_id = self.cx.ext_cx.resolver.expansion_for_ast_pass( module.inner, AstPass::TestHarness, &[], Some(parent), ); - let expn = sp.ctxt().outer_expn(); for test in &mut tests { - test.ident.span = test.ident.span.apply_mark(expn, Transparency::Opaque); + // 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); } @@ -207,12 +208,13 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { // #![main] // test::test_main_static(&[..tests]); // } - let sp = cx.ext_cx.resolver.span_for_ast_pass( + let expn_id = cx.ext_cx.resolver.expansion_for_ast_pass( DUMMY_SP, AstPass::TestHarness, &[sym::main, sym::test, sym::rustc_attrs], None, ); + let sp = DUMMY_SP.with_def_site_ctxt(expn_id); let ecx = &cx.ext_cx; let test_id = Ident::new(sym::test, sp); diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 99afe57ef5497..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)) } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index d800109cf8531..9a296f17aaf4a 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -514,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/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), }, ] From e552840e79330f8a1f36fc676b71fa38b3123a50 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 28 Aug 2019 22:47:52 +0100 Subject: [PATCH 09/10] Document test harness generation Also ensure that we're consistently using the def-site span when appropriate. --- src/libsyntax_ext/test_harness.rs | 82 +++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/src/libsyntax_ext/test_harness.rs b/src/libsyntax_ext/test_harness.rs index eedd7fbee3806..b93c11fad3823 100644 --- a/src/libsyntax_ext/test_harness.rs +++ b/src/libsyntax_ext/test_harness.rs @@ -25,6 +25,7 @@ struct Test { struct TestCtxt<'a> { ext_cx: ExtCtxt<'a>, + def_site: Span, test_cases: Vec, reexport_test_harness_main: Option, test_runner: Option, @@ -125,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 { @@ -141,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); @@ -180,15 +184,26 @@ fn generate_test_harness(sess: &ParseSess, krate: &mut ast::Crate, 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 { - ext_cx: ExtCtxt::new(sess, econfig, resolver), + ext_cx, + def_site, test_cases: Vec::new(), reexport_test_harness_main, test_runner @@ -202,27 +217,40 @@ fn generate_test_harness(sess: &ParseSess, /// 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 expn_id = cx.ext_cx.resolver.expansion_for_ast_pass( - DUMMY_SP, - AstPass::TestHarness, - &[sym::main, sym::test, sym::rustc_attrs], - None, - ); - let sp = DUMMY_SP.with_def_site_ctxt(expn_id); + let sp = cx.def_site; let ecx = &cx.ext_cx; 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; @@ -231,10 +259,6 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { 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 let test_extern_stmt = ecx.stmt_item(sp, ecx.item(sp, test_id, @@ -242,6 +266,10 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { 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![])); @@ -279,7 +307,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { } /// Creates a slice containing every test like so: -/// &[test1, test2] +/// &[&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; From 3f3fc52bfa255e68d84ca40a497137f5c6bae4a8 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 5 Sep 2019 15:05:58 +0100 Subject: [PATCH 10/10] Simplify std lib injection --- src/librustc_interface/passes.rs | 2 +- src/librustc_resolve/diagnostics.rs | 8 ++ src/librustc_resolve/macros.rs | 5 +- src/librustc_resolve/resolve_imports.rs | 7 +- src/libsyntax_ext/proc_macro_harness.rs | 32 +---- src/libsyntax_ext/standard_library_imports.rs | 124 +++++------------- 6 files changed, 52 insertions(+), 126 deletions(-) diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index 649cc8a2fb515..48c3ddf39a22b 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -366,8 +366,8 @@ fn configure_and_expand_inner<'a>( let (krate, name) = syntax_ext::standard_library_imports::inject( krate, &mut resolver, + &sess.parse_sess, alt_std_name, - sess.edition(), ); if let Some(name) = name { sess.parse_sess.injected_crate_name.set(name); 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/macros.rs b/src/librustc_resolve/macros.rs index 02022c98c3502..cf99663beaf48 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -128,9 +128,8 @@ impl<'a> base::Resolver for Resolver<'a> { } } - // Create a Span with modern hygiene with a definition site of the provided - // module, or a fake empty `#[no_implicit_prelude]` module if no module is - // provided. + // 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, diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 4e33ea018a074..ca189e71800b3 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -1307,8 +1307,11 @@ impl<'a, 'b> ImportResolver<'a, 'b> { None => continue, }; - // Filter away ambiguous imports. - let is_good_import = binding.is_import() && !binding.is_ambiguity(); + // 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/proc_macro_harness.rs b/src/libsyntax_ext/proc_macro_harness.rs index 8a4b78a3efa31..31d32d23a645e 100644 --- a/src/libsyntax_ext/proc_macro_harness.rs +++ b/src/libsyntax_ext/proc_macro_harness.rs @@ -392,41 +392,19 @@ fn mk_decls( i }); - let block = P(ast::Expr { - id: ast::DUMMY_NODE_ID, - attrs: syntax::ThinVec::new(), - node: ast::ExprKind::Block(P(ast::Block { - id: ast::DUMMY_NODE_ID, - rules: ast::BlockCheckMode::Default, - stmts: vec![ - ast::Stmt { - id: ast::DUMMY_NODE_ID, - node: ast::StmtKind::Item(krate), - span, - }, - ast::Stmt { - id: ast::DUMMY_NODE_ID, - node: ast::StmtKind::Item(decls_static), - span, - } - ], - span, - }), None), + let block = cx.expr_block(cx.block( span, - }); + vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)], + )); let anon_constant = cx.item_const( span, ast::Ident::new(kw::Underscore, span), - P(ast::Ty { - id: ast::DUMMY_NODE_ID, - node: ast::TyKind::Tup(Vec::new()), - span, - }), + cx.ty(span, ast::TyKind::Tup(Vec::new())), block, ); - // Integrate the new module into existing module structures. + // 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 e5dded9ea5319..c577b1e33cfeb 100644 --- a/src/libsyntax_ext/standard_library_imports.rs +++ b/src/libsyntax_ext/standard_library_imports.rs @@ -1,19 +1,20 @@ use syntax::{ast, attr}; use syntax::edition::Edition; +use syntax::ext::expand::ExpansionConfig; use syntax::ext::hygiene::AstPass; -use syntax::ext::base::Resolver; +use syntax::ext::base::{ExtCtxt, Resolver}; +use syntax::parse::ParseSess; use syntax::ptr::P; -use syntax::source_map::respan; use syntax::symbol::{Ident, Symbol, kw, sym}; use syntax_pos::DUMMY_SP; pub fn inject( mut krate: ast::Crate, resolver: &mut dyn Resolver, + sess: &ParseSess, alt_std_name: Option, - edition: Edition, ) -> (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: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) { @@ -37,112 +38,49 @@ pub fn inject( 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, ...) - for &orig_name_sym in names.iter().rev() { - let (rename, orig_name) = if rust_2018 { - (Ident::new(kw::Underscore, span), Some(orig_name_sym)) + for &name in names.iter().rev() { + let ident = if rust_2018 { + Ident::new(name, span) } else { - (Ident::new(orig_name_sym, call_site), 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::new(sym::macro_use, span)) - )], - vis: respan(span, ast::VisibilityKind::Inherited), - node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)), - ident: rename, - id: ast::DUMMY_NODE_ID, + krate.module.items.insert(0, cx.item( span, - tokens: None, - })); + 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 segments = if rust_2018 { + let import_path = if rust_2018 { [name, sym::prelude, sym::v1].iter() - .map(|symbol| ast::PathSegment::from_ident(ast::Ident::new(*symbol, span))) - .collect() + .map(|symbol| ast::Ident::new(*symbol, span)).collect() } else { [kw::PathRoot, name, sym::prelude, sym::v1].iter() - .map(|symbol| ast::PathSegment::from_ident(ast::Ident::new(*symbol, call_site))) - .collect() + .map(|symbol| ast::Ident::new(*symbol, span)).collect() }; - let use_item = 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, 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, - }); - - let prelude_import_item = if rust_2018 { - let hygienic_extern_crate = P(ast::Item { - attrs: vec![], - vis: respan(span, ast::VisibilityKind::Inherited), - node: ast::ItemKind::ExternCrate(alt_std_name), - ident: ast::Ident::new(name, span), - id: ast::DUMMY_NODE_ID, - span, - tokens: None, - }); - - // Use an anonymous const to hide `extern crate std as hygienic_std` - // FIXME: Once inter-crate hygiene exists, this can just be `use_item`. - P(ast::Item { - attrs: Vec::new(), - vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), - node: ast::ItemKind::Const( - P(ast::Ty { - id: ast::DUMMY_NODE_ID, - node: ast::TyKind::Tup(Vec::new()), - span, - }), - P(ast::Expr { - id: ast::DUMMY_NODE_ID, - attrs: syntax::ThinVec::new(), - node: ast::ExprKind::Block(P(ast::Block { - id: ast::DUMMY_NODE_ID, - rules: ast::BlockCheckMode::Default, - stmts: vec![ - ast::Stmt { - id: ast::DUMMY_NODE_ID, - node: ast::StmtKind::Item(use_item), - span, - }, - ast::Stmt { - id: ast::DUMMY_NODE_ID, - node: ast::StmtKind::Item(hygienic_extern_crate), - span, - } - ], - span, - }), None), - span, - }) - ), - id: ast::DUMMY_NODE_ID, - ident: ast::Ident::new(kw::Underscore, span), - span, - tokens: None, - }) - } else { - // Have `extern crate std` at the root, so don't need to create a named - // extern crate item. - use_item - }; + ); - krate.module.items.insert(0, prelude_import_item); + krate.module.items.insert(0, use_item); (krate, Some(name)) }