mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-25 17:28:17 +00:00
Reorganize proc-macro-srv
This commit is contained in:
parent
a7a365e8f8
commit
218e00d0bb
5 changed files with 131 additions and 148 deletions
|
|
@ -24,13 +24,13 @@ impl SpanTransformer for SpanTrans {
|
|||
_: &mut Self::Table,
|
||||
span: Self::Span,
|
||||
) -> proc_macro_api::legacy_protocol::SpanId {
|
||||
proc_macro_api::legacy_protocol::SpanId(span.0 as u32)
|
||||
proc_macro_api::legacy_protocol::SpanId(span.0)
|
||||
}
|
||||
fn span_for_token_id(
|
||||
_: &Self::Table,
|
||||
id: proc_macro_api::legacy_protocol::SpanId,
|
||||
) -> Self::Span {
|
||||
SpanId(id.0 as u32)
|
||||
SpanId(id.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +99,7 @@ fn run_json() -> io::Result<()> {
|
|||
lib,
|
||||
&env,
|
||||
current_dir,
|
||||
macro_name,
|
||||
¯o_name,
|
||||
macro_body,
|
||||
attributes,
|
||||
def_site,
|
||||
|
|
@ -112,6 +112,7 @@ fn run_json() -> io::Result<()> {
|
|||
CURRENT_API_VERSION,
|
||||
)
|
||||
})
|
||||
.map_err(|e| e.into_string().unwrap_or_default())
|
||||
.map_err(msg::PanicMessage)
|
||||
}),
|
||||
SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({
|
||||
|
|
@ -130,7 +131,7 @@ fn run_json() -> io::Result<()> {
|
|||
lib,
|
||||
&env,
|
||||
current_dir,
|
||||
macro_name,
|
||||
¯o_name,
|
||||
macro_body,
|
||||
attributes,
|
||||
def_site,
|
||||
|
|
@ -151,6 +152,7 @@ fn run_json() -> io::Result<()> {
|
|||
tree,
|
||||
span_data_table,
|
||||
})
|
||||
.map_err(|e| e.into_string().unwrap_or_default())
|
||||
.map_err(msg::PanicMessage)
|
||||
}),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Handles dynamic library loading for proc macro
|
||||
|
||||
mod proc_macros;
|
||||
mod version;
|
||||
|
||||
use proc_macro::bridge;
|
||||
|
|
@ -10,57 +11,56 @@ use libloading::Library;
|
|||
use object::Object;
|
||||
use paths::{Utf8Path, Utf8PathBuf};
|
||||
|
||||
use crate::{ProcMacroKind, ProcMacroSrvSpan, proc_macros::ProcMacros, server_impl::TopSubtree};
|
||||
use crate::{
|
||||
PanicMessage, ProcMacroKind, ProcMacroSrvSpan, dylib::proc_macros::ProcMacros,
|
||||
server_impl::TopSubtree,
|
||||
};
|
||||
|
||||
/// Loads dynamic library in platform dependent manner.
|
||||
///
|
||||
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
|
||||
/// and [here](https://github.com/rust-lang/rust/issues/60593).
|
||||
///
|
||||
/// Usage of RTLD_DEEPBIND
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
|
||||
///
|
||||
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
#[cfg(windows)]
|
||||
unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
|
||||
// SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
unsafe { Library::new(file) }
|
||||
pub(crate) struct Expander {
|
||||
inner: ProcMacroLibrary,
|
||||
modified_time: SystemTime,
|
||||
}
|
||||
|
||||
/// Loads dynamic library in platform dependent manner.
|
||||
///
|
||||
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
|
||||
/// and [here](https://github.com/rust-lang/rust/issues/60593).
|
||||
///
|
||||
/// Usage of RTLD_DEEPBIND
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
|
||||
///
|
||||
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
#[cfg(unix)]
|
||||
unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
|
||||
// not defined by POSIX, different values on mips vs other targets
|
||||
#[cfg(target_env = "gnu")]
|
||||
use libc::RTLD_DEEPBIND;
|
||||
use libloading::os::unix::Library as UnixLibrary;
|
||||
// defined by POSIX
|
||||
use libloading::os::unix::RTLD_NOW;
|
||||
impl Expander {
|
||||
pub(crate) fn new(
|
||||
temp_dir: &TempDir,
|
||||
lib: &Utf8Path,
|
||||
) -> Result<Expander, LoadProcMacroDylibError> {
|
||||
// Some libraries for dynamic loading require canonicalized path even when it is
|
||||
// already absolute
|
||||
let lib = lib.canonicalize_utf8()?;
|
||||
let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?;
|
||||
|
||||
// MUSL and bionic don't have it..
|
||||
#[cfg(not(target_env = "gnu"))]
|
||||
const RTLD_DEEPBIND: std::os::raw::c_int = 0x0;
|
||||
let path = ensure_file_with_lock_free_access(temp_dir, &lib)?;
|
||||
let library = ProcMacroLibrary::open(path.as_ref())?;
|
||||
|
||||
// SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
|
||||
Ok(Expander { inner: library, modified_time })
|
||||
}
|
||||
|
||||
pub(crate) fn expand<S: ProcMacroSrvSpan>(
|
||||
&self,
|
||||
macro_name: &str,
|
||||
macro_body: TopSubtree<S>,
|
||||
attributes: Option<TopSubtree<S>>,
|
||||
def_site: S,
|
||||
call_site: S,
|
||||
mixed_site: S,
|
||||
) -> Result<TopSubtree<S>, PanicMessage>
|
||||
where
|
||||
<S::Server as bridge::server::Types>::TokenStream: Default,
|
||||
{
|
||||
self.inner
|
||||
.proc_macros
|
||||
.expand(macro_name, macro_body, attributes, def_site, call_site, mixed_site)
|
||||
}
|
||||
|
||||
pub(crate) fn list_macros(&self) -> impl Iterator<Item = (&str, ProcMacroKind)> {
|
||||
self.inner.proc_macros.list_macros()
|
||||
}
|
||||
|
||||
pub(crate) fn modified_time(&self) -> SystemTime {
|
||||
self.modified_time
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -134,57 +134,6 @@ impl ProcMacroLibrary {
|
|||
}
|
||||
}
|
||||
|
||||
// Drop order matters as we can't remove the dylib before the library is unloaded
|
||||
pub(crate) struct Expander {
|
||||
inner: ProcMacroLibrary,
|
||||
_remove_on_drop: RemoveFileOnDrop,
|
||||
modified_time: SystemTime,
|
||||
}
|
||||
|
||||
impl Expander {
|
||||
pub(crate) fn new(
|
||||
temp_dir: &TempDir,
|
||||
lib: &Utf8Path,
|
||||
) -> Result<Expander, LoadProcMacroDylibError> {
|
||||
// Some libraries for dynamic loading require canonicalized path even when it is
|
||||
// already absolute
|
||||
let lib = lib.canonicalize_utf8()?;
|
||||
let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?;
|
||||
|
||||
let path = ensure_file_with_lock_free_access(temp_dir, &lib)?;
|
||||
let library = ProcMacroLibrary::open(path.as_ref())?;
|
||||
|
||||
Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time })
|
||||
}
|
||||
|
||||
pub(crate) fn expand<S: ProcMacroSrvSpan>(
|
||||
&self,
|
||||
macro_name: &str,
|
||||
macro_body: TopSubtree<S>,
|
||||
attributes: Option<TopSubtree<S>>,
|
||||
def_site: S,
|
||||
call_site: S,
|
||||
mixed_site: S,
|
||||
) -> Result<TopSubtree<S>, String>
|
||||
where
|
||||
<S::Server as bridge::server::Types>::TokenStream: Default,
|
||||
{
|
||||
let result = self
|
||||
.inner
|
||||
.proc_macros
|
||||
.expand(macro_name, macro_body, attributes, def_site, call_site, mixed_site);
|
||||
result.map_err(|e| e.into_string().unwrap_or_default())
|
||||
}
|
||||
|
||||
pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
|
||||
self.inner.proc_macros.list_macros()
|
||||
}
|
||||
|
||||
pub(crate) fn modified_time(&self) -> SystemTime {
|
||||
self.modified_time
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::InvalidData, e)
|
||||
}
|
||||
|
|
@ -214,15 +163,6 @@ fn find_registrar_symbol(obj: &object::File<'_>) -> object::Result<Option<String
|
|||
}))
|
||||
}
|
||||
|
||||
struct RemoveFileOnDrop(Utf8PathBuf);
|
||||
impl Drop for RemoveFileOnDrop {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(windows)]
|
||||
std::fs::remove_file(&self.0).unwrap();
|
||||
_ = self.0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy the dylib to temp directory to prevent locking in Windows
|
||||
#[cfg(windows)]
|
||||
fn ensure_file_with_lock_free_access(
|
||||
|
|
@ -259,3 +199,54 @@ fn ensure_file_with_lock_free_access(
|
|||
) -> io::Result<Utf8PathBuf> {
|
||||
Ok(path.to_owned())
|
||||
}
|
||||
|
||||
/// Loads dynamic library in platform dependent manner.
|
||||
///
|
||||
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
|
||||
/// and [here](https://github.com/rust-lang/rust/issues/60593).
|
||||
///
|
||||
/// Usage of RTLD_DEEPBIND
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
|
||||
///
|
||||
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
#[cfg(windows)]
|
||||
unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
|
||||
// SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
unsafe { Library::new(file) }
|
||||
}
|
||||
|
||||
/// Loads dynamic library in platform dependent manner.
|
||||
///
|
||||
/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
|
||||
/// and [here](https://github.com/rust-lang/rust/issues/60593).
|
||||
///
|
||||
/// Usage of RTLD_DEEPBIND
|
||||
/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
|
||||
///
|
||||
/// It seems that on Windows that behaviour is default, so we do nothing in that case.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
#[cfg(unix)]
|
||||
unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
|
||||
// not defined by POSIX, different values on mips vs other targets
|
||||
#[cfg(target_env = "gnu")]
|
||||
use libc::RTLD_DEEPBIND;
|
||||
use libloading::os::unix::Library as UnixLibrary;
|
||||
// defined by POSIX
|
||||
use libloading::os::unix::RTLD_NOW;
|
||||
|
||||
// MUSL and bionic don't have it..
|
||||
#[cfg(not(target_env = "gnu"))]
|
||||
const RTLD_DEEPBIND: std::os::raw::c_int = 0x0;
|
||||
|
||||
// SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library
|
||||
unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,20 +75,13 @@ impl ProcMacros {
|
|||
Err(bridge::PanicMessage::String(format!("proc-macro `{macro_name}` is missing")).into())
|
||||
}
|
||||
|
||||
pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
|
||||
self.0
|
||||
.iter()
|
||||
.map(|proc_macro| match proc_macro {
|
||||
pub(crate) fn list_macros(&self) -> impl Iterator<Item = (&str, ProcMacroKind)> {
|
||||
self.0.iter().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::Bang)
|
||||
}
|
||||
bridge::client::ProcMacro::Attr { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::Attr)
|
||||
(trait_name, ProcMacroKind::CustomDerive)
|
||||
}
|
||||
bridge::client::ProcMacro::Bang { name, .. } => (name, ProcMacroKind::Bang),
|
||||
bridge::client::ProcMacro::Attr { name, .. } => (name, ProcMacroKind::Attr),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ extern crate ra_ap_rustc_lexer as rustc_lexer;
|
|||
extern crate rustc_lexer;
|
||||
|
||||
mod dylib;
|
||||
mod proc_macros;
|
||||
mod server_impl;
|
||||
|
||||
use std::{
|
||||
|
|
@ -81,16 +80,17 @@ impl ProcMacroSrv<'_> {
|
|||
lib: impl AsRef<Utf8Path>,
|
||||
env: &[(String, String)],
|
||||
current_dir: Option<impl AsRef<Path>>,
|
||||
macro_name: String,
|
||||
macro_name: &str,
|
||||
macro_body: tt::TopSubtree<S>,
|
||||
attribute: Option<tt::TopSubtree<S>>,
|
||||
def_site: S,
|
||||
call_site: S,
|
||||
mixed_site: S,
|
||||
) -> Result<Vec<tt::TokenTree<S>>, String> {
|
||||
) -> Result<Vec<tt::TokenTree<S>>, PanicMessage> {
|
||||
let snapped_env = self.env;
|
||||
let expander =
|
||||
self.expander(lib.as_ref()).map_err(|err| format!("failed to load macro: {err}"))?;
|
||||
let expander = self.expander(lib.as_ref()).map_err(|err| PanicMessage {
|
||||
message: Some(format!("failed to load macro: {err}")),
|
||||
})?;
|
||||
|
||||
let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref));
|
||||
|
||||
|
|
@ -99,11 +99,11 @@ impl ProcMacroSrv<'_> {
|
|||
let result = thread::scope(|s| {
|
||||
let thread = thread::Builder::new()
|
||||
.stack_size(EXPANDER_STACK_SIZE)
|
||||
.name(macro_name.clone())
|
||||
.name(macro_name.to_owned())
|
||||
.spawn_scoped(s, move || {
|
||||
expander
|
||||
.expand(
|
||||
¯o_name,
|
||||
macro_name,
|
||||
server_impl::TopSubtree(macro_body.0.into_vec()),
|
||||
attribute.map(|it| server_impl::TopSubtree(it.0.into_vec())),
|
||||
def_site,
|
||||
|
|
@ -112,12 +112,7 @@ impl ProcMacroSrv<'_> {
|
|||
)
|
||||
.map(|tt| tt.0)
|
||||
});
|
||||
let res = match thread {
|
||||
Ok(handle) => handle.join(),
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
|
||||
match res {
|
||||
match thread.unwrap().join() {
|
||||
Ok(res) => res,
|
||||
Err(e) => std::panic::resume_unwind(e),
|
||||
}
|
||||
|
|
@ -132,7 +127,7 @@ impl ProcMacroSrv<'_> {
|
|||
dylib_path: &Utf8Path,
|
||||
) -> Result<Vec<(String, ProcMacroKind)>, String> {
|
||||
let expander = self.expander(dylib_path)?;
|
||||
Ok(expander.list_macros())
|
||||
Ok(expander.list_macros().map(|(k, v)| (k.to_owned(), v)).collect())
|
||||
}
|
||||
|
||||
fn expander(&self, path: &Utf8Path) -> Result<Arc<dylib::Expander>, String> {
|
||||
|
|
@ -186,6 +181,8 @@ impl ProcMacroSrvSpan for Span {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PanicMessage {
|
||||
message: Option<String>,
|
||||
}
|
||||
|
|
@ -265,8 +262,9 @@ impl Drop for EnvChange<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(dir) = &self.prev_working_dir {
|
||||
if let Err(err) = std::env::set_current_dir(dir) {
|
||||
if let Some(dir) = &self.prev_working_dir
|
||||
&& let Err(err) = std::env::set_current_dir(dir)
|
||||
{
|
||||
eprintln!(
|
||||
"Failed to set the current working dir to {}. Error: {:?}",
|
||||
dir.display(),
|
||||
|
|
@ -275,7 +273,6 @@ impl Drop for EnvChange<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ pub(super) fn from_token_tree<Span: Copy>(
|
|||
token_trees.push(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
|
||||
spacing: tt::Spacing::Alone,
|
||||
span: literal.span,
|
||||
char: '-' as char,
|
||||
char: '-',
|
||||
})));
|
||||
symbol = Symbol::intern(&symbol.as_str()[1..]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue