From ed9a09d40c7c12d87f8923fad5264beb69749d38 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 27 Nov 2016 10:58:46 +0000 Subject: [PATCH] Support paths in macro invocations. --- src/librustc_resolve/lib.rs | 27 ++++-- src/librustc_resolve/macros.rs | 118 ++++++++++++++++-------- src/librustc_resolve/resolve_imports.rs | 17 +++- src/libsyntax/ext/expand.rs | 7 +- 4 files changed, 116 insertions(+), 53 deletions(-) diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 1c8c77a13d916..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()), @@ -1316,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); @@ -2359,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, @@ -2378,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, @@ -3059,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 ff91e7dc971c1..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; @@ -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(); - let ext = match self.resolve_legacy_scope(&invocation.legacy_scope, name, false) { - Some(MacroBinding::Legacy(binding)) => binding.ext.clone(), - Some(MacroBinding::Modern(binding)) => binding.get_macro(self), - None => match self.resolve_in_item_lexical_scope(name, MacroNS, None) { - Some(binding) => binding.get_macro(self), - None => return Err(if force { + + 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(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)); } - Ok(ext) + 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 {