Skip to content

Use hygiene for AST passes #63919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 7, 2019
8 changes: 8 additions & 0 deletions src/librustc/ich/impls_syntax.rs
Original file line number Diff line number Diff line change
@@ -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,
46 changes: 25 additions & 21 deletions src/librustc/lint/mod.rs
Original file line number Diff line number Diff line change
@@ -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
}

@@ -888,7 +892,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
27 changes: 16 additions & 11 deletions src/librustc_interface/passes.rs
Original file line number Diff line number Diff line change
@@ -233,7 +233,7 @@ pub fn register_plugins<'a>(
syntax::attr::inject(krate, &sess.parse_sess, &sess.opts.debugging_opts.crate_attr)
});

let (mut krate, features) = syntax::config::features(
let (krate, features) = syntax::config::features(
krate,
&sess.parse_sess,
sess.edition(),
@@ -268,16 +268,6 @@ pub fn register_plugins<'a>(
middle::recursion_limit::update_limits(sess, &krate);
});

krate = time(sess, "crate injection", || {
let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| &**s);
let (krate, name) =
syntax_ext::standard_library_imports::inject(krate, alt_std_name, sess.edition());
if let Some(name) = name {
sess.parse_sess.injected_crate_name.set(name);
}
krate
});

let registrars = time(sess, "plugin loading", || {
plugin::load::load_plugins(
sess,
@@ -370,6 +360,21 @@ fn configure_and_expand_inner<'a>(
&resolver_arenas,
);
syntax_ext::register_builtin_macros(&mut resolver, sess.edition());

krate = time(sess, "crate injection", || {
let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
let (krate, name) = syntax_ext::standard_library_imports::inject(
krate,
&mut resolver,
&sess.parse_sess,
alt_std_name,
);
if let Some(name) = name {
sess.parse_sess.injected_crate_name.set(name);
}
krate
});

syntax_ext::plugin_macro_defs::inject(
&mut krate, &mut resolver, plugin_info.syntax_exts, sess.edition()
);
3 changes: 2 additions & 1 deletion src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
@@ -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]
8 changes: 8 additions & 0 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -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 {
22 changes: 22 additions & 0 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Module<'a>>,
/// A fake module that contains no definition and no prelude. Used so that
/// some AST passes can generate identifiers that only resolve to local or
/// language items.
empty_module: Module<'a>,
module_map: FxHashMap<DefId, Module<'a>>,
extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>,
binding_parent_modules: FxHashMap<PtrKey<'a, NameBinding<'a>>, Module<'a>>,
@@ -913,6 +917,7 @@ pub struct Resolver<'a> {
non_macro_attrs: [Lrc<SyntaxExtension>; 2],
macro_defs: FxHashMap<ExpnId, DefId>,
local_macro_def_scopes: FxHashMap<NodeId, Module<'a>>,
ast_transform_scopes: FxHashMap<ExpnId, Module<'a>>,
unused_macros: NodeMap<Span>,
proc_macro_stubs: NodeSet,
/// Traces collected during macro resolution and validated when it's complete.
@@ -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(),

43 changes: 33 additions & 10 deletions src/librustc_resolve/macros.rs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ use crate::{ModuleOrUniformRoot, KNOWN_TOOLS};
use crate::Namespace::*;
use crate::resolve_imports::ImportResolver;
use rustc::hir::def::{self, DefKind, NonMacroAttrKind};
use rustc::hir::def_id;
use rustc::middle::stability;
use rustc::{ty, lint, span_bug};
use syntax::ast::{self, NodeId, Ident};
@@ -25,6 +26,7 @@ use syntax_pos::{Span, DUMMY_SP};

use std::{mem, ptr};
use rustc_data_structures::sync::Lrc;
use syntax_pos::hygiene::AstPass;

type Res = def::Res<NodeId>;

@@ -95,16 +97,6 @@ impl<'a> base::Resolver for Resolver<'a> {
self.session.next_node_id()
}

fn get_module_scope(&mut self, id: NodeId) -> ExpnId {
let expn_id = ExpnId::fresh(Some(ExpnData::default(
ExpnKind::Macro(MacroKind::Attr, sym::test_case), DUMMY_SP, self.session.edition()
)));
let module = self.module_map[&self.definitions.local_def_id(id)];
self.invocation_parent_scopes.insert(expn_id, ParentScope::module(module));
self.definitions.set_invocation_parent(expn_id, module.def_id().unwrap().index);
expn_id
}

fn resolve_dollar_crates(&mut self) {
hygiene::update_dollar_crate_names(|ctxt| {
let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt));
@@ -136,6 +128,37 @@ impl<'a> base::Resolver for Resolver<'a> {
}
}

// Create a new Expansion with a definition site of the provided module, or
// a fake empty `#[no_implicit_prelude]` module if no module is provided.
fn expansion_for_ast_pass(
&mut self,
call_site: Span,
pass: AstPass,
features: &[Symbol],
parent_module_id: Option<NodeId>,
) -> ExpnId {
let expn_id = ExpnId::fresh(Some(ExpnData::allow_unstable(
ExpnKind::AstPass(pass),
call_site,
self.session.edition(),
features.into(),
)));

let parent_scope = if let Some(module_id) = parent_module_id {
let parent_def_id = self.definitions.local_def_id(module_id);
self.definitions.add_parent_module_of_macro_def(expn_id, parent_def_id);
self.module_map[&parent_def_id]
} else {
self.definitions.add_parent_module_of_macro_def(
expn_id,
def_id::DefId::local(def_id::CRATE_DEF_INDEX),
);
self.empty_module
};
self.ast_transform_scopes.insert(expn_id, parent_scope);
expn_id
}

fn resolve_imports(&mut self) {
ImportResolver { r: self }.resolve_imports()
}
11 changes: 5 additions & 6 deletions src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
@@ -1307,12 +1307,11 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
None => continue,
};

// Filter away ambiguous and gensymed imports. Gensymed imports
// (e.g. implicitly injected `std`) cannot be properly encoded in metadata,
// so they can cause name conflict errors downstream.
let is_good_import = binding.is_import() && !binding.is_ambiguity() &&
// Note that as_str() de-gensyms the Symbol
!(ident.is_gensymed() && ident.name.as_str() != "_");
// Filter away ambiguous imports and anything that has def-site
// hygiene.
// FIXME: Implement actual cross-crate hygiene.
let is_good_import = binding.is_import() && !binding.is_ambiguity()
&& !ident.span.modern().from_expansion();
if is_good_import || binding.is_macro_def() {
let res = binding.res();
if res != Res::Err {
20 changes: 13 additions & 7 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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};
@@ -653,13 +653,19 @@ bitflags::bitflags! {
pub trait Resolver {
fn next_node_id(&mut self) -> NodeId;

fn get_module_scope(&mut self, id: NodeId) -> ExpnId;

fn resolve_dollar_crates(&mut self);
fn visit_ast_fragment_with_placeholders(&mut self, expn_id: ExpnId, fragment: &AstFragment,
extra_placeholders: &[NodeId]);
fn register_builtin_macro(&mut self, ident: ast::Ident, ext: SyntaxExtension);

fn expansion_for_ast_pass(
&mut self,
call_site: Span,
pass: AstPass,
features: &[Symbol],
parent_module_id: Option<NodeId>,
) -> ExpnId;

fn resolve_imports(&mut self);

fn resolve_macro_invocation(
@@ -744,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.
12 changes: 8 additions & 4 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 2 additions & 2 deletions src/libsyntax_ext/plugin_macro_defs.rs
Original file line number Diff line number Diff line change
@@ -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 {
69 changes: 33 additions & 36 deletions src/libsyntax_ext/proc_macro_harness.rs
Original file line number Diff line number Diff line change
@@ -3,15 +3,15 @@ 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;
use syntax::ptr::P;
use syntax::symbol::{kw, sym};
use syntax::visit::{self, Visitor};
use syntax_pos::{Span, DUMMY_SP};
use syntax_pos::hygiene::AstPass;

struct ProcMacroDerive {
trait_name: ast::Name,
@@ -308,8 +308,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {

// Creates a new module which looks like:
//
// #[doc(hidden)]
// mod $gensym {
// const _: () = {
// extern crate proc_macro;
//
// use proc_macro::bridge::client::ProcMacro;
@@ -327,32 +326,30 @@ fn mk_decls(
custom_attrs: &[ProcMacroDef],
custom_macros: &[ProcMacroDef],
) -> P<ast::Item> {
let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
ExpnKind::Macro(MacroKind::Attr, sym::proc_macro), DUMMY_SP, cx.parse_sess.edition,
[sym::rustc_attrs, sym::proc_macro_internals][..].into(),
));

let hidden = cx.meta_list_item_word(span, sym::hidden);
let doc = cx.meta_list(span, sym::doc, vec![hidden]);
let doc_hidden = cx.attribute(doc);

let proc_macro = Ident::with_dummy_span(sym::proc_macro);
let expn_id = cx.resolver.expansion_for_ast_pass(
DUMMY_SP,
AstPass::ProcMacroHarness,
&[sym::rustc_attrs, sym::proc_macro_internals],
None,
);
let span = DUMMY_SP.with_def_site_ctxt(expn_id);

let proc_macro = Ident::new(sym::proc_macro, span);
let krate = cx.item(span,
proc_macro,
Vec::new(),
ast::ItemKind::ExternCrate(None));

let bridge = Ident::from_str("bridge");
let client = Ident::from_str("client");
let proc_macro_ty = Ident::from_str("ProcMacro");
let custom_derive = Ident::from_str("custom_derive");
let attr = Ident::from_str("attr");
let bang = Ident::from_str("bang");
let crate_kw = Ident::with_dummy_span(kw::Crate);
let bridge = Ident::from_str_and_span("bridge", span);
let client = Ident::from_str_and_span("client", span);
let proc_macro_ty = Ident::from_str_and_span("ProcMacro", span);
let custom_derive = Ident::from_str_and_span("custom_derive", span);
let attr = Ident::from_str_and_span("attr", span);
let bang = Ident::from_str_and_span("bang", span);

let decls = {
let local_path = |sp: Span, name| {
cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![crate_kw, name]))
cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![name]))
};
let proc_macro_ty_method_path = |method| cx.expr_path(cx.path(span, vec![
proc_macro, bridge, client, proc_macro_ty, method,
@@ -381,7 +378,7 @@ fn mk_decls(

let decls_static = cx.item_static(
span,
Ident::from_str("_DECLS"),
Ident::from_str_and_span("_DECLS", span),
cx.ty_rptr(span,
cx.ty(span, ast::TyKind::Slice(
cx.ty_path(cx.path(span,
@@ -392,22 +389,22 @@ fn mk_decls(
).map(|mut i| {
let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);
i.attrs.push(cx.attribute(attr));
i.vis = respan(span, ast::VisibilityKind::Public);
i
});

let module = cx.item_mod(
let block = cx.expr_block(cx.block(
span,
span,
ast::Ident::from_str("decls").gensym(),
vec![doc_hidden],
vec![krate, decls_static],
).map(|mut i| {
i.vis = respan(span, ast::VisibilityKind::Public);
i
});
vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)],
));

// Integrate the new module into existing module structures.
let module = AstFragment::Items(smallvec![module]);
cx.monotonic_expander().fully_expand_fragment(module).make_items().pop().unwrap()
let anon_constant = cx.item_const(
span,
ast::Ident::new(kw::Underscore, span),
cx.ty(span, ast::TyKind::Tup(Vec::new())),
block,
);

// Integrate the new item into existing module structures.
let items = AstFragment::Items(smallvec![anon_constant]);
cx.monotonic_expander().fully_expand_fragment(items).make_items().pop().unwrap()
}
110 changes: 55 additions & 55 deletions src/libsyntax_ext/standard_library_imports.rs
Original file line number Diff line number Diff line change
@@ -1,86 +1,86 @@
use syntax::{ast, attr};
use syntax::edition::Edition;
use syntax::ext::hygiene::MacroKind;
use syntax::ext::expand::ExpansionConfig;
use syntax::ext::hygiene::AstPass;
use syntax::ext::base::{ExtCtxt, Resolver};
use syntax::parse::ParseSess;
use syntax::ptr::P;
use syntax::source_map::{ExpnData, ExpnKind, dummy_spanned, respan};
use syntax::symbol::{Ident, Symbol, kw, sym};
use syntax_pos::DUMMY_SP;

use std::iter;

pub fn inject(
mut krate: ast::Crate, alt_std_name: Option<&str>, edition: Edition
mut krate: ast::Crate,
resolver: &mut dyn Resolver,
sess: &ParseSess,
alt_std_name: Option<Symbol>,
) -> (ast::Crate, Option<Symbol>) {
let rust_2018 = edition >= Edition::Edition2018;
let rust_2018 = sess.edition >= Edition::Edition2018;

// the first name in this list is the crate name of the crate with the prelude
let names: &[&str] = if attr::contains_name(&krate.attrs, sym::no_core) {
let names: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) {
return (krate, None);
} else if attr::contains_name(&krate.attrs, sym::no_std) {
if attr::contains_name(&krate.attrs, sym::compiler_builtins) {
&["core"]
&[sym::core]
} else {
&["core", "compiler_builtins"]
&[sym::core, sym::compiler_builtins]
}
} else {
&["std"]
&[sym::std]
};

let expn_id = resolver.expansion_for_ast_pass(
DUMMY_SP,
AstPass::StdImports,
&[sym::prelude_import],
None,
);
let span = DUMMY_SP.with_def_site_ctxt(expn_id);
let call_site = DUMMY_SP.with_call_site_ctxt(expn_id);

let ecfg = ExpansionConfig::default("std_lib_injection".to_string());
let cx = ExtCtxt::new(sess, ecfg, resolver);


// .rev() to preserve ordering above in combination with insert(0, ...)
let alt_std_name = alt_std_name.map(Symbol::intern);
for orig_name_str in names.iter().rev() {
// HACK(eddyb) gensym the injected crates on the Rust 2018 edition,
// so they don't accidentally interfere with the new import paths.
let orig_name_sym = Symbol::intern(orig_name_str);
let orig_name_ident = Ident::with_dummy_span(orig_name_sym);
let (rename, orig_name) = if rust_2018 {
(orig_name_ident.gensym(), Some(orig_name_sym))
for &name in names.iter().rev() {
let ident = if rust_2018 {
Ident::new(name, span)
} else {
(orig_name_ident, None)
Ident::new(name, call_site)
};
krate.module.items.insert(0, P(ast::Item {
attrs: vec![attr::mk_attr_outer(
attr::mk_word_item(ast::Ident::with_dummy_span(sym::macro_use))
)],
vis: dummy_spanned(ast::VisibilityKind::Inherited),
node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)),
ident: rename,
id: ast::DUMMY_NODE_ID,
span: DUMMY_SP,
tokens: None,
}));
krate.module.items.insert(0, cx.item(
span,
ident,
vec![cx.attribute(cx.meta_word(span, sym::macro_use))],
ast::ItemKind::ExternCrate(alt_std_name),
));
}

// the crates have been injected, the assumption is that the first one is the one with
// the prelude.
// The crates have been injected, the assumption is that the first one is
// the one with the prelude.
let name = names[0];

let span = DUMMY_SP.fresh_expansion(ExpnData::allow_unstable(
ExpnKind::Macro(MacroKind::Attr, sym::std_inject), DUMMY_SP, edition,
[sym::prelude_import][..].into(),
));
let import_path = if rust_2018 {
[name, sym::prelude, sym::v1].iter()
.map(|symbol| ast::Ident::new(*symbol, span)).collect()
} else {
[kw::PathRoot, name, sym::prelude, sym::v1].iter()
.map(|symbol| ast::Ident::new(*symbol, span)).collect()
};

krate.module.items.insert(0, P(ast::Item {
attrs: vec![attr::mk_attr_outer(
attr::mk_word_item(ast::Ident::new(sym::prelude_import, span)))],
vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited),
node: ast::ItemKind::Use(P(ast::UseTree {
prefix: ast::Path {
segments: iter::once(ast::Ident::with_dummy_span(kw::PathRoot))
.chain(
[name, "prelude", "v1"].iter().cloned()
.map(ast::Ident::from_str)
).map(ast::PathSegment::from_ident).collect(),
span,
},
let use_item = cx.item(
span,
ast::Ident::invalid(),
vec![cx.attribute(cx.meta_word(span, sym::prelude_import))],
ast::ItemKind::Use(P(ast::UseTree {
prefix: cx.path(span, import_path),
kind: ast::UseTreeKind::Glob,
span,
})),
id: ast::DUMMY_NODE_ID,
ident: ast::Ident::invalid(),
span,
tokens: None,
}));
);

krate.module.items.insert(0, use_item);

(krate, Some(Symbol::intern(name)))
(krate, Some(name))
}
19 changes: 9 additions & 10 deletions src/libsyntax_ext/test.rs
Original file line number Diff line number Diff line change
@@ -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),
243 changes: 95 additions & 148 deletions src/libsyntax_ext/test_harness.rs

Large diffs are not rendered by default.

28 changes: 25 additions & 3 deletions src/libsyntax_pos/hygiene.rs
Original file line number Diff line number Diff line change
@@ -360,7 +360,7 @@ impl SyntaxContext {
}

/// Extend a syntax context with a given expansion and transparency.
pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
crate fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
HygieneData::with(|data| data.apply_mark(self, expn_id, transparency))
}

@@ -550,7 +550,7 @@ impl Span {
/// The returned span belongs to the created expansion and has the new properties,
/// but its location is inherited from the current span.
pub fn fresh_expansion(self, expn_data: ExpnData) -> Span {
self.fresh_expansion_with_transparency(expn_data, Transparency::SemiTransparent)
self.fresh_expansion_with_transparency(expn_data, Transparency::Transparent)
}

pub fn fresh_expansion_with_transparency(
@@ -639,8 +639,9 @@ pub enum ExpnKind {
/// No expansion, aka root expansion. Only `ExpnId::root()` has this kind.
Root,
/// Expansion produced by a macro.
/// FIXME: Some code injected by the compiler before HIR lowering also gets this kind.
Macro(MacroKind, Symbol),
/// Transform done by the compiler on the AST.
AstPass(AstPass),
/// Desugaring done by the compiler during HIR lowering.
Desugaring(DesugaringKind)
}
@@ -650,6 +651,7 @@ impl ExpnKind {
match *self {
ExpnKind::Root => kw::PathRoot,
ExpnKind::Macro(_, descr) => descr,
ExpnKind::AstPass(kind) => Symbol::intern(kind.descr()),
ExpnKind::Desugaring(kind) => Symbol::intern(kind.descr()),
}
}
@@ -683,6 +685,26 @@ impl MacroKind {
}
}

/// The kind of AST transform.
#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
pub enum AstPass {
StdImports,
TestHarness,
ProcMacroHarness,
PluginMacroDefs,
}

impl AstPass {
fn descr(self) -> &'static str {
match self {
AstPass::StdImports => "standard library imports",
AstPass::TestHarness => "test harness",
AstPass::ProcMacroHarness => "proc macro harness",
AstPass::PluginMacroDefs => "plugin macro definitions",
}
}
}

/// The kind of compiler desugaring.
#[derive(Clone, Copy, PartialEq, Debug, RustcEncodable, RustcDecodable)]
pub enum DesugaringKind {
20 changes: 20 additions & 0 deletions src/libsyntax_pos/lib.rs
Original file line number Diff line number Diff line change
@@ -442,6 +442,7 @@ impl Span {
let (pre, post) = match expn_data.kind {
ExpnKind::Root => break,
ExpnKind::Desugaring(..) => ("desugaring of ", ""),
ExpnKind::AstPass(..) => ("", ""),
ExpnKind::Macro(macro_kind, _) => match macro_kind {
MacroKind::Bang => ("", "!"),
MacroKind::Attr => ("#[", "]"),
@@ -513,6 +514,25 @@ impl Span {
span.ctxt)
}

/// Equivalent of `Span::def_site` from the proc macro API,
/// except that the location is taken from the `self` span.
pub fn with_def_site_ctxt(self, expn_id: ExpnId) -> Span {
self.with_ctxt_from_mark(expn_id, Transparency::Opaque)
}

/// Equivalent of `Span::call_site` from the proc macro API,
/// except that the location is taken from the `self` span.
pub fn with_call_site_ctxt(&self, expn_id: ExpnId) -> Span {
self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
}

/// Span with a context reproducing `macro_rules` hygiene (hygienic locals, unhygienic items).
/// FIXME: This should be eventually replaced either with `with_def_site_ctxt` (preferably),
/// or with `with_call_site_ctxt` (where necessary).
pub fn with_legacy_ctxt(&self, expn_id: ExpnId) -> Span {
self.with_ctxt_from_mark(expn_id, Transparency::SemiTransparent)
}

/// Produces a span with the same location as `self` and context produced by a macro with the
/// given ID and transparency, assuming that macro was defined directly and not produced by
/// some other macro (which is the case for built-in and procedural macros).
27 changes: 12 additions & 15 deletions src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions src/test/ui/hygiene/auxiliary/not-libstd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub fn not_in_lib_std() {}
29 changes: 29 additions & 0 deletions src/test/ui/hygiene/prelude-import-hygiene.rs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this is kinda arguable.
If we can reroute standard library (including prelude imports) to somewhere else with --extern std=/path/to/my_std, then why can't we do the same thing in source code with extern crate my_std as std.
cc #63687 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it the other way --extern std=/path/to/my_std means that there doesn't need to be a way to override std in source code, especially one that only works on the 2018 edition.

Looking at the linked issue, the crate in question isn't #![no_std] and uses the 2015 edition, so it's not overriding anything there.


#[cfg(rust2018)]
mod x {
// The extern crate item should override `std` in the extern prelude.
fn f() {
std::not_in_lib_std();
}
}

fn main() {}
4 changes: 3 additions & 1 deletion src/test/ui/imports/gensymed.rs
Original file line number Diff line number Diff line change
@@ -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() {}
21 changes: 0 additions & 21 deletions src/test/ui/inaccessible-test-modules.stderr

This file was deleted.

32 changes: 16 additions & 16 deletions src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
Original file line number Diff line number Diff line change
@@ -2,80 +2,80 @@ 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);
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),
},
]
44 changes: 22 additions & 22 deletions src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
Original file line number Diff line number Diff line change
@@ -3,110 +3,110 @@ 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));
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),
},
]
96 changes: 48 additions & 48 deletions src/test/ui/proc-macro/dollar-crate.stdout
Original file line number Diff line number Diff line change
@@ -2,243 +2,243 @@ 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);
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);
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);
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);
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),
},
]
22 changes: 22 additions & 0 deletions src/test/ui/test-attrs/decl-macro-test.rs
Original file line number Diff line number Diff line change
@@ -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!();
Original file line number Diff line number Diff line change
@@ -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() {}
21 changes: 21 additions & 0 deletions src/test/ui/test-attrs/inaccessible-test-modules.stderr
Original file line number Diff line number Diff line change
@@ -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`.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.