Properly clean proc-macro-srv proc-macro temp dir
diff --git a/Cargo.lock b/Cargo.lock
index 0cbbb5d..53c2d04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -51,6 +51,15 @@
 checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
 
 [[package]]
+name = "atomic-polyfill"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
+dependencies = [
+ "critical-section",
+]
+
+[[package]]
 name = "autocfg"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -126,6 +135,12 @@
 checksum = "26c4925bc979b677330a8c7fe7a8c94af2dbb4a2d37b4a20a80d884400f46baa"
 
 [[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
 name = "camino"
 version = "1.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -319,6 +334,15 @@
 checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
 
 [[package]]
+name = "cobs"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
+dependencies = [
+ "thiserror 2.0.12",
+]
+
+[[package]]
 name = "countme"
 version = "3.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -340,6 +364,12 @@
 ]
 
 [[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
+[[package]]
 name = "crossbeam-channel"
 version = "0.5.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -597,6 +627,15 @@
 checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 
 [[package]]
+name = "hash32"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
 name = "hashbrown"
 version = "0.14.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -623,6 +662,20 @@
 ]
 
 [[package]]
+name = "heapless"
+version = "0.7.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
+dependencies = [
+ "atomic-polyfill",
+ "hash32",
+ "rustc_version",
+ "serde",
+ "spin",
+ "stable_deref_trait",
+]
+
+[[package]]
 name = "hermit-abi"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1593,6 +1646,17 @@
 checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
 
 [[package]]
+name = "postcard"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
+dependencies = [
+ "cobs",
+ "heapless",
+ "serde",
+]
+
+[[package]]
 name = "potential_utf"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1639,6 +1703,7 @@
  "ra-ap-rustc_lexer 0.122.0",
  "span",
  "syntax-bridge",
+ "temp-dir",
  "tt",
 ]
 
@@ -1647,6 +1712,7 @@
 version = "0.0.0"
 dependencies = [
  "clap",
+ "postcard",
  "proc-macro-api",
  "proc-macro-srv",
  "tt",
@@ -2024,6 +2090,15 @@
 ]
 
 [[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
 name = "ryu"
 version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2241,6 +2316,15 @@
 ]
 
 [[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml
index 16ec3b0..91e9e62 100644
--- a/crates/proc-macro-srv-cli/Cargo.toml
+++ b/crates/proc-macro-srv-cli/Cargo.toml
@@ -15,10 +15,13 @@
 proc-macro-api.workspace = true
 tt.workspace = true
 clap = {version = "4.5.42", default-features = false, features = ["std"]}
+postcard = { version = "1.1.3", optional = true }
 
 [features]
+default = ["postcard"]
 sysroot-abi = ["proc-macro-srv/sysroot-abi"]
 in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"]
+postcard = ["dep:postcard"]
 
 
 [[bin]]
diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs
index b6ebc56..97a622e 100644
--- a/crates/proc-macro-srv-cli/src/main.rs
+++ b/crates/proc-macro-srv-cli/src/main.rs
@@ -39,6 +39,7 @@
 #[derive(Copy, Clone)]
 enum ProtocolFormat {
     Json,
+    #[cfg(feature = "postcard")]
     Postcard,
 }
 
@@ -50,12 +51,14 @@
     fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
         match self {
             ProtocolFormat::Json => Some(clap::builder::PossibleValue::new("json")),
+            #[cfg(feature = "postcard")]
             ProtocolFormat::Postcard => Some(clap::builder::PossibleValue::new("postcard")),
         }
     }
     fn from_str(input: &str, _ignore_case: bool) -> Result<Self, String> {
         match input {
             "json" => Ok(ProtocolFormat::Json),
+            #[cfg(feature = "postcard")]
             "postcard" => Ok(ProtocolFormat::Postcard),
             _ => Err(format!("unknown protocol format: {input}")),
         }
diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs
index 6bf58ee..46be8a2 100644
--- a/crates/proc-macro-srv-cli/src/main_loop.rs
+++ b/crates/proc-macro-srv-cli/src/main_loop.rs
@@ -37,6 +37,7 @@
 pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> {
     match format {
         ProtocolFormat::Json => run_json(),
+        #[cfg(feature = "postcard")]
         ProtocolFormat::Postcard => unimplemented!(),
     }
 }
@@ -96,7 +97,7 @@
 
                         srv.expand(
                             lib,
-                            env,
+                            &env,
                             current_dir,
                             macro_name,
                             macro_body,
@@ -127,7 +128,7 @@
                         });
                         srv.expand(
                             lib,
-                            env,
+                            &env,
                             current_dir,
                             macro_name,
                             macro_body,
diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml
index 4034f24..d037e71 100644
--- a/crates/proc-macro-srv/Cargo.toml
+++ b/crates/proc-macro-srv/Cargo.toml
@@ -16,6 +16,7 @@
 object.workspace = true
 libloading.workspace = true
 memmap2.workspace = true
+temp-dir.workspace = true
 
 tt.workspace = true
 syntax-bridge.workspace = true
@@ -26,6 +27,7 @@
 
 ra-ap-rustc_lexer.workspace = true
 
+
 [target.'cfg(unix)'.dependencies]
 libc.workspace = true
 
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs
index c49159d..095e9fa 100644
--- a/crates/proc-macro-srv/src/dylib.rs
+++ b/crates/proc-macro-srv/src/dylib.rs
@@ -4,6 +4,7 @@
 
 use proc_macro::bridge;
 use std::{fmt, fs, io, time::SystemTime};
+use temp_dir::TempDir;
 
 use libloading::Library;
 use object::Object;
@@ -141,13 +142,16 @@
 }
 
 impl Expander {
-    pub(crate) fn new(lib: &Utf8Path) -> Result<Expander, LoadProcMacroDylibError> {
+    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(&lib)?;
+        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 })
@@ -221,7 +225,10 @@
 
 /// Copy the dylib to temp directory to prevent locking in Windows
 #[cfg(windows)]
-fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> {
+fn ensure_file_with_lock_free_access(
+    temp_dir: &TempDir,
+    path: &Utf8Path,
+) -> io::Result<Utf8PathBuf> {
     use std::collections::hash_map::RandomState;
     use std::hash::{BuildHasher, Hasher};
 
@@ -229,9 +236,7 @@
         return Ok(path.to_path_buf());
     }
 
-    let mut to = Utf8PathBuf::from_path_buf(std::env::temp_dir()).unwrap();
-    to.push("rust-analyzer-proc-macros");
-    _ = fs::create_dir(&to);
+    let mut to = Utf8Path::from_path(temp_dir.path()).unwrap().to_owned();
 
     let file_name = path.file_stem().ok_or_else(|| {
         io::Error::new(io::ErrorKind::InvalidInput, format!("File path is invalid: {path}"))
@@ -248,6 +253,9 @@
 }
 
 #[cfg(unix)]
-fn ensure_file_with_lock_free_access(path: &Utf8Path) -> io::Result<Utf8PathBuf> {
+fn ensure_file_with_lock_free_access(
+    _temp_dir: &TempDir,
+    path: &Utf8Path,
+) -> io::Result<Utf8PathBuf> {
     Ok(path.to_owned())
 }
diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs
index 0f7c839..29fe5ae 100644
--- a/crates/proc-macro-srv/src/lib.rs
+++ b/crates/proc-macro-srv/src/lib.rs
@@ -42,6 +42,7 @@
 
 use paths::{Utf8Path, Utf8PathBuf};
 use span::Span;
+use temp_dir::TempDir;
 
 use crate::server_impl::TokenStream;
 
@@ -59,11 +60,16 @@
 pub struct ProcMacroSrv<'env> {
     expanders: Mutex<HashMap<Utf8PathBuf, Arc<dylib::Expander>>>,
     env: &'env EnvSnapshot,
+    temp_dir: TempDir,
 }
 
 impl<'env> ProcMacroSrv<'env> {
     pub fn new(env: &'env EnvSnapshot) -> Self {
-        Self { expanders: Default::default(), env }
+        Self {
+            expanders: Default::default(),
+            env,
+            temp_dir: TempDir::with_prefix("proc-macro-srv").unwrap(),
+        }
     }
 }
 
@@ -73,7 +79,7 @@
     pub fn expand<S: ProcMacroSrvSpan>(
         &self,
         lib: impl AsRef<Utf8Path>,
-        env: Vec<(String, String)>,
+        env: &[(String, String)],
         current_dir: Option<impl AsRef<Path>>,
         macro_name: String,
         macro_body: tt::TopSubtree<S>,
@@ -131,7 +137,7 @@
 
     fn expander(&self, path: &Utf8Path) -> Result<Arc<dylib::Expander>, String> {
         let expander = || {
-            let expander = dylib::Expander::new(path)
+            let expander = dylib::Expander::new(&self.temp_dir, path)
                 .map_err(|err| format!("Cannot create expander for {path}: {err}",));
             expander.map(Arc::new)
         };
@@ -203,7 +209,7 @@
 static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
 
 struct EnvChange<'snap> {
-    changed_vars: Vec<String>,
+    changed_vars: Vec<&'snap str>,
     prev_working_dir: Option<PathBuf>,
     snap: &'snap EnvSnapshot,
     _guard: std::sync::MutexGuard<'snap, ()>,
@@ -212,7 +218,7 @@
 impl<'snap> EnvChange<'snap> {
     fn apply(
         snap: &'snap EnvSnapshot,
-        new_vars: Vec<(String, String)>,
+        new_vars: &'snap [(String, String)],
         current_dir: Option<&Path>,
     ) -> EnvChange<'snap> {
         let guard = ENV_LOCK.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
@@ -232,11 +238,11 @@
         EnvChange {
             snap,
             changed_vars: new_vars
-                .into_iter()
+                .iter()
                 .map(|(k, v)| {
                     // SAFETY: We have acquired the environment lock
-                    unsafe { env::set_var(&k, v) };
-                    k
+                    unsafe { env::set_var(k, v) };
+                    &**k
                 })
                 .collect(),
             prev_working_dir,
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index 7aa3857..f5a76e3 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -55,7 +55,7 @@
     expect_spanned: Expect,
 ) {
     let path = proc_macro_test_dylib_path();
-    let expander = dylib::Expander::new(&path).unwrap();
+    let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap();
 
     let def_site = SpanId(0);
     let call_site = SpanId(1);