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
This commit is contained in:
commit
b462e8fa61
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)*) }
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue