├── .gitignore
├── LICENSE
├── README.md
├── demo
├── Cargo.lock
├── Cargo.toml
└── src
│ └── main.rs
├── docs
└── demo.png
├── instru
├── Cargo.toml
├── examples
│ └── main.rs
└── src
│ └── lib.rs
└── instru_plugin
├── Cargo.toml
└── src
└── lib.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Dan Aloni
2 |
3 | Permission is hereby granted, free of charge, to any
4 | person obtaining a copy of this software and associated
5 | documentation files (the "Software"), to deal in the
6 | Software without restriction, including without
7 | limitation the rights to use, copy, modify, merge,
8 | publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software
10 | is furnished to do so, subject to the following
11 | conditions:
12 |
13 | The above copyright notice and this permission notice
14 | shall be included in all copies or substantial portions
15 | of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 | DEALINGS IN THE SOFTWARE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Instru: Instrument Rust code using Rust
2 |
3 | This crate (`instru`), along with changes to nightly Rust (November 10, 2018)
4 | and Cargo, demonstrates the ability to perform whole-program tracing by
5 | applying code injections to the AST (Abstract Syntax Tree) during the compilation
6 | phase to all crates as a preprocessing stage, without modifying their sources.
7 |
8 | Here's an example of a program self-tracing itself:
9 |
10 |
11 |
12 | The instrumented program crate in the `demo` directory is similar to one of
13 | `smallvec`'s [examples](demo/src/main.rs), and it makes no direct referencs
14 | to `instru`.
15 |
16 | To demo the use of `instru`, modified versions of Rust nightly and Cargo are
17 | required. The `instru_plugin` implements a special compiler plugin, and `instru`
18 | implements the code that is called by the code that the plugin adds to the AST.
19 |
20 |
21 | ## Rust changes
22 |
23 | [da-x's Rust instru Branch](https://github.com/da-x/rust/tree/instru)
24 |
25 | The content of this branch over Rust nightly contain the following changes:
26 |
27 | * Implementation of the `function!` macro, which returns a static `&str` which
28 | tells the name of the current module's function. There's currently an [open
29 | RFC on the subject](https://github.com/rust-lang/rfcs/issues/1743).
30 |
31 | * The addition of a whole-crate AST transformation plugin API. The
32 | `WholeCrateTransformers` API allows a `ast::Crate -> ast::Crate` function to
33 | operate on the Crate, before and/or after macro expansion, adding or removing
34 | code all over.
35 |
36 |
37 | ## Cargo changes
38 |
39 | [da-x's Cargo instru Branch](https://github.com/da-x/cargo/tree/instru)
40 |
41 | For top level programs:
42 |
43 | * The addition of a `active-plugins` config to a profile. This is a list of strings.
44 |
45 | For plugin crates:
46 |
47 | * The addition of a `plugin-recursive-until-crates` to `[lib]`. It's a list of crate names.
48 |
49 | * The addition of a `plugin-dependencies` to `[lib]`. It's a list of dependencies.
50 |
51 |
52 | ### Remarks:
53 |
54 | * For each crate on which the plugin is activated, `plugin-dependencies` gives the
55 | list of dependencies that would be implicitly added to the compilation of the
56 | crate. These would be called henceforth "plugin support code". This would be code
57 | that is called by the code that is added to the crate's AST by the plugin. These
58 | dependencies will be added as implicit dependencies of all dependent crates (expect
59 | those coming from Rust, e.g.: `std`), on which the plugin activates.
60 |
61 | * If a crate's name is in `plugin-recursive-until-crates`, the propagation
62 | of the plugin's activation in the dependency tree stops. This limits the number of
63 | build variations of the same crate inside a single execution, so that there would be
64 | some crates that link a single time, both to the plugin's support code and the
65 | main executable.
66 |
67 | * Due to these changes, it is possible that the same crate of the same version
68 | will be built and linked twice into the executable. One build will be _without_ the
69 | activation of the plugin, and the other _with_ the activation of the plugin. The reason is
70 | that the plugin support code may depend on the same crate as the program on which
71 | it is activated, either indirectly or directly.
72 |
73 | # Notes
74 |
75 | Feel free to contact me regarding anything related to this work.
76 |
--------------------------------------------------------------------------------
/demo/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "ansi_term"
3 | version = "0.11.0"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "demo"
11 | version = "0.1.0"
12 | dependencies = [
13 | "instru_plugin 0.1.0",
14 | "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "difference"
19 | version = "2.0.0"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "instru"
24 | version = "0.1.0"
25 | dependencies = [
26 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
27 | "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
28 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
29 | "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
30 | ]
31 |
32 | [[package]]
33 | name = "instru_plugin"
34 | version = "0.1.0"
35 | dependencies = [
36 | "instru 0.1.0",
37 | ]
38 |
39 | [[package]]
40 | name = "lazy_static"
41 | version = "1.2.0"
42 | source = "registry+https://github.com/rust-lang/crates.io-index"
43 |
44 | [[package]]
45 | name = "smallvec"
46 | version = "0.6.5"
47 | source = "registry+https://github.com/rust-lang/crates.io-index"
48 | dependencies = [
49 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
50 | ]
51 |
52 | [[package]]
53 | name = "unreachable"
54 | version = "1.0.0"
55 | source = "registry+https://github.com/rust-lang/crates.io-index"
56 | dependencies = [
57 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
58 | ]
59 |
60 | [[package]]
61 | name = "void"
62 | version = "1.0.2"
63 | source = "registry+https://github.com/rust-lang/crates.io-index"
64 |
65 | [[package]]
66 | name = "winapi"
67 | version = "0.3.6"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | dependencies = [
70 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
71 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
72 | ]
73 |
74 | [[package]]
75 | name = "winapi-i686-pc-windows-gnu"
76 | version = "0.4.0"
77 | source = "registry+https://github.com/rust-lang/crates.io-index"
78 |
79 | [[package]]
80 | name = "winapi-x86_64-pc-windows-gnu"
81 | version = "0.4.0"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 |
84 | [metadata]
85 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
86 | "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
87 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
88 | "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d"
89 | "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
90 | "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
91 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
92 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
93 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
94 |
--------------------------------------------------------------------------------
/demo/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "demo"
3 | version = "0.1.0"
4 | authors = ["Dan Aloni "]
5 |
6 | [dependencies]
7 | smallvec = "*"
8 | instru_plugin = { path = "../instru_plugin" }
9 |
10 | [profile.dev]
11 | active-plugins = ["instru_plugin"]
12 |
--------------------------------------------------------------------------------
/demo/src/main.rs:
--------------------------------------------------------------------------------
1 | extern crate smallvec;
2 |
3 | use smallvec::SmallVec;
4 |
5 | fn main()
6 | {
7 | println!("Hello world!");
8 | let mut v = SmallVec::<[u8; 4]>::new(); // initialize an empty vector
9 |
10 | // The vector can hold up to 4 items without spilling onto the heap.
11 | v.extend(0..4);
12 | assert_eq!(v.len(), 4);
13 | assert!(!v.spilled());
14 |
15 | // Pushing another element will force the buffer to spill:
16 | v.push(4);
17 | assert_eq!(v.len(), 5);
18 | assert!(v.spilled());
19 | println!("Hello world again!");
20 | }
21 |
--------------------------------------------------------------------------------
/docs/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/da-x/instru/c53f729b0b2aadc5dd82f6ad2d470c73573216f6/docs/demo.png
--------------------------------------------------------------------------------
/instru/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "instru"
3 | version = "0.1.0"
4 | authors = ["Dan Aloni "]
5 |
6 | [dependencies]
7 | ansi_term = "0.11"
8 | difference = "2.0"
9 | smallvec = "0.6"
10 | lazy_static = "1.2"
11 |
--------------------------------------------------------------------------------
/instru/examples/main.rs:
--------------------------------------------------------------------------------
1 | extern crate instru;
2 |
3 | use instru::*;
4 |
5 | fn main() {
6 | let x = Wrapper::new(Class::Fn, "fnname", "modpath::path", "examples/main.rs", 1);
7 | let y = Wrapper::new(Class::Fn, "fnname", "modpath::path", "examples/main.rs", 2);
8 | let z = Wrapper::new(Class::Fn, "fnname", "modpath::path", "examples/main.rs", 3);
9 | let o = Wrapper::new(Class::Fn, "otherfn", "modpath::path", "examples/main.rs", 4);
10 | }
11 |
--------------------------------------------------------------------------------
/instru/src/lib.rs:
--------------------------------------------------------------------------------
1 | extern crate ansi_term;
2 | extern crate difference;
3 | extern crate smallvec;
4 | extern crate lazy_static;
5 |
6 | use ansi_term::*;
7 | use difference::{Changeset, Difference};
8 | use lazy_static::lazy_static;
9 | use smallvec::SmallVec;
10 | use std::cell::UnsafeCell;
11 | use std::collections::HashMap;
12 | use std::sync::Arc;
13 | use std::sync::Mutex;
14 |
15 | struct ThreadState {
16 | indent: u32,
17 | prev_indent: u32,
18 | prev_str: Option,
19 | sources: HashMap>>,
20 | }
21 |
22 | lazy_static! {
23 | static ref FILES: Mutex>>> = Mutex::new(HashMap::new());
24 | }
25 |
26 | impl ThreadState {
27 | fn new() -> Self {
28 | Self {
29 | indent: 0,
30 | prev_indent: 0,
31 | prev_str: None,
32 | sources: HashMap::new(),
33 | }
34 | }
35 |
36 | fn get_line(&mut self, filename: &'static str, line_nr: u32) -> String {
37 | if line_nr == 0 {
38 | panic!("line number 0 is not valid");
39 | }
40 |
41 | if let Some(local_source) = self.sources.get(filename) {
42 | let lines = local_source.len();
43 | if line_nr as usize > lines {
44 | panic!("invalid line number {} for {}", line_nr, filename);
45 | }
46 | return local_source[line_nr as usize - 1].clone();
47 | }
48 |
49 | let mut files = FILES.lock().unwrap();
50 | let entry = files.entry(String::from(filename));
51 |
52 | let source = entry.or_insert_with(|| {
53 | let open_file = std::fs::File::open(filename).unwrap();
54 | let reader = std::io::BufReader::new(&open_file);
55 | use std::io::BufRead;
56 | let lines : Vec<_> = reader.lines().map(|x|x.unwrap()).collect();
57 | Arc::new(lines)
58 | });
59 |
60 | self.sources.insert(String::from(filename), source.clone());
61 |
62 | self.get_line(filename, line_nr)
63 | }
64 | }
65 |
66 | thread_local! {
67 | static STATE: UnsafeCell = UnsafeCell::new(ThreadState::new());
68 | }
69 |
70 | #[derive(Clone, Copy)]
71 | pub enum Class {
72 | Fn,
73 | Stmt,
74 | Block,
75 | }
76 |
77 | pub enum Pos {
78 | Enter,
79 | At,
80 | Leave,
81 | }
82 |
83 | pub struct Wrapper {
84 | class: Class,
85 | name: &'static str,
86 | modpath: &'static str,
87 | file: &'static str,
88 | line_nr: u32,
89 | }
90 |
91 | impl Wrapper {
92 | fn specifier(&self, pos: Pos) -> String {
93 | let line_nr = match pos {
94 | Pos::Enter => {
95 | format!(":(->{})", self.line_nr)
96 | }
97 | Pos::At => {
98 | format!(":(@@{})", self.line_nr)
99 | }
100 | Pos::Leave => {
101 | format!(":(<-{})", self.line_nr)
102 | }
103 | };
104 | match self.class {
105 | Class::Fn =>
106 | format!("{}:{}:{}{}",
107 | self.modpath, self.name,
108 | match pos {
109 | Pos::Enter => {
110 | "()"
111 | }
112 | Pos::At => {
113 | ""
114 | }
115 | Pos::Leave => {
116 | "<-"
117 | }
118 | },
119 | line_nr),
120 | Class::Stmt =>
121 | format!("{}:{}:{}",
122 | self.modpath, self.name, line_nr),
123 | Class::Block =>
124 | format!("{}:{}:{}",
125 | self.modpath, self.name, line_nr),
126 | }
127 | }
128 |
129 | pub fn new(class: Class,
130 | name: &'static str,
131 | modpath: &'static str,
132 | file: &'static str,
133 | line_nr: u32) -> Self
134 | {
135 | let s = Self {
136 | name, modpath, file, line_nr, class
137 | };
138 |
139 | match class {
140 | Class::Stmt => {
141 | s.print(Pos::At);
142 | }
143 | Class::Fn | Class::Block => {
144 | s.print_enter();
145 | STATE.with(|state| {
146 | let state = unsafe { &mut *state.get() };
147 |
148 | state.indent += 1;
149 | });
150 | }
151 | }
152 |
153 | s
154 | }
155 |
156 | const MARGIN: usize = 50;
157 |
158 | fn print(&self, pos: Pos) {
159 | STATE.with(|state| {
160 | let state = unsafe { &mut *state.get() };
161 | let specifier = self.specifier(pos);
162 | let width = if specifier.len() <= Wrapper::MARGIN { Wrapper::MARGIN - specifier.len() } else {0};
163 | let prev_indent = state.prev_indent;
164 | state.prev_indent = state.indent;
165 |
166 | print!("[{}] ", {
167 | if prev_indent == state.indent {
168 | Colour::RGB(222, 222, 222)
169 | } else if prev_indent < state.indent {
170 | Colour::RGB(0, 222, 0)
171 | } else {
172 | Colour::RGB(0, 122, 0)
173 | }
174 | } .paint(format!("{:2}", state.indent)));
175 |
176 | if let Some(prev_str) = &state.prev_str {
177 | let changeset = Changeset::new(prev_str, specifier.as_str(), "");
178 | let mut vec : SmallVec<[_; 0x20]> = SmallVec::new();
179 | let mut nr_changes = 0;
180 | for i in &changeset.diffs {
181 | let fn_change = move |x| Colour::White.bold().paint(x);
182 | let fn_same = move |x| Colour::RGB(120, 120, 120).paint(x);
183 | match i {
184 | Difference::Same(x) => {
185 | if nr_changes > 0 {
186 | vec.push(fn_change(x))
187 | } else {
188 | vec.push(fn_same(x))
189 | }
190 | }
191 | Difference::Rem(_) => {
192 | nr_changes += 1;
193 | }
194 | Difference::Add(x) => {
195 | vec.push(fn_change(x));
196 | nr_changes += 1;
197 | }
198 | }
199 | }
200 | print!("{}{:-width$}{}", ANSIStrings(vec.as_slice()), "", ":", width=width);
201 | } else {
202 | print!("{}{:-width$}{}", specifier, "", ":", width=width);
203 | }
204 |
205 | print!(" {}", state.get_line(self.file, self.line_nr));
206 |
207 | state.prev_str = Some(specifier);
208 | });
209 | println!();
210 | }
211 |
212 | fn print_enter(&self) {
213 | self.print(Pos::Enter);
214 | }
215 |
216 | fn print_leave(&self) {
217 | self.print(Pos::Leave);
218 | }
219 | }
220 |
221 | impl Drop for Wrapper {
222 | fn drop(&mut self) {
223 | match self.class {
224 | Class::Stmt => {}
225 | Class::Fn | Class::Block => {
226 | STATE.with(|state| {
227 | let state = unsafe { &mut *state.get() };
228 |
229 | state.indent -= 1;
230 | });
231 | self.print_leave();
232 | }
233 | }
234 | }
235 | }
236 |
237 | pub fn statement(
238 | name: &'static str,
239 | modpath: &'static str,
240 | file: &'static str,
241 | line_nr: u32)
242 | {
243 | let _ = Wrapper::new(Class::Stmt, name, modpath, file, line_nr);
244 | }
245 |
--------------------------------------------------------------------------------
/instru_plugin/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "instru_plugin"
3 | version = "0.1.0"
4 | authors = ["Dan Aloni "]
5 |
6 | [lib]
7 | crate-type = [ "dylib" ]
8 | plugin = true
9 | plugin-recursive-until-crates = [ "instru", "other-dep" ]
10 |
11 | [lib."plugin-dependencies"]
12 | instru = { path = "../instru" }
13 |
--------------------------------------------------------------------------------
/instru_plugin/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![feature(plugin_registrar, quote, rustc_private, custom_attribute)]
2 |
3 | extern crate rustc_plugin;
4 | extern crate syntax;
5 |
6 | use rustc_plugin::registry::Registry;
7 |
8 | use syntax::ast::*;
9 | use syntax::source_map::{Span};
10 | use syntax::ext::base::{ExtCtxt};
11 | use syntax::fold::Folder;
12 | use syntax::ptr::P;
13 | use syntax::ext::base::{WholeCrateTransformer, WholeCrateTransformation};
14 |
15 | struct Trans {
16 | }
17 |
18 | struct PosWrapper<'a, 'b: 'a> {
19 | _cx: &'a mut ExtCtxt<'b>,
20 | span: Span,
21 | }
22 |
23 | impl<'a, 'b> Folder for PosWrapper<'a, 'b> {
24 | fn fold_expr(&mut self, e: P) -> P {
25 | e.map(|e| {
26 | let mut e = syntax::fold::noop_fold_expr(e, self);
27 | e.span = self.span;
28 | e
29 | })
30 | }
31 |
32 | fn fold_mac(&mut self, mac: Mac) -> Mac {
33 | mac
34 | }
35 | }
36 |
37 | struct CodeWrapper<'a, 'b: 'a> {
38 | cx: &'a mut ExtCtxt<'b>
39 | }
40 |
41 |
42 | impl<'a, 'b> Folder for CodeWrapper<'a, 'b> {
43 | fn fold_crate(&mut self, b: Crate) -> Crate {
44 | let Crate { mut module, attrs, span } = syntax::fold::noop_fold_crate(b, self);
45 |
46 | match quote_item!(self.cx, extern crate instru;) {
47 | Some(item) => {
48 | module.items.insert(0, item);
49 | },
50 | None => {},
51 | }
52 |
53 | Crate {
54 | module,
55 | attrs,
56 | span
57 | }
58 | }
59 |
60 | fn fold_block(&mut self, b: P) -> P {
61 | let block = syntax::fold::noop_fold_block(b, self).into_inner();
62 | let Block {id, stmts, rules, span, recovered} = block;
63 |
64 | let block_instru_stmt = quote_stmt!(self.cx,
65 | let ___instru_v = ::instru::Wrapper::new(
66 | ::instru::Class::Block, function!(), module_path!(), file!(), line!())
67 | ).unwrap();
68 |
69 | let mut new_stmts = Vec::new();
70 | let mut stmt_idx = 0;
71 |
72 | if stmt_idx == 0 {
73 | let mut pos_wrapper = PosWrapper { _cx: self.cx, span: span };
74 | for stmt in pos_wrapper.fold_stmt(block_instru_stmt) {
75 | new_stmts.push(stmt);
76 | }
77 | }
78 |
79 | for stmt in stmts.into_iter() {
80 | stmt_idx += 1;
81 |
82 | let instru_stmt = quote_stmt!(self.cx,
83 | ::instru::statement(function!(), module_path!(), file!(), line!())
84 | ).unwrap();
85 |
86 | let mut pos_wrapper = PosWrapper { _cx: self.cx, span: stmt.span };
87 | for stmt in pos_wrapper.fold_stmt(instru_stmt) {
88 | new_stmts.push(stmt);
89 | }
90 |
91 | new_stmts.push(stmt);
92 | }
93 |
94 | P(Block {
95 | id,
96 | stmts: new_stmts,
97 | rules,
98 | span,
99 | recovered,
100 | })
101 | }
102 |
103 | fn fold_mac(&mut self, mac: Mac) -> Mac {
104 | mac
105 | }
106 | }
107 |
108 | impl WholeCrateTransformer for Trans {
109 | fn transform_before_expansion(&self, cx: &mut ExtCtxt, krate: Crate) -> Crate {
110 | let mut f = CodeWrapper { cx };
111 | f.fold_crate(krate)
112 | }
113 | }
114 |
115 | #[plugin_registrar]
116 | pub fn plugin_registrar(reg: &mut Registry) {
117 | // println!("rustc plugin registrar called");
118 | reg.register_whole_crate_transformation(WholeCrateTransformation {
119 | cb: Box::new(Trans { })
120 | });
121 | }
122 |
123 |
--------------------------------------------------------------------------------