Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/proc-macro-api/src/process.rs')
-rw-r--r--crates/proc-macro-api/src/process.rs126
1 files changed, 56 insertions, 70 deletions
diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs
index fcea75ef67..fe274a027a 100644
--- a/crates/proc-macro-api/src/process.rs
+++ b/crates/proc-macro-api/src/process.rs
@@ -12,13 +12,8 @@ use stdx::JodChild;
use crate::{
ProcMacroKind, ServerError,
- legacy_protocol::{
- json::{read_json, write_json},
- msg::{
- CURRENT_API_VERSION, Message, RUST_ANALYZER_SPAN_SUPPORT, Request, Response,
- ServerConfig, SpanMode,
- },
- },
+ legacy_protocol::{self, SpanMode},
+ version,
};
/// Represents a process handling proc-macro communication.
@@ -28,11 +23,16 @@ pub(crate) struct ProcMacroServerProcess {
/// hence the lock on the state.
state: Mutex<ProcessSrvState>,
version: u32,
- mode: SpanMode,
+ protocol: Protocol,
/// Populated when the server exits.
exited: OnceLock<AssertUnwindSafe<ServerError>>,
}
+#[derive(Debug)]
+enum Protocol {
+ LegacyJson { mode: SpanMode },
+}
+
/// Maintains the state of the proc-macro server process.
#[derive(Debug)]
struct ProcessSrvState {
@@ -56,34 +56,40 @@ impl ProcMacroServerProcess {
io::Result::Ok(ProcMacroServerProcess {
state: Mutex::new(ProcessSrvState { process, stdin, stdout }),
version: 0,
- mode: SpanMode::Id,
+ protocol: Protocol::LegacyJson { mode: SpanMode::Id },
exited: OnceLock::new(),
})
};
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::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."
- ),
- )),
+ Ok(v) if v > version::CURRENT_API_VERSION => {
+ #[allow(clippy::disallowed_methods)]
+ let process_version = Command::new(process_path)
+ .arg("--version")
+ .output()
+ .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned())
+ .unwrap_or_else(|_| "unknown version".to_owned());
+ Err(io::Error::other(format!(
+ "Your installed proc-macro server is too new for your rust-analyzer. API version: {}, server version: {process_version}. \
+ This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain.",
+ version::CURRENT_API_VERSION
+ )))
+ }
Ok(v) => {
tracing::info!("Proc-macro server version: {v}");
srv.version = v;
- if srv.version >= RUST_ANALYZER_SPAN_SUPPORT {
- if let Ok(mode) = srv.enable_rust_analyzer_spans() {
- srv.mode = mode;
- }
+ if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT
+ && let Ok(mode) = srv.enable_rust_analyzer_spans()
+ {
+ srv.protocol = Protocol::LegacyJson { mode };
}
- tracing::info!("Proc-macro server span mode: {:?}", srv.mode);
+ tracing::info!("Proc-macro server protocol: {:?}", srv.protocol);
Ok(srv)
}
Err(e) => {
tracing::info!(%e, "proc-macro version check failed");
- Err(
- io::Error::other(format!("proc-macro server version check failed: {e}")),
- )
+ Err(io::Error::other(format!("proc-macro server version check failed: {e}")))
}
}
}
@@ -98,25 +104,24 @@ impl ProcMacroServerProcess {
self.version
}
+ /// Enable support for rust-analyzer span mode if the server supports it.
+ pub(crate) fn rust_analyzer_spans(&self) -> bool {
+ match self.protocol {
+ Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer,
+ }
+ }
+
/// 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)?;
-
- match response {
- Response::ApiVersionCheck(version) => Ok(version),
- _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }),
+ match self.protocol {
+ Protocol::LegacyJson { .. } => legacy_protocol::version_check(self),
}
}
/// 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)?;
-
- match response {
- Response::SetConfig(ServerConfig { span_mode }) => Ok(span_mode),
- _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }),
+ match self.protocol {
+ Protocol::LegacyJson { .. } => legacy_protocol::enable_rust_analyzer_spans(self),
}
}
@@ -125,25 +130,24 @@ impl ProcMacroServerProcess {
&self,
dylib_path: &AbsPath,
) -> Result<Result<Vec<(String, ProcMacroKind)>, String>, ServerError> {
- let request = Request::ListMacros { dylib_path: dylib_path.to_path_buf().into() };
-
- let response = self.send_task(request)?;
-
- match response {
- Response::ListMacros(it) => Ok(it),
- _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }),
+ match self.protocol {
+ Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path),
}
}
- /// 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());
- }
-
+ pub(crate) fn send_task<Request, Response>(
+ &self,
+ serialize_req: impl FnOnce(
+ &mut dyn Write,
+ &mut dyn BufRead,
+ Request,
+ &mut String,
+ ) -> Result<Option<Response>, ServerError>,
+ req: Request,
+ ) -> Result<Response, ServerError> {
let state = &mut *self.state.lock().unwrap();
let mut buf = String::new();
- send_request(&mut state.stdin, &mut state.stdout, req, &mut buf)
+ serialize_req(&mut state.stdin, &mut state.stdout, req, &mut buf)
.and_then(|res| {
res.ok_or_else(|| {
let message = "proc-macro server did not respond with data".to_owned();
@@ -162,10 +166,10 @@ impl ProcMacroServerProcess {
Ok(None) | Err(_) => e,
Ok(Some(status)) => {
let mut msg = String::new();
- if !status.success() {
- if let Some(stderr) = state.process.child.stderr.as_mut() {
- _ = stderr.read_to_string(&mut msg);
- }
+ if !status.success()
+ && let Some(stderr) = state.process.child.stderr.as_mut()
+ {
+ _ = stderr.read_to_string(&mut msg);
}
let server_error = ServerError {
message: format!(
@@ -242,21 +246,3 @@ fn mk_child<'a>(
}
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,
- req: Request,
- buf: &mut String,
-) -> Result<Option<Response>, ServerError> {
- req.write(write_json, &mut writer).map_err(|err| ServerError {
- message: "failed to write request".into(),
- io: Some(Arc::new(err)),
- })?;
- let res = Response::read(read_json, &mut reader, buf).map_err(|err| ServerError {
- message: "failed to read response".into(),
- io: Some(Arc::new(err)),
- })?;
- Ok(res)
-}