├── .gitignore ├── Cargo.toml ├── .travis.yml ├── src ├── main.rs ├── macro_expansion.rs ├── lib.rs └── rustc_server.rs ├── LICENSE ├── azure-pipelines.yml ├── README.md └── tests └── expansion_correctness_tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "proc_macro_expander" 3 | version = "0.1.0" 4 | authors = ["roma"] 5 | 6 | [dependencies] 7 | #dylib = "0.0.3" 8 | quote = "1.0.2" 9 | goblin = "0.0.24" 10 | serde_derive = "1.0.0" 11 | serde = "1.0.0" 12 | serde_json = "1.0.0" 13 | libloading = "0.5.2" 14 | sharedlib = "7.0.0" 15 | 16 | [dependencies.syn] 17 | version = "1.0.5" 18 | features = ["full", "parsing"] 19 | 20 | [dependencies.proc-macro2] 21 | version = "1.0.5" #"0.4.24" 22 | features = ["nightly"] 23 | 24 | [dev-dependencies] 25 | tempfile = "3" 26 | assert_matches = "1.3.0" -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | matrix: 3 | - RUSTFLAGS='--cfg procmacro2_semver_exempt' 4 | os: 5 | - linux 6 | - windows 7 | language: rust 8 | rust: 9 | - nightly 10 | deploy: 11 | provider: releases 12 | api_key: 13 | secure: BqI5K4mI17Ems0RiB/+HmGegoGlNBsUFJ7H9KlYAD0w3uxoErV3ffYnIFRhsr/G1SxoeMDfsnqzW+TsqtMVWO0xfdpk5+TEamFgQCRwrYbIjDzqJmP48nQEgNWB8g5CBwuFqVyq7KtsrtIBlC0XifM3JRu//TPqWQQ4avVVJbUOk46YI+N4fByesnV8s+Ci3PIewW6y4E6omJSU5Im2ZUYdibd1jwqYGYazpMEJ4H4oSVeB+hs/decTGzITc4FH+9FISFYowYulDq9TFxeSDmek88TrLCVuyeVpCqsfpTpX9EJNKBx51YSmY0Og/HiDeh8KRBNZsdD4o+wbGB8MUMMsRoE2Oy8qUXg9Ki7e7nSjZEkYX8ZiQx7689R5vmZN4Q+cDEOw5kWGWWMsLOamVhK/wd3ARh9NNuwog0pqsGCxLLuevWGNHOPyX/KghjwDS4OV3WFw9ZjbtQAK17TTKLqtnWFMql+1oVftYbcK1W1B9bRUT8yZsnVYQAl2Klr2xipgP6tt34J+67gXXeqviGGiQGMEfkfADORQRnrJ6Rvh9YVgzyN6qb6SnJfp25DJgrPSu+gI7BDoBzW8+PyO0wfo5x+95V5LoqWvNcbPiaD66VUNCWHJ51cBhRrMXkF/Boi9UW67+KmwXm30DCy8jvSDzGlyvXMIxZGFRISTGLZ8= 14 | skip_cleanup: true 15 | on: 16 | tags: true 17 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_internals)] 2 | #![feature(proc_macro_span)] 3 | #![feature(proc_macro_diagnostic)] 4 | extern crate proc_macro_expander; 5 | 6 | use std::io::Read; 7 | 8 | use proc_macro_expander::macro_expansion::{ExpansionTask, ExpansionResult}; 9 | 10 | fn read_stdin() -> String { 11 | let mut buff = String::new(); 12 | std::io::stdin() 13 | .read_to_string(&mut buff) 14 | .expect("Cannot read from stdin!"); 15 | 16 | buff 17 | } 18 | 19 | fn main() { 20 | let input = read_stdin(); 21 | let expansion_tasks: Vec = 22 | serde_json::from_str(&input).expect(&format!("Cannot parse '{}'", &input)); 23 | 24 | let results: Vec = expansion_tasks 25 | .iter() 26 | .map(|task| proc_macro_expander::expand_task(&task)) 27 | .collect(); 28 | 29 | println!( 30 | "{}", 31 | &serde_json::to_string(&results).expect("Cannot serialize results!") 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/macro_expansion.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | #[derive(Debug, Serialize, Deserialize)] 4 | pub struct ExpansionTask { 5 | /// Argument of macro call. 6 | /// 7 | /// In custom derive that would be a struct or enum; in attribute-like macro - underlying 8 | /// item; in function-like macro - the macro body. 9 | pub macro_body: String, 10 | 11 | /// Names of macros to expand. 12 | /// 13 | /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In 14 | /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`). 15 | pub macro_name: String, 16 | 17 | /// Possible attributes for the attribute-like macros. 18 | pub attributes: Option, 19 | 20 | pub libs: Vec, 21 | } 22 | 23 | #[derive(Debug, Serialize, Deserialize)] 24 | #[serde(tag = "type")] 25 | pub enum ExpansionResult { 26 | #[serde(rename = "success")] 27 | Success { expansion: String }, 28 | #[serde(rename = "error")] 29 | Error { reason: String }, 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Roman Golyshev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | strategy: 5 | matrix: 6 | linux: 7 | imageName: 'ubuntu-16.04' 8 | mac: 9 | imageName: 'macos-10.13' 10 | windows: 11 | imageName: 'vs2017-win2016' 12 | 13 | pool: 14 | vmImage: $(imageName) 15 | 16 | steps: 17 | - script: | 18 | curl https://sh.rustup.rs -sSf | sh -s -- -y 19 | echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin" 20 | displayName: Install rust 21 | condition: ne( variables['Agent.OS'], 'Windows_NT' ) 22 | - script: | 23 | curl -sSf -o rustup-init.exe https://win.rustup.rs 24 | rustup-init.exe -y 25 | echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" 26 | displayName: Windows install rust 27 | condition: eq( variables['Agent.OS'], 'Windows_NT' ) 28 | - script: rustup install nightly 29 | displayName: Install nightly 30 | - script: cargo +nightly build --all 31 | displayName: Cargo build 32 | env: { RUSTFLAGS: "--cfg procmacro2_semver_exempt" } 33 | - script: cargo +nightly test --all -- --nocapture 34 | displayName: Cargo test 35 | env: { 36 | RUSTFLAGS: "--cfg procmacro2_semver_exempt", 37 | RUST_BACKTRACE: 1 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-proc-macro-expander 2 | 3 | [![Build Status](https://dev.azure.com/fedochet/rust-proc-macro-expander/_apis/build/status/fedochet.rust-proc-macro-expander?branchName=master)](https://dev.azure.com/fedochet/rust-proc-macro-expander/_build/latest?definitionId=1&branchName=master) 4 | [![Build Status](https://travis-ci.org/fedochet/rust-proc-macro-expander.svg?branch=master)](https://travis-ci.org/fedochet/rust-proc-macro-expander) 5 | 6 | This utility is capable of calling compiled Rust custom derive dynamic libraries on arbitrary code. 7 | 8 | **IMPORTANT**: compiler API, used in this utility, is not in stable `rustc` build. 9 | Use nightly rustc version to build it. 10 | 11 | ## Usage 12 | 13 | **IMPORTANT**: should be built with `RUSTFLAGS='--cfg procmacro2_semver_exempt'` as stated [here](https://github.com/alexcrichton/proc-macro2#unstable-features). 14 | 15 | Expander launches as CLI tool and accepts json array of expansion tasks from stdin. 16 | 17 | Assuming you have `expansion_task.json` in current directory, 18 | and compiled procedural macro `id_macro` somewhere: 19 | 20 | ```json 21 | [ 22 | { 23 | "macro_body": "struct S {}", 24 | "macro_name": "id_macro", 25 | "libs": [ "path/to/libid_macro.so" ] 26 | } 27 | ] 28 | ``` 29 | 30 | you can launch proc_macro_expander like this: 31 | 32 | ``` 33 | > cat expansion_task.json | ./proc_macro_expander 34 | 35 | [ {"type": "success", "expansion": "struct S { }"} ] 36 | ``` 37 | 38 | ## Testing 39 | 40 | You can launch tests with this command: 41 | 42 | ``` 43 | > RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo +nightly-2019-04-01 test 44 | ``` 45 | 46 | Current stable and nighly builds are having incompatible ABIs due to [this PR](https://github.com/rust-lang/rust/pull/59820). 47 | That is why `nightly-2019-04-01` is used. As soon as changes from this PR make it into the stable branch, tests should be 48 | able to run on current `nightly`. 49 | -------------------------------------------------------------------------------- /tests/expansion_correctness_tests.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro_expander; 2 | extern crate tempfile; 3 | #[macro_use] 4 | extern crate assert_matches; 5 | 6 | use proc_macro_expander::macro_expansion::{ExpansionTask, ExpansionResult}; 7 | 8 | use std::fs::{canonicalize, create_dir, File}; 9 | use std::{io, fs}; 10 | use std::io::Write; 11 | use std::io::ErrorKind; 12 | use std::path::{PathBuf, Path}; 13 | use std::process::{Command, Stdio}; 14 | use tempfile::TempDir; 15 | 16 | fn proc_macro_expander_exe() -> io::Result { 17 | let mut test_exe = std::env::current_exe()?; 18 | 19 | test_exe.pop(); 20 | if test_exe.ends_with("deps") { 21 | test_exe.pop(); 22 | } 23 | test_exe.push("proc_macro_expander"); 24 | 25 | Ok(test_exe) 26 | } 27 | 28 | fn setup_project_with_derives(root_dir: &Path) -> io::Result<()> { 29 | let mut cargo_toml = File::create(root_dir.join("Cargo.toml"))?; 30 | write!( 31 | &mut cargo_toml, 32 | "{}", 33 | r#" 34 | [package] 35 | name = "test_proc_macro" 36 | version = "0.1.0" 37 | 38 | [dependencies] 39 | serde_derive = "1.0.0" 40 | getset = "0.0.7" 41 | derive_builder = "0.7.1" 42 | "# 43 | )?; 44 | 45 | create_dir(root_dir.join("src"))?; 46 | let mut main_file = File::create(root_dir.join("src").join("main.rs"))?; 47 | write!( 48 | &mut main_file, 49 | "{}", 50 | r#" 51 | fn main() {} 52 | "# 53 | )?; 54 | 55 | Ok(()) 56 | } 57 | 58 | fn setup_proc_macro_project(root_dir: &Path) -> io::Result<()> { 59 | let mut cargo_toml = File::create(root_dir.join("Cargo.toml"))?; 60 | write!( 61 | &mut cargo_toml, 62 | "{}", 63 | r#" 64 | [package] 65 | name = "test_proc_macro" 66 | version = "0.1.0" 67 | 68 | [lib] 69 | proc-macro = true 70 | 71 | [dependencies] 72 | "# 73 | )?; 74 | 75 | create_dir(root_dir.join("src"))?; 76 | let mut lib_file = File::create(root_dir.join("src").join("lib.rs"))?; 77 | write!( 78 | &mut lib_file, 79 | "{}", 80 | r#" 81 | extern crate proc_macro; 82 | 83 | use proc_macro::TokenStream; 84 | 85 | #[proc_macro] 86 | pub fn id_macro(input: TokenStream) -> TokenStream { 87 | input 88 | } 89 | 90 | #[proc_macro] 91 | pub fn make_answer_macro(input: TokenStream) -> TokenStream { 92 | "fn answer() -> u32 { 42 }".parse().unwrap() 93 | } 94 | "# 95 | )?; 96 | 97 | Ok(()) 98 | } 99 | 100 | #[cfg(target_os = "linux")] 101 | static DYLIB_NAME_EXTENSION: &str = ".so"; 102 | 103 | #[cfg(target_os = "macos")] 104 | static DYLIB_NAME_EXTENSION: &str = ".dylib"; 105 | 106 | #[cfg(target_os = "windows")] 107 | static DYLIB_NAME_EXTENSION: &str = ".dll"; 108 | 109 | 110 | #[cfg(not(target_os = "windows"))] 111 | static DYLIB_NAME_PREFIX: &str = "lib"; 112 | 113 | #[cfg(target_os = "windows")] 114 | static DYLIB_NAME_PREFIX: &str = ""; 115 | 116 | fn compile_proc_macro(dir: &Path, proc_macro_name: &str) -> io::Result { 117 | Command::new("cargo") 118 | .current_dir(dir) 119 | .arg("+nightly") 120 | .arg("build") 121 | .arg("-p").arg(proc_macro_name) 122 | .status()?; 123 | 124 | let buf = dir 125 | .join("target") 126 | .join("debug") 127 | .join(format!("{}{}{}", DYLIB_NAME_PREFIX, proc_macro_name, DYLIB_NAME_EXTENSION)); 128 | 129 | if buf.is_file() { 130 | Ok(canonicalize(buf)?) 131 | } else { 132 | Err(io::Error::from(ErrorKind::NotFound)) 133 | } 134 | } 135 | 136 | fn perform_expansion(task: ExpansionTask) -> io::Result { 137 | let expander = proc_macro_expander_exe()?; 138 | 139 | let mut result = Command::new(expander) 140 | .stdin(Stdio::piped()) 141 | .stdout(Stdio::piped()) 142 | .spawn()?; 143 | 144 | write!( 145 | result.stdin.as_mut().unwrap(), 146 | "{}", 147 | &serde_json::to_string(&vec![&task])? 148 | )?; 149 | 150 | result.wait()?; 151 | 152 | let results: Vec = serde_json::from_reader(result.stdout.unwrap())?; 153 | 154 | // FIXME this is terrible 155 | Ok(results.into_iter().nth(0).expect( 156 | &format!("Expansion results for task {:?} are empty!", &task) 157 | )) 158 | } 159 | 160 | #[test] 161 | fn test_simple_bang_proc_macros() -> io::Result<()> { 162 | let tmp_dir = TempDir::new().expect("Cannot create temp dir"); 163 | setup_proc_macro_project(&tmp_dir.path()).expect("Cannot setup test project"); 164 | let proc_macro_dyn_lib = compile_proc_macro(&tmp_dir.path(), "test_proc_macro") 165 | .expect("Cannot find proc macro!"); 166 | 167 | { 168 | let id_macro_task = ExpansionTask { 169 | libs: vec![proc_macro_dyn_lib.clone()], 170 | macro_body: "struct S {}".to_string(), 171 | macro_name: "id_macro".to_string(), 172 | attributes: None, 173 | }; 174 | 175 | let id_macro_expansion = perform_expansion(id_macro_task).expect( 176 | "Cannot perform expansion for 'id_macro'" 177 | ); 178 | 179 | assert_matches!( 180 | id_macro_expansion, 181 | ExpansionResult::Success { ref expansion } if expansion.contains("struct S") 182 | ); 183 | } 184 | 185 | { 186 | let make_answer_macro_task = ExpansionTask { 187 | libs: vec![proc_macro_dyn_lib.clone()], 188 | macro_body: "".to_string(), 189 | macro_name: "make_answer_macro".to_string(), 190 | attributes: None, 191 | }; 192 | 193 | let make_answer_macro_expansion = perform_expansion(make_answer_macro_task).expect( 194 | "Cannot perform expansion for 'make_answer_macro'" 195 | ); 196 | 197 | assert_matches!( 198 | make_answer_macro_expansion, 199 | ExpansionResult::Success { ref expansion } if expansion.contains("fn answer") 200 | ); 201 | } 202 | 203 | 204 | Ok(()) 205 | } 206 | 207 | #[test] 208 | fn test_proc_macro_libraries() { 209 | let tmp_dir = TempDir::new().expect("Cannot create temp dir"); 210 | setup_project_with_derives(&tmp_dir.path()).expect("Cannot setup test project"); 211 | let getset_lib = compile_proc_macro(&tmp_dir.path(), "getset") 212 | .expect("Cannot find proc macro!"); 213 | 214 | { 215 | let expansion_task = ExpansionTask { 216 | libs: vec![getset_lib.clone()], 217 | macro_body: "struct S { #[set] y: i32 }".to_string(), 218 | macro_name: "Setters".to_string(), 219 | attributes: None, 220 | }; 221 | 222 | let expansion_result = perform_expansion(expansion_task).expect( 223 | "Cannot perform expansion for 'Setters'" 224 | ); 225 | 226 | assert_matches!( 227 | expansion_result, 228 | ExpansionResult::Success { ref expansion } 229 | if expansion.contains("fn set_y") 230 | ); 231 | } 232 | } 233 | 234 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_internals)] 2 | #![feature(proc_macro_span)] 3 | #![feature(proc_macro_diagnostic)] 4 | //extern crate dylib; 5 | extern crate sharedlib; 6 | extern crate libloading; 7 | extern crate goblin; 8 | extern crate proc_macro; 9 | #[macro_use] 10 | extern crate serde_derive; 11 | 12 | //use dylib::DynamicLibrary; 13 | use goblin::mach::Mach; 14 | use goblin::Object; 15 | use macro_expansion::{ExpansionResult, ExpansionTask}; 16 | use proc_macro::bridge::client::ProcMacro; 17 | use proc_macro::bridge::server::SameThread; 18 | use std::fs::File; 19 | use std::io::Read; 20 | use std::path::Path; 21 | use sharedlib::{Lib, Data, Symbol}; 22 | use libloading::Library; 23 | 24 | pub mod macro_expansion; 25 | mod rustc_server; 26 | 27 | static NEW_REGISTRAR_SYMBOL: &str = "__rustc_proc_macro_decls_"; 28 | static _OLD_REGISTRAR_SYMBOL: &str = "__rustc_derive_registrar_"; 29 | 30 | const EXEC_STRATEGY: SameThread = SameThread; 31 | 32 | fn parse_string(code: &str) -> Option { 33 | syn::parse_str(code).ok() 34 | } 35 | 36 | fn read_bytes(file: &Path) -> Option> { 37 | let mut fd = File::open(file).ok()?; 38 | let mut buffer = Vec::new(); 39 | fd.read_to_end(&mut buffer).ok()?; 40 | 41 | Some(buffer) 42 | } 43 | 44 | fn get_symbols_from_lib(file: &Path) -> Option> { 45 | let buffer = read_bytes(file)?; 46 | let object = Object::parse(&buffer).ok()?; 47 | 48 | return match object { 49 | Object::Elf(elf) => { 50 | let symbols = elf.dynstrtab.to_vec().ok()?; 51 | let names = symbols.iter().map(|s| s.to_string()).collect(); 52 | 53 | Some(names) 54 | } 55 | 56 | Object::PE(pe) => { 57 | let symbol_names = pe.exports.iter() 58 | .flat_map(|s| s.name) 59 | .map(|n| n.to_string()) 60 | .collect(); 61 | Some(symbol_names) 62 | } 63 | 64 | Object::Mach(mach) => match mach { 65 | Mach::Binary(binary) => { 66 | let exports = binary.exports().ok()?; 67 | let names = exports.iter().map(|s| s.name.clone()).collect(); 68 | 69 | Some(names) 70 | } 71 | 72 | Mach::Fat(_) => None, 73 | }, 74 | 75 | Object::Archive(_) | Object::Unknown(_) => None, 76 | }; 77 | } 78 | 79 | fn is_derive_registrar_symbol(symbol: &str) -> bool { 80 | symbol.contains(NEW_REGISTRAR_SYMBOL) 81 | } 82 | 83 | fn find_registrar_symbol(file: &Path) -> Option { 84 | let symbols = get_symbols_from_lib(file)?; 85 | 86 | symbols 87 | .iter() 88 | .find(|s| is_derive_registrar_symbol(s)) 89 | .map(|s| s.to_string()) 90 | } 91 | 92 | /// Loads dynamic library in platform dependent manner. 93 | /// 94 | /// For unix, you have to use RTLD_DEEPBIND flag to escape problems described 95 | /// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample) 96 | /// and [here](https://github.com/rust-lang/rust/issues/60593). 97 | /// 98 | /// Usage of RTLD_DEEPBIND is suggested by @edwin0cheng 99 | /// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1) 100 | /// 101 | /// It seems that on Windows that behaviour is default, so we do nothing in that case. 102 | #[cfg(windows)] 103 | fn load_library(file: &Path) -> Result { 104 | Library::new(file) 105 | } 106 | 107 | #[cfg(unix)] 108 | fn load_library(file: &Path) -> Result { 109 | use std::os::raw::c_int; 110 | use libloading::os::unix::Library as UnixLibrary; 111 | 112 | const RTLD_NOW: c_int = 0x00002; 113 | const RTLD_DEEPBIND: c_int = 0x00008; 114 | 115 | UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) 116 | } 117 | 118 | struct ProcMacroLibraryLibloading { 119 | lib: Library, 120 | exported_macros: Vec, 121 | } 122 | 123 | impl ProcMacroLibraryLibloading { 124 | fn open(file: &Path) -> Result { 125 | let symbol_name = find_registrar_symbol(file) 126 | .ok_or(format!("Cannot find registrar symbol in file {:?}", file))?; 127 | 128 | let lib = load_library(file).map_err(|e| e.to_string())?; 129 | 130 | let exported_macros = { 131 | let macros: libloading::Symbol<&&[ProcMacro]> = unsafe { lib.get(symbol_name.as_bytes()) } 132 | .map_err(|e| e.to_string())?; 133 | 134 | macros.to_vec() 135 | }; 136 | 137 | Ok(ProcMacroLibraryLibloading { 138 | lib, 139 | exported_macros, 140 | }) 141 | } 142 | } 143 | 144 | struct ProcMacroLibrarySharedLib { 145 | lib: Lib, 146 | exported_macros: Vec, 147 | } 148 | 149 | impl ProcMacroLibrarySharedLib { 150 | fn open(file: &Path) -> Result { 151 | let symbol_name = find_registrar_symbol(file) 152 | .ok_or(format!("Cannot find registrar symbol in file {:?}", file))?; 153 | 154 | let lib = unsafe { Lib::new(file) }.map_err(|e| e.to_string())?; 155 | 156 | let exported_macros = { 157 | // data already implies reference 158 | let macros: Data<&[ProcMacro]> = unsafe { lib.find_data(&symbol_name) } 159 | .map_err(|e| e.to_string())?; 160 | 161 | unsafe { *macros.get() }.to_vec() 162 | }; 163 | 164 | Ok(ProcMacroLibrarySharedLib { 165 | lib, 166 | exported_macros, 167 | }) 168 | } 169 | } 170 | 171 | ///// This struct keeps opened dynamic library and macros, from it, together. 172 | ///// 173 | ///// As long as lib is alive, exported_macros are safe to use. 174 | //struct ProcMacroLibraryDylib { 175 | // lib: DynamicLibrary, 176 | // exported_macros: Vec, 177 | //} 178 | // 179 | //impl ProcMacroLibraryDylib { 180 | // fn open(file: &PathBuf) -> Result { 181 | // let symbol_name = find_registrar_symbol(file) 182 | // .ok_or(format!("Cannot find registrar symbol in file {:?}", file))?; 183 | // 184 | // let lib = DynamicLibrary::open(Some(file))?; 185 | // 186 | // let registrar = unsafe { 187 | // let symbol = lib.symbol(&symbol_name)?; 188 | // std::mem::transmute::<*mut u8, &&[ProcMacro]>(symbol) 189 | // }; 190 | // 191 | // let exported_macros: Vec = registrar.to_vec(); 192 | // 193 | // Ok(ProcMacroLibraryDylib { 194 | // lib, 195 | // exported_macros, 196 | // }) 197 | // } 198 | //} 199 | 200 | type ProcMacroLibraryImpl = ProcMacroLibraryLibloading; 201 | 202 | pub struct Expander { 203 | libs: Vec, 204 | } 205 | 206 | impl Expander { 207 | pub fn new>(libs_paths: &[P]) -> Result { 208 | let mut libs = vec![]; 209 | 210 | for lib in libs_paths { 211 | /* Some libraries for dynamic loading require canonicalized path (even when it is 212 | already absolute 213 | */ 214 | let lib = lib.as_ref().canonicalize().expect( 215 | &format!("Cannot canonicalize {:?}", lib.as_ref()) 216 | ); 217 | 218 | let library = ProcMacroLibraryImpl::open(&lib)?; 219 | libs.push(library) 220 | } 221 | 222 | Ok(Expander { libs }) 223 | } 224 | 225 | pub fn expand( 226 | &self, 227 | macro_name: &str, 228 | macro_body: &str, 229 | attributes: Option<&String>, 230 | ) -> Result { 231 | let parsed_body = parse_string(macro_body).expect( 232 | &format!("Error while parsing this code: '{}'", macro_body) 233 | ); 234 | 235 | let parsed_attributes = attributes.map_or(proc_macro2::TokenStream::new(), |attr| { 236 | parse_string(attr).expect( 237 | &format!("Error while parsing this code: '{}'", attr) 238 | ) 239 | }); 240 | 241 | for lib in &self.libs { 242 | for proc_macro in &lib.exported_macros { 243 | match proc_macro { 244 | ProcMacro::CustomDerive { 245 | trait_name, client, .. 246 | } if *trait_name == macro_name => { 247 | let res = client.run( 248 | &EXEC_STRATEGY, 249 | rustc_server::Rustc::default(), 250 | parsed_body, 251 | ); 252 | 253 | return res.map(|token_stream| token_stream.to_string()); 254 | } 255 | 256 | ProcMacro::Bang { name, client } if *name == macro_name => { 257 | let res = client.run( 258 | &EXEC_STRATEGY, 259 | rustc_server::Rustc::default(), 260 | parsed_body, 261 | ); 262 | 263 | return res.map(|token_stream| token_stream.to_string()); 264 | } 265 | 266 | ProcMacro::Attr { name, client } if *name == macro_name => { 267 | let res = client.run( 268 | &EXEC_STRATEGY, 269 | rustc_server::Rustc::default(), 270 | parsed_attributes, 271 | parsed_body, 272 | ); 273 | 274 | return res.map(|token_stream| token_stream.to_string()); 275 | } 276 | 277 | _ => { 278 | continue; 279 | } 280 | } 281 | } 282 | } 283 | 284 | Err(proc_macro::bridge::PanicMessage::String( 285 | "Nothing to expand".to_string(), 286 | )) 287 | } 288 | } 289 | 290 | pub fn expand_task(task: &ExpansionTask) -> ExpansionResult { 291 | let expander = Expander::new(&task.libs).expect( 292 | &format!("Cannot expand with provided libraries: ${:?}", &task.libs) 293 | ); 294 | 295 | let result = match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { 296 | Ok(expansion) => ExpansionResult::Success { expansion }, 297 | 298 | Err(msg) => { 299 | let reason = format!( 300 | "Cannot perform expansion for {}: error {:?}!", 301 | &task.macro_name, 302 | msg.as_str() 303 | ); 304 | 305 | ExpansionResult::Error { reason } 306 | } 307 | }; 308 | 309 | result 310 | } 311 | -------------------------------------------------------------------------------- /src/rustc_server.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | extern crate proc_macro2; 3 | 4 | use proc_macro::bridge::{server, TokenTree}; 5 | use std::collections::Bound; 6 | use std::collections::HashMap; 7 | use std::hash::{Hash, Hasher}; 8 | use std::iter::FromIterator; 9 | use std::str::FromStr; 10 | use std::vec::IntoIter; 11 | 12 | use proc_macro::{Delimiter, Level, LineColumn, Spacing}; 13 | 14 | //#[derive(Clone)] 15 | //pub struct TokenStream; 16 | type TokenStream = proc_macro2::TokenStream; 17 | 18 | pub struct TokenStreamBuilder { 19 | acc: TokenStream, 20 | } 21 | 22 | impl TokenStreamBuilder { 23 | fn new() -> TokenStreamBuilder { 24 | TokenStreamBuilder { 25 | acc: TokenStream::new(), 26 | } 27 | } 28 | 29 | fn push(&mut self, stream: TokenStream) { 30 | self.acc.extend(stream.into_iter()) 31 | } 32 | 33 | fn build(self) -> TokenStream { 34 | self.acc 35 | } 36 | } 37 | 38 | #[derive(Clone)] 39 | pub struct TokenStreamIter { 40 | trees: IntoIter, 41 | } 42 | 43 | //#[derive(Clone)] 44 | //pub struct Group; 45 | type Group = proc_macro2::Group; 46 | 47 | #[derive(Hash, Eq, PartialEq, Copy, Clone)] 48 | pub struct MyPunct(u32); 49 | 50 | #[derive(Clone)] 51 | struct MyPunctData(proc_macro2::Punct); 52 | 53 | impl Hash for MyPunctData { 54 | fn hash(&self, hasher: &mut H) { 55 | hash_span(&self.0.span(), hasher); 56 | self.0.as_char().hash(hasher); 57 | } 58 | } 59 | 60 | impl Eq for MyPunctData {} 61 | 62 | impl PartialEq for MyPunctData { 63 | fn eq(&self, other: &Self) -> bool { 64 | let punct = &self.0; 65 | let other = &other.0; 66 | return punct.span().eq(&other.span()) 67 | && punct.as_char() == other.as_char() 68 | && punct.spacing() == other.spacing(); 69 | } 70 | } 71 | 72 | #[derive(Hash, Eq, PartialEq, Copy, Clone)] 73 | pub struct MyIdent(u32); 74 | 75 | #[derive(Clone)] 76 | struct MyIdentData(proc_macro2::Ident); 77 | 78 | fn hash_span(span: &proc_macro2::Span, hasher: &mut H) { 79 | let start = span.start(); 80 | start.line.hash(hasher); 81 | start.column.hash(hasher); 82 | 83 | let end = span.end(); 84 | end.line.hash(hasher); 85 | end.column.hash(hasher); 86 | } 87 | 88 | impl Hash for MyIdentData { 89 | fn hash(&self, hasher: &mut H) { 90 | self.0.hash(hasher) 91 | } 92 | } 93 | 94 | impl Eq for MyIdentData {} 95 | 96 | impl PartialEq for MyIdentData { 97 | fn eq(&self, other: &Self) -> bool { 98 | self.0.eq(&other.0) 99 | } 100 | } 101 | 102 | //#[derive(Clone)] 103 | //pub struct Literal; 104 | type Literal = proc_macro2::Literal; 105 | 106 | //#[derive(Clone)] 107 | //pub struct SourceFile; 108 | type SourceFile = proc_macro2::SourceFile; 109 | 110 | #[derive(Hash, Eq, PartialEq, Copy, Clone)] 111 | pub struct MySpan(u32); 112 | 113 | #[derive(Copy, Clone)] 114 | struct MySpanData(proc_macro2::Span); 115 | 116 | impl Hash for MySpanData { 117 | fn hash(&self, hasher: &mut H) { 118 | let column = self.0.start(); 119 | column.line.hash(hasher); 120 | column.column.hash(hasher); 121 | } 122 | } 123 | 124 | impl Eq for MySpanData {} 125 | 126 | impl PartialEq for MySpanData { 127 | fn eq(&self, other: &Self) -> bool { 128 | self.0.eq(&other.0) 129 | } 130 | } 131 | 132 | #[derive(Default)] 133 | struct MySpanInterner { 134 | spans: HashMap, 135 | span_data: Vec, 136 | } 137 | 138 | impl MySpanInterner { 139 | fn intern(&mut self, data: &MySpanData) -> u32 { 140 | if let Some(index) = self.spans.get(data) { 141 | return *index; 142 | } 143 | 144 | let index = self.spans.len() as u32; 145 | self.span_data.push(*data); 146 | self.spans.insert(*data, index); 147 | 148 | index 149 | } 150 | 151 | fn get(&self, index: u32) -> &MySpanData { 152 | &self.span_data[index as usize] 153 | } 154 | } 155 | 156 | #[derive(Default)] 157 | struct MyIdentInterner { 158 | idents: HashMap, 159 | ident_data: Vec, 160 | } 161 | 162 | impl MyIdentInterner { 163 | fn intern(&mut self, data: &MyIdentData) -> u32 { 164 | if let Some(index) = self.idents.get(data) { 165 | return *index; 166 | } 167 | 168 | let index = self.idents.len() as u32; 169 | self.ident_data.push(data.clone()); 170 | self.idents.insert(data.clone(), index); 171 | 172 | index 173 | } 174 | 175 | fn get(&self, index: u32) -> &MyIdentData { 176 | &self.ident_data[index as usize] 177 | } 178 | 179 | fn get_mut(&mut self, index: u32) -> &mut MyIdentData { 180 | self.ident_data 181 | .get_mut(index as usize) 182 | .expect("Should be consistent") 183 | } 184 | } 185 | 186 | #[derive(Default)] 187 | struct MyPunctInterner { 188 | puncts: HashMap, 189 | punct_data: Vec, 190 | } 191 | 192 | impl MyPunctInterner { 193 | fn intern(&mut self, data: &MyPunctData) -> u32 { 194 | if let Some(index) = self.puncts.get(data) { 195 | return *index; 196 | } 197 | 198 | let index = self.puncts.len() as u32; 199 | self.punct_data.push(data.clone()); 200 | self.puncts.insert(data.clone(), index); 201 | 202 | index 203 | } 204 | 205 | fn get(&self, index: u32) -> &MyPunctData { 206 | &self.punct_data[index as usize] 207 | } 208 | 209 | fn get_mut(&mut self, index: u32) -> &mut MyPunctData { 210 | self.punct_data 211 | .get_mut(index as usize) 212 | .expect("Should be consistent") 213 | } 214 | } 215 | 216 | #[derive(Default)] 217 | pub struct Rustc { 218 | span_interner: MySpanInterner, 219 | ident_interner: MyIdentInterner, 220 | punct_interner: MyPunctInterner, 221 | // def_side: MySpan, 222 | // call_site: MySpan, 223 | } 224 | 225 | impl server::Types for Rustc { 226 | type TokenStream = TokenStream; 227 | type TokenStreamBuilder = TokenStreamBuilder; 228 | type TokenStreamIter = TokenStreamIter; 229 | type Group = Group; 230 | type Punct = MyPunct; 231 | type Ident = MyIdent; 232 | type Literal = Literal; 233 | type SourceFile = SourceFile; 234 | type Diagnostic = proc_macro::Diagnostic; 235 | type Span = MySpan; 236 | type MultiSpan = Vec; 237 | } 238 | 239 | impl server::TokenStream for Rustc { 240 | fn new(&mut self) -> Self::TokenStream { 241 | Self::TokenStream::new() 242 | } 243 | 244 | fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { 245 | stream.is_empty() 246 | } 247 | fn from_str(&mut self, src: &str) -> Self::TokenStream { 248 | Self::TokenStream::from_str(src).expect("cannot parse string") 249 | } 250 | fn to_string(&mut self, stream: &Self::TokenStream) -> String { 251 | stream.to_string() 252 | } 253 | fn from_token_tree( 254 | &mut self, 255 | tree: TokenTree, 256 | ) -> Self::TokenStream { 257 | match tree { 258 | TokenTree::Group(group) => { 259 | let tree = proc_macro2::TokenTree::from(group); 260 | Self::TokenStream::from_iter(vec![tree]) 261 | } 262 | 263 | TokenTree::Ident(MyIdent(index)) => { 264 | let MyIdentData(ident) = self.ident_interner.get(index).clone(); 265 | let tree = proc_macro2::TokenTree::from(ident); 266 | Self::TokenStream::from_iter(vec![tree]) 267 | } 268 | 269 | TokenTree::Literal(group) => { 270 | let tree = proc_macro2::TokenTree::from(group); 271 | Self::TokenStream::from_iter(vec![tree]) 272 | } 273 | 274 | TokenTree::Punct(MyPunct(index)) => { 275 | let MyPunctData(punct) = self.punct_interner.get(index).clone(); 276 | let tree = proc_macro2::TokenTree::from(punct); 277 | Self::TokenStream::from_iter(vec![tree]) 278 | } 279 | } 280 | } 281 | 282 | fn into_iter(&mut self, stream: Self::TokenStream) -> Self::TokenStreamIter { 283 | let trees: Vec = stream.into_iter().collect(); 284 | TokenStreamIter { 285 | trees: trees.into_iter(), 286 | } 287 | } 288 | } 289 | 290 | impl server::TokenStreamBuilder for Rustc { 291 | fn new(&mut self) -> Self::TokenStreamBuilder { 292 | Self::TokenStreamBuilder::new() 293 | } 294 | fn push(&mut self, builder: &mut Self::TokenStreamBuilder, stream: Self::TokenStream) { 295 | builder.push(stream) 296 | } 297 | fn build(&mut self, builder: Self::TokenStreamBuilder) -> Self::TokenStream { 298 | builder.build() 299 | } 300 | } 301 | 302 | impl server::TokenStreamIter for Rustc { 303 | fn next( 304 | &mut self, 305 | iter: &mut Self::TokenStreamIter, 306 | ) -> Option> { 307 | iter.trees.next().map(|tree| match tree { 308 | proc_macro2::TokenTree::Group(group) => TokenTree::Group(group), 309 | 310 | proc_macro2::TokenTree::Ident(ident) => { 311 | TokenTree::Ident(MyIdent(self.ident_interner.intern(&MyIdentData(ident)))) 312 | } 313 | 314 | proc_macro2::TokenTree::Literal(literal) => TokenTree::Literal(literal), 315 | 316 | proc_macro2::TokenTree::Punct(punct) => { 317 | TokenTree::Punct(MyPunct(self.punct_interner.intern(&MyPunctData(punct)))) 318 | } 319 | }) 320 | } 321 | } 322 | 323 | fn delim_to_internal(d: proc_macro::Delimiter) -> proc_macro2::Delimiter { 324 | match d { 325 | proc_macro::Delimiter::Parenthesis => proc_macro2::Delimiter::Parenthesis, 326 | proc_macro::Delimiter::Brace => proc_macro2::Delimiter::Brace, 327 | proc_macro::Delimiter::Bracket => proc_macro2::Delimiter::Bracket, 328 | proc_macro::Delimiter::None => proc_macro2::Delimiter::None, 329 | } 330 | } 331 | 332 | fn delim_to_external(d: proc_macro2::Delimiter) -> proc_macro::Delimiter { 333 | match d { 334 | proc_macro2::Delimiter::Parenthesis => proc_macro::Delimiter::Parenthesis, 335 | proc_macro2::Delimiter::Brace => proc_macro::Delimiter::Brace, 336 | proc_macro2::Delimiter::Bracket => proc_macro::Delimiter::Bracket, 337 | proc_macro2::Delimiter::None => proc_macro::Delimiter::None, 338 | } 339 | } 340 | 341 | fn spacing_to_internal(spacing: proc_macro::Spacing) -> proc_macro2::Spacing { 342 | match spacing { 343 | proc_macro::Spacing::Alone => proc_macro2::Spacing::Alone, 344 | proc_macro::Spacing::Joint => proc_macro2::Spacing::Joint, 345 | } 346 | } 347 | 348 | fn spacing_to_external(spacing: proc_macro2::Spacing) -> proc_macro::Spacing { 349 | match spacing { 350 | proc_macro2::Spacing::Alone => proc_macro::Spacing::Alone, 351 | proc_macro2::Spacing::Joint => proc_macro::Spacing::Joint, 352 | } 353 | } 354 | 355 | impl server::Group for Rustc { 356 | fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group { 357 | Self::Group::new(delim_to_internal(delimiter), stream) 358 | } 359 | fn delimiter(&mut self, group: &Self::Group) -> Delimiter { 360 | delim_to_external(group.delimiter()) 361 | } 362 | fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { 363 | group.stream() 364 | } 365 | fn span(&mut self, group: &Self::Group) -> Self::Span { 366 | MySpan(self.span_interner.intern(&MySpanData(group.span()))) 367 | } 368 | 369 | fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) { 370 | let MySpanData(span) = *self.span_interner.get(span.0); 371 | group.set_span(span); 372 | } 373 | 374 | fn span_open(&mut self, group: &Self::Group) -> Self::Span { 375 | MySpan(self.span_interner.intern(&MySpanData(group.span_open()))) 376 | } 377 | 378 | fn span_close(&mut self, group: &Self::Group) -> Self::Span { 379 | MySpan(self.span_interner.intern(&MySpanData(group.span_close()))) 380 | } 381 | } 382 | 383 | impl server::Punct for Rustc { 384 | fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { 385 | MyPunct( 386 | self.punct_interner 387 | .intern(&MyPunctData(proc_macro2::Punct::new( 388 | ch, 389 | spacing_to_internal(spacing), 390 | ))), 391 | ) 392 | } 393 | 394 | fn as_char(&mut self, punct: Self::Punct) -> char { 395 | let MyPunctData(punct) = self.punct_interner.get(punct.0); 396 | punct.as_char() 397 | } 398 | fn spacing(&mut self, punct: Self::Punct) -> Spacing { 399 | let MyPunctData(punct) = self.punct_interner.get(punct.0); 400 | spacing_to_external(punct.spacing()) 401 | } 402 | fn span(&mut self, punct: Self::Punct) -> Self::Span { 403 | let MyPunctData(punct) = self.punct_interner.get(punct.0); 404 | MySpan(self.span_interner.intern(&MySpanData(punct.span()))) 405 | } 406 | fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct { 407 | let MySpanData(span) = *self.span_interner.get(span.0); 408 | self.punct_interner.get_mut(punct.0).0.set_span(span); 409 | punct 410 | } 411 | } 412 | 413 | impl server::Ident for Rustc { 414 | fn new(&mut self, string: &str, span: Self::Span, _is_raw: bool) -> Self::Ident { 415 | let MySpanData(span) = self.span_interner.get(span.0); 416 | MyIdent( 417 | self.ident_interner 418 | .intern(&MyIdentData(proc_macro2::Ident::new(string, *span))), 419 | ) 420 | } 421 | 422 | fn span(&mut self, ident: Self::Ident) -> Self::Span { 423 | let MyIdentData(ident) = self.ident_interner.get(ident.0); 424 | MySpan(self.span_interner.intern(&MySpanData(ident.span()))) 425 | } 426 | fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident { 427 | let MySpanData(span) = *self.span_interner.get(span.0); 428 | self.ident_interner.get_mut(ident.0).0.set_span(span); 429 | ident 430 | } 431 | } 432 | 433 | impl server::Literal for Rustc { 434 | // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. 435 | fn debug(&mut self, literal: &Self::Literal) -> String { 436 | format!("{:?}", literal) 437 | } 438 | 439 | fn integer(&mut self, n: &str) -> Self::Literal { 440 | // note: this implementation may be incorrect 441 | let n: i128 = n.parse().unwrap(); 442 | Literal::i128_unsuffixed(n) 443 | } 444 | 445 | fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { 446 | match kind { 447 | "u8" => { 448 | let n: u8 = n.parse().unwrap(); 449 | Literal::u8_suffixed(n) 450 | } 451 | 452 | "u16" => { 453 | let n: u16 = n.parse().unwrap(); 454 | Literal::u16_suffixed(n) 455 | } 456 | 457 | "u32" => { 458 | let n: u32 = n.parse().unwrap(); 459 | Literal::u32_suffixed(n) 460 | } 461 | 462 | "u64" => { 463 | let n: u64 = n.parse().unwrap(); 464 | Literal::u64_suffixed(n) 465 | } 466 | 467 | "u128" => { 468 | let n: u128 = n.parse().unwrap(); 469 | Literal::u128_suffixed(n) 470 | } 471 | 472 | "usize" => { 473 | let n: usize = n.parse().unwrap(); 474 | Literal::usize_suffixed(n) 475 | } 476 | 477 | "i8" => { 478 | let n: i8 = n.parse().unwrap(); 479 | Literal::i8_suffixed(n) 480 | } 481 | 482 | "i16" => { 483 | let n: i16 = n.parse().unwrap(); 484 | Literal::i16_suffixed(n) 485 | } 486 | 487 | "i32" => { 488 | let n: i32 = n.parse().unwrap(); 489 | Literal::i32_suffixed(n) 490 | } 491 | 492 | "i64" => { 493 | let n: i64 = n.parse().unwrap(); 494 | Literal::i64_suffixed(n) 495 | } 496 | 497 | "i128" => { 498 | let n: i128 = n.parse().unwrap(); 499 | Literal::i128_suffixed(n) 500 | } 501 | 502 | _ => unimplemented!("unknown args for typed_integer: n {}, kind {}", n, kind), 503 | } 504 | } 505 | 506 | fn float(&mut self, n: &str) -> Self::Literal { 507 | // note: this implementation may be incorrect 508 | let n: f64 = n.parse().unwrap(); 509 | Literal::f64_unsuffixed(n) 510 | } 511 | 512 | fn f32(&mut self, n: &str) -> Self::Literal { 513 | let n: f32 = n.parse().unwrap(); 514 | Literal::f32_suffixed(n) 515 | } 516 | 517 | fn f64(&mut self, n: &str) -> Self::Literal { 518 | let n: f64 = n.parse().unwrap(); 519 | Literal::f64_suffixed(n) 520 | } 521 | 522 | fn string(&mut self, string: &str) -> Self::Literal { 523 | Self::Literal::string(string) 524 | } 525 | 526 | fn character(&mut self, ch: char) -> Self::Literal { 527 | Self::Literal::character(ch) 528 | } 529 | 530 | fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal { 531 | Self::Literal::byte_string(bytes) 532 | } 533 | 534 | fn span(&mut self, literal: &Self::Literal) -> Self::Span { 535 | MySpan(self.span_interner.intern(&MySpanData(literal.span()))) 536 | } 537 | 538 | fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { 539 | let MySpanData(span) = *self.span_interner.get(span.0); 540 | literal.set_span(span); 541 | } 542 | 543 | fn subspan( 544 | &mut self, 545 | _literal: &Self::Literal, 546 | _start: Bound, 547 | _end: Bound, 548 | ) -> Option { 549 | None // TODO add some sensible implementation 550 | } 551 | } 552 | 553 | impl server::SourceFile for Rustc { 554 | fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { 555 | file1.eq(file2) 556 | } 557 | fn path(&mut self, file: &Self::SourceFile) -> String { 558 | // match file.path() { 559 | // FileName::Real(ref path) => path 560 | // .to_str() 561 | // .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") 562 | // .to_string(), 563 | /*_ =>*/ 564 | // } 565 | String::from( 566 | file.path() 567 | .to_str() 568 | .expect("non-UTF8 file path in `proc_macro::SourceFile::path`"), 569 | ) 570 | } 571 | fn is_real(&mut self, file: &Self::SourceFile) -> bool { 572 | file.is_real() 573 | } 574 | } 575 | 576 | impl server::Diagnostic for Rustc { 577 | fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { 578 | let mut diag = proc_macro::Diagnostic::new(level, msg); 579 | diag.set_spans(spans); 580 | diag 581 | } 582 | // fn new_span(&mut self, level: Level, msg: &str, span: Self::Span) -> Self::Diagnostic { 583 | //// let MySpanData(span) = *self.span_interner.get(span.0); 584 | //// 585 | //// Self::Diagnostic::spanned(span, level, msg) 586 | // unimplemented!("new_span") 587 | // } 588 | 589 | fn sub( 590 | &mut self, 591 | _diag: &mut Self::Diagnostic, 592 | _level: Level, 593 | _msg: &str, 594 | _spans: Self::MultiSpan, 595 | ) { 596 | unimplemented!("No sub method on proc_macro::Diagnostic") 597 | } 598 | 599 | // fn sub_span(&mut self, diag: &mut Self::Diagnostic, level: Level, msg: &str, span: Self::Span) { 600 | // unimplemented!("sub_span") 601 | // } 602 | 603 | fn emit(&mut self, diag: Self::Diagnostic) { 604 | diag.emit() 605 | } 606 | } 607 | 608 | impl server::Span for Rustc { 609 | fn debug(&mut self, span: Self::Span) -> String { 610 | let MySpanData(span) = self.span_interner.get(span.0); 611 | format!("{:?}", span) 612 | } 613 | fn def_site(&mut self) -> Self::Span { 614 | MySpan( 615 | self.span_interner 616 | .intern(&MySpanData(proc_macro2::Span::def_site())), 617 | ) 618 | } 619 | fn call_site(&mut self) -> Self::Span { 620 | MySpan( 621 | self.span_interner 622 | .intern(&MySpanData(proc_macro2::Span::call_site())), 623 | ) 624 | } 625 | fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { 626 | let MySpanData(span) = self.span_interner.get(span.0); 627 | span.source_file() 628 | } 629 | 630 | /// Recent feature, not yet in the proc_macro2 631 | /// 632 | /// See PR: 633 | /// https://github.com/rust-lang/rust/pull/55780 634 | fn source_text(&mut self, _span: Self::Span) -> Option { 635 | None 636 | } 637 | 638 | fn parent(&mut self, _span: Self::Span) -> Option { 639 | // let MySpanData(span) = *self.span_interner.get(span.0); 640 | // if let Some(span) = span.parent() { 641 | // return Some(MySpan(self.span_interner.intern(&MySpanData(span)))) 642 | // } 643 | 644 | None 645 | } 646 | fn source(&mut self, span: Self::Span) -> Self::Span { 647 | // let MySpanData(span) = *self.span_interner.get(span.0); 648 | // 649 | // MySpan(self.span_interner.intern(&MySpanData(span.source()))) 650 | span 651 | } 652 | fn start(&mut self, span: Self::Span) -> LineColumn { 653 | let MySpanData(span) = *self.span_interner.get(span.0); 654 | 655 | // span.start() 656 | span.unstable().start() 657 | } 658 | fn end(&mut self, span: Self::Span) -> LineColumn { 659 | let MySpanData(span) = *self.span_interner.get(span.0); 660 | 661 | span.unstable().end() 662 | } 663 | fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { 664 | let MySpanData(first) = *self.span_interner.get(first.0); 665 | let MySpanData(second) = *self.span_interner.get(second.0); 666 | 667 | if let Some(join) = first.join(second) { 668 | return Some(MySpan(self.span_interner.intern(&MySpanData(join)))); 669 | } 670 | 671 | None 672 | } 673 | fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { 674 | let MySpanData(span) = *self.span_interner.get(span.0); 675 | let MySpanData(at) = *self.span_interner.get(at.0); 676 | let resolved_at = span.resolved_at(at); 677 | 678 | MySpan(self.span_interner.intern(&MySpanData(resolved_at))) 679 | } 680 | 681 | fn mixed_site(&mut self) -> Self::Span { 682 | unimplemented!("Span::mixed_state is not yet implemented in proc_macro2") 683 | } 684 | } 685 | 686 | impl server::MultiSpan for Rustc { 687 | fn new(&mut self) -> Self::MultiSpan { 688 | unimplemented!("MultiSpan::new is not implemented"); 689 | } 690 | 691 | fn push(&mut self, _other: &mut Self::MultiSpan, _span: Self::Span) { 692 | unimplemented!("MultiSpan::new is not implemented"); 693 | } 694 | } 695 | 696 | //impl server::Span for Rustc { 697 | // fn debug(&mut self, _span: Self::Span) -> String { 698 | // unimplemented!("Span::debug") 699 | // } 700 | // fn def_site(&mut self) -> Self::Span { 701 | // unimplemented!("def_site") 702 | // } 703 | // fn call_site(&mut self) -> Self::Span { 704 | // MySpan(self.span_interner.intern(&MySpanData(proc_macro2::Span::call_site()))) 705 | // } 706 | // fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { 707 | // unimplemented!("source_file") 708 | // } 709 | // fn parent(&mut self, _span: Self::Span) -> Option { 710 | // unimplemented!("parent") 711 | // } 712 | // fn source(&mut self, _span: Self::Span) -> Self::Span { 713 | // unimplemented!("source") 714 | // } 715 | // fn start(&mut self, _span: Self::Span) -> LineColumn { 716 | // unimplemented!("start") 717 | // } 718 | // fn end(&mut self, _span: Self::Span) -> LineColumn { 719 | // unimplemented!("end") 720 | // } 721 | // fn join(&mut self, _first: Self::Span, _second: Self::Span) -> Option { 722 | // unimplemented!("join") 723 | // } 724 | // fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span { 725 | // unimplemented!("Span::resolved_at") 726 | // } 727 | //} 728 | --------------------------------------------------------------------------------