mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
ra_proc_macro: cleanups here and there
This commit is contained in:
parent
36840bd6c7
commit
d3019164dc
12 changed files with 141 additions and 190 deletions
|
@ -2,55 +2,43 @@
|
|||
|
||||
use crate::{expand_task, list_macros};
|
||||
use ra_proc_macro::msg::{self, Message};
|
||||
|
||||
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() {
|
||||
loop {
|
||||
let req = match read_request() {
|
||||
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;
|
||||
}
|
||||
Ok(None) => continue,
|
||||
Ok(Some(req)) => req,
|
||||
};
|
||||
|
||||
match req {
|
||||
msg::Request::ListMacro(task) => {
|
||||
if let Err(err) =
|
||||
write_response(list_macros(&task).map(|it| msg::Response::ListMacro(it)))
|
||||
{
|
||||
eprintln!("Write message error on list macro: {}", err);
|
||||
}
|
||||
}
|
||||
let res = match req {
|
||||
msg::Request::ListMacro(task) => Ok(msg::Response::ListMacro(list_macros(&task))),
|
||||
msg::Request::ExpansionMacro(task) => {
|
||||
if let Err(err) =
|
||||
write_response(expand_task(&task).map(|it| msg::Response::ExpansionMacro(it)))
|
||||
{
|
||||
eprintln!("Write message error on expansion macro: {}", err);
|
||||
}
|
||||
expand_task(&task).map(msg::Response::ExpansionMacro)
|
||||
}
|
||||
};
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
|
@ -9,43 +9,37 @@ use libloading::Library;
|
|||
use memmap::Mmap;
|
||||
use ra_proc_macro::ProcMacroKind;
|
||||
|
||||
use std::io::Error as IoError;
|
||||
use std::io::ErrorKind as IoErrorKind;
|
||||
use std::io;
|
||||
|
||||
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
|
||||
|
||||
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> IoError {
|
||||
IoError::new(IoErrorKind::InvalidData, e)
|
||||
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
|
||||
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)
|
||||
}
|
||||
|
||||
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 buffer = unsafe { Mmap::map(&file)? };
|
||||
let object = Object::parse(&buffer).map_err(invalid_data_err)?;
|
||||
|
||||
match object {
|
||||
let name = match object {
|
||||
Object::Elf(elf) => {
|
||||
let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
|
||||
let name =
|
||||
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)
|
||||
symbols.into_iter().find(is_derive_registrar_symbol).map(&str::to_owned)
|
||||
}
|
||||
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)) => {
|
||||
let exports = binary.exports().map_err(invalid_data_err)?;
|
||||
let name = exports
|
||||
exports
|
||||
.iter()
|
||||
.map(|s| {
|
||||
// In macos doc:
|
||||
|
@ -58,12 +52,12 @@ fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
|
|||
&s.name
|
||||
}
|
||||
})
|
||||
.find(|s| is_derive_registrar_symbol(s))
|
||||
.map(|s| s.to_string());
|
||||
Ok(name)
|
||||
.find(is_derive_registrar_symbol)
|
||||
.map(&str::to_owned)
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
_ => return Ok(None),
|
||||
};
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
/// Loads dynamic library in platform dependent manner.
|
||||
|
@ -93,15 +87,16 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
|
|||
}
|
||||
|
||||
struct ProcMacroLibraryLibloading {
|
||||
// Hold the dylib to prevent it for unloadeding
|
||||
// Hold the dylib to prevent it from unloading
|
||||
_lib: Library,
|
||||
exported_macros: Vec<bridge::client::ProcMacro>,
|
||||
}
|
||||
|
||||
impl ProcMacroLibraryLibloading {
|
||||
fn open(file: &Path) -> Result<Self, IoError> {
|
||||
let symbol_name = find_registrar_symbol(file)?
|
||||
.ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?;
|
||||
fn open(file: &Path) -> io::Result<Self> {
|
||||
let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
|
||||
invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file))
|
||||
})?;
|
||||
|
||||
let lib = load_library(file).map_err(invalid_data_err)?;
|
||||
let exported_macros = {
|
||||
|
@ -121,18 +116,16 @@ pub struct Expander {
|
|||
}
|
||||
|
||||
impl Expander {
|
||||
pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> {
|
||||
let mut libs = vec![];
|
||||
/* Some libraries for dynamic loading require canonicalized path (even when it is
|
||||
already absolute
|
||||
*/
|
||||
let lib =
|
||||
lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));
|
||||
pub fn new(lib: &Path) -> Result<Expander, String> {
|
||||
// Some libraries for dynamic loading require canonicalized path even when it is
|
||||
// already absolute
|
||||
let lib = lib
|
||||
.canonicalize()
|
||||
.unwrap_or_else(|err| panic!("Cannot canonicalize {:?}: {:?}", lib, err));
|
||||
|
||||
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(
|
||||
|
@ -176,7 +169,6 @@ impl Expander {
|
|||
parsed_attributes,
|
||||
parsed_body,
|
||||
);
|
||||
|
||||
return res.map(|it| it.subtree);
|
||||
}
|
||||
_ => continue,
|
||||
|
@ -187,26 +179,21 @@ impl Expander {
|
|||
Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
|
||||
}
|
||||
|
||||
pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> {
|
||||
let mut result = vec![];
|
||||
|
||||
for lib in &self.libs {
|
||||
for proc_macro in &lib.exported_macros {
|
||||
let res = match proc_macro {
|
||||
bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
|
||||
(trait_name.to_string(), ProcMacroKind::CustomDerive)
|
||||
}
|
||||
bridge::client::ProcMacro::Bang { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::FuncLike)
|
||||
}
|
||||
bridge::client::ProcMacro::Attr { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::Attr)
|
||||
}
|
||||
};
|
||||
result.push(res);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
|
||||
self.libs
|
||||
.iter()
|
||||
.flat_map(|it| &it.exported_macros)
|
||||
.map(|proc_macro| match proc_macro {
|
||||
bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
|
||||
(trait_name.to_string(), ProcMacroKind::CustomDerive)
|
||||
}
|
||||
bridge::client::ProcMacro::Bang { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::FuncLike)
|
||||
}
|
||||
bridge::client::ProcMacro::Attr { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::Attr)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
//! 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.
|
||||
//!
|
||||
//! 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
|
||||
//! RA then proc-macro2 token stream.
|
||||
//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipulate and interact with
|
||||
//! RA than `proc-macro2` token stream.
|
||||
//! * 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)
|
||||
|
||||
|
@ -21,36 +21,28 @@ mod dylib;
|
|||
|
||||
use proc_macro::bridge::client::TokenStream;
|
||||
use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
|
||||
let expander = dylib::Expander::new(&task.lib)
|
||||
.expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
|
||||
let expander = create_expander(&task.lib);
|
||||
|
||||
match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
|
||||
Ok(expansion) => Ok(ExpansionResult { expansion }),
|
||||
Err(msg) => {
|
||||
let reason = format!(
|
||||
"Cannot perform expansion for {}: error {:?}!",
|
||||
&task.macro_name,
|
||||
msg.as_str()
|
||||
);
|
||||
Err(reason)
|
||||
Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
|
||||
let expander = dylib::Expander::new(&task.lib)
|
||||
.expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
|
||||
pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
|
||||
let expander = create_expander(&task.lib);
|
||||
|
||||
match expander.list_macros() {
|
||||
Ok(macros) => Ok(ListMacrosResult { macros }),
|
||||
Err(msg) => {
|
||||
let reason =
|
||||
format!("Cannot perform expansion for {:?}: error {:?}!", &task.lib, msg.as_str());
|
||||
Err(reason)
|
||||
}
|
||||
}
|
||||
ListMacrosResult { macros: expander.list_macros() }
|
||||
}
|
||||
|
||||
fn create_expander(lib: &Path) -> dylib::Expander {
|
||||
dylib::Expander::new(lib)
|
||||
.unwrap_or_else(|err| panic!("Cannot create expander for {:?}: {:?}", lib, err))
|
||||
}
|
||||
|
||||
pub mod cli;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! The original idea from fedochet is using proc-macro2 as backend,
|
||||
//! 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 ra_tt as tt;
|
||||
|
|
|
@ -60,6 +60,6 @@ pub fn list(crate_name: &str, version: &str) -> Vec<String> {
|
|||
let path = fixtures::dylib_path(crate_name, version);
|
||||
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()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue