diff --git a/src/doc/trpl/plugins.md b/src/doc/trpl/plugins.md index 3dea1a66ffd7..79502f3cd17f 100644 --- a/src/doc/trpl/plugins.md +++ b/src/doc/trpl/plugins.md @@ -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 diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index a415ff3ed716..74e921acdb40 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -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}; @@ -1963,6 +1963,48 @@ impl LintPass for UnconditionalRecursion { } } +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, diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 616af79326d9..42a6861f452a 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -214,6 +214,7 @@ impl LintStore { Stability, UnconditionalRecursion, InvalidNoMangleItems, + PluginAsLibrary, ); add_builtin_with_new!(sess, diff --git a/src/test/auxiliary/plugin_with_plugin_lib.rs b/src/test/auxiliary/plugin_with_plugin_lib.rs new file mode 100644 index 000000000000..cfc8c015324d --- /dev/null +++ b/src/test/auxiliary/plugin_with_plugin_lib.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , 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) { } diff --git a/src/test/compile-fail-fulldeps/plugin-as-extern-crate.rs b/src/test/compile-fail-fulldeps/plugin-as-extern-crate.rs new file mode 100644 index 000000000000..c5169b61a2bf --- /dev/null +++ b/src/test/compile-fail-fulldeps/plugin-as-extern-crate.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , 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() { } diff --git a/src/test/compile-fail-fulldeps/plugin-plus-extern-crate.rs b/src/test/compile-fail-fulldeps/plugin-plus-extern-crate.rs new file mode 100644 index 000000000000..3dfd8838ebec --- /dev/null +++ b/src/test/compile-fail-fulldeps/plugin-plus-extern-crate.rs @@ -0,0 +1,27 @@ +// 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 or the MIT license +// , 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(); +} diff --git a/src/test/run-pass-fulldeps/plugin-lib-ok-in-plugin.rs b/src/test/run-pass-fulldeps/plugin-lib-ok-in-plugin.rs new file mode 100644 index 000000000000..c612ee75651b --- /dev/null +++ b/src/test/run-pass-fulldeps/plugin-lib-ok-in-plugin.rs @@ -0,0 +1,26 @@ +// 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 or the MIT license +// , 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!()); +} diff --git a/src/test/run-pass-fulldeps/plugin-plus-extern-crate.rs b/src/test/run-pass-fulldeps/plugin-plus-extern-crate.rs index 0c27dba9c627..d1ce83f26778 100644 --- a/src/test/run-pass-fulldeps/plugin-plus-extern-crate.rs +++ b/src/test/run-pass-fulldeps/plugin-plus-extern-crate.rs @@ -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)]