Questions tagged [rust-compiler-plugin]

Rust compiler plugins are user-provided libraries that extend the compiler's behavior with new syntax extensions, compile time and lint checks, etc. Questions pertaining to creating and designing Rust compiler plugins go here.

Rust compiler (rustc) can load various dynamic libraries to extend the existing language syntax - with things like: lints, compile time checking expressions, etc.

Compiler plugins are dynamic libraries that are loaded by calling the following Rust crate attributes:

#![feature(plugin)] // Enable plugin feature game
#![plugin(my_compiler_plugin)]  //Instantiate my_compiler_plugin

In the most of cases, a plugin should only be used through #![my_compiler_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.

#Procedural macros#

One way compiler plugins can be extended is by using procedural macros. Procedural macros are similar to ordinary macros, except they can change syntax tree during compile time. Using this approach, one can make a macro that is syntactically verified during compilation.

For example we wanted to add a macro that converts roman numerals to decimal, and checks for number correctness during compilation, we'd write something like in roman_numerals.rs:

#![crate_type="dylib"]
#![feature(plugin_registrar, rustc_private)]
#![feature(slice_patterns)]

extern crate syntax;
extern crate rustc;

use syntax::codemap::Span;
use syntax::ast::{TokenTree, TtToken};
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
use syntax::ext::build::AstBuilder;  // trait for expr_usize
use rustc::plugin::Registry;

fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree])
        -> Box<MacResult + 'static> {

    static NUMERALS: &'static [(&'static str, usize)] = &[
        ("M", 1000), ("CM", 900), ("D", 500), ("CD", 400),
        ("C",  100), ("XC",  90), ("L",  50), ("XL",  40),
        ("X",   10), ("IX",   9), ("V",   5), ("IV",   4),
        ("I",    1)];

    let text = match args {
        [TtToken(_, token::Ident(s, _))] => s.to_string(),
        _ => {
            cx.span_err(sp, "argument should be a single identifier");
            return DummyResult::any(sp);
        }
    };

    let mut text = &*text;
    let mut total = 0;
    while !text.is_empty() {
        match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) {
            Some(&(rn, val)) => {
                total += val;
                text = &text[rn.len()..];
            }
            None => {
                cx.span_err(sp, "invalid Roman numeral");
                return DummyResult::any(sp);
            }
        }
    }

    MacEager::expr(cx.expr_usize(sp, total))
}


#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("rn", expand_rn);
}

Note the #[plugin_registar] functions that adds plugin as a registered macro. Now, we can use Roman numeral procedural macro (rn!) by writing:

#![feature(plugin)]
#![plugin(roman_numerals)]

fn main() {
    assert_eq!(rn!(MMXV), 2015);
}

#Lints

Another field where syntax extensions are required is providing additional lints for rustc. For example, let's say you want to define a simple lint that notices functions called lintme and issues a warning if it encounters them.

The basic structure of it looks something like this (for full example go here):

declare_lint!(TEST_LINT, Warn,
              "Warn about items named 'lintme'");

struct Pass;


impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(TEST_LINT)
    }
}

impl EarlyLintPass for Pass {
    fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) {
        if it.ident.name == "lintme" {
            cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'");
        }
    }
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_early_lint_pass(box Pass as EarlyLintPassObject);
}

Calling code like

#![plugin(lint_plugin_test)]

fn lintme() { }

produces a compiler warning:

foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default
foo.rs:4 fn lintme() { }
         ^~~~~~~~~~~~~~~

Important part of lint definition are:

  • one or more declare_lint! invocations, which define static Lint structs;
  • a struct holding any state needed by the lint pass (here, there are no such states);
  • a LintPass implementation defining how to check each syntax element. A single LintPass may call span_lint for several different Lints, but should register them all through the get_lints method.

Instability

All crates using plugins are (due to reliance on rustc API which is unstable) restricted to only run on rust nightly. This may be offset by using a user made replacement for libsyntax (e.g. syntex )

###More information:

15 questions
17
votes
1 answer

Why isn't `regex!` a wrapper for `Regex::new` to offer the same regex matching speed?

The Rust Regex crate offers the regex! syntax extension which makes it possible to compile a regex during the standard compile time. This is good in two ways: we don't need to do that work during runtime (better program performance) if our regex is…
Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
6
votes
0 answers

What is the proper way to create a Span when inserting a new field with a Rust compiler plugin?

I want a compiler plugin to annotate a structure with some information. For example, the original struct has only one field: struct X { x: i32 } And I want to add another field: struct X { x: i32, y: MARKTYPE } As I looked into Rust compiler…
Qoros
  • 493
  • 7
  • 16
6
votes
1 answer

How to write a Rust compiler plugin that generates a module?

I am writing a Rust compiler plugin that expands choose! { test_a test_b } to #[cfg(feature = "a")] mod test_a; #[cfg(feature = "b")] mod test_b; It is almost done now, but the module contains nothing in the finally expanded code. I guess…
knight42
  • 910
  • 10
  • 15
6
votes
1 answer

Can I use compiler plugins on stable Rust?

The thing I'm trying to make requires plugin compiler, because I need error reporting at compile time, however I'd like for it to run on Rust stable and not just nightly. Is there a way to run compiler plugins on stable Rust?
Daniel Fath
  • 16,453
  • 7
  • 47
  • 82
5
votes
1 answer

How to add custom llvm pass into rustc

I am trying to add my llvm pass into Rustc. Rustc has one compiling option -C passes=val where we could add extra LLVM passes to run. However, as my try, this option can only accept the pass when the pass code is placed inside the LLVM code tree,…
Jerry
  • 51
  • 2
5
votes
1 answer

How to modify all items of a crate in a compiler plugin?

I'm trying to build a syntax extension that expands an attribute into calls. Before: #[flame] fn flamed() { .. } After: fn flamed() { flame::start_guard("flamed"); .. } This already works. However, I'd also like it to work if I have…
llogiq
  • 13,815
  • 8
  • 40
  • 72
3
votes
1 answer

Is there any way to track the monomorphization process of generics?

I find it is untrackable by just reading the plain source code, when the generic relationship is convoluted. Is there any way to disclose the compiler's instantiation trajectory of a particular generic struct or function? Elaborated: Thanks for…
Tim
  • 99
  • 1
  • 5
3
votes
1 answer

What's the difference between a macro and a compiler plugin?

What can a macro do that a compiler plugin can not, and vice-versa? By "can not", I mean that it is impossible to achieve the same purpose, not that it can be achieved in a different way. For example, a macro can impl some trait for a struct,…
Feng Cen
  • 179
  • 1
  • 6
3
votes
1 answer

How to document macros from a Rust compiler plugin?

I notice that compiler plugins frequently provide macros that the documentation wont even mention. They're registered and created programmatically rather than being defined in a syntax rustdoc recognizes. Naturally, no documentation can be…
mako
  • 1,201
  • 14
  • 30
3
votes
1 answer

When writing a syntax extension, can I look up information about types other than the annotated type?

I would like to write a syntax extension that combines information from a related type when generating a new function. As a nonsense example, pretend I have this code: struct Monster { health: u8, } impl Monster { fn health(&self) {…
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
2
votes
1 answer

How to get struct fields and fields type in a compiler plugin?

I want to generate a HashMap which use struct fields as key, and use usize integer as value. pub struct Article { title: String, content: String, category: String, comments: Vec } pub struct Comment { content:…
Feng Cen
  • 179
  • 1
  • 6
2
votes
1 answer

How can I get original AST by trait identifier with syntax extensions?

I've gotten stuck, when I tried to implement traits programmatically using syntax extensions. I wrote a minimal example here. I hope someone can help (or point me in the right direction). // Just some methods collection I want to implement…
3Hren
  • 23
  • 3
1
vote
1 answer

Rustc compile without tmp dir

Is it possible to compile rust code without a tmp dir? i.e. rustc -C opt-level=3 -o "$DEST" "$@" i'm getting the following error: Compiling failed with exitcode 1, compiler output: error: couldn't create a temp dir: No such file or directory (os…
Rusty Rob
  • 16,489
  • 8
  • 100
  • 116
1
vote
0 answers

Can I build a compiler plugin with a post processor to generate code from previous collected derived structs?

As far as I know current usage for compiler plugin is to define the attribute for compiler to recognize and then the compiler will invoke the code defined and registered in the plugin. I am thinking if it is possible to build a compiler plugin that…
Shisoft
  • 4,197
  • 7
  • 44
  • 61
0
votes
0 answers

How do I get the Rust TypeAlias identifier from the Metadata of an LLVM IR file compiled with `rustc`?

My Problem I would like to get Rust TypeAlias identifiers such as MyU64 in the following code from LLVM IR code compiled with rustc. Is this possible? type MyU64 = u64; fn main() { let my_u64: MyU64 = 987; println!("{}", my_u64); } I know…
woxjro
  • 1
  • 1