about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-12-04 12:51:38 +0000
committerbors <bors@rust-lang.org>2016-12-04 12:51:38 +0000
commitb462e8fa61a6744aa7435f0bef17023062c165df (patch)
treee754bdabbfbebfe00b3f7d6c3926df26bd6e6855 /src
parentd14d74d5f7d39d1e2583bca231c26bbc0d4ee9a0 (diff)
parentff621ec70eac9c687d0154df5405600669041ab3 (diff)
downloadrust-b462e8fa61a6744aa7435f0bef17023062c165df.tar.gz
rust-b462e8fa61a6744aa7435f0bef17023062c165df.zip
Auto merge of #38082 - jseyfried:macro_invocation_paths, r=nrc
macros: support invocation paths (e.g. `foo::bar!()`) behind `#![feature(use_extern_macros)]`

r? @nrc
Diffstat (limited to 'src')
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs8
-rw-r--r--src/librustc_resolve/lib.rs35
-rw-r--r--src/librustc_resolve/macros.rs114
-rw-r--r--src/librustc_resolve/resolve_imports.rs17
-rw-r--r--src/libsyntax/ext/expand.rs7
-rw-r--r--src/test/compile-fail/imports/macro-paths.rs42
-rw-r--r--src/test/compile-fail/macro-with-seps-err-msg.rs6
-rw-r--r--src/test/compile-fail/paths-in-macro-invocations.rs38
-rw-r--r--src/test/run-pass/auxiliary/two_macros.rs4
-rw-r--r--src/test/run-pass/paths-in-macro-invocations.rs46
10 files changed, 218 insertions, 99 deletions
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index d90a49213d1..7bcc543023e 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -501,11 +501,9 @@ impl<'b> Resolver<'b> {
         })
     }
 
-    pub fn get_macro(&mut self, binding: &'b NameBinding<'b>) -> Rc<SyntaxExtension> {
-        let def_id = match binding.kind {
-            NameBindingKind::Def(Def::Macro(def_id)) => def_id,
-            NameBindingKind::Import { binding, .. } => return self.get_macro(binding),
-            NameBindingKind::Ambiguity { b1, .. } => return self.get_macro(b1),
+    pub fn get_macro(&mut self, def: Def) -> Rc<SyntaxExtension> {
+        let def_id = match def {
+            Def::Macro(def_id) => def_id,
             _ => panic!("Expected Def::Macro(..)"),
         };
         if let Some(ext) = self.macro_map.get(&def_id) {
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 4738e73d2a8..d5297a5a47d 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -782,6 +782,7 @@ pub struct ModuleS<'a> {
 
     resolutions: RefCell<FxHashMap<(Name, Namespace), &'a RefCell<NameResolution<'a>>>>,
     legacy_macro_resolutions: RefCell<Vec<(Mark, Name, Span)>>,
+    macro_resolutions: RefCell<Vec<(Box<[Ident]>, PathScope, Span)>>,
 
     // Macro invocations that can expand into items in this module.
     unresolved_invocations: RefCell<FxHashSet<Mark>>,
@@ -810,6 +811,7 @@ impl<'a> ModuleS<'a> {
             normal_ancestor_id: None,
             resolutions: RefCell::new(FxHashMap()),
             legacy_macro_resolutions: RefCell::new(Vec::new()),
+            macro_resolutions: RefCell::new(Vec::new()),
             unresolved_invocations: RefCell::new(FxHashSet()),
             no_implicit_prelude: false,
             glob_importers: RefCell::new(Vec::new()),
@@ -924,6 +926,14 @@ impl<'a> NameBinding<'a> {
         }
     }
 
+    fn get_macro(&self, resolver: &mut Resolver<'a>) -> Rc<SyntaxExtension> {
+        match self.kind {
+            NameBindingKind::Import { binding, .. } => binding.get_macro(resolver),
+            NameBindingKind::Ambiguity { b1, .. } => b1.get_macro(resolver),
+            _ => resolver.get_macro(self.def()),
+        }
+    }
+
     // We sometimes need to treat variants as `pub` for backwards compatibility
     fn pseudo_vis(&self) -> ty::Visibility {
         if self.is_variant() { ty::Visibility::Public } else { self.vis }
@@ -1307,6 +1317,7 @@ impl<'a> Resolver<'a> {
     pub fn resolve_crate(&mut self, krate: &Crate) {
         ImportResolver { resolver: self }.finalize_imports();
         self.current_module = self.graph_root;
+        self.finalize_current_module_macro_resolutions();
         visit::walk_crate(self, krate);
 
         check_unused::check_crate(self, krate);
@@ -2350,10 +2361,13 @@ impl<'a> Resolver<'a> {
 
             let binding = if let Some(module) = module {
                 self.resolve_name_in_module(module, ident.name, ns, false, record_used)
+            } else if opt_ns == Some(MacroNS) {
+                self.resolve_lexical_macro_path_segment(ident.name, ns, record_used)
             } else {
                 match self.resolve_ident_in_lexical_scope(ident, ns, record_used) {
                     Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
-                    Some(LexicalScopeBinding::Def(def)) if opt_ns.is_some() => {
+                    Some(LexicalScopeBinding::Def(def))
+                            if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => {
                         return PathResult::NonModule(PathResolution {
                             base_def: def,
                             depth: path.len() - 1,
@@ -2369,7 +2383,7 @@ impl<'a> Resolver<'a> {
                         module = Some(next_module);
                     } else if binding.def() == Def::Err {
                         return PathResult::NonModule(err_path_resolution());
-                    } else if opt_ns.is_some() {
+                    } else if opt_ns.is_some() && !(opt_ns == Some(MacroNS) && !is_last) {
                         return PathResult::NonModule(PathResolution {
                             base_def: binding.def(),
                             depth: path.len() - i - 1,
@@ -3050,15 +3064,22 @@ impl<'a> Resolver<'a> {
 
         for &AmbiguityError { span, name, b1, b2, lexical } in &self.ambiguity_errors {
             if !reported_spans.insert(span) { continue }
-            let msg1 = format!("`{}` could resolve to the name imported here", name);
-            let msg2 = format!("`{}` could also resolve to the name imported here", name);
+            let participle = |binding: &NameBinding| {
+                if binding.is_import() { "imported" } else { "defined" }
+            };
+            let msg1 = format!("`{}` could resolve to the name {} here", name, participle(b1));
+            let msg2 = format!("`{}` could also resolve to the name {} here", name, participle(b2));
             self.session.struct_span_err(span, &format!("`{}` is ambiguous", name))
                 .span_note(b1.span, &msg1)
                 .span_note(b2.span, &msg2)
-                .note(&if lexical || !b1.is_glob_import() {
-                    "macro-expanded macro imports do not shadow".to_owned()
-                } else {
+                .note(&if !lexical && b1.is_glob_import() {
                     format!("consider adding an explicit import of `{}` to disambiguate", name)
+                } else if let Def::Macro(..) = b1.def() {
+                    format!("macro-expanded {} do not shadow",
+                            if b1.is_import() { "macro imports" } else { "macros" })
+                } else {
+                    format!("macro-expanded {} do not shadow when used in a macro invocation path",
+                            if b1.is_import() { "imports" } else { "items" })
                 })
                 .emit();
         }
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index 3b34a60c585..6c02967672d 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -8,7 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use {Module, ModuleKind, NameBinding, NameBindingKind, Resolver, AmbiguityError};
+use {AmbiguityError, Resolver, ResolutionError, resolve_error};
+use {Module, ModuleKind, NameBinding, NameBindingKind, PathScope, PathResult};
 use Namespace::{self, MacroNS};
 use build_reduced_graph::BuildReducedGraphVisitor;
 use resolve_imports::ImportResolver;
@@ -25,6 +26,7 @@ use syntax::ext::base::{NormalTT, SyntaxExtension};
 use syntax::ext::expand::Expansion;
 use syntax::ext::hygiene::Mark;
 use syntax::ext::tt::macro_rules;
+use syntax::feature_gate::{emit_feature_err, GateIssue};
 use syntax::fold::Folder;
 use syntax::ptr::P;
 use syntax::util::lev_distance::find_best_match_for_name;
@@ -193,7 +195,7 @@ impl<'a> base::Resolver for Resolver<'a> {
     fn find_attr_invoc(&mut self, attrs: &mut Vec<ast::Attribute>) -> Option<ast::Attribute> {
         for i in 0..attrs.len() {
             match self.builtin_macros.get(&attrs[i].name()).cloned() {
-                Some(binding) => match *self.get_macro(binding) {
+                Some(binding) => match *binding.get_macro(self) {
                     MultiModifier(..) | MultiDecorator(..) | SyntaxExtension::AttrProcMacro(..) => {
                         return Some(attrs.remove(i))
                     }
@@ -207,48 +209,72 @@ impl<'a> base::Resolver for Resolver<'a> {
 
     fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool)
                      -> Result<Rc<SyntaxExtension>, Determinacy> {
-        if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
-            self.session.span_err(path.span, "expected macro name without module separators");
+        let ast::Path { ref segments, global, span } = *path;
+        if segments.iter().any(|segment| !segment.parameters.is_empty()) {
+            let kind =
+                if segments.last().unwrap().parameters.is_empty() { "module" } else { "macro" };
+            let msg = format!("type parameters are not allowed on {}s", kind);
+            self.session.span_err(path.span, &msg);
             return Err(Determinacy::Determined);
         }
-        let name = path.segments[0].identifier.name;
 
+        let path_scope = if global { PathScope::Global } else { PathScope::Lexical };
+        let path: Vec<_> = segments.iter().map(|seg| seg.identifier).collect();
         let invocation = self.invocations[&scope];
         self.current_module = invocation.module.get();
+
+        if path.len() > 1 || global {
+            if !self.use_extern_macros {
+                let msg = "non-ident macro paths are experimental";
+                let feature = "use_extern_macros";
+                emit_feature_err(&self.session.parse_sess, feature, span, GateIssue::Language, msg);
+                return Err(Determinacy::Determined);
+            }
+
+            let ext = match self.resolve_path(&path, path_scope, Some(MacroNS), None) {
+                PathResult::NonModule(path_res) => Ok(self.get_macro(path_res.base_def)),
+                PathResult::Module(..) => unreachable!(),
+                PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
+                _ => Err(Determinacy::Determined),
+            };
+            self.current_module.macro_resolutions.borrow_mut()
+                .push((path.into_boxed_slice(), path_scope, span));
+            return ext;
+        }
+
+        let name = path[0].name;
         let result = match self.resolve_legacy_scope(&invocation.legacy_scope, name, false) {
             Some(MacroBinding::Legacy(binding)) => Ok(binding.ext.clone()),
-            Some(MacroBinding::Modern(binding)) => Ok(self.get_macro(binding)),
-            None => match self.resolve_in_item_lexical_scope(name, MacroNS, None) {
-                Some(binding) => Ok(self.get_macro(binding)),
-                None => return Err(if force {
+            Some(MacroBinding::Modern(binding)) => Ok(binding.get_macro(self)),
+            None => match self.resolve_lexical_macro_path_segment(name, MacroNS, None) {
+                Ok(binding) => Ok(binding.get_macro(self)),
+                Err(Determinacy::Undetermined) if !force => return Err(Determinacy::Undetermined),
+                _ => {
                     let msg = format!("macro undefined: '{}!'", name);
-                    let mut err = self.session.struct_span_err(path.span, &msg);
+                    let mut err = self.session.struct_span_err(span, &msg);
                     self.suggest_macro_name(&name.as_str(), &mut err);
                     err.emit();
-                    Determinacy::Determined
-                } else {
-                    Determinacy::Undetermined
-                }),
+                    return Err(Determinacy::Determined);
+                },
             },
         };
 
         if self.use_extern_macros {
-            self.current_module.legacy_macro_resolutions.borrow_mut()
-                .push((scope, name, path.span));
+            self.current_module.legacy_macro_resolutions.borrow_mut().push((scope, name, span));
         }
         result
     }
 }
 
 impl<'a> Resolver<'a> {
-    // Resolve the name in the module's lexical scope, excluding non-items.
-    fn resolve_in_item_lexical_scope(&mut self,
-                                     name: Name,
-                                     ns: Namespace,
-                                     record_used: Option<Span>)
-                                     -> Option<&'a NameBinding<'a>> {
+    // Resolve the initial segment of a non-global macro path (e.g. `foo` in `foo::bar!();`)
+    pub fn resolve_lexical_macro_path_segment(&mut self,
+                                              name: Name,
+                                              ns: Namespace,
+                                              record_used: Option<Span>)
+                                              -> Result<&'a NameBinding<'a>, Determinacy> {
         let mut module = self.current_module;
-        let mut potential_expanded_shadower = None;
+        let mut potential_expanded_shadower: Option<&NameBinding> = None;
         loop {
             // Since expanded macros may not shadow the lexical scope (enforced below),
             // we can ignore unresolved invocations (indicated by the penultimate argument).
@@ -256,26 +282,30 @@ impl<'a> Resolver<'a> {
                 Ok(binding) => {
                     let span = match record_used {
                         Some(span) => span,
-                        None => return Some(binding),
+                        None => return Ok(binding),
                     };
-                    if let Some(shadower) = potential_expanded_shadower {
-                        self.ambiguity_errors.push(AmbiguityError {
-                            span: span, name: name, b1: shadower, b2: binding, lexical: true,
-                        });
-                        return Some(shadower);
-                    } else if binding.expansion == Mark::root() {
-                        return Some(binding);
-                    } else {
-                        potential_expanded_shadower = Some(binding);
+                    match potential_expanded_shadower {
+                        Some(shadower) if shadower.def() != binding.def() => {
+                            self.ambiguity_errors.push(AmbiguityError {
+                                span: span, name: name, b1: shadower, b2: binding, lexical: true,
+                            });
+                            return Ok(shadower);
+                        }
+                        _ if binding.expansion == Mark::root() => return Ok(binding),
+                        _ => potential_expanded_shadower = Some(binding),
                     }
                 },
-                Err(Determinacy::Undetermined) => return None,
+                Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined),
                 Err(Determinacy::Determined) => {}
             }
 
             match module.kind {
                 ModuleKind::Block(..) => module = module.parent.unwrap(),
-                ModuleKind::Def(..) => return potential_expanded_shadower,
+                ModuleKind::Def(..) => return match potential_expanded_shadower {
+                    Some(binding) => Ok(binding),
+                    None if record_used.is_some() => Err(Determinacy::Determined),
+                    None => Err(Determinacy::Undetermined),
+                },
             }
         }
     }
@@ -343,12 +373,22 @@ impl<'a> Resolver<'a> {
 
     pub fn finalize_current_module_macro_resolutions(&mut self) {
         let module = self.current_module;
+        for &(ref path, scope, span) in module.macro_resolutions.borrow().iter() {
+            match self.resolve_path(path, scope, Some(MacroNS), Some(span)) {
+                PathResult::NonModule(_) => {},
+                PathResult::Failed(msg, _) => {
+                    resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
+                }
+                _ => unreachable!(),
+            }
+        }
+
         for &(mark, name, span) in module.legacy_macro_resolutions.borrow().iter() {
             let legacy_scope = &self.invocations[&mark].legacy_scope;
             let legacy_resolution = self.resolve_legacy_scope(legacy_scope, name, true);
-            let resolution = self.resolve_in_item_lexical_scope(name, MacroNS, Some(span));
+            let resolution = self.resolve_lexical_macro_path_segment(name, MacroNS, Some(span));
             let (legacy_resolution, resolution) = match (legacy_resolution, resolution) {
-                (Some(legacy_resolution), Some(resolution)) => (legacy_resolution, resolution),
+                (Some(legacy_resolution), Ok(resolution)) => (legacy_resolution, resolution),
                 _ => continue,
             };
             let (legacy_span, participle) = match legacy_resolution {
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index 2a803d72fd1..b634d57a842 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -10,7 +10,7 @@
 
 use self::ImportDirectiveSubclass::*;
 
-use {Module, PerNS};
+use {AmbiguityError, Module, PerNS};
 use Namespace::{self, TypeNS, MacroNS};
 use {NameBinding, NameBindingKind, PathResult, PathScope, PrivacyError, ToNameBinding};
 use Resolver;
@@ -73,6 +73,7 @@ pub struct NameResolution<'a> {
     single_imports: SingleImports<'a>,
     /// The least shadowable known binding for this name, or None if there are no known bindings.
     pub binding: Option<&'a NameBinding<'a>>,
+    shadows_glob: Option<&'a NameBinding<'a>>,
 }
 
 #[derive(Clone, Debug)]
@@ -151,6 +152,18 @@ impl<'a> Resolver<'a> {
 
         if let Some(span) = record_used {
             if let Some(binding) = resolution.binding {
+                if let Some(shadowed_glob) = resolution.shadows_glob {
+                    // If we ignore unresolved invocations, we must forbid
+                    // expanded shadowing to avoid time travel.
+                    if ignore_unresolved_invocations &&
+                       binding.expansion != Mark::root() &&
+                       ns != MacroNS && // In MacroNS, `try_define` always forbids this shadowing
+                       binding.def() != shadowed_glob.def() {
+                        self.ambiguity_errors.push(AmbiguityError {
+                            span: span, name: name, lexical: false, b1: binding, b2: shadowed_glob,
+                        });
+                    }
+                }
                 if self.record_use(name, ns, binding, span) {
                     return Ok(self.dummy_binding);
                 }
@@ -298,6 +311,7 @@ impl<'a> Resolver<'a> {
                 if binding.is_glob_import() {
                     if !old_binding.is_glob_import() &&
                        !(ns == MacroNS && old_binding.expansion != Mark::root()) {
+                        resolution.shadows_glob = Some(binding);
                     } else if binding.def() != old_binding.def() {
                         resolution.binding = Some(this.ambiguity(old_binding, binding));
                     } else if !old_binding.vis.is_at_least(binding.vis, this) {
@@ -310,6 +324,7 @@ impl<'a> Resolver<'a> {
                         resolution.binding = Some(this.ambiguity(binding, old_binding));
                     } else {
                         resolution.binding = Some(binding);
+                        resolution.shadows_glob = Some(old_binding);
                     }
                 } else {
                     return Err(old_binding);
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index fd6cae1e1b6..4138acafac6 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -400,12 +400,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                                           &self.cx.ecfg.features.unwrap());
         }
 
-        if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
-            self.cx.span_err(path.span, "expected macro name without module separators");
-            return kind.dummy(span);
-        }
-
-        let extname = path.segments[0].identifier.name;
+        let extname = path.segments.last().unwrap().identifier.name;
         let ident = ident.unwrap_or(keywords::Invalid.ident());
         let marked_tts = mark_tts(&tts, mark);
         let opt_expanded = match *ext {
diff --git a/src/test/compile-fail/imports/macro-paths.rs b/src/test/compile-fail/imports/macro-paths.rs
new file mode 100644
index 00000000000..97c05392e7d
--- /dev/null
+++ b/src/test/compile-fail/imports/macro-paths.rs
@@ -0,0 +1,42 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:two_macros.rs
+
+#![feature(use_extern_macros)]
+
+extern crate two_macros;
+
+mod foo {
+    pub mod bar {
+        pub use two_macros::m;
+    }
+}
+
+fn f() {
+    use foo::*; //~ NOTE could also resolve to the name imported here
+    bar::m! { //~ ERROR ambiguous
+              //~| NOTE macro-expanded items do not shadow when used in a macro invocation path
+        mod bar { pub use two_macros::m; } //~ NOTE could resolve to the name defined here
+                                           //~^^^ NOTE in this expansion
+    }
+}
+
+pub mod baz { //~ NOTE could also resolve to the name defined here
+    pub use two_macros::m;
+}
+
+fn g() {
+    baz::m! { //~ ERROR ambiguous
+              //~| NOTE macro-expanded items do not shadow when used in a macro invocation path
+        mod baz { pub use two_macros::m; } //~ NOTE could resolve to the name defined here
+                                           //~^^^ NOTE in this expansion
+    }
+}
diff --git a/src/test/compile-fail/macro-with-seps-err-msg.rs b/src/test/compile-fail/macro-with-seps-err-msg.rs
index 408bb15ba28..d5fc9a510f0 100644
--- a/src/test/compile-fail/macro-with-seps-err-msg.rs
+++ b/src/test/compile-fail/macro-with-seps-err-msg.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 fn main() {
-    globnar::brotz!(); //~ ERROR expected macro name without module separators
-    ::foo!(); //~ ERROR expected macro name without module separators
-    foo::<T>!(); //~ ERROR expected macro name without module separators
+    globnar::brotz!(); //~ ERROR non-ident macro paths are experimental
+    ::foo!(); //~ ERROR non-ident macro paths are experimental
+    foo::<T>!(); //~ ERROR type parameters are not allowed on macros
 }
diff --git a/src/test/compile-fail/paths-in-macro-invocations.rs b/src/test/compile-fail/paths-in-macro-invocations.rs
deleted file mode 100644
index c69b7e526cc..00000000000
--- a/src/test/compile-fail/paths-in-macro-invocations.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-::foo::bar!(); //~ ERROR expected macro name without module separators
-foo::bar!(); //~ ERROR expected macro name without module separators
-
-trait T {
-    foo::bar!(); //~ ERROR expected macro name without module separators
-    ::foo::bar!(); //~ ERROR expected macro name without module separators
-}
-
-struct S {
-    x: foo::bar!(), //~ ERROR expected macro name without module separators
-    y: ::foo::bar!(), //~ ERROR expected macro name without module separators
-}
-
-impl S {
-    foo::bar!(); //~ ERROR expected macro name without module separators
-    ::foo::bar!(); //~ ERROR expected macro name without module separators
-}
-
-fn main() {
-    foo::bar!(); //~ ERROR expected macro name without module separators
-    ::foo::bar!(); //~ ERROR expected macro name without module separators
-
-    let _ = foo::bar!(); //~ ERROR expected macro name without module separators
-    let _ = ::foo::bar!(); //~ ERROR expected macro name without module separators
-
-    let foo::bar!() = 0; //~ ERROR expected macro name without module separators
-    let ::foo::bar!() = 0; //~ ERROR expected macro name without module separators
-}
diff --git a/src/test/run-pass/auxiliary/two_macros.rs b/src/test/run-pass/auxiliary/two_macros.rs
index 060960f0dbc..0da6ba13696 100644
--- a/src/test/run-pass/auxiliary/two_macros.rs
+++ b/src/test/run-pass/auxiliary/two_macros.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 #[macro_export]
-macro_rules! macro_one { () => ("one") }
+macro_rules! macro_one { ($($t:tt)*) => ($($t)*) }
 
 #[macro_export]
-macro_rules! macro_two { () => ("two") }
+macro_rules! macro_two { ($($t:tt)*) => ($($t)*) }
diff --git a/src/test/run-pass/paths-in-macro-invocations.rs b/src/test/run-pass/paths-in-macro-invocations.rs
new file mode 100644
index 00000000000..69f8906778a
--- /dev/null
+++ b/src/test/run-pass/paths-in-macro-invocations.rs
@@ -0,0 +1,46 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:two_macros.rs
+
+#![feature(use_extern_macros)]
+
+extern crate two_macros;
+
+::two_macros::macro_one!();
+two_macros::macro_one!();
+
+mod foo { pub use two_macros::macro_one as bar; }
+
+trait T {
+    foo::bar!();
+    ::foo::bar!();
+}
+
+struct S {
+    x: foo::bar!(i32),
+    y: ::foo::bar!(i32),
+}
+
+impl S {
+    foo::bar!();
+    ::foo::bar!();
+}
+
+fn main() {
+    foo::bar!();
+    ::foo::bar!();
+
+    let _ = foo::bar!(0);
+    let _ = ::foo::bar!(0);
+
+    let foo::bar!(_) = 0;
+    let ::foo::bar!(_) = 0;
+}