Skip to content

Macro/plugin cleanup #22241

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

Merged
merged 3 commits into from
Feb 17, 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
10 changes: 10 additions & 0 deletions src/doc/trpl/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 43 additions & 1 deletion src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/librustc/lint/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ impl LintStore {
Stability,
UnconditionalRecursion,
InvalidNoMangleItems,
PluginAsLibrary,
);

add_builtin_with_new!(sess,
Expand Down
76 changes: 29 additions & 47 deletions src/librustc/metadata/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -451,21 +449,7 @@ impl<'a> CrateReader<'a> {
}).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;
Expand Down Expand Up @@ -517,30 +501,21 @@ impl<'a> CrateReader<'a> {
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
Expand All @@ -558,7 +533,7 @@ impl<'a> PluginMetadata<'a> {
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,
Expand All @@ -572,28 +547,35 @@ impl<'a> PluginMetadata<'a> {
}

/// 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
Expand Down
Loading