├── .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 | --------------------------------------------------------------------------------