Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forbid undefined names in macro use / macro reexport #22167

Merged
merged 1 commit into from
Feb 12, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 41 additions & 26 deletions src/librustc/plugin/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use plugin::registry::Registry;
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;
Expand Down Expand Up @@ -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) {
Expand All @@ -128,9 +130,9 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
}
}

// 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()[] {
Expand All @@ -147,14 +149,14 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
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");
}
}
}
Expand All @@ -168,11 +170,11 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
}
};

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");
}
}
}
Expand All @@ -183,7 +185,7 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
}
}

self.load_macros(item, macro_selection, Some(reexport))
self.load_macros(item, import, reexport)
}

fn visit_mac(&mut self, _: &ast::Mac) {
Expand All @@ -195,10 +197,10 @@ impl<'a, 'v> Visitor<'v> for PluginLoader<'a> {
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;
}
}
Expand All @@ -211,19 +213,32 @@ impl<'a> PluginLoader<'a> {

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,
Expand Down
20 changes: 20 additions & 0 deletions src/test/compile-fail/macro-reexport-undef.rs
Original file line number Diff line number Diff line change
@@ -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!();
}
19 changes: 19 additions & 0 deletions src/test/compile-fail/macro-use-undef.rs
Original file line number Diff line number Diff line change
@@ -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!();
}