diff options
Diffstat (limited to 'crates/ra_proc_macro_srv')
-rw-r--r-- | crates/ra_proc_macro_srv/src/cli.rs | 58 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/dylib.rs | 103 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/lib.rs | 36 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/rustc_server.rs | 13 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt | 3 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/tests/utils.rs | 2 |
6 files changed, 95 insertions, 120 deletions
diff --git a/crates/ra_proc_macro_srv/src/cli.rs b/crates/ra_proc_macro_srv/src/cli.rs index c771f2b38..5f1f3ba3c 100644 --- a/crates/ra_proc_macro_srv/src/cli.rs +++ b/crates/ra_proc_macro_srv/src/cli.rs | |||
@@ -2,55 +2,43 @@ | |||
2 | 2 | ||
3 | use crate::{expand_task, list_macros}; | 3 | use crate::{expand_task, list_macros}; |
4 | use ra_proc_macro::msg::{self, Message}; | 4 | use ra_proc_macro::msg::{self, Message}; |
5 | |||
6 | use std::io; | 5 | use std::io; |
7 | 6 | ||
8 | fn read_request() -> Result<Option<msg::Request>, io::Error> { | ||
9 | let stdin = io::stdin(); | ||
10 | let mut stdin = stdin.lock(); | ||
11 | msg::Request::read(&mut stdin) | ||
12 | } | ||
13 | |||
14 | fn write_response(res: Result<msg::Response, String>) -> Result<(), io::Error> { | ||
15 | let msg: msg::Response = match res { | ||
16 | Ok(res) => res, | ||
17 | Err(err) => msg::Response::Error(msg::ResponseError { | ||
18 | code: msg::ErrorCode::ExpansionError, | ||
19 | message: err, | ||
20 | }), | ||
21 | }; | ||
22 | |||
23 | let stdout = io::stdout(); | ||
24 | let mut stdout = stdout.lock(); | ||
25 | msg.write(&mut stdout) | ||
26 | } | ||
27 | |||
28 | pub fn run() { | 7 | pub fn run() { |
29 | loop { | 8 | loop { |
30 | let req = match read_request() { | 9 | let req = match read_request() { |
31 | Err(err) => { | 10 | Err(err) => { |
32 | eprintln!("Read message error on ra_proc_macro_srv: {}", err.to_string()); | 11 | eprintln!("Read message error on ra_proc_macro_srv: {}", err); |
33 | continue; | 12 | continue; |
34 | } | 13 | } |
35 | Ok(None) => continue, | 14 | Ok(None) => continue, |
36 | Ok(Some(req)) => req, | 15 | Ok(Some(req)) => req, |
37 | }; | 16 | }; |
38 | 17 | ||
39 | match req { | 18 | let res = match req { |
40 | msg::Request::ListMacro(task) => { | 19 | msg::Request::ListMacro(task) => Ok(msg::Response::ListMacro(list_macros(&task))), |
41 | if let Err(err) = | ||
42 | write_response(list_macros(&task).map(|it| msg::Response::ListMacro(it))) | ||
43 | { | ||
44 | eprintln!("Write message error on list macro: {}", err); | ||
45 | } | ||
46 | } | ||
47 | msg::Request::ExpansionMacro(task) => { | 20 | msg::Request::ExpansionMacro(task) => { |
48 | if let Err(err) = | 21 | expand_task(&task).map(msg::Response::ExpansionMacro) |
49 | write_response(expand_task(&task).map(|it| msg::Response::ExpansionMacro(it))) | ||
50 | { | ||
51 | eprintln!("Write message error on expansion macro: {}", err); | ||
52 | } | ||
53 | } | 22 | } |
23 | }; | ||
24 | |||
25 | let msg = res.unwrap_or_else(|err| { | ||
26 | msg::Response::Error(msg::ResponseError { | ||
27 | code: msg::ErrorCode::ExpansionError, | ||
28 | message: err, | ||
29 | }) | ||
30 | }); | ||
31 | |||
32 | if let Err(err) = write_response(msg) { | ||
33 | eprintln!("Write message error: {}", err); | ||
54 | } | 34 | } |
55 | } | 35 | } |
56 | } | 36 | } |
37 | |||
38 | fn read_request() -> io::Result<Option<msg::Request>> { | ||
39 | msg::Request::read(&mut io::stdin().lock()) | ||
40 | } | ||
41 | |||
42 | fn write_response(msg: msg::Response) -> io::Result<()> { | ||
43 | msg.write(&mut io::stdout().lock()) | ||
44 | } | ||
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs index 16bd7466e..d202eb0fd 100644 --- a/crates/ra_proc_macro_srv/src/dylib.rs +++ b/crates/ra_proc_macro_srv/src/dylib.rs | |||
@@ -9,43 +9,37 @@ use libloading::Library; | |||
9 | use memmap::Mmap; | 9 | use memmap::Mmap; |
10 | use ra_proc_macro::ProcMacroKind; | 10 | use ra_proc_macro::ProcMacroKind; |
11 | 11 | ||
12 | use std::io::Error as IoError; | 12 | use std::io; |
13 | use std::io::ErrorKind as IoErrorKind; | ||
14 | 13 | ||
15 | const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; | 14 | const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; |
16 | 15 | ||
17 | fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> IoError { | 16 | fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error { |
18 | IoError::new(IoErrorKind::InvalidData, e) | 17 | io::Error::new(io::ErrorKind::InvalidData, e) |
19 | } | 18 | } |
20 | 19 | ||
21 | fn is_derive_registrar_symbol(symbol: &str) -> bool { | 20 | fn is_derive_registrar_symbol(symbol: &str) -> bool { |
22 | symbol.contains(NEW_REGISTRAR_SYMBOL) | 21 | symbol.contains(NEW_REGISTRAR_SYMBOL) |
23 | } | 22 | } |
24 | 23 | ||
25 | fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> { | 24 | fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> { |
26 | let file = File::open(file)?; | 25 | let file = File::open(file)?; |
27 | let buffer = unsafe { Mmap::map(&file)? }; | 26 | let buffer = unsafe { Mmap::map(&file)? }; |
28 | let object = Object::parse(&buffer).map_err(invalid_data_err)?; | 27 | let object = Object::parse(&buffer).map_err(invalid_data_err)?; |
29 | 28 | ||
30 | match object { | 29 | let name = match object { |
31 | Object::Elf(elf) => { | 30 | Object::Elf(elf) => { |
32 | let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?; | 31 | let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?; |
33 | let name = | 32 | symbols.into_iter().find(|s| is_derive_registrar_symbol(s)).map(&str::to_owned) |
34 | symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string()); | ||
35 | Ok(name) | ||
36 | } | ||
37 | Object::PE(pe) => { | ||
38 | let name = pe | ||
39 | .exports | ||
40 | .iter() | ||
41 | .flat_map(|s| s.name) | ||
42 | .find(|s| is_derive_registrar_symbol(s)) | ||
43 | .map(|s| s.to_string()); | ||
44 | Ok(name) | ||
45 | } | 33 | } |
34 | Object::PE(pe) => pe | ||
35 | .exports | ||
36 | .iter() | ||
37 | .flat_map(|s| s.name) | ||
38 | .find(|s| is_derive_registrar_symbol(s)) | ||
39 | .map(&str::to_owned), | ||
46 | Object::Mach(Mach::Binary(binary)) => { | 40 | Object::Mach(Mach::Binary(binary)) => { |
47 | let exports = binary.exports().map_err(invalid_data_err)?; | 41 | let exports = binary.exports().map_err(invalid_data_err)?; |
48 | let name = exports | 42 | exports |
49 | .iter() | 43 | .iter() |
50 | .map(|s| { | 44 | .map(|s| { |
51 | // In macos doc: | 45 | // In macos doc: |
@@ -59,11 +53,11 @@ fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> { | |||
59 | } | 53 | } |
60 | }) | 54 | }) |
61 | .find(|s| is_derive_registrar_symbol(s)) | 55 | .find(|s| is_derive_registrar_symbol(s)) |
62 | .map(|s| s.to_string()); | 56 | .map(&str::to_owned) |
63 | Ok(name) | ||
64 | } | 57 | } |
65 | _ => Ok(None), | 58 | _ => return Ok(None), |
66 | } | 59 | }; |
60 | return Ok(name); | ||
67 | } | 61 | } |
68 | 62 | ||
69 | /// Loads dynamic library in platform dependent manner. | 63 | /// Loads dynamic library in platform dependent manner. |
@@ -93,15 +87,16 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> { | |||
93 | } | 87 | } |
94 | 88 | ||
95 | struct ProcMacroLibraryLibloading { | 89 | struct ProcMacroLibraryLibloading { |
96 | // Hold the dylib to prevent it for unloadeding | 90 | // Hold the dylib to prevent it from unloading |
97 | _lib: Library, | 91 | _lib: Library, |
98 | exported_macros: Vec<bridge::client::ProcMacro>, | 92 | exported_macros: Vec<bridge::client::ProcMacro>, |
99 | } | 93 | } |
100 | 94 | ||
101 | impl ProcMacroLibraryLibloading { | 95 | impl ProcMacroLibraryLibloading { |
102 | fn open(file: &Path) -> Result<Self, IoError> { | 96 | fn open(file: &Path) -> io::Result<Self> { |
103 | let symbol_name = find_registrar_symbol(file)? | 97 | let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| { |
104 | .ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?; | 98 | invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display())) |
99 | })?; | ||
105 | 100 | ||
106 | let lib = load_library(file).map_err(invalid_data_err)?; | 101 | let lib = load_library(file).map_err(invalid_data_err)?; |
107 | let exported_macros = { | 102 | let exported_macros = { |
@@ -121,18 +116,16 @@ pub struct Expander { | |||
121 | } | 116 | } |
122 | 117 | ||
123 | impl Expander { | 118 | impl Expander { |
124 | pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> { | 119 | pub fn new(lib: &Path) -> Result<Expander, String> { |
125 | let mut libs = vec![]; | 120 | // Some libraries for dynamic loading require canonicalized path even when it is |
126 | /* Some libraries for dynamic loading require canonicalized path (even when it is | 121 | // already absolute |
127 | already absolute | 122 | let lib = lib |
128 | */ | 123 | .canonicalize() |
129 | let lib = | 124 | .unwrap_or_else(|err| panic!("Cannot canonicalize {}: {:?}", lib.display(), err)); |
130 | lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref())); | ||
131 | 125 | ||
132 | let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?; | 126 | let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?; |
133 | libs.push(library); | ||
134 | 127 | ||
135 | Ok(Expander { libs }) | 128 | Ok(Expander { libs: vec![library] }) |
136 | } | 129 | } |
137 | 130 | ||
138 | pub fn expand( | 131 | pub fn expand( |
@@ -176,7 +169,6 @@ impl Expander { | |||
176 | parsed_attributes, | 169 | parsed_attributes, |
177 | parsed_body, | 170 | parsed_body, |
178 | ); | 171 | ); |
179 | |||
180 | return res.map(|it| it.subtree); | 172 | return res.map(|it| it.subtree); |
181 | } | 173 | } |
182 | _ => continue, | 174 | _ => continue, |
@@ -187,26 +179,21 @@ impl Expander { | |||
187 | Err(bridge::PanicMessage::String("Nothing to expand".to_string())) | 179 | Err(bridge::PanicMessage::String("Nothing to expand".to_string())) |
188 | } | 180 | } |
189 | 181 | ||
190 | pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> { | 182 | pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { |
191 | let mut result = vec![]; | 183 | self.libs |
192 | 184 | .iter() | |
193 | for lib in &self.libs { | 185 | .flat_map(|it| &it.exported_macros) |
194 | for proc_macro in &lib.exported_macros { | 186 | .map(|proc_macro| match proc_macro { |
195 | let res = match proc_macro { | 187 | bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { |
196 | bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { | 188 | (trait_name.to_string(), ProcMacroKind::CustomDerive) |
197 | (trait_name.to_string(), ProcMacroKind::CustomDerive) | 189 | } |
198 | } | 190 | bridge::client::ProcMacro::Bang { name, .. } => { |
199 | bridge::client::ProcMacro::Bang { name, .. } => { | 191 | (name.to_string(), ProcMacroKind::FuncLike) |
200 | (name.to_string(), ProcMacroKind::FuncLike) | 192 | } |
201 | } | 193 | bridge::client::ProcMacro::Attr { name, .. } => { |
202 | bridge::client::ProcMacro::Attr { name, .. } => { | 194 | (name.to_string(), ProcMacroKind::Attr) |
203 | (name.to_string(), ProcMacroKind::Attr) | 195 | } |
204 | } | 196 | }) |
205 | }; | 197 | .collect() |
206 | result.push(res); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | Ok(result) | ||
211 | } | 198 | } |
212 | } | 199 | } |
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs index c62b0ed89..3aca859db 100644 --- a/crates/ra_proc_macro_srv/src/lib.rs +++ b/crates/ra_proc_macro_srv/src/lib.rs | |||
@@ -3,10 +3,10 @@ | |||
3 | //! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code. | 3 | //! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code. |
4 | //! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander. | 4 | //! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander. |
5 | //! | 5 | //! |
6 | //! But we change some several design for fitting RA needs: | 6 | //! But we adapt it to better fit RA needs: |
7 | //! | 7 | //! |
8 | //! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipute and interact with | 8 | //! * We use `ra_tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with |
9 | //! RA then proc-macro2 token stream. | 9 | //! RA than `proc-macro2` token stream. |
10 | //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` | 10 | //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` |
11 | //! rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue) | 11 | //! rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue) |
12 | 12 | ||
@@ -21,36 +21,28 @@ mod dylib; | |||
21 | 21 | ||
22 | use proc_macro::bridge::client::TokenStream; | 22 | use proc_macro::bridge::client::TokenStream; |
23 | use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; | 23 | use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; |
24 | use std::path::Path; | ||
24 | 25 | ||
25 | pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> { | 26 | pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> { |
26 | let expander = dylib::Expander::new(&task.lib) | 27 | let expander = create_expander(&task.lib); |
27 | .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib)); | ||
28 | 28 | ||
29 | match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { | 29 | match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { |
30 | Ok(expansion) => Ok(ExpansionResult { expansion }), | 30 | Ok(expansion) => Ok(ExpansionResult { expansion }), |
31 | Err(msg) => { | 31 | Err(msg) => { |
32 | let reason = format!( | 32 | Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg)) |
33 | "Cannot perform expansion for {}: error {:?}!", | ||
34 | &task.macro_name, | ||
35 | msg.as_str() | ||
36 | ); | ||
37 | Err(reason) | ||
38 | } | 33 | } |
39 | } | 34 | } |
40 | } | 35 | } |
41 | 36 | ||
42 | pub(crate) fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> { | 37 | pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult { |
43 | let expander = dylib::Expander::new(&task.lib) | 38 | let expander = create_expander(&task.lib); |
44 | .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib)); | ||
45 | 39 | ||
46 | match expander.list_macros() { | 40 | ListMacrosResult { macros: expander.list_macros() } |
47 | Ok(macros) => Ok(ListMacrosResult { macros }), | 41 | } |
48 | Err(msg) => { | 42 | |
49 | let reason = | 43 | fn create_expander(lib: &Path) -> dylib::Expander { |
50 | format!("Cannot perform expansion for {:?}: error {:?}!", &task.lib, msg.as_str()); | 44 | dylib::Expander::new(lib) |
51 | Err(reason) | 45 | .unwrap_or_else(|err| panic!("Cannot create expander for {}: {:?}", lib.display(), err)) |
52 | } | ||
53 | } | ||
54 | } | 46 | } |
55 | 47 | ||
56 | pub mod cli; | 48 | pub mod cli; |
diff --git a/crates/ra_proc_macro_srv/src/rustc_server.rs b/crates/ra_proc_macro_srv/src/rustc_server.rs index ec0d35692..f481d70b2 100644 --- a/crates/ra_proc_macro_srv/src/rustc_server.rs +++ b/crates/ra_proc_macro_srv/src/rustc_server.rs | |||
@@ -6,7 +6,7 @@ | |||
6 | //! The original idea from fedochet is using proc-macro2 as backend, | 6 | //! The original idea from fedochet is using proc-macro2 as backend, |
7 | //! we use ra_tt instead for better intergation with RA. | 7 | //! we use ra_tt instead for better intergation with RA. |
8 | //! | 8 | //! |
9 | //! FIXME: No span and source file informatin is implemented yet | 9 | //! FIXME: No span and source file information is implemented yet |
10 | 10 | ||
11 | use crate::proc_macro::bridge::{self, server}; | 11 | use crate::proc_macro::bridge::{self, server}; |
12 | use ra_tt as tt; | 12 | use ra_tt as tt; |
@@ -76,7 +76,16 @@ impl Extend<TokenTree> for TokenStream { | |||
76 | impl Extend<TokenStream> for TokenStream { | 76 | impl Extend<TokenStream> for TokenStream { |
77 | fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) { | 77 | fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) { |
78 | for item in streams { | 78 | for item in streams { |
79 | self.subtree.token_trees.extend(&mut item.into_iter()) | 79 | for tkn in item { |
80 | match tkn { | ||
81 | tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => { | ||
82 | self.subtree.token_trees.extend(subtree.token_trees); | ||
83 | } | ||
84 | _ => { | ||
85 | self.subtree.token_trees.push(tkn); | ||
86 | } | ||
87 | } | ||
88 | } | ||
80 | } | 89 | } |
81 | } | 90 | } |
82 | } | 91 | } |
diff --git a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt index 24507d98d..1f5d940fa 100644 --- a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt +++ b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt | |||
@@ -25,8 +25,7 @@ SUBTREE $ | |||
25 | SUBTREE () 4294967295 | 25 | SUBTREE () 4294967295 |
26 | IDENT feature 4294967295 | 26 | IDENT feature 4294967295 |
27 | PUNCH = [alone] 4294967295 | 27 | PUNCH = [alone] 4294967295 |
28 | SUBTREE $ | 28 | LITERAL "cargo-clippy" 0 |
29 | LITERAL "cargo-clippy" 0 | ||
30 | PUNCH , [alone] 4294967295 | 29 | PUNCH , [alone] 4294967295 |
31 | IDENT allow 4294967295 | 30 | IDENT allow 4294967295 |
32 | SUBTREE () 4294967295 | 31 | SUBTREE () 4294967295 |
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs index 1ee409449..2139ec7a4 100644 --- a/crates/ra_proc_macro_srv/src/tests/utils.rs +++ b/crates/ra_proc_macro_srv/src/tests/utils.rs | |||
@@ -60,6 +60,6 @@ pub fn list(crate_name: &str, version: &str) -> Vec<String> { | |||
60 | let path = fixtures::dylib_path(crate_name, version); | 60 | let path = fixtures::dylib_path(crate_name, version); |
61 | let task = ListMacrosTask { lib: path }; | 61 | let task = ListMacrosTask { lib: path }; |
62 | 62 | ||
63 | let res = list_macros(&task).unwrap(); | 63 | let res = list_macros(&task); |
64 | res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect() | 64 | res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect() |
65 | } | 65 | } |