提交 901d2c7b 编写于 作者: M Manish Goregaokar

Rollup merge of #22241 - kmcallister:macro-plugin-cleanup, r=sfackler

......@@ -39,6 +39,16 @@ If present, arguments passed as `#![plugin(foo(... args ...))]` are not
interpreted by rustc itself. They are provided to the plugin through the
`Registry`'s [`args` method](../rustc/plugin/registry/struct.Registry.html#method.args).
In the vast majority of cases, a plugin should *only* be used through
`#![plugin]` and not through an `extern crate` item. Linking a plugin would
pull in all of libsyntax and librustc as dependencies of your crate. This is
generally unwanted unless you are building another plugin. The
`plugin_as_library` lint checks these guidelines.
The usual practice is to put compiler plugins in their own crate, separate from
any `macro_rules!` macros or ordinary Rust code meant to be used by consumers
of a library.
# Syntax extensions
Plugins can extend Rust's syntax in various ways. One kind of syntax extension
......
......@@ -26,7 +26,7 @@
//! a `pub fn new()`.
use self::MethodContext::*;
use metadata::csearch;
use metadata::{csearch, decoder};
use middle::def::*;
use middle::subst::Substs;
use middle::ty::{self, Ty};
......@@ -1964,6 +1964,48 @@ fn id_refers_to_this_method<'tcx>(tcx: &ty::ctxt<'tcx>,
}
}
declare_lint! {
PLUGIN_AS_LIBRARY,
Warn,
"compiler plugin used as ordinary library in non-plugin crate"
}
#[derive(Copy)]
pub struct PluginAsLibrary;
impl LintPass for PluginAsLibrary {
fn get_lints(&self) -> LintArray {
lint_array![PLUGIN_AS_LIBRARY]
}
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
if cx.sess().plugin_registrar_fn.get().is_some() {
// We're compiling a plugin; it's fine to link other plugins.
return;
}
match it.node {
ast::ItemExternCrate(..) => (),
_ => return,
};
let md = match cx.sess().cstore.find_extern_mod_stmt_cnum(it.id) {
Some(cnum) => cx.sess().cstore.get_crate_data(cnum),
None => {
// Probably means we aren't linking the crate for some reason.
//
// Not sure if / when this could happen.
return;
}
};
if decoder::get_plugin_registrar_fn(md.data()).is_some() {
cx.span_lint(PLUGIN_AS_LIBRARY, it.span,
"compiler plugin used as an ordinary library");
}
}
}
declare_lint! {
pub UNUSED_IMPORTS,
Warn,
......
......@@ -214,6 +214,7 @@ pub fn register_builtin(&mut self, sess: Option<&Session>) {
Stability,
UnconditionalRecursion,
InvalidNoMangleItems,
PluginAsLibrary,
);
add_builtin_with_new!(sess,
......
......@@ -121,12 +121,10 @@ fn register_native_lib(sess: &Session,
sess.cstore.add_used_library(name, kind);
}
pub struct PluginMetadata<'a> {
sess: &'a Session,
// Extra info about a crate loaded for plugins or exported macros.
struct ExtensionCrate {
metadata: PMDSource,
dylib: Option<Path>,
info: CrateInfo,
vi_span: Span,
target_only: bool,
}
......@@ -451,21 +449,7 @@ fn resolve_crate_deps(&mut self,
}).collect()
}
pub fn read_plugin_metadata<'b>(&'b mut self,
krate: CrateOrString<'b>) -> PluginMetadata<'b> {
let (info, span) = match krate {
CrateOrString::Krate(c) => {
(self.extract_crate_info(c).unwrap(), c.span)
}
CrateOrString::Str(sp, s) => {
(CrateInfo {
name: s.to_string(),
ident: s.to_string(),
id: ast::DUMMY_NODE_ID,
should_link: false,
}, sp)
}
};
fn read_extension_crate(&mut self, span: Span, info: &CrateInfo) -> ExtensionCrate {
let target_triple = &self.sess.opts.target_triple[];
let is_cross = target_triple != config::host_triple();
let mut should_link = info.should_link && !is_cross;
......@@ -517,30 +501,21 @@ pub fn read_plugin_metadata<'b>(&'b mut self,
PMDSource::Owned(library.metadata)
};
PluginMetadata {
sess: self.sess,
ExtensionCrate {
metadata: metadata,
dylib: dylib.map(|p| p.0),
info: info,
vi_span: span,
target_only: target_only,
}
}
}
#[derive(Copy)]
pub enum CrateOrString<'a> {
Krate(&'a ast::Item),
Str(Span, &'a str)
}
/// Read exported macros.
pub fn read_exported_macros(&mut self, krate: &ast::Item) -> Vec<ast::MacroDef> {
let ci = self.extract_crate_info(krate).unwrap();
let ekrate = self.read_extension_crate(krate.span, &ci);
impl<'a> PluginMetadata<'a> {
/// Read exported macros
pub fn exported_macros(&self) -> Vec<ast::MacroDef> {
let imported_from = Some(token::intern(&self.info.ident[]).ident());
let source_name = format!("<{} macros>", &self.info.ident[]);
let source_name = format!("<{} macros>", krate.ident);
let mut macros = vec![];
decoder::each_exported_macro(self.metadata.as_slice(),
decoder::each_exported_macro(ekrate.metadata.as_slice(),
&*self.sess.cstore.intr,
|name, attrs, body| {
// NB: Don't use parse::parse_tts_from_source_str because it parses with
......@@ -558,7 +533,7 @@ pub fn exported_macros(&self) -> Vec<ast::MacroDef> {
attrs: attrs,
id: ast::DUMMY_NODE_ID,
span: span,
imported_from: imported_from,
imported_from: Some(krate.ident),
// overridden in plugin/load.rs
export: false,
use_locally: false,
......@@ -572,28 +547,35 @@ pub fn exported_macros(&self) -> Vec<ast::MacroDef> {
}
/// Look for a plugin registrar. Returns library path and symbol name.
pub fn plugin_registrar(&self) -> Option<(Path, String)> {
if self.target_only {
pub fn find_plugin_registrar(&mut self, span: Span, name: &str) -> Option<(Path, String)> {
let ekrate = self.read_extension_crate(span, &CrateInfo {
name: name.to_string(),
ident: name.to_string(),
id: ast::DUMMY_NODE_ID,
should_link: false,
});
if ekrate.target_only {
// Need to abort before syntax expansion.
let message = format!("plugin crate `{}` is not available for triple `{}` \
let message = format!("plugin `{}` is not available for triple `{}` \
(only found {})",
self.info.ident,
name,
config::host_triple(),
self.sess.opts.target_triple);
self.sess.span_err(self.vi_span, &message[]);
self.sess.span_err(span, &message[]);
self.sess.abort_if_errors();
}
let registrar = decoder::get_plugin_registrar_fn(self.metadata.as_slice())
.map(|id| decoder::get_symbol(self.metadata.as_slice(), id));
let registrar = decoder::get_plugin_registrar_fn(ekrate.metadata.as_slice())
.map(|id| decoder::get_symbol(ekrate.metadata.as_slice(), id));
match (self.dylib.as_ref(), registrar) {
match (ekrate.dylib.as_ref(), registrar) {
(Some(dylib), Some(reg)) => Some((dylib.clone(), reg)),
(None, Some(_)) => {
let message = format!("plugin crate `{}` only found in rlib format, \
let message = format!("plugin `{}` only found in rlib format, \
but must be available in dylib format",
self.info.ident);
self.sess.span_err(self.vi_span, &message[]);
name);
self.sess.span_err(span, &message[]);
// No need to abort because the loading code will just ignore this
// empty dylib.
None
......
// Copyright 2012-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.
//! Used by `rustc` when loading a crate with exported macros.
use session::Session;
use metadata::creader::CrateReader;
use std::collections::{HashSet, HashMap};
use syntax::ast;
use syntax::attr;
use syntax::codemap::Span;
use syntax::parse::token;
use syntax::visit;
use syntax::visit::Visitor;
use syntax::attr::AttrMetaMethods;
struct MacroLoader<'a> {
sess: &'a Session,
span_whitelist: HashSet<Span>,
reader: CrateReader<'a>,
macros: Vec<ast::MacroDef>,
}
impl<'a> MacroLoader<'a> {
fn new(sess: &'a Session) -> MacroLoader<'a> {
MacroLoader {
sess: sess,
span_whitelist: HashSet::new(),
reader: CrateReader::new(sess),
macros: vec![],
}
}
}
/// Read exported macros.
pub fn read_macro_defs(sess: &Session, krate: &ast::Crate) -> Vec<ast::MacroDef> {
let mut loader = MacroLoader::new(sess);
// We need to error on `#[macro_use] extern crate` when it isn't at the
// crate root, because `$crate` won't work properly. Identify these by
// spans, because the crate map isn't set up yet.
for item in &krate.module.items {
if let ast::ItemExternCrate(_) = item.node {
loader.span_whitelist.insert(item.span);
}
}
visit::walk_crate(&mut loader, krate);
loader.macros
}
pub type MacroSelection = HashMap<token::InternedString, Span>;
// note that macros aren't expanded yet, and therefore macros can't add macro imports.
impl<'a, 'v> Visitor<'v> for MacroLoader<'a> {
fn visit_item(&mut self, item: &ast::Item) {
// We're only interested in `extern crate`.
match item.node {
ast::ItemExternCrate(_) => {}
_ => {
visit::walk_item(self, item);
return;
}
}
// Parse the attributes relating to macros.
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()[] {
"phase" => {
self.sess.span_err(attr.span, "#[phase] is deprecated");
}
"plugin" => {
self.sess.span_err(attr.span, "#[plugin] on `extern crate` is deprecated");
self.sess.span_help(attr.span, &format!("use a crate attribute instead, \
i.e. #![plugin({})]",
item.ident.as_str())[]);
}
"macro_use" => {
let names = attr.meta_item_list();
if names.is_none() {
// no names => load all
import = None;
}
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(attr.span, "bad macro import");
}
}
}
}
"macro_reexport" => {
let names = match attr.meta_item_list() {
Some(names) => names,
None => {
self.sess.span_err(attr.span, "bad macro reexport");
continue;
}
};
for attr in names {
if let ast::MetaWord(ref name) = attr.node {
reexport.insert(name.clone(), attr.span);
} else {
self.sess.span_err(attr.span, "bad macro reexport");
}
}
}
_ => used = false,
}
if used {
attr::mark_used(attr);
}
}
self.load_macros(item, import, reexport)
}
fn visit_mac(&mut self, _: &ast::Mac) {
// bummer... can't see macro imports inside macros.
// do nothing.
}
}
impl<'a> MacroLoader<'a> {
fn load_macros<'b>(&mut self,
vi: &ast::Item,
import: Option<MacroSelection>,
reexport: MacroSelection) {
if let Some(sel) = import.as_ref() {
if sel.is_empty() && reexport.is_empty() {
return;
}
}
if !self.span_whitelist.contains(&vi.span) {
self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
the crate root");
return;
}
let macros = self.reader.read_exported_macros(vi);
let mut seen = HashSet::new();
for mut def in macros {
let name = token::get_ident(def.ident);
seen.insert(name.clone());
def.use_locally = match import.as_ref() {
None => true,
Some(sel) => sel.contains_key(&name),
};
def.export = reexport.contains_key(&name);
self.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");
}
}
}
}
......@@ -18,3 +18,4 @@
pub mod csearch;
pub mod loader;
pub mod filesearch;
pub mod macro_import;
......@@ -8,24 +8,19 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Used by `rustc` when loading a plugin, or a crate with exported macros.
//! Used by `rustc` when loading a plugin.
use session::Session;
use metadata::creader::{CrateOrString, CrateReader};
use metadata::creader::CrateReader;
use plugin::registry::Registry;
use std::mem;
use std::env;
use std::dynamic_lib::DynamicLibrary;
use std::collections::{HashSet, HashMap};
use std::borrow::ToOwned;
use syntax::ast;
use syntax::attr;
use syntax::codemap::{Span, COMMAND_LINE_SP};
use syntax::parse::token;
use syntax::ptr::P;
use syntax::visit;
use syntax::visit::Visitor;
use syntax::attr::AttrMetaMethods;
/// Pointer to a registrar function.
......@@ -37,51 +32,17 @@ pub struct PluginRegistrar {
pub args: Vec<P<ast::MetaItem>>,
}
/// Information about loaded plugins.
pub struct Plugins {
/// Imported macros.
pub macros: Vec<ast::MacroDef>,
/// Registrars, as function pointers.
pub registrars: Vec<PluginRegistrar>,
}
pub struct PluginLoader<'a> {
struct PluginLoader<'a> {
sess: &'a Session,
span_whitelist: HashSet<Span>,
reader: CrateReader<'a>,
pub plugins: Plugins,
}
impl<'a> PluginLoader<'a> {
fn new(sess: &'a Session) -> PluginLoader<'a> {
PluginLoader {
sess: sess,
reader: CrateReader::new(sess),
span_whitelist: HashSet::new(),
plugins: Plugins {
macros: vec!(),
registrars: vec!(),
},
}
}
plugins: Vec<PluginRegistrar>,
}
/// Read plugin metadata and dynamically load registrar functions.
pub fn load_plugins(sess: &Session, krate: &ast::Crate,
addl_plugins: Option<Vec<String>>) -> Plugins {
addl_plugins: Option<Vec<String>>) -> Vec<PluginRegistrar> {
let mut loader = PluginLoader::new(sess);
// We need to error on `#[macro_use] extern crate` when it isn't at the
// crate root, because `$crate` won't work properly. Identify these by
// spans, because the crate map isn't set up yet.
for item in &krate.module.items {
if let ast::ItemExternCrate(_) = item.node {
loader.span_whitelist.insert(item.span);
}
}
visit::walk_crate(&mut loader, krate);
for attr in &krate.attrs {
if !attr.check_name("plugin") {
continue;
......@@ -102,156 +63,34 @@ pub fn load_plugins(sess: &Session, krate: &ast::Crate,
}
let args = plugin.meta_item_list().map(ToOwned::to_owned).unwrap_or_default();
loader.load_plugin(CrateOrString::Str(plugin.span, &*plugin.name()),
args);
loader.load_plugin(plugin.span, &*plugin.name(), args);
}
}
if let Some(plugins) = addl_plugins {
for plugin in plugins {
loader.load_plugin(CrateOrString::Str(COMMAND_LINE_SP, &plugin), vec![]);
}
}
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) {
// We're only interested in `extern crate`.
match item.node {
ast::ItemExternCrate(_) => {}
_ => {
visit::walk_item(self, item);
return;
}
}
// 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()[] {
"phase" => {
self.sess.span_err(attr.span, "#[phase] is deprecated");
}
"plugin" => {
self.sess.span_err(attr.span, "#[plugin] on `extern crate` is deprecated");
self.sess.span_help(attr.span, &format!("use a crate attribute instead, \
i.e. #![plugin({})]",
item.ident.as_str())[]);
}
"macro_use" => {
let names = attr.meta_item_list();
if names.is_none() {
// no names => load all
import = None;
}
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(attr.span, "bad macro import");
}
}
}
}
"macro_reexport" => {
let names = match attr.meta_item_list() {
Some(names) => names,
None => {
self.sess.span_err(attr.span, "bad macro reexport");
continue;
}
};
for attr in names {
if let ast::MetaWord(ref name) = attr.node {
reexport.insert(name.clone(), attr.span);
} else {
self.sess.span_err(attr.span, "bad macro reexport");
}
}
}
_ => used = false,
}
if used {
attr::mark_used(attr);
}
loader.load_plugin(COMMAND_LINE_SP, &plugin, vec![]);
}
self.load_macros(item, import, reexport)
}
fn visit_mac(&mut self, _: &ast::Mac) {
// bummer... can't see plugins inside macros.
// do nothing.
}
loader.plugins
}
impl<'a> PluginLoader<'a> {
pub fn load_macros<'b>(&mut self,
vi: &ast::Item,
import: Option<MacroSelection>,
reexport: MacroSelection) {
if let Some(sel) = import.as_ref() {
if sel.is_empty() && reexport.is_empty() {
return;
}
}
if !self.span_whitelist.contains(&vi.span) {
self.sess.span_err(vi.span, "an `extern crate` loading macros must be at \
the crate root");
return;
}
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);
seen.insert(name.clone());
def.use_locally = match import.as_ref() {
None => true,
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");
}
fn new(sess: &'a Session) -> PluginLoader<'a> {
PluginLoader {
sess: sess,
reader: CrateReader::new(sess),
plugins: vec![],
}
}
pub fn load_plugin<'b>(&mut self,
c: CrateOrString<'b>,
args: Vec<P<ast::MetaItem>>) {
let registrar = {
let pmd = self.reader.read_plugin_metadata(c);
pmd.plugin_registrar()
};
fn load_plugin(&mut self, span: Span, name: &str, args: Vec<P<ast::MetaItem>>) {
let registrar = self.reader.find_plugin_registrar(span, name);
if let Some((lib, symbol)) = registrar {
let fun = self.dylink_registrar(c, lib, symbol);
self.plugins.registrars.push(PluginRegistrar {
let fun = self.dylink_registrar(span, lib, symbol);
self.plugins.push(PluginRegistrar {
fun: fun,
args: args,
});
......@@ -259,8 +98,8 @@ pub fn load_plugin<'b>(&mut self,
}
// Dynamically link a registrar function into the compiler process.
fn dylink_registrar<'b>(&mut self,
c: CrateOrString<'b>,
fn dylink_registrar(&mut self,
span: Span,
path: Path,
symbol: String) -> PluginRegistrarFun {
// Make sure the path contains a / or the linker will search for it.
......@@ -272,11 +111,7 @@ fn dylink_registrar<'b>(&mut self,
// inside this crate, so continue would spew "macro undefined"
// errors
Err(err) => {
if let CrateOrString::Krate(cr) = c {
self.sess.span_fatal(cr.span, &err[])
} else {
self.sess.fatal(&err[])
}
self.sess.span_fatal(span, &err[])
}
};
......@@ -288,11 +123,7 @@ fn dylink_registrar<'b>(&mut self,
}
// again fatal if we can't register macros
Err(err) => {
if let CrateOrString::Krate(cr) = c {
self.sess.span_fatal(cr.span, &err[])
} else {
self.sess.fatal(&err[])
}
self.sess.span_fatal(span, &err[])
}
};
......
......@@ -12,11 +12,11 @@
use rustc::session::config::{self, Input, OutputFilenames};
use rustc::session::search_paths::PathKind;
use rustc::lint;
use rustc::metadata;
use rustc::metadata::creader::CrateReader;
use rustc::middle::{stability, ty, reachable};
use rustc::middle::dependency_format;
use rustc::middle;
use rustc::plugin::load::Plugins;
use rustc::plugin::registry::Registry;
use rustc::plugin;
use rustc::util::common::time;
......@@ -409,10 +409,12 @@ pub fn phase_2_configure_and_expand(sess: &Session,
syntax::std_inject::maybe_inject_crates_ref(krate,
sess.opts.alt_std_name.clone()));
let macros = time(time_passes, "macro loading", (), |_|
metadata::macro_import::read_macro_defs(sess, &krate));
let mut addl_plugins = Some(addl_plugins);
let Plugins { macros, registrars }
= time(time_passes, "plugin loading", (), |_|
plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap()));
let registrars = time(time_passes, "plugin loading", (), |_|
plugin::load::load_plugins(sess, &krate, addl_plugins.take().unwrap()));
let mut registry = Registry::new(sess, &krate);
......
// 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.
// force-host
#![feature(plugin_registrar)]
#![deny(plugin_as_library)] // should have no effect in a plugin crate
extern crate macro_crate_test;
extern crate rustc;
use rustc::plugin::Registry;
#[plugin_registrar]
pub fn plugin_registrar(_: &mut Registry) { }
......@@ -16,6 +16,6 @@
#![feature(plugin)]
#![plugin(rlib_crate_test)]
//~^ ERROR: plugin crate `rlib_crate_test` only found in rlib format, but must be available in dylib format
//~^ ERROR: plugin `rlib_crate_test` only found in rlib format, but must be available in dylib format
fn main() {}
// Copyright 2013-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:macro_crate_test.rs
// ignore-stage1
// ignore-cross-compile
//
// macro_crate_test will not compile on a cross-compiled target because
// libsyntax is not compiled for it.
#![deny(plugin_as_library)]
extern crate macro_crate_test; //~ ERROR compiler plugin used as an ordinary library
fn main() { }
// Copyright 2013-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:macro_crate_test.rs
// ignore-stage1
// ignore-cross-compile
//
// macro_crate_test will not compile on a cross-compiled target because
// libsyntax is not compiled for it.
#![deny(plugin_as_library)]
#![feature(plugin)]
#![plugin(macro_crate_test)]
extern crate macro_crate_test; //~ ERROR compiler plugin used as an ordinary library
fn main() {
assert_eq!(1, make_a_1!());
macro_crate_test::foo();
}
// Copyright 2013-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:macro_crate_test.rs
// aux-build:plugin_with_plugin_lib.rs
// ignore-stage1
// ignore-cross-compile
//
// macro_crate_test will not compile on a cross-compiled target because
// libsyntax is not compiled for it.
#![deny(plugin_as_library)]
#![feature(plugin)]
#![plugin(macro_crate_test)]
#![plugin(plugin_with_plugin_lib)]
fn main() {
assert_eq!(1, make_a_1!());
}
......@@ -15,6 +15,7 @@
// macro_crate_test will not compile on a cross-compiled target because
// libsyntax is not compiled for it.
#![allow(plugin_as_library)]
#![feature(plugin)]
#![plugin(macro_crate_test)]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册