diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index a3f86c542836e..e14a86eb954f0 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -616,6 +616,7 @@ impl<'a> LoweringContext<'a> { format: codemap::CompilerDesugaring(reason), allow_internal_unstable: true, allow_internal_unsafe: false, + local_inner_macros: false, edition: codemap::hygiene::default_edition(), }); span.with_ctxt(SyntaxContext::empty().apply_mark(mark)) diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 935bc4c8c6d8c..790ef70296380 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -395,6 +395,7 @@ impl_stable_hash_for!(struct ::syntax_pos::hygiene::ExpnInfo { format, allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, edition }); diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index 60d28d8098b4c..373ab04de4b82 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -103,6 +103,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { format: MacroAttribute(Symbol::intern(name)), allow_internal_unstable: true, allow_internal_unsafe: false, + local_inner_macros: false, edition: hygiene::default_edition(), }); diff --git a/src/librustc_plugin/registry.rs b/src/librustc_plugin/registry.rs index ea15f4c75b96c..b1ab86674cf90 100644 --- a/src/librustc_plugin/registry.rs +++ b/src/librustc_plugin/registry.rs @@ -108,6 +108,7 @@ impl<'a> Registry<'a> { def_info: _, allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, unstable_feature, edition, } => { @@ -117,6 +118,7 @@ impl<'a> Registry<'a> { def_info: Some((nid, self.krate_span)), allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, unstable_feature, edition, } @@ -152,6 +154,7 @@ impl<'a> Registry<'a> { def_info: None, allow_internal_unstable: false, allow_internal_unsafe: false, + local_inner_macros: false, unstable_feature: None, edition: hygiene::default_edition(), }); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index ebdaa456170b2..c9d00f80b0ba3 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -451,11 +451,18 @@ impl<'a> Resolver<'a> { kind: MacroKind, force: bool) -> Result { let ast::Path { ref segments, span } = *path; - let path: Vec<_> = segments.iter().map(|seg| seg.ident).collect(); + let mut path: Vec<_> = segments.iter().map(|seg| seg.ident).collect(); let invocation = self.invocations[&scope]; let module = invocation.module.get(); self.current_module = if module.is_trait() { module.parent.unwrap() } else { module }; + // Possibly apply the macro helper hack + if self.use_extern_macros && kind == MacroKind::Bang && path.len() == 1 && + path[0].span.ctxt().outer().expn_info().map_or(false, |info| info.local_inner_macros) { + let root = Ident::new(keywords::DollarCrate.name(), path[0].span); + path.insert(0, root); + } + if path.len() > 1 { if !self.use_extern_macros && self.gated_errors.insert(span) { let msg = "non-ident macro paths are experimental"; diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 16d786dd6cad2..9afce74f53cc4 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -621,6 +621,9 @@ pub enum SyntaxExtension { /// Whether the contents of the macro can use `unsafe` /// without triggering the `unsafe_code` lint. allow_internal_unsafe: bool, + /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) + /// for a given macro. + local_inner_macros: bool, /// The macro's feature name if it is unstable, and the stability feature unstable_feature: Option<(Symbol, u32)>, /// Edition of the crate in which the macro is defined diff --git a/src/libsyntax/ext/derive.rs b/src/libsyntax/ext/derive.rs index 940fb6405f1b6..32ace937ac006 100644 --- a/src/libsyntax/ext/derive.rs +++ b/src/libsyntax/ext/derive.rs @@ -64,6 +64,7 @@ pub fn add_derived_markers(cx: &mut ExtCtxt, span: Span, traits: &[ast::Path] format: ExpnFormat::MacroAttribute(Symbol::intern(&pretty_name)), allow_internal_unstable: true, allow_internal_unsafe: false, + local_inner_macros: false, edition: hygiene::default_edition(), }); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 69c99c63aafe3..23880c1270f9d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -542,6 +542,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(Symbol::intern(&format!("{}", attr.path))), allow_internal_unstable: false, allow_internal_unsafe: false, + local_inner_macros: false, edition: ext.edition(), }); @@ -695,6 +696,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { def_site_span: Option, allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, // can't infer this type unstable_feature: Option<(Symbol, u32)>, edition| { @@ -729,6 +731,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: macro_bang_format(path), allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, edition, }); Ok(()) @@ -737,7 +740,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let opt_expanded = match *ext { DeclMacro(ref expand, def_span, edition) => { if let Err(dummy_span) = validate_and_set_expn_info(self, def_span.map(|(_, s)| s), - false, false, None, + false, false, false, None, edition) { dummy_span } else { @@ -750,12 +753,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { def_info, allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, unstable_feature, edition, } => { if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s), allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, unstable_feature, edition) { dummy_span @@ -777,6 +782,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: macro_bang_format(path), allow_internal_unstable, allow_internal_unsafe: false, + local_inner_macros: false, edition: hygiene::default_edition(), }); @@ -816,6 +822,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // FIXME probably want to follow macro_rules macros here. allow_internal_unstable, allow_internal_unsafe: false, + local_inner_macros: false, edition, }); @@ -890,6 +897,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { format: MacroAttribute(pretty_name), allow_internal_unstable: false, allow_internal_unsafe: false, + local_inner_macros: false, edition: ext.edition(), }; diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 4ee5357f4766c..3b3892729d93c 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -286,6 +286,12 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: if body.legacy { let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable"); let allow_internal_unsafe = attr::contains_name(&def.attrs, "allow_internal_unsafe"); + let mut local_inner_macros = false; + if let Some(macro_export) = attr::find_by_name(&def.attrs, "macro_export") { + if let Some(l) = macro_export.meta_item_list() { + local_inner_macros = attr::list_contains_name(&l, "local_inner_macros"); + } + } let unstable_feature = attr::find_stability(&sess.span_diagnostic, &def.attrs, def.span).and_then(|stability| { @@ -301,6 +307,7 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: def_info: Some((def.id, def.span)), allow_internal_unstable, allow_internal_unsafe, + local_inner_macros, unstable_feature, edition, } diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 66e8e0d7a9c6c..68121d42b69c6 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -29,6 +29,7 @@ fn ignored_span(sp: Span) -> Span { format: MacroAttribute(Symbol::intern("std_inject")), allow_internal_unstable: true, allow_internal_unsafe: false, + local_inner_macros: false, edition: hygiene::default_edition(), }); sp.with_ctxt(SyntaxContext::empty().apply_mark(mark)) diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 141fd122ff57b..51fbe34028e8f 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -311,6 +311,7 @@ fn generate_test_harness(sess: &ParseSess, format: MacroAttribute(Symbol::intern("test")), allow_internal_unstable: true, allow_internal_unsafe: false, + local_inner_macros: false, edition: hygiene::default_edition(), }); diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index af3ef181c59dc..311251832664e 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -76,6 +76,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, def_info: None, allow_internal_unstable: false, allow_internal_unsafe: false, + local_inner_macros: false, unstable_feature: None, edition: hygiene::default_edition(), }); @@ -132,6 +133,7 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, def_info: None, allow_internal_unstable: true, allow_internal_unsafe: false, + local_inner_macros: false, unstable_feature: None, edition: hygiene::default_edition(), }); diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index ee343e47bd890..ef29e5a6b022b 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -368,6 +368,7 @@ fn mk_registrar(cx: &mut ExtCtxt, format: MacroAttribute(Symbol::intern("proc_macro")), allow_internal_unstable: true, allow_internal_unsafe: false, + local_inner_macros: false, edition: hygiene::default_edition(), }); let span = DUMMY_SP.apply_mark(mark); diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 3365d5954033f..d1b9c7a84d16f 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -496,6 +496,9 @@ pub struct ExpnInfo { /// Whether the macro is allowed to use `unsafe` internally /// even if the user crate has `#![forbid(unsafe_code)]`. pub allow_internal_unsafe: bool, + /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) + /// for a given macro. + pub local_inner_macros: bool, /// Edition of the crate in which the macro is defined. pub edition: Edition, } diff --git a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs index d7ede76383868..ac39118c5f1e0 100644 --- a/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs +++ b/src/test/run-pass-fulldeps/auxiliary/plugin_args.rs @@ -54,6 +54,7 @@ pub fn plugin_registrar(reg: &mut Registry) { def_info: None, allow_internal_unstable: false, allow_internal_unsafe: false, + local_inner_macros: false, unstable_feature: None, edition: hygiene::default_edition(), }); diff --git a/src/test/ui/hygiene/auxiliary/local_inner_macros.rs b/src/test/ui/hygiene/auxiliary/local_inner_macros.rs new file mode 100644 index 0000000000000..caa2903cc7035 --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/local_inner_macros.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[macro_export] +macro_rules! helper1 { + () => ( struct S; ) +} + +#[macro_export(local_inner_macros)] +macro_rules! helper2 { + () => ( helper1!(); ) +} + +#[macro_export(local_inner_macros)] +macro_rules! public_macro { + () => ( helper2!(); ) +} + +#[macro_export(local_inner_macros)] +macro_rules! public_macro_dynamic { + ($helper: ident) => ( $helper!(); ) +} diff --git a/src/test/ui/hygiene/local_inner_macros.rs b/src/test/ui/hygiene/local_inner_macros.rs new file mode 100644 index 0000000000000..787e2df3ec246 --- /dev/null +++ b/src/test/ui/hygiene/local_inner_macros.rs @@ -0,0 +1,31 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-pass +// aux-build:local_inner_macros.rs + +#![feature(use_extern_macros)] + +extern crate local_inner_macros; + +use local_inner_macros::{public_macro, public_macro_dynamic}; + +public_macro!(); + +macro_rules! local_helper { + () => ( struct Z; ) +} + +public_macro_dynamic!(local_helper); + +fn main() { + let s = S; + let z = Z; +} diff --git a/src/test/ui/hygiene/local_inner_macros_disabled.rs b/src/test/ui/hygiene/local_inner_macros_disabled.rs new file mode 100644 index 0000000000000..00b38787db849 --- /dev/null +++ b/src/test/ui/hygiene/local_inner_macros_disabled.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// `local_inner_macros` has no effect if `feature(use_extern_macros)` is not enabled + +// aux-build:local_inner_macros.rs +// error-pattern: cannot find macro `helper2!` in this scope + +#[macro_use(public_macro)] +extern crate local_inner_macros; + +public_macro!(); + +fn main() {} diff --git a/src/test/ui/hygiene/local_inner_macros_disabled.stderr b/src/test/ui/hygiene/local_inner_macros_disabled.stderr new file mode 100644 index 0000000000000..64cb6c400cfc9 --- /dev/null +++ b/src/test/ui/hygiene/local_inner_macros_disabled.stderr @@ -0,0 +1,10 @@ +error: cannot find macro `helper2!` in this scope + --> $DIR/local_inner_macros_disabled.rs:19:1 + | +LL | public_macro!(); + | ^^^^^^^^^^^^^^^^ + | + = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + +error: aborting due to previous error +