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>>,
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()),
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);
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,
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,
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();
}
// 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;
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;
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();
- 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<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).
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),
+ },
}
}
}
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 {
use self::ImportDirectiveSubclass::*;
-use {Module, PerNS};
+use {AmbiguityError, Module, PerNS};
use Namespace::{self, TypeNS, MacroNS};
use {NameBinding, NameBindingKind, PathResult, PathScope, PrivacyError, ToNameBinding};
use Resolver;
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)]
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);
}
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) {
resolution.binding = Some(this.ambiguity(binding, old_binding));
} else {
resolution.binding = Some(binding);
+ resolution.shadows_glob = Some(old_binding);
}
} else {
return Err(old_binding);