diff options
| -rw-r--r-- | Cargo.lock | 5 | ||||
| -rw-r--r-- | crates/ra_hir_expand/src/proc_macro.rs | 27 | ||||
| -rw-r--r-- | crates/ra_proc_macro/Cargo.toml | 5 | ||||
| -rw-r--r-- | crates/ra_proc_macro/src/lib.rs | 85 | ||||
| -rw-r--r-- | crates/ra_proc_macro/src/msg.rs | 93 | ||||
| -rw-r--r-- | crates/ra_proc_macro/src/process.rs | 196 | ||||
| -rw-r--r-- | crates/ra_proc_macro/src/rpc.rs | 266 | ||||
| -rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 14 | ||||
| -rw-r--r-- | crates/ra_tt/src/lib.rs | 7 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 1 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/world.rs | 20 |
11 files changed, 696 insertions, 23 deletions
diff --git a/Cargo.lock b/Cargo.lock index 196a37743..f618fcc3d 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
| @@ -1075,7 +1075,12 @@ dependencies = [ | |||
| 1075 | name = "ra_proc_macro" | 1075 | name = "ra_proc_macro" |
| 1076 | version = "0.1.0" | 1076 | version = "0.1.0" |
| 1077 | dependencies = [ | 1077 | dependencies = [ |
| 1078 | "crossbeam-channel", | ||
| 1079 | "jod-thread", | ||
| 1080 | "log", | ||
| 1078 | "ra_tt", | 1081 | "ra_tt", |
| 1082 | "serde", | ||
| 1083 | "serde_json", | ||
| 1079 | ] | 1084 | ] |
| 1080 | 1085 | ||
| 1081 | [[package]] | 1086 | [[package]] |
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs index 4d270e0de..97d1208ec 100644 --- a/crates/ra_hir_expand/src/proc_macro.rs +++ b/crates/ra_hir_expand/src/proc_macro.rs | |||
| @@ -9,6 +9,15 @@ pub struct ProcMacroExpander { | |||
| 9 | proc_macro_id: ProcMacroId, | 9 | proc_macro_id: ProcMacroId, |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | macro_rules! err { | ||
| 13 | ($fmt:literal, $($tt:tt),*) => { | ||
| 14 | mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*))) | ||
| 15 | }; | ||
| 16 | ($fmt:literal) => { | ||
| 17 | mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string())) | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 12 | impl ProcMacroExpander { | 21 | impl ProcMacroExpander { |
| 13 | pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { | 22 | pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { |
| 14 | ProcMacroExpander { krate, proc_macro_id } | 23 | ProcMacroExpander { krate, proc_macro_id } |
| @@ -25,8 +34,24 @@ impl ProcMacroExpander { | |||
| 25 | .proc_macro | 34 | .proc_macro |
| 26 | .get(self.proc_macro_id.0 as usize) | 35 | .get(self.proc_macro_id.0 as usize) |
| 27 | .clone() | 36 | .clone() |
| 28 | .ok_or_else(|| mbe::ExpandError::ConversionError)?; | 37 | .ok_or_else(|| err!("No derive macro found."))?; |
| 38 | |||
| 39 | let tt = remove_derive_atr(tt, &proc_macro.name) | ||
| 40 | .ok_or_else(|| err!("Fail to remove derive for custom derive"))?; | ||
| 29 | 41 | ||
| 30 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) | 42 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) |
| 31 | } | 43 | } |
| 32 | } | 44 | } |
| 45 | |||
| 46 | fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option<tt::Subtree> { | ||
| 47 | // FIXME: proper handle the remove derive | ||
| 48 | // We assume the first 2 tokens are #[derive(name)] | ||
| 49 | if tt.token_trees.len() > 2 { | ||
| 50 | let mut tt = tt.clone(); | ||
| 51 | tt.token_trees.remove(0); | ||
| 52 | tt.token_trees.remove(0); | ||
| 53 | return Some(tt); | ||
| 54 | } | ||
| 55 | |||
| 56 | None | ||
| 57 | } | ||
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml index bc2c37296..d009ceb82 100644 --- a/crates/ra_proc_macro/Cargo.toml +++ b/crates/ra_proc_macro/Cargo.toml | |||
| @@ -10,3 +10,8 @@ doctest = false | |||
| 10 | 10 | ||
| 11 | [dependencies] | 11 | [dependencies] |
| 12 | ra_tt = { path = "../ra_tt" } | 12 | ra_tt = { path = "../ra_tt" } |
| 13 | serde = { version = "1.0", features = ["derive"] } | ||
| 14 | serde_json = "1.0" | ||
| 15 | log = "0.4.8" | ||
| 16 | crossbeam-channel = "0.4.0" | ||
| 17 | jod-thread = "0.1.1" | ||
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs index 5e21dd487..51fbb046a 100644 --- a/crates/ra_proc_macro/src/lib.rs +++ b/crates/ra_proc_macro/src/lib.rs | |||
| @@ -5,55 +5,104 @@ | |||
| 5 | //! is used to provide basic infrastructure for communication between two | 5 | //! is used to provide basic infrastructure for communication between two |
| 6 | //! processes: Client (RA itself), Server (the external program) | 6 | //! processes: Client (RA itself), Server (the external program) |
| 7 | 7 | ||
| 8 | mod rpc; | ||
| 9 | mod process; | ||
| 10 | pub mod msg; | ||
| 11 | |||
| 12 | use process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | ||
| 8 | use ra_tt::{SmolStr, Subtree}; | 13 | use ra_tt::{SmolStr, Subtree}; |
| 14 | use rpc::ProcMacroKind; | ||
| 9 | use std::{ | 15 | use std::{ |
| 10 | path::{Path, PathBuf}, | 16 | path::{Path, PathBuf}, |
| 11 | sync::Arc, | 17 | sync::Arc, |
| 12 | }; | 18 | }; |
| 13 | 19 | ||
| 14 | #[derive(Debug, Clone, PartialEq, Eq)] | 20 | pub use rpc::{ExpansionResult, ExpansionTask}; |
| 21 | |||
| 22 | #[derive(Debug, Clone)] | ||
| 15 | pub struct ProcMacroProcessExpander { | 23 | pub struct ProcMacroProcessExpander { |
| 16 | process: Arc<ProcMacroProcessSrv>, | 24 | process: Arc<ProcMacroProcessSrv>, |
| 25 | dylib_path: PathBuf, | ||
| 17 | name: SmolStr, | 26 | name: SmolStr, |
| 18 | } | 27 | } |
| 19 | 28 | ||
| 29 | impl Eq for ProcMacroProcessExpander {} | ||
| 30 | impl PartialEq for ProcMacroProcessExpander { | ||
| 31 | fn eq(&self, other: &Self) -> bool { | ||
| 32 | self.name == other.name | ||
| 33 | && self.dylib_path == other.dylib_path | ||
| 34 | && Arc::ptr_eq(&self.process, &other.process) | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 20 | impl ra_tt::TokenExpander for ProcMacroProcessExpander { | 38 | impl ra_tt::TokenExpander for ProcMacroProcessExpander { |
| 21 | fn expand( | 39 | fn expand( |
| 22 | &self, | 40 | &self, |
| 23 | _subtree: &Subtree, | 41 | subtree: &Subtree, |
| 24 | _attr: Option<&Subtree>, | 42 | _attr: Option<&Subtree>, |
| 25 | ) -> Result<Subtree, ra_tt::ExpansionError> { | 43 | ) -> Result<Subtree, ra_tt::ExpansionError> { |
| 26 | // FIXME: do nothing for now | 44 | self.process.custom_derive(&self.dylib_path, subtree, &self.name) |
| 27 | Ok(Subtree::default()) | ||
| 28 | } | 45 | } |
| 29 | } | 46 | } |
| 30 | 47 | ||
| 31 | #[derive(Debug, Clone, PartialEq, Eq)] | 48 | #[derive(Debug)] |
| 32 | pub struct ProcMacroProcessSrv { | 49 | enum ProcMacroClientKind { |
| 33 | path: PathBuf, | 50 | Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread }, |
| 51 | Dummy, | ||
| 34 | } | 52 | } |
| 35 | 53 | ||
| 36 | #[derive(Debug, Clone, PartialEq, Eq)] | 54 | #[derive(Debug)] |
| 37 | pub enum ProcMacroClient { | 55 | pub struct ProcMacroClient { |
| 38 | Process { process: Arc<ProcMacroProcessSrv> }, | 56 | kind: ProcMacroClientKind, |
| 39 | Dummy, | ||
| 40 | } | 57 | } |
| 41 | 58 | ||
| 42 | impl ProcMacroClient { | 59 | impl ProcMacroClient { |
| 43 | pub fn extern_process(process_path: &Path) -> ProcMacroClient { | 60 | pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> { |
| 44 | let process = ProcMacroProcessSrv { path: process_path.into() }; | 61 | let (thread, process) = ProcMacroProcessSrv::run(process_path)?; |
| 45 | ProcMacroClient::Process { process: Arc::new(process) } | 62 | Ok(ProcMacroClient { |
| 63 | kind: ProcMacroClientKind::Process { process: Arc::new(process), thread }, | ||
| 64 | }) | ||
| 46 | } | 65 | } |
| 47 | 66 | ||
| 48 | pub fn dummy() -> ProcMacroClient { | 67 | pub fn dummy() -> ProcMacroClient { |
| 49 | ProcMacroClient::Dummy | 68 | ProcMacroClient { kind: ProcMacroClientKind::Dummy } |
| 50 | } | 69 | } |
| 51 | 70 | ||
| 52 | pub fn by_dylib_path( | 71 | pub fn by_dylib_path( |
| 53 | &self, | 72 | &self, |
| 54 | _dylib_path: &Path, | 73 | dylib_path: &Path, |
| 55 | ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> { | 74 | ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> { |
| 56 | // FIXME: return empty for now | 75 | match &self.kind { |
| 57 | vec![] | 76 | ProcMacroClientKind::Dummy => vec![], |
| 77 | ProcMacroClientKind::Process { process, .. } => { | ||
| 78 | let macros = match process.find_proc_macros(dylib_path) { | ||
| 79 | Err(err) => { | ||
| 80 | eprintln!("Fail to find proc macro. Error: {:#?}", err); | ||
| 81 | return vec![]; | ||
| 82 | } | ||
| 83 | Ok(macros) => macros, | ||
| 84 | }; | ||
| 85 | |||
| 86 | macros | ||
| 87 | .into_iter() | ||
| 88 | .filter_map(|(name, kind)| { | ||
| 89 | // FIXME: Support custom derive only for now. | ||
| 90 | match kind { | ||
| 91 | ProcMacroKind::CustomDerive => { | ||
| 92 | let name = SmolStr::new(&name); | ||
| 93 | let expander: Arc<dyn ra_tt::TokenExpander> = | ||
| 94 | Arc::new(ProcMacroProcessExpander { | ||
| 95 | process: process.clone(), | ||
| 96 | name: name.clone(), | ||
| 97 | dylib_path: dylib_path.into(), | ||
| 98 | }); | ||
| 99 | Some((name, expander)) | ||
| 100 | } | ||
| 101 | _ => None, | ||
| 102 | } | ||
| 103 | }) | ||
| 104 | .collect() | ||
| 105 | } | ||
| 106 | } | ||
| 58 | } | 107 | } |
| 59 | } | 108 | } |
diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs new file mode 100644 index 000000000..aa95bcc8f --- /dev/null +++ b/crates/ra_proc_macro/src/msg.rs | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | //! Defines messages for cross-process message based on `ndjson` wire protocol | ||
| 2 | |||
| 3 | use std::{ | ||
| 4 | convert::TryFrom, | ||
| 5 | io::{self, BufRead, Write}, | ||
| 6 | }; | ||
| 7 | |||
| 8 | use crate::{ | ||
| 9 | rpc::{ListMacrosResult, ListMacrosTask}, | ||
| 10 | ExpansionResult, ExpansionTask, | ||
| 11 | }; | ||
| 12 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
| 13 | |||
| 14 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| 15 | pub enum Request { | ||
| 16 | ListMacro(ListMacrosTask), | ||
| 17 | ExpansionMacro(ExpansionTask), | ||
| 18 | } | ||
| 19 | |||
| 20 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| 21 | pub enum Response { | ||
| 22 | Error(ResponseError), | ||
| 23 | ListMacro(ListMacrosResult), | ||
| 24 | ExpansionMacro(ExpansionResult), | ||
| 25 | } | ||
| 26 | |||
| 27 | macro_rules! impl_try_from_response { | ||
| 28 | ($ty:ty, $tag:ident) => { | ||
| 29 | impl TryFrom<Response> for $ty { | ||
| 30 | type Error = &'static str; | ||
| 31 | fn try_from(value: Response) -> Result<Self, Self::Error> { | ||
| 32 | match value { | ||
| 33 | Response::$tag(res) => Ok(res), | ||
| 34 | _ => Err("Fail to convert from response"), | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | impl_try_from_response!(ListMacrosResult, ListMacro); | ||
| 42 | impl_try_from_response!(ExpansionResult, ExpansionMacro); | ||
| 43 | |||
| 44 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| 45 | pub struct ResponseError { | ||
| 46 | pub code: ErrorCode, | ||
| 47 | pub message: String, | ||
| 48 | } | ||
| 49 | |||
| 50 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
| 51 | pub enum ErrorCode { | ||
| 52 | ServerErrorEnd, | ||
| 53 | ExpansionError, | ||
| 54 | } | ||
| 55 | |||
| 56 | pub trait Message: Sized + Serialize + DeserializeOwned { | ||
| 57 | fn read(r: &mut impl BufRead) -> io::Result<Option<Self>> { | ||
| 58 | let text = match read_json(r)? { | ||
| 59 | None => return Ok(None), | ||
| 60 | Some(text) => text, | ||
| 61 | }; | ||
| 62 | let msg = serde_json::from_str(&text)?; | ||
| 63 | Ok(Some(msg)) | ||
| 64 | } | ||
| 65 | fn write(self, w: &mut impl Write) -> io::Result<()> { | ||
| 66 | let text = serde_json::to_string(&self)?; | ||
| 67 | write_json(w, &text) | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | impl Message for Request {} | ||
| 72 | impl Message for Response {} | ||
| 73 | |||
| 74 | fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> { | ||
| 75 | let mut buf = String::new(); | ||
| 76 | if inp.read_line(&mut buf)? == 0 { | ||
| 77 | return Ok(None); | ||
| 78 | } | ||
| 79 | // Remove ending '\n' | ||
| 80 | let buf = &buf[..buf.len() - 1]; | ||
| 81 | if buf.is_empty() { | ||
| 82 | return Ok(None); | ||
| 83 | } | ||
| 84 | Ok(Some(buf.to_string())) | ||
| 85 | } | ||
| 86 | |||
| 87 | fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> { | ||
| 88 | log::debug!("> {}", msg); | ||
| 89 | out.write_all(msg.as_bytes())?; | ||
| 90 | out.write_all(b"\n")?; | ||
| 91 | out.flush()?; | ||
| 92 | Ok(()) | ||
| 93 | } | ||
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs new file mode 100644 index 000000000..e8c85be38 --- /dev/null +++ b/crates/ra_proc_macro/src/process.rs | |||
| @@ -0,0 +1,196 @@ | |||
| 1 | //! Handle process life-time and message passing for proc-macro client | ||
| 2 | |||
| 3 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
| 4 | use ra_tt::Subtree; | ||
| 5 | |||
| 6 | use crate::msg::{ErrorCode, Message, Request, Response, ResponseError}; | ||
| 7 | use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; | ||
| 8 | |||
| 9 | use io::{BufRead, BufReader}; | ||
| 10 | use std::{ | ||
| 11 | convert::{TryFrom, TryInto}, | ||
| 12 | io::{self, Write}, | ||
| 13 | path::{Path, PathBuf}, | ||
| 14 | process::{Child, Command, Stdio}, | ||
| 15 | sync::{Arc, Weak}, | ||
| 16 | }; | ||
| 17 | |||
| 18 | #[derive(Debug, Default)] | ||
| 19 | pub(crate) struct ProcMacroProcessSrv { | ||
| 20 | inner: Option<Weak<Sender<Task>>>, | ||
| 21 | } | ||
| 22 | |||
| 23 | #[derive(Debug)] | ||
| 24 | pub(crate) struct ProcMacroProcessThread { | ||
| 25 | // XXX: drop order is significant | ||
| 26 | sender: Arc<Sender<Task>>, | ||
| 27 | handle: jod_thread::JoinHandle<()>, | ||
| 28 | } | ||
| 29 | |||
| 30 | struct Task { | ||
| 31 | req: Request, | ||
| 32 | result_tx: Sender<Option<Response>>, | ||
| 33 | } | ||
| 34 | |||
| 35 | struct Process { | ||
| 36 | path: PathBuf, | ||
| 37 | child: Child, | ||
| 38 | } | ||
| 39 | |||
| 40 | impl Drop for Process { | ||
| 41 | fn drop(&mut self) { | ||
| 42 | let _ = self.child.kill(); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | impl Process { | ||
| 47 | fn run(process_path: &Path) -> Result<Process, io::Error> { | ||
| 48 | let child = Command::new(process_path.clone()) | ||
| 49 | .stdin(Stdio::piped()) | ||
| 50 | .stdout(Stdio::piped()) | ||
| 51 | .stderr(Stdio::null()) | ||
| 52 | .spawn()?; | ||
| 53 | |||
| 54 | Ok(Process { path: process_path.into(), child }) | ||
| 55 | } | ||
| 56 | |||
| 57 | fn restart(&mut self) -> Result<(), io::Error> { | ||
| 58 | let _ = self.child.kill(); | ||
| 59 | self.child = Command::new(self.path.clone()) | ||
| 60 | .stdin(Stdio::piped()) | ||
| 61 | .stdout(Stdio::piped()) | ||
| 62 | .stderr(Stdio::null()) | ||
| 63 | .spawn()?; | ||
| 64 | Ok(()) | ||
| 65 | } | ||
| 66 | |||
| 67 | fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> { | ||
| 68 | let stdin = self.child.stdin.take()?; | ||
| 69 | let stdout = self.child.stdout.take()?; | ||
| 70 | let read = BufReader::new(stdout); | ||
| 71 | |||
| 72 | Some((stdin, read)) | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | impl ProcMacroProcessSrv { | ||
| 77 | pub fn run( | ||
| 78 | process_path: &Path, | ||
| 79 | ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> { | ||
| 80 | let process = Process::run(process_path)?; | ||
| 81 | |||
| 82 | let (task_tx, task_rx) = bounded(0); | ||
| 83 | let handle = jod_thread::spawn(move || { | ||
| 84 | client_loop(task_rx, process); | ||
| 85 | }); | ||
| 86 | |||
| 87 | let task_tx = Arc::new(task_tx); | ||
| 88 | let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) }; | ||
| 89 | let thread = ProcMacroProcessThread { handle, sender: task_tx }; | ||
| 90 | |||
| 91 | Ok((thread, srv)) | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn find_proc_macros( | ||
| 95 | &self, | ||
| 96 | dylib_path: &Path, | ||
| 97 | ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> { | ||
| 98 | let task = ListMacrosTask { lib: dylib_path.to_path_buf() }; | ||
| 99 | |||
| 100 | let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?; | ||
| 101 | Ok(result.macros) | ||
| 102 | } | ||
| 103 | |||
| 104 | pub fn custom_derive( | ||
| 105 | &self, | ||
| 106 | dylib_path: &Path, | ||
| 107 | subtree: &Subtree, | ||
| 108 | derive_name: &str, | ||
| 109 | ) -> Result<Subtree, ra_tt::ExpansionError> { | ||
| 110 | let task = ExpansionTask { | ||
| 111 | macro_body: subtree.clone(), | ||
| 112 | macro_name: derive_name.to_string(), | ||
| 113 | attributes: None, | ||
| 114 | lib: dylib_path.to_path_buf(), | ||
| 115 | }; | ||
| 116 | |||
| 117 | let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?; | ||
| 118 | Ok(result.expansion) | ||
| 119 | } | ||
| 120 | |||
| 121 | pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError> | ||
| 122 | where | ||
| 123 | R: TryFrom<Response, Error = &'static str>, | ||
| 124 | { | ||
| 125 | let sender = match &self.inner { | ||
| 126 | None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())), | ||
| 127 | Some(it) => it, | ||
| 128 | }; | ||
| 129 | |||
| 130 | let (result_tx, result_rx) = bounded(0); | ||
| 131 | let sender = match sender.upgrade() { | ||
| 132 | None => { | ||
| 133 | return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into())) | ||
| 134 | } | ||
| 135 | Some(it) => it, | ||
| 136 | }; | ||
| 137 | sender.send(Task { req: req.into(), result_tx }).unwrap(); | ||
| 138 | let res = result_rx | ||
| 139 | .recv() | ||
| 140 | .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?; | ||
| 141 | |||
| 142 | match res { | ||
| 143 | Some(Response::Error(err)) => { | ||
| 144 | return Err(ra_tt::ExpansionError::ExpansionError(err.message)); | ||
| 145 | } | ||
| 146 | Some(res) => Ok(res.try_into().map_err(|err| { | ||
| 147 | ra_tt::ExpansionError::Unknown(format!( | ||
| 148 | "Fail to get response, reason : {:#?} ", | ||
| 149 | err | ||
| 150 | )) | ||
| 151 | })?), | ||
| 152 | None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())), | ||
| 153 | } | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | fn client_loop(task_rx: Receiver<Task>, mut process: Process) { | ||
| 158 | let (mut stdin, mut stdout) = match process.stdio() { | ||
| 159 | None => return, | ||
| 160 | Some(it) => it, | ||
| 161 | }; | ||
| 162 | |||
| 163 | for task in task_rx { | ||
| 164 | let Task { req, result_tx } = task; | ||
| 165 | |||
| 166 | match send_request(&mut stdin, &mut stdout, req) { | ||
| 167 | Ok(res) => result_tx.send(res).unwrap(), | ||
| 168 | Err(_err) => { | ||
| 169 | let res = Response::Error(ResponseError { | ||
| 170 | code: ErrorCode::ServerErrorEnd, | ||
| 171 | message: "Server closed".into(), | ||
| 172 | }); | ||
| 173 | result_tx.send(res.into()).unwrap(); | ||
| 174 | // Restart the process | ||
| 175 | if process.restart().is_err() { | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | let stdio = match process.stdio() { | ||
| 179 | None => break, | ||
| 180 | Some(it) => it, | ||
| 181 | }; | ||
| 182 | stdin = stdio.0; | ||
| 183 | stdout = stdio.1; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | fn send_request( | ||
| 190 | mut writer: &mut impl Write, | ||
| 191 | mut reader: &mut impl BufRead, | ||
| 192 | req: Request, | ||
| 193 | ) -> Result<Option<Response>, io::Error> { | ||
| 194 | req.write(&mut writer)?; | ||
| 195 | Ok(Response::read(&mut reader)?) | ||
| 196 | } | ||
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs new file mode 100644 index 000000000..66b3f55db --- /dev/null +++ b/crates/ra_proc_macro/src/rpc.rs | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | //! Data struture serialization related stuffs for RPC | ||
| 2 | //! | ||
| 3 | //! Define all necessary rpc serialization data structure, | ||
| 4 | //! which include ra_tt related data and some task messages. | ||
| 5 | //! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier, | ||
| 6 | //! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")] | ||
| 7 | //! for separation of code responsibility. | ||
| 8 | |||
| 9 | use ra_tt::{ | ||
| 10 | Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId, | ||
| 11 | TokenTree, | ||
| 12 | }; | ||
| 13 | use serde::{Deserialize, Serialize}; | ||
| 14 | use std::path::PathBuf; | ||
| 15 | |||
| 16 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
| 17 | pub struct ListMacrosTask { | ||
| 18 | pub lib: PathBuf, | ||
| 19 | } | ||
| 20 | |||
| 21 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
| 22 | pub enum ProcMacroKind { | ||
| 23 | CustomDerive, | ||
| 24 | FuncLike, | ||
| 25 | Attr, | ||
| 26 | } | ||
| 27 | |||
| 28 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] | ||
| 29 | pub struct ListMacrosResult { | ||
| 30 | pub macros: Vec<(String, ProcMacroKind)>, | ||
| 31 | } | ||
| 32 | |||
| 33 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
| 34 | pub struct ExpansionTask { | ||
| 35 | /// Argument of macro call. | ||
| 36 | /// | ||
| 37 | /// In custom derive that would be a struct or enum; in attribute-like macro - underlying | ||
| 38 | /// item; in function-like macro - the macro body. | ||
| 39 | #[serde(with = "SubtreeDef")] | ||
| 40 | pub macro_body: Subtree, | ||
| 41 | |||
| 42 | /// Names of macros to expand. | ||
| 43 | /// | ||
| 44 | /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In | ||
| 45 | /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`). | ||
| 46 | pub macro_name: String, | ||
| 47 | |||
| 48 | /// Possible attributes for the attribute-like macros. | ||
| 49 | #[serde(with = "opt_subtree_def")] | ||
| 50 | pub attributes: Option<Subtree>, | ||
| 51 | |||
| 52 | pub lib: PathBuf, | ||
| 53 | } | ||
| 54 | |||
| 55 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] | ||
| 56 | pub struct ExpansionResult { | ||
| 57 | #[serde(with = "SubtreeDef")] | ||
| 58 | pub expansion: Subtree, | ||
| 59 | } | ||
| 60 | |||
| 61 | #[derive(Serialize, Deserialize)] | ||
| 62 | #[serde(remote = "DelimiterKind")] | ||
| 63 | enum DelimiterKindDef { | ||
| 64 | Parenthesis, | ||
| 65 | Brace, | ||
| 66 | Bracket, | ||
| 67 | } | ||
| 68 | |||
| 69 | #[derive(Serialize, Deserialize)] | ||
| 70 | #[serde(remote = "TokenId")] | ||
| 71 | struct TokenIdDef(u32); | ||
| 72 | |||
| 73 | #[derive(Serialize, Deserialize)] | ||
| 74 | #[serde(remote = "Delimiter")] | ||
| 75 | struct DelimiterDef { | ||
| 76 | #[serde(with = "TokenIdDef")] | ||
| 77 | pub id: TokenId, | ||
| 78 | #[serde(with = "DelimiterKindDef")] | ||
| 79 | pub kind: DelimiterKind, | ||
| 80 | } | ||
| 81 | |||
| 82 | #[derive(Serialize, Deserialize)] | ||
| 83 | #[serde(remote = "Subtree")] | ||
| 84 | struct SubtreeDef { | ||
| 85 | #[serde(default, with = "opt_delimiter_def")] | ||
| 86 | pub delimiter: Option<Delimiter>, | ||
| 87 | #[serde(with = "vec_token_tree")] | ||
| 88 | pub token_trees: Vec<TokenTree>, | ||
| 89 | } | ||
| 90 | |||
| 91 | #[derive(Serialize, Deserialize)] | ||
| 92 | #[serde(remote = "TokenTree")] | ||
| 93 | enum TokenTreeDef { | ||
| 94 | #[serde(with = "LeafDef")] | ||
| 95 | Leaf(Leaf), | ||
| 96 | #[serde(with = "SubtreeDef")] | ||
| 97 | Subtree(Subtree), | ||
| 98 | } | ||
| 99 | |||
| 100 | #[derive(Serialize, Deserialize)] | ||
| 101 | #[serde(remote = "Leaf")] | ||
| 102 | enum LeafDef { | ||
| 103 | #[serde(with = "LiteralDef")] | ||
| 104 | Literal(Literal), | ||
| 105 | #[serde(with = "PunctDef")] | ||
| 106 | Punct(Punct), | ||
| 107 | #[serde(with = "IdentDef")] | ||
| 108 | Ident(Ident), | ||
| 109 | } | ||
| 110 | |||
| 111 | #[derive(Serialize, Deserialize)] | ||
| 112 | #[serde(remote = "Literal")] | ||
| 113 | struct LiteralDef { | ||
| 114 | pub text: SmolStr, | ||
| 115 | #[serde(with = "TokenIdDef")] | ||
| 116 | pub id: TokenId, | ||
| 117 | } | ||
| 118 | |||
| 119 | #[derive(Serialize, Deserialize)] | ||
| 120 | #[serde(remote = "Punct")] | ||
| 121 | struct PunctDef { | ||
| 122 | pub char: char, | ||
| 123 | #[serde(with = "SpacingDef")] | ||
| 124 | pub spacing: Spacing, | ||
| 125 | #[serde(with = "TokenIdDef")] | ||
| 126 | pub id: TokenId, | ||
| 127 | } | ||
| 128 | |||
| 129 | #[derive(Serialize, Deserialize)] | ||
| 130 | #[serde(remote = "Spacing")] | ||
| 131 | enum SpacingDef { | ||
| 132 | Alone, | ||
| 133 | Joint, | ||
| 134 | } | ||
| 135 | |||
| 136 | #[derive(Serialize, Deserialize)] | ||
| 137 | #[serde(remote = "Ident")] | ||
| 138 | struct IdentDef { | ||
| 139 | pub text: SmolStr, | ||
| 140 | #[serde(with = "TokenIdDef")] | ||
| 141 | pub id: TokenId, | ||
| 142 | } | ||
| 143 | |||
| 144 | mod opt_delimiter_def { | ||
| 145 | use super::{Delimiter, DelimiterDef}; | ||
| 146 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||
| 147 | |||
| 148 | pub fn serialize<S>(value: &Option<Delimiter>, serializer: S) -> Result<S::Ok, S::Error> | ||
| 149 | where | ||
| 150 | S: Serializer, | ||
| 151 | { | ||
| 152 | #[derive(Serialize)] | ||
| 153 | struct Helper<'a>(#[serde(with = "DelimiterDef")] &'a Delimiter); | ||
| 154 | value.as_ref().map(Helper).serialize(serializer) | ||
| 155 | } | ||
| 156 | |||
| 157 | pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Delimiter>, D::Error> | ||
| 158 | where | ||
| 159 | D: Deserializer<'de>, | ||
| 160 | { | ||
| 161 | #[derive(Deserialize)] | ||
| 162 | struct Helper(#[serde(with = "DelimiterDef")] Delimiter); | ||
| 163 | let helper = Option::deserialize(deserializer)?; | ||
| 164 | Ok(helper.map(|Helper(external)| external)) | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | mod opt_subtree_def { | ||
| 169 | use super::{Subtree, SubtreeDef}; | ||
| 170 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||
| 171 | |||
| 172 | pub fn serialize<S>(value: &Option<Subtree>, serializer: S) -> Result<S::Ok, S::Error> | ||
| 173 | where | ||
| 174 | S: Serializer, | ||
| 175 | { | ||
| 176 | #[derive(Serialize)] | ||
| 177 | struct Helper<'a>(#[serde(with = "SubtreeDef")] &'a Subtree); | ||
| 178 | value.as_ref().map(Helper).serialize(serializer) | ||
| 179 | } | ||
| 180 | |||
| 181 | pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Subtree>, D::Error> | ||
| 182 | where | ||
| 183 | D: Deserializer<'de>, | ||
| 184 | { | ||
| 185 | #[derive(Deserialize)] | ||
| 186 | struct Helper(#[serde(with = "SubtreeDef")] Subtree); | ||
| 187 | let helper = Option::deserialize(deserializer)?; | ||
| 188 | Ok(helper.map(|Helper(external)| external)) | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | mod vec_token_tree { | ||
| 193 | use super::{TokenTree, TokenTreeDef}; | ||
| 194 | use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; | ||
| 195 | |||
| 196 | pub fn serialize<S>(value: &Vec<TokenTree>, serializer: S) -> Result<S::Ok, S::Error> | ||
| 197 | where | ||
| 198 | S: Serializer, | ||
| 199 | { | ||
| 200 | #[derive(Serialize)] | ||
| 201 | struct Helper<'a>(#[serde(with = "TokenTreeDef")] &'a TokenTree); | ||
| 202 | |||
| 203 | let items: Vec<_> = value.iter().map(Helper).collect(); | ||
| 204 | let mut seq = serializer.serialize_seq(Some(items.len()))?; | ||
| 205 | for element in items { | ||
| 206 | seq.serialize_element(&element)?; | ||
| 207 | } | ||
| 208 | seq.end() | ||
| 209 | } | ||
| 210 | |||
| 211 | pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<TokenTree>, D::Error> | ||
| 212 | where | ||
| 213 | D: Deserializer<'de>, | ||
| 214 | { | ||
| 215 | #[derive(Deserialize)] | ||
| 216 | struct Helper(#[serde(with = "TokenTreeDef")] TokenTree); | ||
| 217 | |||
| 218 | let helper = Vec::deserialize(deserializer)?; | ||
| 219 | Ok(helper.into_iter().map(|Helper(external)| external).collect()) | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | #[cfg(test)] | ||
| 224 | mod tests { | ||
| 225 | use super::*; | ||
| 226 | |||
| 227 | fn fixture_token_tree() -> Subtree { | ||
| 228 | let mut subtree = Subtree::default(); | ||
| 229 | subtree | ||
| 230 | .token_trees | ||
| 231 | .push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into())); | ||
| 232 | subtree | ||
| 233 | .token_trees | ||
| 234 | .push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into())); | ||
| 235 | subtree.token_trees.push(TokenTree::Subtree( | ||
| 236 | Subtree { | ||
| 237 | delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }), | ||
| 238 | token_trees: vec![], | ||
| 239 | } | ||
| 240 | .into(), | ||
| 241 | )); | ||
| 242 | subtree | ||
| 243 | } | ||
| 244 | |||
| 245 | #[test] | ||
| 246 | fn test_proc_macro_rpc_works() { | ||
| 247 | let tt = fixture_token_tree(); | ||
| 248 | let task = ExpansionTask { | ||
| 249 | macro_body: tt.clone(), | ||
| 250 | macro_name: Default::default(), | ||
| 251 | attributes: None, | ||
| 252 | lib: Default::default(), | ||
| 253 | }; | ||
| 254 | |||
| 255 | let json = serde_json::to_string(&task).unwrap(); | ||
| 256 | let back: ExpansionTask = serde_json::from_str(&json).unwrap(); | ||
| 257 | |||
| 258 | assert_eq!(task.macro_body, back.macro_body); | ||
| 259 | |||
| 260 | let result = ExpansionResult { expansion: tt.clone() }; | ||
| 261 | let json = serde_json::to_string(&result).unwrap(); | ||
| 262 | let back: ExpansionResult = serde_json::from_str(&json).unwrap(); | ||
| 263 | |||
| 264 | assert_eq!(result, back); | ||
| 265 | } | ||
| 266 | } | ||
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 738fd6f61..0aea01d83 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
| 2 | 2 | ||
| 3 | use std::{ | 3 | use std::{ |
| 4 | ffi::OsStr, | ||
| 4 | ops, | 5 | ops, |
| 5 | path::{Path, PathBuf}, | 6 | path::{Path, PathBuf}, |
| 6 | }; | 7 | }; |
| @@ -299,7 +300,10 @@ pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) | |||
| 299 | Message::CompilerArtifact(message) => { | 300 | Message::CompilerArtifact(message) => { |
| 300 | if message.target.kind.contains(&"proc-macro".to_string()) { | 301 | if message.target.kind.contains(&"proc-macro".to_string()) { |
| 301 | let package_id = message.package_id; | 302 | let package_id = message.package_id; |
| 302 | if let Some(filename) = message.filenames.get(0) { | 303 | // Skip rmeta file |
| 304 | if let Some(filename) = | ||
| 305 | message.filenames.iter().filter(|name| is_dylib(name)).next() | ||
| 306 | { | ||
| 303 | acc.proc_dylib_paths.insert(package_id, filename.clone()); | 307 | acc.proc_dylib_paths.insert(package_id, filename.clone()); |
| 304 | } | 308 | } |
| 305 | } | 309 | } |
| @@ -316,3 +320,11 @@ pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) | |||
| 316 | 320 | ||
| 317 | acc | 321 | acc |
| 318 | } | 322 | } |
| 323 | |||
| 324 | // FIXME: File a better way to know if it is a dylib | ||
| 325 | fn is_dylib(path: &Path) -> bool { | ||
| 326 | match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) { | ||
| 327 | None => false, | ||
| 328 | Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"), | ||
| 329 | } | ||
| 330 | } | ||
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs index 1015ce0a6..bd484aa30 100644 --- a/crates/ra_tt/src/lib.rs +++ b/crates/ra_tt/src/lib.rs | |||
| @@ -189,7 +189,12 @@ impl Subtree { | |||
| 189 | pub mod buffer; | 189 | pub mod buffer; |
| 190 | 190 | ||
| 191 | #[derive(Debug, PartialEq, Eq)] | 191 | #[derive(Debug, PartialEq, Eq)] |
| 192 | pub enum ExpansionError {} | 192 | pub enum ExpansionError { |
| 193 | IOError(String), | ||
| 194 | JsonError(String), | ||
| 195 | Unknown(String), | ||
| 196 | ExpansionError(String), | ||
| 197 | } | ||
| 193 | 198 | ||
| 194 | pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { | 199 | pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { |
| 195 | fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) | 200 | fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index c233f72ff..9fd568601 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
| @@ -109,6 +109,7 @@ fn get_config( | |||
| 109 | }, | 109 | }, |
| 110 | rustfmt_args: config.rustfmt_args.clone(), | 110 | rustfmt_args: config.rustfmt_args.clone(), |
| 111 | vscode_lldb: config.vscode_lldb, | 111 | vscode_lldb: config.vscode_lldb, |
| 112 | proc_macro_srv: None, // FIXME: get this from config | ||
| 112 | } | 113 | } |
| 113 | } | 114 | } |
| 114 | 115 | ||
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index a15a7085f..4e2ddcb8b 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
| @@ -58,6 +58,7 @@ pub struct Config { | |||
| 58 | pub rustfmt_args: Vec<String>, | 58 | pub rustfmt_args: Vec<String>, |
| 59 | pub check: CheckConfig, | 59 | pub check: CheckConfig, |
| 60 | pub vscode_lldb: bool, | 60 | pub vscode_lldb: bool, |
| 61 | pub proc_macro_srv: Option<String>, | ||
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | /// `WorldState` is the primary mutable state of the language server | 64 | /// `WorldState` is the primary mutable state of the language server |
| @@ -167,8 +168,23 @@ impl WorldState { | |||
| 167 | vfs_file.map(|f| FileId(f.0)) | 168 | vfs_file.map(|f| FileId(f.0)) |
| 168 | }; | 169 | }; |
| 169 | 170 | ||
| 170 | let proc_macro_client = | 171 | let proc_macro_client = match &config.proc_macro_srv { |
| 171 | ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv")); | 172 | None => ProcMacroClient::dummy(), |
| 173 | Some(srv) => { | ||
| 174 | let path = Path::new(&srv); | ||
| 175 | match ProcMacroClient::extern_process(path) { | ||
| 176 | Ok(it) => it, | ||
| 177 | Err(err) => { | ||
| 178 | log::error!( | ||
| 179 | "Fail to run ra_proc_macro_srv from path {}, error : {}", | ||
| 180 | path.to_string_lossy(), | ||
| 181 | err | ||
| 182 | ); | ||
| 183 | ProcMacroClient::dummy() | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
| 187 | }; | ||
| 172 | 188 | ||
| 173 | workspaces | 189 | workspaces |
| 174 | .iter() | 190 | .iter() |
