diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index aedb8fef2885c..7bab4a8d725dc 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -12,6 +12,7 @@ use hir::def_id::DefId; use util::nodemap::NodeMap; use syntax::ast; use syntax::ext::base::MacroKind; +use syntax_pos::Span; use hir; #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] @@ -116,6 +117,7 @@ pub type ExportMap = NodeMap>; pub struct Export { pub name: ast::Name, // The name of the target. pub def: Def, // The definition of the target. + pub span: Span, // The span of the target definition. } impl CtorKind { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index b4b9966cbe47b..c2ad598b0c503 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -683,7 +683,7 @@ impl<'a, 'tcx> CrateMetadata { }, ext.kind() ); - callback(def::Export { name: name, def: def }); + callback(def::Export { name: name, def: def, span: DUMMY_SP }); } } return @@ -720,6 +720,7 @@ impl<'a, 'tcx> CrateMetadata { callback(def::Export { def: def, name: self.item_name(child_index), + span: self.entry(child_index).span.decode(self), }); } } @@ -732,12 +733,10 @@ impl<'a, 'tcx> CrateMetadata { } let def_key = self.def_key(child_index); + let span = child.span.decode(self); if let (Some(def), Some(name)) = (self.get_def(child_index), def_key.disambiguated_data.data.get_opt_name()) { - callback(def::Export { - def: def, - name: name, - }); + callback(def::Export { def: def, name: name, span: span }); // For non-reexport structs and variants add their constructors to children. // Reexport lists automatically contain constructors when necessary. match def { @@ -745,10 +744,7 @@ impl<'a, 'tcx> CrateMetadata { if let Some(ctor_def_id) = self.get_struct_ctor_def_id(child_index) { let ctor_kind = self.get_ctor_kind(child_index); let ctor_def = Def::StructCtor(ctor_def_id, ctor_kind); - callback(def::Export { - def: ctor_def, - name: name, - }); + callback(def::Export { def: ctor_def, name: name, span: span }); } } Def::Variant(def_id) => { @@ -756,10 +752,7 @@ impl<'a, 'tcx> CrateMetadata { // value namespace, they are reserved for possible future use. let ctor_kind = self.get_ctor_kind(child_index); let ctor_def = Def::VariantCtor(def_id, ctor_kind); - callback(def::Export { - def: ctor_def, - name: name, - }); + callback(def::Export { def: ctor_def, name: name, span: span }); } _ => {} } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 03c61067d64c2..49d170467ee1f 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -602,7 +602,7 @@ impl<'a> Resolver<'a> { let ident = Ident::with_empty_ctxt(name); let result = self.resolve_ident_in_module(module, ident, MacroNS, false, None); if let Ok(binding) = result { - self.macro_exports.push(Export { name: name, def: binding.def() }); + self.macro_exports.push(Export { name: name, def: binding.def(), span: span }); } else { span_err!(self.session, span, E0470, "reexported macro not found"); } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index df3be8fa0f810..f72c2eadb04a8 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -653,7 +653,7 @@ impl<'a> Resolver<'a> { if attr::contains_name(&item.attrs, "macro_export") { let def = Def::Macro(def_id, MacroKind::Bang); - self.macro_exports.push(Export { name: ident.name, def: def }); + self.macro_exports.push(Export { name: ident.name, def: def, span: item.span }); } } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index dbc8bca548b76..2f4ac12cd7363 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -21,7 +21,7 @@ use rustc::ty; use rustc::lint::builtin::PRIVATE_IN_PUBLIC; use rustc::hir::def_id::DefId; use rustc::hir::def::*; -use rustc::util::nodemap::FxHashSet; +use rustc::util::nodemap::FxHashMap; use syntax::ast::{Ident, NodeId}; use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; @@ -763,10 +763,11 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { *module.globs.borrow_mut() = Vec::new(); let mut reexports = Vec::new(); + let mut exported_macro_names = FxHashMap(); if module as *const _ == self.graph_root as *const _ { - let mut exported_macro_names = FxHashSet(); - for export in mem::replace(&mut self.macro_exports, Vec::new()).into_iter().rev() { - if exported_macro_names.insert(export.name) { + let macro_exports = mem::replace(&mut self.macro_exports, Vec::new()); + for export in macro_exports.into_iter().rev() { + if exported_macro_names.insert(export.name, export.span).is_none() { reexports.push(export); } } @@ -786,7 +787,17 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { if !def.def_id().is_local() { self.session.cstore.export_macros(def.def_id().krate); } - reexports.push(Export { name: ident.name, def: def }); + if let Def::Macro(..) = def { + if let Some(&span) = exported_macro_names.get(&ident.name) { + let msg = + format!("a macro named `{}` has already been exported", ident); + self.session.struct_span_err(span, &msg) + .span_label(span, &format!("`{}` already exported", ident)) + .span_note(binding.span, "previous macro export here") + .emit(); + } + } + reexports.push(Export { name: ident.name, def: def, span: binding.span }); } } diff --git a/src/test/compile-fail/duplicate-check-macro-exports.rs b/src/test/compile-fail/duplicate-check-macro-exports.rs new file mode 100644 index 0000000000000..53d7e54ee5b7b --- /dev/null +++ b/src/test/compile-fail/duplicate-check-macro-exports.rs @@ -0,0 +1,19 @@ +// Copyright 2017 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. + +#![feature(use_extern_macros)] + +pub use std::panic; //~ NOTE previous macro export here + +#[macro_export] +macro_rules! panic { () => {} } //~ ERROR a macro named `panic` has already been exported +//~| NOTE `panic` already exported + +fn main() {}