Forbid undefined names in macro use / macro reexport

Fixes #21062.
This commit is contained in:
Keegan McAllister 2015-02-10 19:30:33 -08:00
parent a954663db6
commit d4288717c4
3 changed files with 80 additions and 26 deletions

View File

@ -17,7 +17,7 @@
use std::mem;
use std::env;
use std::dynamic_lib::DynamicLibrary;
use std::collections::HashSet;
use std::collections::{HashSet, HashMap};
use std::borrow::ToOwned;
use syntax::ast;
use syntax::attr;
@ -116,6 +116,8 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate,
return loader.plugins;
}
pub type MacroSelection = HashMap<token::InternedString, Span>;
// note that macros aren't expanded yet, and therefore macros can't add plugins.
impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
fn visit_item(&mut self, item: &ast::Item) {
@ -128,9 +130,9 @@ fn visit_item(&mut self, item: &ast::Item) {
}
}
// Parse the attributes relating to macro / plugin loading.
let mut macro_selection = Some(HashSet::new()); // None => load all
let mut reexport = HashSet::new();
// Parse the attributes relating to macro loading.
let mut import = Some(HashMap::new()); // None => load all
let mut reexport = HashMap::new();
for attr in &item.attrs {
let mut used = true;
match &attr.name()[] {
@ -147,14 +149,14 @@ fn visit_item(&mut self, item: &ast::Item) {
let names = attr.meta_item_list();
if names.is_none() {
// no names => load all
macro_selection = None;
import = None;
}
if let (Some(sel), Some(names)) = (macro_selection.as_mut(), names) {
for name in names {
if let ast::MetaWord(ref name) = name.node {
sel.insert(name.clone());
if let (Some(sel), Some(names)) = (import.as_mut(), names) {
for attr in names {
if let ast::MetaWord(ref name) = attr.node {
sel.insert(name.clone(), attr.span);
} else {
self.sess.span_err(name.span, "bad macro import");
self.sess.span_err(attr.span, "bad macro import");
}
}
}
@ -168,11 +170,11 @@ fn visit_item(&mut self, item: &ast::Item) {
}
};
for name in names {
if let ast::MetaWord(ref name) = name.node {
reexport.insert(name.clone());
for attr in names {
if let ast::MetaWord(ref name) = attr.node {
reexport.insert(name.clone(), attr.span);
} else {
self.sess.span_err(name.span, "bad macro reexport");
self.sess.span_err(attr.span, "bad macro reexport");
}
}
}
@ -183,7 +185,7 @@ fn visit_item(&mut self, item: &ast::Item) {
}
}
self.load_macros(item, macro_selection, Some(reexport))
self.load_macros(item, import, reexport)
}
fn visit_mac(&mut self, _: &ast::Mac) {
@ -195,10 +197,10 @@ fn visit_mac(&mut self, _: &ast::Mac) {
impl<'a> PluginLoader<'a> {
pub fn load_macros<'b>(&mut self,
vi: &ast::Item,
macro_selection: Option<HashSet<token::InternedString>>,
reexport: Option<HashSet<token::InternedString>>) {
if let (Some(sel), Some(re)) = (macro_selection.as_ref(), reexport.as_ref()) {
if sel.is_empty() && re.is_empty() {
import: Option<MacroSelection>,
reexport: MacroSelection) {
if let Some(sel) = import.as_ref() {
if sel.is_empty() && reexport.is_empty() {
return;
}
}
@ -211,19 +213,32 @@ pub fn load_macros<'b>(&mut self,
let pmd = self.reader.read_plugin_metadata(CrateOrString::Krate(vi));
let mut seen = HashSet::new();
for mut def in pmd.exported_macros() {
let name = token::get_ident(def.ident);
def.use_locally = match macro_selection.as_ref() {
seen.insert(name.clone());
def.use_locally = match import.as_ref() {
None => true,
Some(sel) => sel.contains(&name),
};
def.export = if let Some(ref re) = reexport {
re.contains(&name)
} else {
false // Don't reexport macros from crates loaded from the command line
Some(sel) => sel.contains_key(&name),
};
def.export = reexport.contains_key(&name);
self.plugins.macros.push(def);
}
if let Some(sel) = import.as_ref() {
for (name, span) in sel.iter() {
if !seen.contains(name) {
self.sess.span_err(*span, "imported macro not found");
}
}
}
for (name, span) in reexport.iter() {
if !seen.contains(name) {
self.sess.span_err(*span, "reexported macro not found");
}
}
}
pub fn load_plugin<'b>(&mut self,

View File

@ -0,0 +1,20 @@
// Copyright 2015 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
// ignore-stage1
#[macro_use(macro_two)]
#[macro_reexport(no_way)] //~ ERROR reexported macro not found
extern crate two_macros;
pub fn main() {
macro_two!();
}

View File

@ -0,0 +1,19 @@
// Copyright 2015 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
// ignore-stage1
#[macro_use(macro_two, no_way)] //~ ERROR imported macro not found
extern crate two_macros;
pub fn main() {
macro_two!();
}