From 44422409a43544a1300506c83fbc2e8f3049d8a5 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 29 Jul 2018 14:51:17 +0300 Subject: [PATCH] resolve: Modularize crate-local `#[macro_export] macro_rules` --- src/librustc_resolve/build_reduced_graph.rs | 16 +++- src/librustc_resolve/lib.rs | 18 ++-- src/librustc_resolve/macros.rs | 40 +++++---- src/librustc_resolve/resolve_imports.rs | 18 +++- .../run-pass/auxiliary/issue_38715-modern.rs | 18 ++++ src/test/run-pass/issue-38715.rs | 4 + src/test/ui/duplicate-check-macro-exports.rs | 2 +- .../ui/duplicate-check-macro-exports.stderr | 18 ++-- .../local-modularized-tricky-fail-1.rs | 57 +++++++++++++ .../local-modularized-tricky-fail-1.stderr | 84 +++++++++++++++++++ .../local-modularized-tricky-fail-2.rs | 58 +++++++++++++ .../local-modularized-tricky-fail-2.stderr | 36 ++++++++ .../imports/local-modularized-tricky-pass.rs | 31 +++++++ src/test/ui/imports/local-modularized.rs | 47 +++++++++++ 14 files changed, 415 insertions(+), 32 deletions(-) create mode 100644 src/test/run-pass/auxiliary/issue_38715-modern.rs create mode 100644 src/test/ui/imports/local-modularized-tricky-fail-1.rs create mode 100644 src/test/ui/imports/local-modularized-tricky-fail-1.stderr create mode 100644 src/test/ui/imports/local-modularized-tricky-fail-2.rs create mode 100644 src/test/ui/imports/local-modularized-tricky-fail-2.stderr create mode 100644 src/test/ui/imports/local-modularized-tricky-pass.rs create mode 100644 src/test/ui/imports/local-modularized.rs diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index da2847dc55793..4553a2ab5779f 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -59,7 +59,20 @@ impl<'a> ToNameBinding<'a> for (Module<'a>, ty::Visibility, Span, Mark) { impl<'a> ToNameBinding<'a> for (Def, ty::Visibility, Span, Mark) { fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> { arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Def(self.0), + kind: NameBindingKind::Def(self.0, false), + vis: self.1, + span: self.2, + expansion: self.3, + }) + } +} + +pub(crate) struct IsMacroExport; + +impl<'a> ToNameBinding<'a> for (Def, ty::Visibility, Span, Mark, IsMacroExport) { + fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> { + arenas.alloc_name_binding(NameBinding { + kind: NameBindingKind::Def(self.0, true), vis: self.1, span: self.2, expansion: self.3, @@ -772,6 +785,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { fn visit_invoc(&mut self, id: ast::NodeId) -> &'b InvocationData<'b> { let mark = id.placeholder_to_mark(); self.resolver.current_module.unresolved_invocations.borrow_mut().insert(mark); + self.resolver.unresolved_invocations_macro_export.insert(mark); let invocation = self.resolver.invocations[&mark]; invocation.module.set(self.resolver.current_module); invocation.legacy_scope.set(self.legacy_scope); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 725b3b4f15034..77eb97b02ffd6 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1117,7 +1117,7 @@ impl<'a> ToNameBinding<'a> for &'a NameBinding<'a> { #[derive(Clone, Debug)] enum NameBindingKind<'a> { - Def(Def), + Def(Def, /* is_macro_export */ bool), Module(Module<'a>), Import { binding: &'a NameBinding<'a>, @@ -1161,7 +1161,7 @@ impl<'a> NameBinding<'a> { fn def(&self) -> Def { match self.kind { - NameBindingKind::Def(def) => def, + NameBindingKind::Def(def, _) => def, NameBindingKind::Module(module) => module.def().unwrap(), NameBindingKind::Import { binding, .. } => binding.def(), NameBindingKind::Ambiguity { .. } => Def::Err, @@ -1191,8 +1191,8 @@ impl<'a> NameBinding<'a> { fn is_variant(&self) -> bool { match self.kind { - NameBindingKind::Def(Def::Variant(..)) | - NameBindingKind::Def(Def::VariantCtor(..)) => true, + NameBindingKind::Def(Def::Variant(..), _) | + NameBindingKind::Def(Def::VariantCtor(..), _) => true, _ => false, } } @@ -1241,7 +1241,7 @@ impl<'a> NameBinding<'a> { fn is_macro_def(&self) -> bool { match self.kind { - NameBindingKind::Def(Def::Macro(..)) => true, + NameBindingKind::Def(Def::Macro(..), _) => true, _ => false, } } @@ -1397,7 +1397,7 @@ pub struct Resolver<'a> { macro_map: FxHashMap>, macro_defs: FxHashMap, local_macro_def_scopes: FxHashMap>, - macro_exports: Vec, + macro_exports: Vec, // FIXME: Remove when `use_extern_macros` is stabilized pub whitelisted_legacy_custom_derives: Vec, pub found_unresolved_macro: bool, @@ -1427,6 +1427,9 @@ pub struct Resolver<'a> { /// Only supposed to be used by rustdoc, otherwise should be false. pub ignore_extern_prelude_feature: bool, + + /// Macro invocations in the whole crate that can expand into a `#[macro_export] macro_rules`. + unresolved_invocations_macro_export: FxHashSet, } /// Nothing really interesting here, it just provides memory for the rest of the crate. @@ -1701,7 +1704,7 @@ impl<'a> Resolver<'a> { arenas, dummy_binding: arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Def(Def::Err), + kind: NameBindingKind::Def(Def::Err, false), expansion: Mark::root(), span: DUMMY_SP, vis: ty::Visibility::Public, @@ -1731,6 +1734,7 @@ impl<'a> Resolver<'a> { current_type_ascription: Vec::new(), injected_crate: None, ignore_extern_prelude_feature: false, + unresolved_invocations_macro_export: FxHashSet(), } } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 29b6f958cc119..1d42ad4e4902e 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -11,7 +11,7 @@ use {AmbiguityError, CrateLint, Resolver, ResolutionError, resolve_error}; use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult}; use Namespace::{self, MacroNS}; -use build_reduced_graph::BuildReducedGraphVisitor; +use build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport}; use resolve_imports::ImportResolver; use rustc::hir::def_id::{DefId, BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, DefIndex, DefIndexAddressSpace}; @@ -193,7 +193,9 @@ impl<'a> base::Resolver for Resolver<'a> { self.current_module = invocation.module.get(); self.current_module.unresolved_invocations.borrow_mut().remove(&mark); + self.unresolved_invocations_macro_export.remove(&mark); self.current_module.unresolved_invocations.borrow_mut().extend(derives); + self.unresolved_invocations_macro_export.extend(derives); for &derive in derives { self.invocations.insert(derive, invocation); } @@ -215,7 +217,7 @@ impl<'a> base::Resolver for Resolver<'a> { let kind = ext.kind(); self.macro_map.insert(def_id, ext); let binding = self.arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Def(Def::Macro(def_id, kind)), + kind: NameBindingKind::Def(Def::Macro(def_id, kind), false), span: DUMMY_SP, vis: ty::Visibility::Invisible, expansion: Mark::root(), @@ -711,12 +713,15 @@ impl<'a> Resolver<'a> { match (legacy_resolution, resolution) { (Some(MacroBinding::Legacy(legacy_binding)), Ok(MacroBinding::Modern(binding))) => { - let msg1 = format!("`{}` could refer to the macro defined here", ident); - let msg2 = format!("`{}` could also refer to the macro imported here", ident); - self.session.struct_span_err(span, &format!("`{}` is ambiguous", ident)) - .span_note(legacy_binding.span, &msg1) - .span_note(binding.span, &msg2) - .emit(); + if legacy_binding.def_id != binding.def_ignoring_ambiguity().def_id() { + let msg1 = format!("`{}` could refer to the macro defined here", ident); + let msg2 = + format!("`{}` could also refer to the macro imported here", ident); + self.session.struct_span_err(span, &format!("`{}` is ambiguous", ident)) + .span_note(legacy_binding.span, &msg1) + .span_note(binding.span, &msg2) + .emit(); + } }, (None, Err(_)) => { assert!(def.is_none()); @@ -850,12 +855,19 @@ impl<'a> Resolver<'a> { let def = Def::Macro(def_id, MacroKind::Bang); self.all_macros.insert(ident.name, def); if attr::contains_name(&item.attrs, "macro_export") { - self.macro_exports.push(Export { - ident: ident.modern(), - def: def, - vis: ty::Visibility::Public, - span: item.span, - }); + if self.use_extern_macros { + let module = self.graph_root; + let vis = ty::Visibility::Public; + self.define(module, ident, MacroNS, + (def, vis, item.span, expansion, IsMacroExport)); + } else { + self.macro_exports.push(Export { + ident: ident.modern(), + def: def, + vis: ty::Visibility::Public, + span: item.span, + }); + } } else { self.unused_macros.insert(def_id); } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 50eb89be69011..c83fcf65c40f3 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -211,7 +211,9 @@ impl<'a> Resolver<'a> { // if it cannot be shadowed by some new item/import expanded from a macro. // This happens either if there are no unexpanded macros, or expanded names cannot // shadow globs (that happens in macro namespace or with restricted shadowing). - let unexpanded_macros = !module.unresolved_invocations.borrow().is_empty(); + let unexpanded_macros = !module.unresolved_invocations.borrow().is_empty() || + (ns == MacroNS && ptr::eq(module, self.graph_root) && + !self.unresolved_invocations_macro_export.is_empty()); if let Some(binding) = resolution.binding { if !unexpanded_macros || ns == MacroNS || restricted_shadowing { return check_usable(self, binding); @@ -363,6 +365,18 @@ impl<'a> Resolver<'a> { resolution.binding = Some(binding); resolution.shadowed_glob = Some(old_binding); } + } else if let (&NameBindingKind::Def(_, true), &NameBindingKind::Def(_, true)) = + (&old_binding.kind, &binding.kind) { + + this.session.buffer_lint_with_diagnostic( + DUPLICATE_MACRO_EXPORTS, + CRATE_NODE_ID, + binding.span, + &format!("a macro named `{}` has already been exported", ident), + BuiltinLintDiagnostics::DuplicatedMacroExports( + ident, old_binding.span, binding.span)); + + resolution.binding = Some(binding); } else { return Err(old_binding); } @@ -766,7 +780,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { match binding.kind { // Never suggest the name that has binding error // i.e. the name that cannot be previously resolved - NameBindingKind::Def(Def::Err) => return None, + NameBindingKind::Def(Def::Err, _) => return None, _ => Some(&i.name), } }, diff --git a/src/test/run-pass/auxiliary/issue_38715-modern.rs b/src/test/run-pass/auxiliary/issue_38715-modern.rs new file mode 100644 index 0000000000000..68aced7f979c9 --- /dev/null +++ b/src/test/run-pass/auxiliary/issue_38715-modern.rs @@ -0,0 +1,18 @@ +// Copyright 2016 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. + +#![feature(use_extern_macros)] +#![allow(duplicate_macro_exports)] + +#[macro_export] +macro_rules! foo_modern { ($i:ident) => {} } + +#[macro_export] +macro_rules! foo_modern { () => {} } diff --git a/src/test/run-pass/issue-38715.rs b/src/test/run-pass/issue-38715.rs index 054785e62b8ea..b158234eb1154 100644 --- a/src/test/run-pass/issue-38715.rs +++ b/src/test/run-pass/issue-38715.rs @@ -9,12 +9,16 @@ // except according to those terms. // aux-build:issue_38715.rs +// aux-build:issue_38715-modern.rs // Test that `#[macro_export] macro_rules!` shadow earlier `#[macro_export] macro_rules!` #[macro_use] extern crate issue_38715; +#[macro_use] +extern crate issue_38715_modern; fn main() { foo!(); + foo_modern!(); } diff --git a/src/test/ui/duplicate-check-macro-exports.rs b/src/test/ui/duplicate-check-macro-exports.rs index d874a43d53d79..d8159bff567ad 100644 --- a/src/test/ui/duplicate-check-macro-exports.rs +++ b/src/test/ui/duplicate-check-macro-exports.rs @@ -13,6 +13,6 @@ pub use std::panic; #[macro_export] -macro_rules! panic { () => {} } //~ ERROR a macro named `panic` has already been exported +macro_rules! panic { () => {} } //~ ERROR the name `panic` is defined multiple times fn main() {} diff --git a/src/test/ui/duplicate-check-macro-exports.stderr b/src/test/ui/duplicate-check-macro-exports.stderr index 6d3bb669df975..651e984b27452 100644 --- a/src/test/ui/duplicate-check-macro-exports.stderr +++ b/src/test/ui/duplicate-check-macro-exports.stderr @@ -1,14 +1,18 @@ -error: a macro named `panic` has already been exported +error[E0255]: the name `panic` is defined multiple times --> $DIR/duplicate-check-macro-exports.rs:16:1 | -LL | macro_rules! panic { () => {} } //~ ERROR a macro named `panic` has already been exported - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `panic` already exported +LL | pub use std::panic; + | ---------- previous import of the macro `panic` here +... +LL | macro_rules! panic { () => {} } //~ ERROR the name `panic` is defined multiple times + | ^^^^^^^^^^^^^^^^^^ `panic` redefined here | -note: previous macro export here - --> $DIR/duplicate-check-macro-exports.rs:13:9 + = note: `panic` must be defined only once in the macro namespace of this module +help: You can use `as` to change the binding name of the import | -LL | pub use std::panic; - | ^^^^^^^^^^ +LL | pub use std::panic as other_panic; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error +For more information about this error, try `rustc --explain E0255`. diff --git a/src/test/ui/imports/local-modularized-tricky-fail-1.rs b/src/test/ui/imports/local-modularized-tricky-fail-1.rs new file mode 100644 index 0000000000000..445344732f7bd --- /dev/null +++ b/src/test/ui/imports/local-modularized-tricky-fail-1.rs @@ -0,0 +1,57 @@ +// 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. + +#![feature(decl_macro)] + +macro_rules! define_exported { () => { + #[macro_export] + macro_rules! exported { + () => () + } +}} +macro_rules! define_panic { () => { + #[macro_export] + macro_rules! panic { + () => () + } +}} +macro_rules! define_include { () => { + #[macro_export] + macro_rules! include { + () => () + } +}} + +use inner1::*; + +mod inner1 { + pub macro exported() {} +} + +exported!(); //~ ERROR `exported` is ambiguous + +mod inner2 { + define_exported!(); +} + +fn main() { + panic!(); //~ ERROR `panic` is ambiguous + //~^ ERROR `panic` is ambiguous +} + +mod inner3 { + define_panic!(); +} + +mod inner4 { + define_include!(); +} + +include!(); //~ ERROR `include` is ambiguous diff --git a/src/test/ui/imports/local-modularized-tricky-fail-1.stderr b/src/test/ui/imports/local-modularized-tricky-fail-1.stderr new file mode 100644 index 0000000000000..e9a81e7ae4c4c --- /dev/null +++ b/src/test/ui/imports/local-modularized-tricky-fail-1.stderr @@ -0,0 +1,84 @@ +error[E0659]: `exported` is ambiguous + --> $DIR/local-modularized-tricky-fail-1.rs:38:1 + | +LL | exported!(); //~ ERROR `exported` is ambiguous + | ^^^^^^^^ + | +note: `exported` could refer to the name defined here + --> $DIR/local-modularized-tricky-fail-1.rs:15:5 + | +LL | / macro_rules! exported { +LL | | () => () +LL | | } + | |_____^ +... +LL | define_exported!(); + | ------------------- in this macro invocation +note: `exported` could also refer to the name imported here + --> $DIR/local-modularized-tricky-fail-1.rs:32:5 + | +LL | use inner1::*; + | ^^^^^^^^^ + = note: macro-expanded macros do not shadow + +error[E0659]: `include` is ambiguous + --> $DIR/local-modularized-tricky-fail-1.rs:57:1 + | +LL | include!(); //~ ERROR `include` is ambiguous + | ^^^^^^^ + | +note: `include` could refer to the name defined here + --> $DIR/local-modularized-tricky-fail-1.rs:27:5 + | +LL | / macro_rules! include { +LL | | () => () +LL | | } + | |_____^ +... +LL | define_include!(); + | ------------------ in this macro invocation + = note: `include` is also a builtin macro + = note: macro-expanded macros do not shadow + +error[E0659]: `panic` is ambiguous + --> $DIR/local-modularized-tricky-fail-1.rs:45:5 + | +LL | panic!(); //~ ERROR `panic` is ambiguous + | ^^^^^ + | +note: `panic` could refer to the name defined here + --> $DIR/local-modularized-tricky-fail-1.rs:21:5 + | +LL | / macro_rules! panic { +LL | | () => () +LL | | } + | |_____^ +... +LL | define_panic!(); + | ---------------- in this macro invocation + = note: `panic` is also a builtin macro + = note: macro-expanded macros do not shadow + +error[E0659]: `panic` is ambiguous + --> $DIR/local-modularized-tricky-fail-1.rs:45:5 + | +LL | panic!(); //~ ERROR `panic` is ambiguous + | ^^^^^^^^^ + | +note: `panic` could refer to the name defined here + --> $DIR/local-modularized-tricky-fail-1.rs:21:5 + | +LL | / macro_rules! panic { +LL | | () => () +LL | | } + | |_____^ +... +LL | define_panic!(); + | ---------------- in this macro invocation + = note: `panic` is also a builtin macro + = note: macro-expanded macros do not shadow + = 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 4 previous errors + +For more information about this error, try `rustc --explain E0659`. diff --git a/src/test/ui/imports/local-modularized-tricky-fail-2.rs b/src/test/ui/imports/local-modularized-tricky-fail-2.rs new file mode 100644 index 0000000000000..afdebc4533ff6 --- /dev/null +++ b/src/test/ui/imports/local-modularized-tricky-fail-2.rs @@ -0,0 +1,58 @@ +// 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` that doen't originate from macro expansions can be placed +// into the root module soon enough to act as usual items and shadow globs and preludes. + +#![feature(decl_macro)] + +// `macro_export` shadows globs +use inner1::*; + +mod inner1 { + pub macro exported() {} +} + +exported!(); + +mod deep { + fn deep() { + type Deeper = [u8; { + #[macro_export] + macro_rules! exported { + () => ( struct Б; ) //~ ERROR non-ascii idents are not fully supported + } + + 0 + }]; + } +} + +// `macro_export` shadows std prelude +fn main() { + panic!(); +} + +mod inner3 { + #[macro_export] + macro_rules! panic { + () => ( struct Г; ) //~ ERROR non-ascii idents are not fully supported + } +} + +// `macro_export` shadows builtin macros +include!(); + +mod inner4 { + #[macro_export] + macro_rules! include { + () => ( struct Д; ) //~ ERROR non-ascii idents are not fully supported + } +} diff --git a/src/test/ui/imports/local-modularized-tricky-fail-2.stderr b/src/test/ui/imports/local-modularized-tricky-fail-2.stderr new file mode 100644 index 0000000000000..5681dd5962850 --- /dev/null +++ b/src/test/ui/imports/local-modularized-tricky-fail-2.stderr @@ -0,0 +1,36 @@ +error[E0658]: non-ascii idents are not fully supported. (see issue #28979) + --> $DIR/local-modularized-tricky-fail-2.rs:30:32 + | +LL | exported!(); + | ------------ in this macro invocation +... +LL | () => ( struct Б; ) //~ ERROR non-ascii idents are not fully supported + | ^ + | + = help: add #![feature(non_ascii_idents)] to the crate attributes to enable + +error[E0658]: non-ascii idents are not fully supported. (see issue #28979) + --> $DIR/local-modularized-tricky-fail-2.rs:46:24 + | +LL | panic!(); + | --------- in this macro invocation +... +LL | () => ( struct Г; ) //~ ERROR non-ascii idents are not fully supported + | ^ + | + = help: add #![feature(non_ascii_idents)] to the crate attributes to enable + +error[E0658]: non-ascii idents are not fully supported. (see issue #28979) + --> $DIR/local-modularized-tricky-fail-2.rs:56:24 + | +LL | include!(); + | ----------- in this macro invocation +... +LL | () => ( struct Д; ) //~ ERROR non-ascii idents are not fully supported + | ^ + | + = help: add #![feature(non_ascii_idents)] to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/imports/local-modularized-tricky-pass.rs b/src/test/ui/imports/local-modularized-tricky-pass.rs new file mode 100644 index 0000000000000..04df357e106cb --- /dev/null +++ b/src/test/ui/imports/local-modularized-tricky-pass.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 + +#![feature(use_extern_macros)] + +macro_rules! define_exported { () => { + #[macro_export] + macro_rules! exported { + () => () + } +}} + +mod inner1 { + use super::*; + exported!(); +} + +mod inner2 { + define_exported!(); +} + +fn main() {} diff --git a/src/test/ui/imports/local-modularized.rs b/src/test/ui/imports/local-modularized.rs new file mode 100644 index 0000000000000..a5297c54c9e02 --- /dev/null +++ b/src/test/ui/imports/local-modularized.rs @@ -0,0 +1,47 @@ +// 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 + +#![feature(use_extern_macros)] + +#[macro_export(local_inner_macros)] +macro_rules! dollar_crate_exported { + (1) => { $crate::exported!(); }; + (2) => { exported!(); }; +} + +// Before `exported` is defined +exported!(); + +mod inner { + + ::exported!(); + crate::exported!(); + dollar_crate_exported!(1); + dollar_crate_exported!(2); + + mod inner_inner { + #[macro_export] + macro_rules! exported { + () => () + } + } + + // After `exported` is defined + ::exported!(); + crate::exported!(); + dollar_crate_exported!(1); + dollar_crate_exported!(2); +} + +exported!(); + +fn main() {}