ra_proc_macro: cleanups here and there

This commit is contained in:
veetaha 2020-04-20 21:26:10 +03:00
parent 36840bd6c7
commit d3019164dc
12 changed files with 141 additions and 190 deletions

View File

@ -2,7 +2,7 @@
//! //!
//! We separate proc-macro expanding logic to an extern program to allow //! We separate proc-macro expanding logic to an extern program to allow
//! different implementations (e.g. wasm or dylib loading). And this crate //! different implementations (e.g. wasm or dylib loading). And this crate
//! is used to provide basic infrastructure for communication between two //! is used to provide basic infrastructure for communication between two
//! processes: Client (RA itself), Server (the external program) //! processes: Client (RA itself), Server (the external program)
mod rpc; mod rpc;
@ -13,6 +13,7 @@ use process::{ProcMacroProcessSrv, ProcMacroProcessThread};
use ra_tt::{SmolStr, Subtree}; use ra_tt::{SmolStr, Subtree};
use std::{ use std::{
ffi::OsStr, ffi::OsStr,
io,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc, sync::Arc,
}; };
@ -57,14 +58,10 @@ pub struct ProcMacroClient {
} }
impl ProcMacroClient { impl ProcMacroClient {
pub fn extern_process<I, S>( pub fn extern_process(
process_path: &Path, process_path: PathBuf,
args: I, args: impl IntoIterator<Item = impl AsRef<OsStr>>,
) -> Result<ProcMacroClient, std::io::Error> ) -> io::Result<ProcMacroClient> {
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?; let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
Ok(ProcMacroClient { Ok(ProcMacroClient {
kind: ProcMacroClientKind::Process { process: Arc::new(process), thread }, kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
@ -84,7 +81,7 @@ impl ProcMacroClient {
ProcMacroClientKind::Process { process, .. } => { ProcMacroClientKind::Process { process, .. } => {
let macros = match process.find_proc_macros(dylib_path) { let macros = match process.find_proc_macros(dylib_path) {
Err(err) => { Err(err) => {
eprintln!("Fail to find proc macro. Error: {:#?}", err); eprintln!("Failed to find proc macros. Error: {:#?}", err);
return vec![]; return vec![];
} }
Ok(macros) => macros, Ok(macros) => macros,

View File

@ -1,4 +1,4 @@
//! Defines messages for cross-process message based on `ndjson` wire protocol //! Defines messages for cross-process message passing based on `ndjson` wire protocol
use std::{ use std::{
convert::TryFrom, convert::TryFrom,
@ -31,7 +31,7 @@ macro_rules! impl_try_from_response {
fn try_from(value: Response) -> Result<Self, Self::Error> { fn try_from(value: Response) -> Result<Self, Self::Error> {
match value { match value {
Response::$tag(res) => Ok(res), Response::$tag(res) => Ok(res),
_ => Err("Fail to convert from response"), _ => Err(concat!("Failed to convert response to ", stringify!($tag))),
} }
} }
} }
@ -53,18 +53,16 @@ pub enum ErrorCode {
ExpansionError, ExpansionError,
} }
pub trait Message: Sized + Serialize + DeserializeOwned { pub trait Message: Serialize + DeserializeOwned {
fn read(r: &mut impl BufRead) -> io::Result<Option<Self>> { fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> {
let text = match read_json(r)? { Ok(match read_json(inp)? {
None => return Ok(None), None => None,
Some(text) => text, Some(text) => Some(serde_json::from_str(&text)?),
}; })
let msg = serde_json::from_str(&text)?;
Ok(Some(msg))
} }
fn write(self, w: &mut impl Write) -> io::Result<()> { fn write(self, out: &mut impl Write) -> io::Result<()> {
let text = serde_json::to_string(&self)?; let text = serde_json::to_string(&self)?;
write_json(w, &text) write_json(out, &text)
} }
} }
@ -73,15 +71,12 @@ impl Message for Response {}
fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> { fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
let mut buf = String::new(); let mut buf = String::new();
if inp.read_line(&mut buf)? == 0 { inp.read_line(&mut buf)?;
return Ok(None); buf.pop(); // Remove traling '\n'
} Ok(match buf.len() {
// Remove ending '\n' 0 => None,
let buf = &buf[..buf.len() - 1]; _ => Some(buf),
if buf.is_empty() { })
return Ok(None);
}
Ok(Some(buf.to_string()))
} }
fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> { fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {

View File

@ -45,24 +45,23 @@ impl Drop for Process {
} }
impl Process { impl Process {
fn run<I, S>(process_path: &Path, args: I) -> Result<Process, io::Error> fn run(
where process_path: PathBuf,
I: IntoIterator<Item = S>, args: impl IntoIterator<Item = impl AsRef<OsStr>>,
S: AsRef<OsStr>, ) -> Result<Process, io::Error> {
{ let child = Command::new(&process_path)
let child = Command::new(process_path.clone())
.args(args) .args(args)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::null()) .stderr(Stdio::null())
.spawn()?; .spawn()?;
Ok(Process { path: process_path.into(), child }) Ok(Process { path: process_path, child })
} }
fn restart(&mut self) -> Result<(), io::Error> { fn restart(&mut self) -> Result<(), io::Error> {
let _ = self.child.kill(); let _ = self.child.kill();
self.child = Command::new(self.path.clone()) self.child = Command::new(&self.path)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.stderr(Stdio::null()) .stderr(Stdio::null())
@ -80,14 +79,10 @@ impl Process {
} }
impl ProcMacroProcessSrv { impl ProcMacroProcessSrv {
pub fn run<I, S>( pub fn run(
process_path: &Path, process_path: PathBuf,
args: I, args: impl IntoIterator<Item = impl AsRef<OsStr>>,
) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> ) -> io::Result<(ProcMacroProcessThread, ProcMacroProcessSrv)> {
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let process = Process::run(process_path, args)?; let process = Process::run(process_path, args)?;
let (task_tx, task_rx) = bounded(0); let (task_tx, task_rx) = bounded(0);

View File

@ -1,9 +1,9 @@
//! Data struture serialization related stuffs for RPC //! Data struture serialization related stuff for RPC
//! //!
//! Define all necessary rpc serialization data structure, //! Defines all necessary rpc serialization data structures,
//! which include ra_tt related data and some task messages. //! which includes `ra_tt` related data and some task messages.
//! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier, //! Although adding `Serialize` and `Deserialize` traits to `ra_tt` directly seems
//! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")] //! to be much easier, we deliberately duplicate `ra_tt` structs with `#[serde(with = "XXDef")]`
//! for separation of code responsibility. //! for separation of code responsibility.
use ra_tt::{ use ra_tt::{
@ -34,15 +34,15 @@ pub struct ListMacrosResult {
pub struct ExpansionTask { pub struct ExpansionTask {
/// Argument of macro call. /// Argument of macro call.
/// ///
/// In custom derive that would be a struct or enum; in attribute-like macro - underlying /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
/// item; in function-like macro - the macro body. /// item; in function-like macro - the macro body.
#[serde(with = "SubtreeDef")] #[serde(with = "SubtreeDef")]
pub macro_body: Subtree, pub macro_body: Subtree,
/// Names of macros to expand. /// Names of macros to expand. // TODO: are they comma-separated?
/// ///
/// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In
/// attribute-like and functiona-like macros - single name of macro itself (`show_streams`). /// attribute-like and function-like macros - single name of macro itself (`show_streams`).
pub macro_name: String, pub macro_name: String,
/// Possible attributes for the attribute-like macros. /// Possible attributes for the attribute-like macros.

View File

@ -2,55 +2,43 @@
use crate::{expand_task, list_macros}; use crate::{expand_task, list_macros};
use ra_proc_macro::msg::{self, Message}; use ra_proc_macro::msg::{self, Message};
use std::io; use std::io;
fn read_request() -> Result<Option<msg::Request>, io::Error> {
let stdin = io::stdin();
let mut stdin = stdin.lock();
msg::Request::read(&mut stdin)
}
fn write_response(res: Result<msg::Response, String>) -> Result<(), io::Error> {
let msg: msg::Response = match res {
Ok(res) => res,
Err(err) => msg::Response::Error(msg::ResponseError {
code: msg::ErrorCode::ExpansionError,
message: err,
}),
};
let stdout = io::stdout();
let mut stdout = stdout.lock();
msg.write(&mut stdout)
}
pub fn run() { pub fn run() {
loop { loop {
let req = match read_request() { let req = match read_request() {
Err(err) => { Err(err) => {
eprintln!("Read message error on ra_proc_macro_srv: {}", err.to_string()); eprintln!("Read message error on ra_proc_macro_srv: {}", err);
continue; continue;
} }
Ok(None) => continue, Ok(None) => continue,
Ok(Some(req)) => req, Ok(Some(req)) => req,
}; };
match req { let res = match req {
msg::Request::ListMacro(task) => { msg::Request::ListMacro(task) => Ok(msg::Response::ListMacro(list_macros(&task))),
if let Err(err) =
write_response(list_macros(&task).map(|it| msg::Response::ListMacro(it)))
{
eprintln!("Write message error on list macro: {}", err);
}
}
msg::Request::ExpansionMacro(task) => { msg::Request::ExpansionMacro(task) => {
if let Err(err) = expand_task(&task).map(msg::Response::ExpansionMacro)
write_response(expand_task(&task).map(|it| msg::Response::ExpansionMacro(it)))
{
eprintln!("Write message error on expansion macro: {}", err);
}
} }
};
let msg = res.unwrap_or_else(|err| {
msg::Response::Error(msg::ResponseError {
code: msg::ErrorCode::ExpansionError,
message: err,
})
});
if let Err(err) = write_response(msg) {
eprintln!("Write message error: {}", err);
} }
} }
} }
fn read_request() -> io::Result<Option<msg::Request>> {
msg::Request::read(&mut io::stdin().lock())
}
fn write_response(msg: msg::Response) -> io::Result<()> {
msg.write(&mut io::stdout().lock())
}

View File

@ -9,43 +9,37 @@ use libloading::Library;
use memmap::Mmap; use memmap::Mmap;
use ra_proc_macro::ProcMacroKind; use ra_proc_macro::ProcMacroKind;
use std::io::Error as IoError; use std::io;
use std::io::ErrorKind as IoErrorKind;
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> IoError { fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
IoError::new(IoErrorKind::InvalidData, e) io::Error::new(io::ErrorKind::InvalidData, e)
} }
fn is_derive_registrar_symbol(symbol: &str) -> bool { fn is_derive_registrar_symbol(symbol: &&str) -> bool {
symbol.contains(NEW_REGISTRAR_SYMBOL) symbol.contains(NEW_REGISTRAR_SYMBOL)
} }
fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> { fn find_registrar_symbol(file: &Path) -> io::Result<Option<String>> {
let file = File::open(file)?; let file = File::open(file)?;
let buffer = unsafe { Mmap::map(&file)? }; let buffer = unsafe { Mmap::map(&file)? };
let object = Object::parse(&buffer).map_err(invalid_data_err)?; let object = Object::parse(&buffer).map_err(invalid_data_err)?;
match object { let name = match object {
Object::Elf(elf) => { Object::Elf(elf) => {
let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?; let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
let name = symbols.into_iter().find(is_derive_registrar_symbol).map(&str::to_owned)
symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string());
Ok(name)
}
Object::PE(pe) => {
let name = pe
.exports
.iter()
.flat_map(|s| s.name)
.find(|s| is_derive_registrar_symbol(s))
.map(|s| s.to_string());
Ok(name)
} }
Object::PE(pe) => pe
.exports
.iter()
.flat_map(|s| s.name)
.find(is_derive_registrar_symbol)
.map(&str::to_owned),
Object::Mach(Mach::Binary(binary)) => { Object::Mach(Mach::Binary(binary)) => {
let exports = binary.exports().map_err(invalid_data_err)?; let exports = binary.exports().map_err(invalid_data_err)?;
let name = exports exports
.iter() .iter()
.map(|s| { .map(|s| {
// In macos doc: // In macos doc:
@ -58,12 +52,12 @@ fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
&s.name &s.name
} }
}) })
.find(|s| is_derive_registrar_symbol(s)) .find(is_derive_registrar_symbol)
.map(|s| s.to_string()); .map(&str::to_owned)
Ok(name)
} }
_ => Ok(None), _ => return Ok(None),
} };
Ok(name)
} }
/// Loads dynamic library in platform dependent manner. /// Loads dynamic library in platform dependent manner.
@ -93,15 +87,16 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
} }
struct ProcMacroLibraryLibloading { struct ProcMacroLibraryLibloading {
// Hold the dylib to prevent it for unloadeding // Hold the dylib to prevent it from unloading
_lib: Library, _lib: Library,
exported_macros: Vec<bridge::client::ProcMacro>, exported_macros: Vec<bridge::client::ProcMacro>,
} }
impl ProcMacroLibraryLibloading { impl ProcMacroLibraryLibloading {
fn open(file: &Path) -> Result<Self, IoError> { fn open(file: &Path) -> io::Result<Self> {
let symbol_name = find_registrar_symbol(file)? let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
.ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?; invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file))
})?;
let lib = load_library(file).map_err(invalid_data_err)?; let lib = load_library(file).map_err(invalid_data_err)?;
let exported_macros = { let exported_macros = {
@ -121,18 +116,16 @@ pub struct Expander {
} }
impl Expander { impl Expander {
pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> { pub fn new(lib: &Path) -> Result<Expander, String> {
let mut libs = vec![]; // Some libraries for dynamic loading require canonicalized path even when it is
/* Some libraries for dynamic loading require canonicalized path (even when it is // already absolute
already absolute let lib = lib
*/ .canonicalize()
let lib = .unwrap_or_else(|err| panic!("Cannot canonicalize {:?}: {:?}", lib, err));
lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));
let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?; let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
libs.push(library);
Ok(Expander { libs }) Ok(Expander { libs: vec![library] })
} }
pub fn expand( pub fn expand(
@ -176,7 +169,6 @@ impl Expander {
parsed_attributes, parsed_attributes,
parsed_body, parsed_body,
); );
return res.map(|it| it.subtree); return res.map(|it| it.subtree);
} }
_ => continue, _ => continue,
@ -187,26 +179,21 @@ impl Expander {
Err(bridge::PanicMessage::String("Nothing to expand".to_string())) Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
} }
pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> { pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
let mut result = vec![]; self.libs
.iter()
for lib in &self.libs { .flat_map(|it| &it.exported_macros)
for proc_macro in &lib.exported_macros { .map(|proc_macro| match proc_macro {
let res = match proc_macro { bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { (trait_name.to_string(), ProcMacroKind::CustomDerive)
(trait_name.to_string(), ProcMacroKind::CustomDerive) }
} bridge::client::ProcMacro::Bang { name, .. } => {
bridge::client::ProcMacro::Bang { name, .. } => { (name.to_string(), ProcMacroKind::FuncLike)
(name.to_string(), ProcMacroKind::FuncLike) }
} bridge::client::ProcMacro::Attr { name, .. } => {
bridge::client::ProcMacro::Attr { name, .. } => { (name.to_string(), ProcMacroKind::Attr)
(name.to_string(), ProcMacroKind::Attr) }
} })
}; .collect()
result.push(res);
}
}
Ok(result)
} }
} }

View File

@ -3,10 +3,10 @@
//! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code. //! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code.
//! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander. //! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander.
//! //!
//! But we change some several design for fitting RA needs: //! But we adapt it to better fit RA needs:
//! //!
//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipute and interact with //! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipulate and interact with
//! RA then proc-macro2 token stream. //! RA than `proc-macro2` token stream.
//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
//! rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue) //! rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue)
@ -21,36 +21,28 @@ mod dylib;
use proc_macro::bridge::client::TokenStream; use proc_macro::bridge::client::TokenStream;
use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
use std::path::Path;
pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> { pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
let expander = dylib::Expander::new(&task.lib) let expander = create_expander(&task.lib);
.expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) { match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
Ok(expansion) => Ok(ExpansionResult { expansion }), Ok(expansion) => Ok(ExpansionResult { expansion }),
Err(msg) => { Err(msg) => {
let reason = format!( Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
"Cannot perform expansion for {}: error {:?}!",
&task.macro_name,
msg.as_str()
);
Err(reason)
} }
} }
} }
pub(crate) fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> { pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
let expander = dylib::Expander::new(&task.lib) let expander = create_expander(&task.lib);
.expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
match expander.list_macros() { ListMacrosResult { macros: expander.list_macros() }
Ok(macros) => Ok(ListMacrosResult { macros }), }
Err(msg) => {
let reason = fn create_expander(lib: &Path) -> dylib::Expander {
format!("Cannot perform expansion for {:?}: error {:?}!", &task.lib, msg.as_str()); dylib::Expander::new(lib)
Err(reason) .unwrap_or_else(|err| panic!("Cannot create expander for {:?}: {:?}", lib, err))
}
}
} }
pub mod cli; pub mod cli;

View File

@ -6,7 +6,7 @@
//! The original idea from fedochet is using proc-macro2 as backend, //! The original idea from fedochet is using proc-macro2 as backend,
//! we use ra_tt instead for better intergation with RA. //! we use ra_tt instead for better intergation with RA.
//! //!
//! FIXME: No span and source file informatin is implemented yet //! FIXME: No span and source file information is implemented yet
use crate::proc_macro::bridge::{self, server}; use crate::proc_macro::bridge::{self, server};
use ra_tt as tt; use ra_tt as tt;

View File

@ -60,6 +60,6 @@ pub fn list(crate_name: &str, version: &str) -> Vec<String> {
let path = fixtures::dylib_path(crate_name, version); let path = fixtures::dylib_path(crate_name, version);
let task = ListMacrosTask { lib: path }; let task = ListMacrosTask { lib: path };
let res = list_macros(&task).unwrap(); let res = list_macros(&task);
res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect() res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
} }

View File

@ -51,7 +51,7 @@ fn main() -> Result<()> {
cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)? cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
} }
args::Command::ProcMacro => run_proc_macro_sv()?, args::Command::ProcMacro => run_proc_macro_srv()?,
args::Command::RunServer => run_server()?, args::Command::RunServer => run_server()?,
args::Command::Version => println!("rust-analyzer {}", env!("REV")), args::Command::Version => println!("rust-analyzer {}", env!("REV")),
} }
@ -65,7 +65,7 @@ fn setup_logging() -> Result<()> {
Ok(()) Ok(())
} }
fn run_proc_macro_sv() -> Result<()> { fn run_proc_macro_srv() -> Result<()> {
ra_proc_macro_srv::cli::run(); ra_proc_macro_srv::cli::run();
Ok(()) Ok(())
} }

View File

@ -76,7 +76,7 @@ pub(crate) fn load_cargo(
ProcMacroClient::dummy() ProcMacroClient::dummy()
} else { } else {
let path = std::env::current_exe()?; let path = std::env::current_exe()?;
ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap() ProcMacroClient::extern_process(path, &["proc-macro"]).unwrap()
}; };
let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client); let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
Ok((host, source_roots)) Ok((host, source_roots))

View File

@ -148,20 +148,17 @@ impl WorldState {
let proc_macro_client = match &config.proc_macro_srv { let proc_macro_client = match &config.proc_macro_srv {
None => ProcMacroClient::dummy(), None => ProcMacroClient::dummy(),
Some((path, args)) => { Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
let path = std::path::Path::new(path); Ok(it) => it,
match ProcMacroClient::extern_process(path, args) { Err(err) => {
Ok(it) => it, log::error!(
Err(err) => { "Fail to run ra_proc_macro_srv from path {}, error: {:?}",
log::error!( path,
"Fail to run ra_proc_macro_srv from path {}, error : {}", err
path.to_string_lossy(), );
err ProcMacroClient::dummy()
);
ProcMacroClient::dummy()
}
} }
} },
}; };
workspaces workspaces