Skip to content

Commit aef05d4

Browse files
committed
Fix copied proc-macros not being cleaned up on exit
1 parent 1bafbe1 commit aef05d4

File tree

2 files changed

+49
-59
lines changed

2 files changed

+49
-59
lines changed

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

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

55
use proc_macro::bridge;
6-
use std::{
7-
fmt,
8-
fs::{self, File},
9-
io,
10-
time::SystemTime,
11-
};
6+
use std::{fmt, fs, io, time::SystemTime};
127

138
use libloading::Library;
149
use memmap2::Mmap;
1510
use object::Object;
16-
use paths::{AbsPath, Utf8Path, Utf8PathBuf};
11+
use paths::{Utf8Path, Utf8PathBuf};
1712
use proc_macro_api::ProcMacroKind;
1813

1914
use crate::ProcMacroSrvSpan;
@@ -28,14 +23,9 @@ fn is_derive_registrar_symbol(symbol: &str) -> bool {
2823
symbol.contains(NEW_REGISTRAR_SYMBOL)
2924
}
3025

31-
fn find_registrar_symbol(file: &Utf8Path) -> io::Result<Option<String>> {
32-
let file = File::open(file)?;
33-
let buffer = unsafe { Mmap::map(&file)? };
34-
35-
Ok(object::File::parse(&*buffer)
36-
.map_err(invalid_data_err)?
37-
.exports()
38-
.map_err(invalid_data_err)?
26+
fn find_registrar_symbol(buffer: &[u8]) -> object::Result<Option<String>> {
27+
Ok(object::File::parse(buffer)?
28+
.exports()?
3929
.into_iter()
4030
.map(|export| export.name())
4131
.filter_map(|sym| String::from_utf8(sym.into()).ok())
@@ -118,17 +108,17 @@ struct ProcMacroLibraryLibloading {
118108
}
119109

120110
impl ProcMacroLibraryLibloading {
121-
fn open(file: &Utf8Path) -> Result<Self, LoadProcMacroDylibError> {
122-
let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
123-
invalid_data_err(format!("Cannot find registrar symbol in file {file}"))
124-
})?;
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+
})?;
125117

126-
let abs_file: &AbsPath = file
127-
.try_into()
128-
.map_err(|_| invalid_data_err(format!("expected an absolute path, got {file}")))?;
129-
let version_info = version::read_dylib_info(abs_file)?;
118+
let version_info = version::read_dylib_info(&buffer)?;
119+
drop(buffer);
130120

131-
let lib = load_library(file).map_err(invalid_data_err)?;
121+
let lib = load_library(path).map_err(invalid_data_err)?;
132122
let proc_macros = crate::proc_macros::ProcMacros::from_lib(
133123
&lib,
134124
symbol_name,
@@ -138,20 +128,22 @@ impl ProcMacroLibraryLibloading {
138128
}
139129
}
140130

141-
pub(crate) struct Expander {
142-
inner: ProcMacroLibraryLibloading,
143-
path: Utf8PathBuf,
144-
modified_time: SystemTime,
145-
}
146-
147-
impl Drop for Expander {
131+
struct RemoveFileOnDrop(Utf8PathBuf);
132+
impl Drop for RemoveFileOnDrop {
148133
fn drop(&mut self) {
149134
#[cfg(windows)]
150-
std::fs::remove_file(&self.path).ok();
151-
_ = self.path;
135+
std::fs::remove_file(&self.0).unwrap();
136+
_ = self.0;
152137
}
153138
}
154139

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+
155147
impl Expander {
156148
pub(crate) fn new(lib: &Utf8Path) -> Result<Expander, LoadProcMacroDylibError> {
157149
// Some libraries for dynamic loading require canonicalized path even when it is
@@ -160,10 +152,9 @@ impl Expander {
160152
let modified_time = fs::metadata(&lib).and_then(|it| it.modified())?;
161153

162154
let path = ensure_file_with_lock_free_access(&lib)?;
163-
164155
let library = ProcMacroLibraryLibloading::open(path.as_ref())?;
165156

166-
Ok(Expander { inner: library, path, modified_time })
157+
Ok(Expander { inner: library, _remove_on_drop: RemoveFileOnDrop(path), modified_time })
167158
}
168159

169160
pub(crate) fn expand<S: ProcMacroSrvSpan>(
@@ -205,20 +196,23 @@ fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf>
205196
}
206197

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

209202
let file_name = path.file_name().ok_or_else(|| {
210203
io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}"))
211204
})?;
212205

213-
// Generate a unique number by abusing `HashMap`'s hasher.
214-
// Maybe this will also "inspire" a libs team member to finally put `rand` in libstd.
215-
let t = RandomState::new().build_hasher().finish();
216-
217-
let mut unique_name = t.to_string();
218-
unique_name.push_str(file_name);
219-
220-
to.push(unique_name);
221-
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)?;
222216
Ok(to)
223217
}
224218

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,

0 commit comments

Comments
 (0)