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

RFC: Generalize #[macro_registrar] to #[plugin_registrar] #86

Merged
merged 4 commits into from
Jun 4, 2014
Merged
Changes from 1 commit
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
80 changes: 80 additions & 0 deletions active/0000-plugin-registrar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
- Start Date: 2014-05-22
- RFC PR #:
- Rust Issue #:

# Summary

Generalize the `#[macro_registrar]` feature so it can register other kinds of compiler plugins.

# Motivation

I want to implement [loadable lints](https://github.com/mozilla/rust/issues/14067) and use them for project-specific static analysis passes in Servo. Landing this first will allow more evolution of the plugin system without breaking source compatibility for existing users.

# Detailed design

To register a procedural macro in current Rust:

~~~ .rs
use syntax::ast::Name;
use syntax::parse::token;
use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT};

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
register(token::intern("named_entities"),
NormalTT(box BasicMacroExpander {
expander: named_entities::expand,
span: None
},
None));
}
~~~

I propose an interface like

~~~ .rs
use syntax::parse::token;
use syntax::ext::Registry;
use syntax::ext::base::{BasicMacroExpander, NormalTT};

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro(token::intern("named_entities"),
NormalTT(box BasicMacroExpander {
expander: named_entities::expand,
span: None
},
None));
}
~~~

Then the struct `Registry` could provide additional methods such as `register_lint` as those features are implemented.

It could also provide convenience methods:

~~~ .rs
use syntax::ext::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_simple_macro("named_entities", named_entities::expand);
}
~~~

# Drawbacks

Breaking change for existing procedural macros.

More moving parts.

# Alternatives

We could add `#[lint_registrar]` etc. alongside `#[macro_registrar]`. This seems like it will produce more duplicated effort all around. It doesn't provide convenience methods, and it won't support API evolution as well.

# Unresolved questions

Naming bikeshed.

Should `Registry` be provided by `libsyntax`, when it's used to register more than just syntax extensions?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Whatever library provides it will need to be linked by all syntax extensions. If it stays in libsyntax, that seems odd for a lint registry, although lints presumably need to use the AST and therefore need to link against libsyntax anyway.

Of course, supporting lints also poses a problem. I haven't really looked at them before, but they apparently use a Context type defined in librustc. Since librustc links against libsyntax, libsyntax can't very well link against librustc. Extracting the registry to a separate library doesn't help, because it would have to link against librustc to get the same Context type, and librustc would have to link against it.

I see a few options here:

  1. Go with the alternative of a separate registrar per type. That's kind of crappy though.
  2. Extract rustc::middle::lint::Context out of librustc and put it in libsyntax or a brand new library. This probably isn't doable, it has a few other librustc types embedded in it.
  3. Define the registry in a separate library and use trait magic to make it generic. Both librustc and libsyntax can then provide trait implementations for lints and syntax extensions.

The third is the most complicated, but allows this little library to stand on its own, and for syntax extensions to only link against either libsyntax or librustc as needed.


What set of convenience methods should we provide?