about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/proc-macro-api
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/proc-macro-api')
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs5
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs72
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs8
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs39
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs53
5 files changed, 131 insertions, 46 deletions
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs
index ec89f6a9e65..c8f774031b5 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/json.rs
@@ -1,6 +1,7 @@
 //! Protocol functions for json.
 use std::io::{self, BufRead, Write};
 
+/// Reads a JSON message from the input stream.
 pub fn read_json<'a>(
     inp: &mut impl BufRead,
     buf: &'a mut String,
@@ -26,10 +27,10 @@ pub fn read_json<'a>(
     }
 }
 
+/// Writes a JSON message to the output stream.
 pub fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
     tracing::debug!("> {}", msg);
     out.write_all(msg.as_bytes())?;
     out.write_all(b"\n")?;
-    out.flush()?;
-    Ok(())
+    out.flush()
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
index 4b831e4aceb..55185aa492d 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs
@@ -10,7 +10,7 @@ use serde_derive::{Deserialize, Serialize};
 use crate::ProcMacroKind;
 
 pub use self::flat::{
-    deserialize_span_data_index_map, serialize_span_data_index_map, FlatTree, SpanDataIndexMap,
+    FlatTree, SpanDataIndexMap, deserialize_span_data_index_map, serialize_span_data_index_map,
 };
 pub use span::TokenId;
 
@@ -20,69 +20,103 @@ pub const VERSION_CHECK_VERSION: u32 = 1;
 pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
 pub const HAS_GLOBAL_SPANS: u32 = 3;
 pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
-/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field
+/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field.
 pub const EXTENDED_LEAF_DATA: u32 = 5;
 
+/// Current API version of the proc-macro protocol.
 pub const CURRENT_API_VERSION: u32 = EXTENDED_LEAF_DATA;
 
+/// Represents requests sent from the client to the proc-macro-srv.
 #[derive(Debug, Serialize, Deserialize)]
 pub enum Request {
+    /// Retrieves a list of macros from a given dynamic library.
     /// Since [`NO_VERSION_CHECK_VERSION`]
     ListMacros { dylib_path: Utf8PathBuf },
+
+    /// Expands a procedural macro.
     /// Since [`NO_VERSION_CHECK_VERSION`]
     ExpandMacro(Box<ExpandMacro>),
+
+    /// Performs an API version check between the client and the server.
     /// Since [`VERSION_CHECK_VERSION`]
     ApiVersionCheck {},
+
+    /// Sets server-specific configurations.
     /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
     SetConfig(ServerConfig),
 }
 
+/// Defines the mode used for handling span data.
 #[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
 pub enum SpanMode {
+    /// Default mode, where spans are identified by an ID.
     #[default]
     Id,
+
+    /// Rust Analyzer-specific span handling mode.
     RustAnalyzer,
 }
 
+/// Represents responses sent from the proc-macro-srv to the client.
 #[derive(Debug, Serialize, Deserialize)]
 pub enum Response {
+    /// Returns a list of available macros in a dynamic library.
     /// Since [`NO_VERSION_CHECK_VERSION`]
     ListMacros(Result<Vec<(String, ProcMacroKind)>, String>),
+
+    /// Returns result of a macro expansion.
     /// Since [`NO_VERSION_CHECK_VERSION`]
     ExpandMacro(Result<FlatTree, PanicMessage>),
+
+    /// Returns the API version supported by the server.
     /// Since [`NO_VERSION_CHECK_VERSION`]
     ApiVersionCheck(u32),
+
+    /// Confirms the application of a configuration update.
     /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
     SetConfig(ServerConfig),
+
+    /// Returns the result of a macro expansion, including extended span data.
     /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
     ExpandMacroExtended(Result<ExpandMacroExtended, PanicMessage>),
 }
 
+/// Configuration settings for the proc-macro-srv.
 #[derive(Debug, Serialize, Deserialize, Default)]
 #[serde(default)]
 pub struct ServerConfig {
+    /// Defines how span data should be handled.
     pub span_mode: SpanMode,
 }
 
+/// Represents an extended macro expansion response, including span data mappings.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct ExpandMacroExtended {
+    /// The expanded syntax tree.
     pub tree: FlatTree,
+    /// Additional span data mappings.
     pub span_data_table: Vec<u32>,
 }
 
+/// Represents an error message when a macro expansion results in a panic.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct PanicMessage(pub String);
 
+/// Represents a macro expansion request sent from the client.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct ExpandMacro {
+    /// The path to the dynamic library containing the macro.
     pub lib: Utf8PathBuf,
     /// Environment variables to set during macro expansion.
     pub env: Vec<(String, String)>,
+    /// The current working directory for the macro expansion.
     pub current_dir: Option<String>,
+    /// Macro expansion data, including the macro body, name and attributes.
     #[serde(flatten)]
     pub data: ExpandMacroData,
 }
 
+/// Represents the input data required for expanding a macro.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct ExpandMacroData {
     /// Argument of macro call.
@@ -103,18 +137,24 @@ pub struct ExpandMacroData {
     #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
     #[serde(default)]
     pub has_global_spans: ExpnGlobals,
+    /// Table of additional span data.
     #[serde(skip_serializing_if = "Vec::is_empty")]
     #[serde(default)]
     pub span_data_table: Vec<u32>,
 }
 
+/// Represents global expansion settings, including span resolution.
 #[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
 pub struct ExpnGlobals {
+    /// Determines whether to serialize the expansion settings.
     #[serde(skip_serializing)]
     #[serde(default)]
     pub serialize: bool,
+    /// Defines the `def_site` span location.
     pub def_site: usize,
+    /// Defines the `call_site` span location.
     pub call_site: usize,
+    /// Defines the `mixed_site` span location.
     pub mixed_site: usize,
 }
 
@@ -150,16 +190,18 @@ pub trait Message: serde::Serialize + DeserializeOwned {
 impl Message for Request {}
 impl Message for Response {}
 
+/// Type alias for a function that reads protocol messages from a buffered input stream.
 #[allow(type_alias_bounds)]
 type ProtocolRead<R: BufRead> =
     for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut String) -> io::Result<Option<&'buf String>>;
+/// Type alias for a function that writes protocol messages to an output stream.
 #[allow(type_alias_bounds)]
 type ProtocolWrite<W: Write> = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) -> io::Result<()>;
 
 #[cfg(test)]
 mod tests {
-    use intern::{sym, Symbol};
-    use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TextSize};
+    use intern::{Symbol, sym};
+    use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContext, TextRange, TextSize};
     use tt::{
         Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
         TopSubtreeBuilder,
@@ -180,12 +222,12 @@ mod tests {
             open: Span {
                 range: TextRange::empty(TextSize::new(0)),
                 anchor,
-                ctx: SyntaxContextId::root(Edition::CURRENT),
+                ctx: SyntaxContext::root(Edition::CURRENT),
             },
             close: Span {
                 range: TextRange::empty(TextSize::new(19)),
                 anchor,
-                ctx: SyntaxContextId::root(Edition::CURRENT),
+                ctx: SyntaxContext::root(Edition::CURRENT),
             },
             kind: DelimiterKind::Invisible,
         });
@@ -196,7 +238,7 @@ mod tests {
                 span: Span {
                     range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
                     anchor,
-                    ctx: SyntaxContextId::root(Edition::CURRENT),
+                    ctx: SyntaxContext::root(Edition::CURRENT),
                 },
                 is_raw: tt::IdentIsRaw::No,
             }
@@ -208,7 +250,7 @@ mod tests {
                 span: Span {
                     range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
                     anchor,
-                    ctx: SyntaxContextId::root(Edition::CURRENT),
+                    ctx: SyntaxContext::root(Edition::CURRENT),
                 },
                 is_raw: tt::IdentIsRaw::Yes,
             }
@@ -219,7 +261,7 @@ mod tests {
             span: Span {
                 range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
                 anchor,
-                ctx: SyntaxContextId::root(Edition::CURRENT),
+                ctx: SyntaxContext::root(Edition::CURRENT),
             },
             kind: tt::LitKind::Str,
             suffix: None,
@@ -229,7 +271,7 @@ mod tests {
             span: Span {
                 range: TextRange::at(TextSize::new(13), TextSize::of('@')),
                 anchor,
-                ctx: SyntaxContextId::root(Edition::CURRENT),
+                ctx: SyntaxContext::root(Edition::CURRENT),
             },
             spacing: Spacing::Joint,
         }));
@@ -238,23 +280,23 @@ mod tests {
             Span {
                 range: TextRange::at(TextSize::new(14), TextSize::of('{')),
                 anchor,
-                ctx: SyntaxContextId::root(Edition::CURRENT),
+                ctx: SyntaxContext::root(Edition::CURRENT),
             },
         );
         builder.push(Leaf::Literal(Literal {
-            symbol: sym::INTEGER_0.clone(),
+            symbol: sym::INTEGER_0,
             span: Span {
                 range: TextRange::at(TextSize::new(15), TextSize::of("0u32")),
                 anchor,
-                ctx: SyntaxContextId::root(Edition::CURRENT),
+                ctx: SyntaxContext::root(Edition::CURRENT),
             },
             kind: tt::LitKind::Integer,
-            suffix: Some(sym::u32.clone()),
+            suffix: Some(sym::u32),
         }));
         builder.close(Span {
             range: TextRange::at(TextSize::new(19), TextSize::of('}')),
             anchor,
-            ctx: SyntaxContextId::root(Edition::CURRENT),
+            ctx: SyntaxContext::root(Edition::CURRENT),
         });
 
         builder.build()
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
index c194f301714..597ffa05d20 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
@@ -40,9 +40,7 @@ use std::collections::VecDeque;
 use intern::Symbol;
 use rustc_hash::FxHashMap;
 use serde_derive::{Deserialize, Serialize};
-use span::{
-    EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, TextRange, TokenId,
-};
+use span::{EditionedFileId, ErasedFileAstId, Span, SpanAnchor, SyntaxContext, TextRange, TokenId};
 
 use crate::legacy_protocol::msg::{ENCODE_CLOSE_SPAN_VERSION, EXTENDED_LEAF_DATA};
 
@@ -74,7 +72,9 @@ pub fn deserialize_span_data_index_map(map: &[u32]) -> SpanDataIndexMap {
                     ast_id: ErasedFileAstId::from_raw(ast_id),
                 },
                 range: TextRange::new(start.into(), end.into()),
-                ctx: SyntaxContextId::from_u32(e),
+                // SAFETY: We only receive spans from the server. If someone mess up the communication UB can happen,
+                // but that will be their problem.
+                ctx: unsafe { SyntaxContext::from_u32(e) },
             }
         })
         .collect()
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index dc3328ebcda..25c30b6db4a 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -13,20 +13,23 @@ mod process;
 
 use paths::{AbsPath, AbsPathBuf};
 use span::Span;
-use std::{fmt, io, sync::Arc};
+use std::{fmt, io, sync::Arc, time::SystemTime};
 
 use crate::{
     legacy_protocol::msg::{
-        deserialize_span_data_index_map, flat::serialize_span_data_index_map, ExpandMacro,
-        ExpandMacroData, ExpnGlobals, FlatTree, PanicMessage, Request, Response, SpanDataIndexMap,
-        HAS_GLOBAL_SPANS, RUST_ANALYZER_SPAN_SUPPORT,
+        ExpandMacro, ExpandMacroData, ExpnGlobals, FlatTree, HAS_GLOBAL_SPANS, PanicMessage,
+        RUST_ANALYZER_SPAN_SUPPORT, Request, Response, SpanDataIndexMap,
+        deserialize_span_data_index_map, flat::serialize_span_data_index_map,
     },
     process::ProcMacroServerProcess,
 };
 
+/// Represents different kinds of procedural macros that can be expanded by the external server.
 #[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)]
 pub enum ProcMacroKind {
+    /// A macro that derives implementations for a struct or enum.
     CustomDerive,
+    /// An attribute-like procedural macro.
     Attr,
     // This used to be called FuncLike, so that's what the server expects currently.
     #[serde(alias = "Bang")]
@@ -46,11 +49,13 @@ pub struct ProcMacroClient {
     path: AbsPathBuf,
 }
 
+/// Represents a dynamically loaded library containing procedural macros.
 pub struct MacroDylib {
     path: AbsPathBuf,
 }
 
 impl MacroDylib {
+    /// Creates a new MacroDylib instance with the given path.
     pub fn new(path: AbsPathBuf) -> MacroDylib {
         MacroDylib { path }
     }
@@ -66,6 +71,7 @@ pub struct ProcMacro {
     dylib_path: Arc<AbsPathBuf>,
     name: Box<str>,
     kind: ProcMacroKind,
+    dylib_last_modified: Option<SystemTime>,
 }
 
 impl Eq for ProcMacro {}
@@ -73,11 +79,13 @@ impl PartialEq for ProcMacro {
     fn eq(&self, other: &Self) -> bool {
         self.name == other.name
             && self.kind == other.kind
-            && Arc::ptr_eq(&self.dylib_path, &other.dylib_path)
+            && self.dylib_path == other.dylib_path
+            && self.dylib_last_modified == other.dylib_last_modified
             && Arc::ptr_eq(&self.process, &other.process)
     }
 }
 
+/// Represents errors encountered when communicating with the proc-macro server.
 #[derive(Clone, Debug)]
 pub struct ServerError {
     pub message: String,
@@ -97,15 +105,17 @@ impl fmt::Display for ServerError {
 
 impl ProcMacroClient {
     /// Spawns an external process as the proc macro server and returns a client connected to it.
-    pub fn spawn(
+    pub fn spawn<'a>(
         process_path: &AbsPath,
-        env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)>
-            + Clone,
+        env: impl IntoIterator<
+            Item = (impl AsRef<std::ffi::OsStr>, &'a Option<impl 'a + AsRef<std::ffi::OsStr>>),
+        > + Clone,
     ) -> io::Result<ProcMacroClient> {
         let process = ProcMacroServerProcess::run(process_path, env)?;
         Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
     }
 
+    /// Returns the absolute path to the proc-macro server.
     pub fn server_path(&self) -> &AbsPath {
         &self.path
     }
@@ -116,6 +126,9 @@ impl ProcMacroClient {
         let macros = self.process.find_proc_macros(&dylib.path)?;
 
         let dylib_path = Arc::new(dylib.path);
+        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
+            .ok()
+            .and_then(|metadata| metadata.modified().ok());
         match macros {
             Ok(macros) => Ok(macros
                 .into_iter()
@@ -124,26 +137,32 @@ impl ProcMacroClient {
                     name: name.into(),
                     kind,
                     dylib_path: dylib_path.clone(),
+                    dylib_last_modified,
                 })
                 .collect()),
             Err(message) => Err(ServerError { message, io: None }),
         }
     }
 
+    /// Checks if the proc-macro server has exited.
     pub fn exited(&self) -> Option<&ServerError> {
         self.process.exited()
     }
 }
 
 impl ProcMacro {
+    /// Returns the name of the procedural macro.
     pub fn name(&self) -> &str {
         &self.name
     }
 
+    /// Returns the type of procedural macro.
     pub fn kind(&self) -> ProcMacroKind {
         self.kind
     }
 
+    /// Expands the procedural macro by sending an expansion request to the server.
+    /// This includes span information and environmental context.
     pub fn expand(
         &self,
         subtree: tt::SubtreeView<'_, Span>,
@@ -152,7 +171,7 @@ impl ProcMacro {
         def_site: Span,
         call_site: Span,
         mixed_site: Span,
-        current_dir: Option<String>,
+        current_dir: String,
     ) -> Result<Result<tt::TopSubtree<Span>, PanicMessage>, ServerError> {
         let version = self.process.version();
 
@@ -180,7 +199,7 @@ impl ProcMacro {
             },
             lib: self.dylib_path.to_path_buf().into(),
             env,
-            current_dir,
+            current_dir: Some(current_dir),
         };
 
         let response = self.process.send_task(Request::ExpandMacro(Box::new(task)))?;
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index d998b23d3bb..fcea75ef672 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -11,16 +11,17 @@ use paths::AbsPath;
 use stdx::JodChild;
 
 use crate::{
+    ProcMacroKind, ServerError,
     legacy_protocol::{
         json::{read_json, write_json},
         msg::{
-            Message, Request, Response, ServerConfig, SpanMode, CURRENT_API_VERSION,
-            RUST_ANALYZER_SPAN_SUPPORT,
+            CURRENT_API_VERSION, Message, RUST_ANALYZER_SPAN_SUPPORT, Request, Response,
+            ServerConfig, SpanMode,
         },
     },
-    ProcMacroKind, ServerError,
 };
 
+/// Represents a process handling proc-macro communication.
 #[derive(Debug)]
 pub(crate) struct ProcMacroServerProcess {
     /// The state of the proc-macro server process, the protocol is currently strictly sequential
@@ -32,6 +33,7 @@ pub(crate) struct ProcMacroServerProcess {
     exited: OnceLock<AssertUnwindSafe<ServerError>>,
 }
 
+/// Maintains the state of the proc-macro server process.
 #[derive(Debug)]
 struct ProcessSrvState {
     process: Process,
@@ -40,10 +42,12 @@ struct ProcessSrvState {
 }
 
 impl ProcMacroServerProcess {
-    pub(crate) fn run(
+    /// Starts the proc-macro server and performs a version check
+    pub(crate) fn run<'a>(
         process_path: &AbsPath,
-        env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)>
-            + Clone,
+        env: impl IntoIterator<
+            Item = (impl AsRef<std::ffi::OsStr>, &'a Option<impl 'a + AsRef<std::ffi::OsStr>>),
+        > + Clone,
     ) -> io::Result<ProcMacroServerProcess> {
         let create_srv = || {
             let mut process = Process::run(process_path, env.clone())?;
@@ -59,8 +63,7 @@ impl ProcMacroServerProcess {
         let mut srv = create_srv()?;
         tracing::info!("sending proc-macro server version check");
         match srv.version_check() {
-            Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::new(
-                io::ErrorKind::Other,
+            Ok(v) if v > CURRENT_API_VERSION => Err(io::Error::other(
                 format!( "The version of the proc-macro server ({v}) in your Rust toolchain is newer than the version supported by your rust-analyzer ({CURRENT_API_VERSION}).
             This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain."
                 ),
@@ -79,20 +82,23 @@ impl ProcMacroServerProcess {
             Err(e) => {
                 tracing::info!(%e, "proc-macro version check failed");
                 Err(
-                    io::Error::new(io::ErrorKind::Other, format!("proc-macro server version check failed: {e}")),
+                    io::Error::other(format!("proc-macro server version check failed: {e}")),
                 )
             }
         }
     }
 
+    /// Returns the server error if the process has exited.
     pub(crate) fn exited(&self) -> Option<&ServerError> {
         self.exited.get().map(|it| &it.0)
     }
 
+    /// Retrieves the API version of the proc-macro server.
     pub(crate) fn version(&self) -> u32 {
         self.version
     }
 
+    /// Checks the API version of the running proc-macro server.
     fn version_check(&self) -> Result<u32, ServerError> {
         let request = Request::ApiVersionCheck {};
         let response = self.send_task(request)?;
@@ -103,6 +109,7 @@ impl ProcMacroServerProcess {
         }
     }
 
+    /// Enable support for rust-analyzer span mode if the server supports it.
     fn enable_rust_analyzer_spans(&self) -> Result<SpanMode, ServerError> {
         let request = Request::SetConfig(ServerConfig { span_mode: SpanMode::RustAnalyzer });
         let response = self.send_task(request)?;
@@ -113,6 +120,7 @@ impl ProcMacroServerProcess {
         }
     }
 
+    /// Finds proc-macros in a given dynamic library.
     pub(crate) fn find_proc_macros(
         &self,
         dylib_path: &AbsPath,
@@ -127,6 +135,7 @@ impl ProcMacroServerProcess {
         }
     }
 
+    /// Sends a request to the proc-macro server and waits for a response.
     pub(crate) fn send_task(&self, req: Request) -> Result<Response, ServerError> {
         if let Some(server_error) = self.exited.get() {
             return Err(server_error.0.clone());
@@ -177,20 +186,25 @@ impl ProcMacroServerProcess {
     }
 }
 
+/// Manages the execution of the proc-macro server process.
 #[derive(Debug)]
 struct Process {
     child: JodChild,
 }
 
 impl Process {
-    fn run(
+    /// Runs a new proc-macro server process with the specified environment variables.
+    fn run<'a>(
         path: &AbsPath,
-        env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)>,
+        env: impl IntoIterator<
+            Item = (impl AsRef<std::ffi::OsStr>, &'a Option<impl 'a + AsRef<std::ffi::OsStr>>),
+        >,
     ) -> io::Result<Process> {
         let child = JodChild(mk_child(path, env)?);
         Ok(Process { child })
     }
 
+    /// Retrieves stdin and stdout handles for the process.
     fn stdio(&mut self) -> Option<(ChildStdin, BufReader<ChildStdout>)> {
         let stdin = self.child.stdin.take()?;
         let stdout = self.child.stdout.take()?;
@@ -200,14 +214,22 @@ impl Process {
     }
 }
 
-fn mk_child(
+/// Creates and configures a new child process for the proc-macro server.
+fn mk_child<'a>(
     path: &AbsPath,
-    env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)>,
+    extra_env: impl IntoIterator<
+        Item = (impl AsRef<std::ffi::OsStr>, &'a Option<impl 'a + AsRef<std::ffi::OsStr>>),
+    >,
 ) -> io::Result<Child> {
     #[allow(clippy::disallowed_methods)]
     let mut cmd = Command::new(path);
-    cmd.envs(env)
-        .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
+    for env in extra_env {
+        match env {
+            (key, Some(val)) => cmd.env(key, val),
+            (key, None) => cmd.env_remove(key),
+        };
+    }
+    cmd.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
         .stdin(Stdio::piped())
         .stdout(Stdio::piped())
         .stderr(Stdio::inherit());
@@ -221,6 +243,7 @@ fn mk_child(
     cmd.spawn()
 }
 
+/// Sends a request to the server and reads the response.
 fn send_request(
     mut writer: &mut impl Write,
     mut reader: &mut impl BufRead,