Skip to content

Commit 5182170

Browse files
authored
Merge pull request #18660 from Veykril/push-snumrtvzwqvw
fix: copied proc-macros not being cleaned up on exit
2 parents c17d430 + aef05d4 commit 5182170

File tree

3 files changed

+72
-69
lines changed

3 files changed

+72
-69
lines changed

src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
mod version;
44

55
use proc_macro::bridge;
6-
use std::{fmt, fs::File, io};
6+
use std::{fmt, fs, io, time::SystemTime};
77

88
use libloading::Library;
99
use memmap2::Mmap;
1010
use object::Object;
11-
use paths::{AbsPath, Utf8Path, Utf8PathBuf};
11+
use paths::{Utf8Path, Utf8PathBuf};
1212
use proc_macro_api::ProcMacroKind;
1313

1414
use crate::ProcMacroSrvSpan;
@@ -23,14 +23,9 @@ fn is_derive_registrar_symbol(symbol: &str) -> bool {
2323
symbol.contains(NEW_REGISTRAR_SYMBOL)
2424
}
2525

26-
fn find_registrar_symbol(file: &Utf8Path) -> io::Result<Option<String>> {
27-
let file = File::open(file)?;
28-
let buffer = unsafe { Mmap::map(&file)? };
29-
30-
Ok(object::File::parse(&*buffer)
31-
.map_err(invalid_data_err)?
32-
.exports()
33-
.map_err(invalid_data_err)?
26+
fn find_registrar_symbol(buffer: &[u8]) -> object::Result<Option<String>> {
27+
Ok(object::File::parse(buffer)?
28+
.exports()?
3429
.into_iter()
3530
.map(|export| export.name())
3631
.filter_map(|sym| String::from_utf8(sym.into()).ok())
@@ -113,17 +108,17 @@ struct ProcMacroLibraryLibloading {
113108
}
114109

115110
impl ProcMacroLibraryLibloading {
116-
fn open(file: &Utf8Path) -> Result<Self, LoadProcMacroDylibError> {
117-
let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
118-
invalid_data_err(format!("Cannot find registrar symbol in file {file}"))
119-
})?;
111+
fn open(path: &Utf8Path) -> Result<Self, LoadProcMacroDylibError> {
112+
let buffer = unsafe { Mmap::map(&fs::File::open(path)?)? };
113+
let symbol_name =
114+
find_registrar_symbol(&buffer).map_err(invalid_data_err)?.ok_or_else(|| {
115+
invalid_data_err(format!("Cannot find registrar symbol in file {path}"))
116+
})?;
120117

121-
let abs_file: &AbsPath = file
122-
.try_into()
123-
.map_err(|_| invalid_data_err(format!("expected an absolute path, got {file}")))?;
124-
let version_info = version::read_dylib_info(abs_file)?;
118+
let version_info = version::read_dylib_info(&buffer)?;
119+
drop(buffer);
125120

126-
let lib = load_library(file).map_err(invalid_data_err)?;
121+
let lib = load_library(path).map_err(invalid_data_err)?;
127122
let proc_macros = crate::proc_macros::ProcMacros::from_lib(
128123
&lib,
129124
symbol_name,
@@ -133,30 +128,33 @@ impl ProcMacroLibraryLibloading {
133128
}
134129
}
135130

136-
pub(crate) struct Expander {
137-
inner: ProcMacroLibraryLibloading,
138-
path: Utf8PathBuf,
139-
}
140-
141-
impl Drop for Expander {
131+
struct RemoveFileOnDrop(Utf8PathBuf);
132+
impl Drop for RemoveFileOnDrop {
142133
fn drop(&mut self) {
143134
#[cfg(windows)]
144-
std::fs::remove_file(&self.path).ok();
145-
_ = self.path;
135+
std::fs::remove_file(&self.0).unwrap();
136+
_ = self.0;
146137
}
147138
}
148139

140+
// Drop order matters as we can't remove the dylib before the library is unloaded
141+
pub(crate) struct Expander {
142+
inner: ProcMacroLibraryLibloading,
143+
_remove_on_drop: RemoveFileOnDrop,
144+
modified_time: SystemTime,
145+
}
146+
149147
impl Expander {
150148
pub(crate) fn new(lib: &Utf8Path) -> Result<Expander, LoadProcMacroDylibError> {
151149
// Some libraries for dynamic loading require canonicalized path even when it is
152150
// already absolute
153151
let lib = lib.canonicalize_utf8()?;
152+
let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?;
154153

155154
let path = ensure_file_with_lock_free_access(&lib)?;
156-
157155
let library = ProcMacroLibraryLibloading::open(path.as_ref())?;
158156

159-
Ok(Expander { inner: library, path })
157+
Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time })
160158
}
161159

162160
pub(crate) fn expand<S: ProcMacroSrvSpan>(
@@ -181,6 +179,10 @@ impl Expander {
181179
pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
182180
self.inner.proc_macros.list_macros()
183181
}
182+
183+
pub(crate) fn modified_time(&self) -> SystemTime {
184+
self.modified_time
185+
}
184186
}
185187

186188
/// Copy the dylib to temp directory to prevent locking in Windows
@@ -194,20 +196,23 @@ fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf>
194196
}
195197

196198
let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap();
199+
to.push("rust-analyzer-proc-macros");
200+
_ = fs::create_dir(&to);
197201

198202
let file_name = path.file_name().ok_or_else(|| {
199203
io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}"))
200204
})?;
201205

202-
// Generate a unique number by abusing `HashMap`'s hasher.
203-
// Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
204-
let t = RandomState::new().build_hasher().finish();
205-
206-
let mut unique_name = t.to_string();
207-
unique_name.push_str(file_name);
208-
209-
to.push(unique_name);
210-
std::fs::copy(path, &to)?;
206+
to.push({
207+
// Generate a unique number by abusing `HashMap`'s hasher.
208+
// Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
209+
let t = RandomState::new().build_hasher().finish();
210+
let mut unique_name = t.to_string();
211+
unique_name.push_str(file_name);
212+
unique_name.push('-');
213+
unique_name
214+
});
215+
fs::copy(path, &to)?;
211216
Ok(to)
212217
}
213218

src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/version.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
//! Reading proc-macro rustc version information from binary data
22
3-
use std::{
4-
fs::File,
5-
io::{self, Read},
6-
};
3+
use std::io::{self, Read};
74

8-
use memmap2::Mmap;
95
use object::read::{File as BinaryFile, Object, ObjectSection};
10-
use paths::AbsPath;
116

127
#[derive(Debug)]
138
#[allow(dead_code)]
@@ -21,14 +16,14 @@ pub struct RustCInfo {
2116
}
2217

2318
/// Read rustc dylib information
24-
pub fn read_dylib_info(dylib_path: &AbsPath) -> io::Result<RustCInfo> {
19+
pub fn read_dylib_info(buffer: &[u8]) -> io::Result<RustCInfo> {
2520
macro_rules! err {
2621
($e:literal) => {
2722
io::Error::new(io::ErrorKind::InvalidData, $e)
2823
};
2924
}
3025

31-
let ver_str = read_version(dylib_path)?;
26+
let ver_str = read_version(buffer)?;
3227
let mut items = ver_str.split_whitespace();
3328
let tag = items.next().ok_or_else(|| err!("version format error"))?;
3429
if tag != "rustc" {
@@ -106,11 +101,8 @@ fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'
106101
///
107102
/// Check this issue for more about the bytes layout:
108103
/// <https://github.com/rust-lang/rust-analyzer/issues/6174>
109-
pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
110-
let dylib_file = File::open(dylib_path)?;
111-
let dylib_mmapped = unsafe { Mmap::map(&dylib_file) }?;
112-
113-
let dot_rustc = read_section(&dylib_mmapped, ".rustc")?;
104+
pub fn read_version(buffer: &[u8]) -> io::Result<String> {
105+
let dot_rustc = read_section(buffer, ".rustc")?;
114106

115107
// check if magic is valid
116108
if &dot_rustc[0..4] != b"rust" {
@@ -159,8 +151,12 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
159151

160152
#[test]
161153
fn test_version_check() {
162-
let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
163-
let info = read_dylib_info(&path).unwrap();
154+
let info = read_dylib_info(&unsafe {
155+
memmap2::Mmap::map(&std::fs::File::open(crate::proc_macro_test_dylib_path()).unwrap())
156+
.unwrap()
157+
})
158+
.unwrap();
159+
164160
assert_eq!(
165161
info.version_string,
166162
crate::RUSTC_VERSION_STRING,

src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ use std::{
3535
fs,
3636
path::{Path, PathBuf},
3737
thread,
38-
time::SystemTime,
3938
};
4039

4140
use paths::{Utf8Path, Utf8PathBuf};
@@ -53,7 +52,7 @@ use crate::server_impl::TokenStream;
5352
pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION");
5453

5554
pub struct ProcMacroSrv<'env> {
56-
expanders: HashMap<(Utf8PathBuf, SystemTime), dylib::Expander>,
55+
expanders: HashMap<Utf8PathBuf, dylib::Expander>,
5756
span_mode: SpanMode,
5857
env: &'env EnvSnapshot,
5958
}
@@ -81,10 +80,9 @@ impl<'env> ProcMacroSrv<'env> {
8180
) -> Result<(msg::FlatTree, Vec<u32>), msg::PanicMessage> {
8281
let span_mode = self.span_mode;
8382
let snapped_env = self.env;
84-
let expander = self.expander(lib.as_ref()).map_err(|err| {
85-
debug_assert!(false, "should list macros before asking to expand");
86-
msg::PanicMessage(format!("failed to load macro: {err}"))
87-
})?;
83+
let expander = self
84+
.expander(lib.as_ref())
85+
.map_err(|err| msg::PanicMessage(format!("failed to load macro: {err}")))?;
8886

8987
let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref));
9088

@@ -107,16 +105,20 @@ impl<'env> ProcMacroSrv<'env> {
107105
}
108106

109107
fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> {
110-
let time = fs::metadata(path)
111-
.and_then(|it| it.modified())
112-
.map_err(|err| format!("Failed to get file metadata for {path}: {err}",))?;
113-
114-
Ok(match self.expanders.entry((path.to_path_buf(), time)) {
115-
Entry::Vacant(v) => v.insert(
116-
dylib::Expander::new(path)
117-
.map_err(|err| format!("Cannot create expander for {path}: {err}",))?,
118-
),
119-
Entry::Occupied(e) => e.into_mut(),
108+
let expander = || {
109+
dylib::Expander::new(path)
110+
.map_err(|err| format!("Cannot create expander for {path}: {err}",))
111+
};
112+
113+
Ok(match self.expanders.entry(path.to_path_buf()) {
114+
Entry::Vacant(v) => v.insert(expander()?),
115+
Entry::Occupied(mut e) => {
116+
let time = fs::metadata(path).and_then(|it| it.modified()).ok();
117+
if Some(e.get().modified_time()) != time {
118+
e.insert(expander()?);
119+
}
120+
e.into_mut()
121+
}
120122
})
121123
}
122124
}

0 commit comments

Comments
 (0)