diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index d90a49213d141..7bcc543023e78 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 { - 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 { + 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 e1200149dcc41..47e6d21cd7d01 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -783,6 +783,7 @@ pub struct ModuleS<'a> { resolutions: RefCell>>>, legacy_macro_resolutions: RefCell>, + macro_resolutions: RefCell, PathScope, Span)>>, // Macro invocations that can expand into items in this module. unresolved_invocations: RefCell>, @@ -811,6 +812,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()), @@ -925,6 +927,14 @@ impl<'a> NameBinding<'a> { } } + fn get_macro(&self, resolver: &mut Resolver<'a>) -> Rc { + 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 } @@ -1308,6 +1318,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); @@ -2351,10 +2362,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, @@ -2370,7 +2384,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, @@ -3051,15 +3065,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 3b34a60c58525..6c02967672d84 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) -> Option { 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, 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) - -> 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) + -> 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 2a803d72fd1bd..b634d57a842f6 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 fd6cae1e1b668..4138acafac69a 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 0000000000000..97c05392e7de4 --- /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 or the MIT license +// , 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 408bb15ba28cd..d5fc9a510f0db 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::!(); //~ 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::!(); //~ 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 c69b7e526cc3b..0000000000000 --- 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 or the MIT license -// , 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 060960f0dbc88..0da6ba13696da 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 0000000000000..69f8906778a16 --- /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 or the MIT license +// , 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; +}