From 1dad405ebe0245044fde0003ca29fe3f9acf43c8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 7 Nov 2025 23:58:27 +0530 Subject: [PATCH 1/5] make postcard first class member of proc-macro-srv-cli --- crates/proc-macro-srv-cli/Cargo.toml | 3 +- crates/proc-macro-srv-cli/src/main.rs | 7 +- crates/proc-macro-srv-cli/src/main_loop.rs | 146 ++++++++++++++++++++- 3 files changed, 146 insertions(+), 10 deletions(-) diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index dd31e74915bf..f6022cf2c7bd 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -14,14 +14,13 @@ publish = false proc-macro-srv.workspace = true proc-macro-api.workspace = true tt.workspace = true +postcard.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", "proc-macro-api/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 9d74fa637aa9..9149adb4687e 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -31,7 +31,7 @@ fn main() -> std::io::Result<()> { clap::Arg::new("format") .long("format") .action(clap::ArgAction::Set) - .default_value("json") + .default_value("postcard") .value_parser(clap::builder::EnumValueParser::::new()), clap::Arg::new("version") .long("version") @@ -51,26 +51,23 @@ fn main() -> std::io::Result<()> { #[derive(Copy, Clone)] enum ProtocolFormat { Json, - #[cfg(feature = "postcard")] Postcard, } impl ValueEnum for ProtocolFormat { fn value_variants<'a>() -> &'a [Self] { - &[ProtocolFormat::Json] + &[ProtocolFormat::Json, ProtocolFormat::Postcard] } fn to_possible_value(&self) -> Option { 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 { 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 55331075704a..c48ae3c2ed0c 100644 --- a/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,5 +1,5 @@ //! The main loop of the proc-macro server. -use std::io; +use std::{io, thread}; use proc_macro_api::{ legacy_protocol::{ @@ -14,6 +14,7 @@ use proc_macro_api::{ use proc_macro_srv::{EnvSnapshot, SpanId}; use crate::ProtocolFormat; +use std::io::BufReader; struct SpanTrans; @@ -37,8 +38,7 @@ impl SpanTransformer for SpanTrans { pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> { match format { ProtocolFormat::Json => run_json(), - #[cfg(feature = "postcard")] - ProtocolFormat::Postcard => unimplemented!(), + ProtocolFormat::Postcard => run_postcard(), } } @@ -171,3 +171,143 @@ fn run_json() -> io::Result<()> { Ok(()) } + +fn run_postcard() -> io::Result<()> { + fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { + match kind { + proc_macro_srv::ProcMacroKind::CustomDerive => { + proc_macro_api::ProcMacroKind::CustomDerive + } + proc_macro_srv::ProcMacroKind::Bang => proc_macro_api::ProcMacroKind::Bang, + proc_macro_srv::ProcMacroKind::Attr => proc_macro_api::ProcMacroKind::Attr, + } + } + + let stdin = io::stdin(); + let stdout = io::stdout(); + let mut reader = BufReader::new(stdin.lock()); + let mut writer = stdout.lock(); + let mut buf = vec![0; 1024]; + + let env = proc_macro_srv::EnvSnapshot::default(); + let srv = proc_macro_srv::ProcMacroSrv::new(&env); + + let mut span_mode = msg::SpanMode::Id; + use proc_macro_api::legacy_protocol::postcard_wire; + + while let Some(req) = postcard_wire::read_postcard(&mut reader, &mut buf)? { + let Ok(req) = postcard_wire::decode_cobs(req) else { + thread::sleep(std::time::Duration::from_secs(1)); + continue; + }; + dbg!(&req); + + let res = match req { + msg::Request::ListMacros { dylib_path } => { + msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { + macros.into_iter().map(|(name, kind)| (name, macro_kind_to_api(kind))).collect() + })) + } + msg::Request::ExpandMacro(task) => { + let msg::ExpandMacro { + lib, + env, + current_dir, + data: + msg::ExpandMacroData { + macro_body, + macro_name, + attributes, + has_global_spans: + msg::ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, + span_data_table, + }, + } = *task; + match span_mode { + msg::SpanMode::Id => msg::Response::ExpandMacro({ + let def_site = proc_macro_srv::SpanId(def_site as u32); + let call_site = proc_macro_srv::SpanId(call_site as u32); + let mixed_site = proc_macro_srv::SpanId(mixed_site as u32); + + let macro_body = + macro_body.to_subtree_unresolved::(CURRENT_API_VERSION); + let attributes = attributes + .map(|it| it.to_subtree_unresolved::(CURRENT_API_VERSION)); + + srv.expand( + lib, + &env, + current_dir, + ¯o_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + msg::FlatTree::new_raw::( + tt::SubtreeView::new(&it), + CURRENT_API_VERSION, + ) + }) + .map_err(|e| e.into_string().unwrap_or_default()) + .map_err(msg::PanicMessage) + }), + msg::SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({ + let mut span_data_table = + msg::deserialize_span_data_index_map(&span_data_table); + + let def_site = span_data_table[def_site]; + let call_site = span_data_table[call_site]; + let mixed_site = span_data_table[mixed_site]; + + let macro_body = + macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); + let attributes = attributes.map(|it| { + it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table) + }); + srv.expand( + lib, + &env, + current_dir, + ¯o_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + ( + msg::FlatTree::new( + tt::SubtreeView::new(&it), + CURRENT_API_VERSION, + &mut span_data_table, + ), + msg::serialize_span_data_index_map(&span_data_table), + ) + }) + .map(|(tree, span_data_table)| msg::ExpandMacroExtended { + tree, + span_data_table, + }) + .map_err(|e| e.into_string().unwrap_or_default()) + .map_err(msg::PanicMessage) + }), + } + } + msg::Request::ApiVersionCheck {} => msg::Response::ApiVersionCheck(CURRENT_API_VERSION), + msg::Request::SetConfig(config) => { + span_mode = config.span_mode; + msg::Response::SetConfig(config) + } + }; + + dbg!(&res); + let res = postcard_wire::encode_cobs(&res).unwrap(); + postcard_wire::write_postcard(&mut writer, &res)?; + } + + Ok(()) +} From f31214f96a0ae4076efdb712cd08948ac68aac72 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 7 Nov 2025 23:58:55 +0530 Subject: [PATCH 2/5] add postcard related methods to proc-macro-apo --- Cargo.lock | 15 +++++ Cargo.toml | 1 + crates/proc-macro-api/Cargo.toml | 1 + crates/proc-macro-api/src/legacy_protocol.rs | 38 ++++++++++- .../src/legacy_protocol/postcard_wire.rs | 27 ++++++++ crates/proc-macro-api/src/lib.rs | 3 +- crates/proc-macro-api/src/process.rs | 64 ++++++++++++++++++- crates/proc-macro-srv-cli/src/main_loop.rs | 3 - 8 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs diff --git a/Cargo.lock b/Cargo.lock index 4de8d09dcab8..d78c0f765d88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -560,6 +560,18 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "ena" version = "0.14.3" @@ -1785,6 +1797,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless", "serde", ] @@ -1820,6 +1834,7 @@ dependencies = [ "indexmap", "intern", "paths", + "postcard", "proc-macro-srv", "rustc-hash 2.1.1", "serde", diff --git a/Cargo.toml b/Cargo.toml index 8ff7e0e8a2a9..946e54b40b02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,7 @@ object = { version = "0.36.7", default-features = false, features = [ "macho", "pe", ] } +postcard = {version = "1.1.3", features = ["alloc"]} process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index 63745b9f7493..4077e11b7185 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -29,6 +29,7 @@ proc-macro-srv = {workspace = true, optional = true} span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true +postcard.workspace = true [features] sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] diff --git a/crates/proc-macro-api/src/legacy_protocol.rs b/crates/proc-macro-api/src/legacy_protocol.rs index 0a72052cc53b..5d590520c8c8 100644 --- a/crates/proc-macro-api/src/legacy_protocol.rs +++ b/crates/proc-macro-api/src/legacy_protocol.rs @@ -2,6 +2,7 @@ pub mod json; pub mod msg; +pub mod postcard_wire; use std::{ io::{BufRead, Write}, @@ -151,7 +152,11 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result, +) -> Result, ServerError> { + let bytes = postcard_wire::encode_cobs(&req) + .map_err(|_| ServerError { message: "failed to write request".into(), io: None })?; + + postcard_wire::write_postcard(&mut writer, &bytes).map_err(|err| ServerError { + message: "failed to write request".into(), + io: Some(Arc::new(err)), + })?; + + let frame = postcard_wire::read_postcard(&mut reader, buf).map_err(|err| ServerError { + message: "failed to read response".into(), + io: Some(Arc::new(err)), + })?; + + match frame { + None => Ok(None), + Some(bytes) => { + let resp: Response = postcard_wire::decode_cobs(bytes).map_err(|e| ServerError { + message: format!("failed to decode message: {e}"), + io: None, + })?; + Ok(Some(resp)) + } + } +} diff --git a/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs b/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs new file mode 100644 index 000000000000..2c8bb5a0c914 --- /dev/null +++ b/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs @@ -0,0 +1,27 @@ +//! Postcard encode and decode implementations. + +use std::io::{self, BufRead, Write}; + +pub fn read_postcard<'a>( + input: &mut impl BufRead, + buf: &'a mut Vec, +) -> io::Result> { + buf.clear(); + let n = input.read_until(0, buf)?; + if n == 0 { + return Ok(None); + } + Ok(Some(&mut buf[..])) +} +pub fn write_postcard(out: &mut impl Write, msg: &[u8]) -> io::Result<()> { + out.write_all(msg)?; + out.flush() +} + +pub fn encode_cobs(value: &T) -> Result, postcard::Error> { + postcard::to_allocvec_cobs(value) +} + +pub fn decode_cobs(bytes: &mut [u8]) -> Result { + postcard::from_bytes_cobs(bytes) +} diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 870d81f97684..877c45f56cfd 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -31,9 +31,10 @@ pub mod version { /// 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; pub const HASHED_AST_ID: u32 = 6; + pub const POSTCARD_WIRE: u32 = 7; /// Current API version of the proc-macro protocol. - pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; + pub const CURRENT_API_VERSION: u32 = POSTCARD_WIRE; } /// Represents different kinds of procedural macros that can be expanded by the external server. diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index fe274a027a80..95b12d0b24e0 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -31,6 +31,7 @@ pub(crate) struct ProcMacroServerProcess { #[derive(Debug)] enum Protocol { LegacyJson { mode: SpanMode }, + Postcard { mode: SpanMode }, } /// Maintains the state of the proc-macro server process. @@ -82,7 +83,11 @@ impl ProcMacroServerProcess { if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT && let Ok(mode) = srv.enable_rust_analyzer_spans() { - srv.protocol = Protocol::LegacyJson { mode }; + if srv.version >= version::POSTCARD_WIRE { + srv.protocol = Protocol::Postcard { mode }; + } else { + srv.protocol = Protocol::LegacyJson { mode }; + } } tracing::info!("Proc-macro server protocol: {:?}", srv.protocol); Ok(srv) @@ -99,6 +104,10 @@ impl ProcMacroServerProcess { self.exited.get().map(|it| &it.0) } + pub(crate) fn use_postcard(&self) -> bool { + matches!(self.protocol, Protocol::Postcard { .. }) + } + /// Retrieves the API version of the proc-macro server. pub(crate) fn version(&self) -> u32 { self.version @@ -108,6 +117,7 @@ impl ProcMacroServerProcess { pub(crate) fn rust_analyzer_spans(&self) -> bool { match self.protocol { Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, + Protocol::Postcard { mode } => mode == SpanMode::RustAnalyzer, } } @@ -115,6 +125,7 @@ impl ProcMacroServerProcess { fn version_check(&self) -> Result { match self.protocol { Protocol::LegacyJson { .. } => legacy_protocol::version_check(self), + Protocol::Postcard { .. } => legacy_protocol::version_check(self), } } @@ -122,6 +133,7 @@ impl ProcMacroServerProcess { fn enable_rust_analyzer_spans(&self) -> Result { match self.protocol { Protocol::LegacyJson { .. } => legacy_protocol::enable_rust_analyzer_spans(self), + Protocol::Postcard { .. } => legacy_protocol::enable_rust_analyzer_spans(self), } } @@ -132,6 +144,7 @@ impl ProcMacroServerProcess { ) -> Result, String>, ServerError> { match self.protocol { Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path), + Protocol::Postcard { .. } => legacy_protocol::find_proc_macros(self, dylib_path), } } @@ -188,6 +201,55 @@ impl ProcMacroServerProcess { } }) } + + pub(crate) fn send_task_bin( + &self, + serialize_req: impl FnOnce( + &mut dyn Write, + &mut dyn BufRead, + Request, + &mut Vec, + ) -> Result, ServerError>, + req: Request, + ) -> Result { + let state = &mut *self.state.lock().unwrap(); + let mut buf = Vec::::new(); + serialize_req(&mut state.stdin, &mut state.stdout, req, &mut buf) + .and_then(|res| { + res.ok_or_else(|| ServerError { + message: "proc-macro server did not respond with data".to_owned(), + io: Some(Arc::new(io::Error::new( + io::ErrorKind::BrokenPipe, + "proc-macro server did not respond with data", + ))), + }) + }) + .map_err(|e| { + if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { + match state.process.child.try_wait() { + Ok(None) | Err(_) => e, + Ok(Some(status)) => { + let mut msg = String::new(); + if !status.success() + && let Some(stderr) = state.process.child.stderr.as_mut() + { + _ = stderr.read_to_string(&mut msg); + } + let server_error = ServerError { + message: format!( + "proc-macro server exited with {status}{}{msg}", + if msg.is_empty() { "" } else { ": " } + ), + io: None, + }; + self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone() + } + } + } else { + e + } + }) + } } /// Manages the execution of the proc-macro server process. diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs index c48ae3c2ed0c..017efb72220c 100644 --- a/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/crates/proc-macro-srv-cli/src/main_loop.rs @@ -200,8 +200,6 @@ fn run_postcard() -> io::Result<()> { thread::sleep(std::time::Duration::from_secs(1)); continue; }; - dbg!(&req); - let res = match req { msg::Request::ListMacros { dylib_path } => { msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { @@ -304,7 +302,6 @@ fn run_postcard() -> io::Result<()> { } }; - dbg!(&res); let res = postcard_wire::encode_cobs(&res).unwrap(); postcard_wire::write_postcard(&mut writer, &res)?; } From e344bfa7eae548b3d8a0ea88ee2f228071cf1285 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 22 Nov 2025 21:06:51 +0530 Subject: [PATCH 3/5] add postcard abstraction inside Message trait --- crates/proc-macro-api/src/legacy_protocol.rs | 10 +++--- .../proc-macro-api/src/legacy_protocol/msg.rs | 34 ++++++++++++++++++- .../{postcard_wire.rs => postcard.rs} | 8 ++--- crates/proc-macro-srv-cli/src/main_loop.rs | 25 +++++--------- 4 files changed, 51 insertions(+), 26 deletions(-) rename crates/proc-macro-api/src/legacy_protocol/{postcard_wire.rs => postcard.rs} (64%) diff --git a/crates/proc-macro-api/src/legacy_protocol.rs b/crates/proc-macro-api/src/legacy_protocol.rs index 5d590520c8c8..cb869fddbcb3 100644 --- a/crates/proc-macro-api/src/legacy_protocol.rs +++ b/crates/proc-macro-api/src/legacy_protocol.rs @@ -2,7 +2,7 @@ pub mod json; pub mod msg; -pub mod postcard_wire; +pub mod postcard; use std::{ io::{BufRead, Write}, @@ -183,15 +183,15 @@ fn send_request_postcard( req: Request, buf: &mut Vec, ) -> Result, ServerError> { - let bytes = postcard_wire::encode_cobs(&req) + let bytes = postcard::encode_cobs(&req) .map_err(|_| ServerError { message: "failed to write request".into(), io: None })?; - postcard_wire::write_postcard(&mut writer, &bytes).map_err(|err| ServerError { + postcard::write_postcard(&mut writer, &bytes).map_err(|err| ServerError { message: "failed to write request".into(), io: Some(Arc::new(err)), })?; - let frame = postcard_wire::read_postcard(&mut reader, buf).map_err(|err| ServerError { + let frame = postcard::read_postcard(&mut reader, buf).map_err(|err| ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)), })?; @@ -199,7 +199,7 @@ fn send_request_postcard( match frame { None => Ok(None), Some(bytes) => { - let resp: Response = postcard_wire::decode_cobs(bytes).map_err(|e| ServerError { + let resp: Response = postcard::decode_cobs(bytes).map_err(|e| ServerError { message: format!("failed to decode message: {e}"), io: None, })?; diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs index 487f50b145e8..d3744dee0ee5 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -8,7 +8,10 @@ use paths::Utf8PathBuf; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; -use crate::ProcMacroKind; +use crate::{ + ProcMacroKind, + legacy_protocol::postcard::{decode_cobs, encode_cobs}, +}; /// Represents requests sent from the client to the proc-macro-srv. #[derive(Debug, Serialize, Deserialize)] @@ -169,6 +172,26 @@ pub trait Message: serde::Serialize + DeserializeOwned { let text = serde_json::to_string(&self)?; to_proto(out, &text) } + + fn read_postcard( + from_proto: ProtocolReadPostcard, + inp: &mut R, + buf: &mut Vec, + ) -> io::Result> { + Ok(match from_proto(inp, buf)? { + None => None, + Some(buf) => Some(decode_cobs(buf)?), + }) + } + + fn write_postcard( + self, + to_proto: ProtocolWritePostcard, + out: &mut W, + ) -> io::Result<()> { + let buf = encode_cobs(&self)?; + to_proto(out, &buf) + } } impl Message for Request {} @@ -182,6 +205,15 @@ type ProtocolRead = #[allow(type_alias_bounds)] type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) -> io::Result<()>; +/// Type alias for a function that reads protocol postcard messages from a buffered input stream. +#[allow(type_alias_bounds)] +type ProtocolReadPostcard = + for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut Vec) -> io::Result>; +/// Type alias for a function that writes protocol postcard messages to an output stream. +#[allow(type_alias_bounds)] +type ProtocolWritePostcard = + for<'o, 'msg> fn(out: &'o mut W, msg: &'msg [u8]) -> io::Result<()>; + #[cfg(test)] mod tests { use intern::{Symbol, sym}; diff --git a/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs b/crates/proc-macro-api/src/legacy_protocol/postcard.rs similarity index 64% rename from crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs rename to crates/proc-macro-api/src/legacy_protocol/postcard.rs index 2c8bb5a0c914..eab26439db12 100644 --- a/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs +++ b/crates/proc-macro-api/src/legacy_protocol/postcard.rs @@ -18,10 +18,10 @@ pub fn write_postcard(out: &mut impl Write, msg: &[u8]) -> io::Result<()> { out.flush() } -pub fn encode_cobs(value: &T) -> Result, postcard::Error> { - postcard::to_allocvec_cobs(value) +pub fn encode_cobs(value: &T) -> io::Result> { + postcard::to_allocvec_cobs(value).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } -pub fn decode_cobs(bytes: &mut [u8]) -> Result { - postcard::from_bytes_cobs(bytes) +pub fn decode_cobs(bytes: &mut [u8]) -> io::Result { + postcard::from_bytes_cobs(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs index 017efb72220c..b0e7108d20a5 100644 --- a/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,5 +1,5 @@ //! The main loop of the proc-macro server. -use std::{io, thread}; +use std::io; use proc_macro_api::{ legacy_protocol::{ @@ -8,14 +8,13 @@ use proc_macro_api::{ self, ExpandMacroData, ExpnGlobals, Message, SpanMode, SpanTransformer, deserialize_span_data_index_map, serialize_span_data_index_map, }, + postcard::{read_postcard, write_postcard}, }, version::CURRENT_API_VERSION, }; use proc_macro_srv::{EnvSnapshot, SpanId}; use crate::ProtocolFormat; -use std::io::BufReader; - struct SpanTrans; impl SpanTransformer for SpanTrans { @@ -183,23 +182,18 @@ fn run_postcard() -> io::Result<()> { } } - let stdin = io::stdin(); - let stdout = io::stdout(); - let mut reader = BufReader::new(stdin.lock()); - let mut writer = stdout.lock(); - let mut buf = vec![0; 1024]; + let mut buf = Vec::new(); + let mut read_request = + || msg::Request::read_postcard(read_postcard, &mut io::stdin().lock(), &mut buf); + let write_response = + |msg: msg::Response| msg.write_postcard(write_postcard, &mut io::stdout().lock()); let env = proc_macro_srv::EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env); let mut span_mode = msg::SpanMode::Id; - use proc_macro_api::legacy_protocol::postcard_wire; - while let Some(req) = postcard_wire::read_postcard(&mut reader, &mut buf)? { - let Ok(req) = postcard_wire::decode_cobs(req) else { - thread::sleep(std::time::Duration::from_secs(1)); - continue; - }; + while let Some(req) = read_request()? { let res = match req { msg::Request::ListMacros { dylib_path } => { msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { @@ -302,8 +296,7 @@ fn run_postcard() -> io::Result<()> { } }; - let res = postcard_wire::encode_cobs(&res).unwrap(); - postcard_wire::write_postcard(&mut writer, &res)?; + write_response(res)?; } Ok(()) From c7435a9bdf1471ef93efdacd639dced3d580267e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 23 Nov 2025 23:22:27 +0530 Subject: [PATCH 4/5] add review suggestions --- crates/proc-macro-api/src/legacy_protocol.rs | 26 +-- .../src/legacy_protocol/json.rs | 4 +- .../proc-macro-api/src/legacy_protocol/msg.rs | 24 +-- .../src/legacy_protocol/postcard.rs | 8 +- crates/proc-macro-api/src/lib.rs | 5 +- crates/proc-macro-api/src/process.rs | 180 +++++++++--------- crates/proc-macro-srv-cli/src/main.rs | 2 +- 7 files changed, 112 insertions(+), 137 deletions(-) diff --git a/crates/proc-macro-api/src/legacy_protocol.rs b/crates/proc-macro-api/src/legacy_protocol.rs index cb869fddbcb3..6d521d00cd90 100644 --- a/crates/proc-macro-api/src/legacy_protocol.rs +++ b/crates/proc-macro-api/src/legacy_protocol.rs @@ -21,6 +21,7 @@ use crate::{ ServerConfig, SpanDataIndexMap, deserialize_span_data_index_map, flat::serialize_span_data_index_map, }, + postcard::{read_postcard, write_postcard}, }, process::ProcMacroServerProcess, version, @@ -153,7 +154,7 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result, ) -> Result, ServerError> { - let bytes = postcard::encode_cobs(&req) - .map_err(|_| ServerError { message: "failed to write request".into(), io: None })?; - - postcard::write_postcard(&mut writer, &bytes).map_err(|err| ServerError { + req.write_postcard(write_postcard, &mut writer).map_err(|err| ServerError { message: "failed to write request".into(), io: Some(Arc::new(err)), })?; - - let frame = postcard::read_postcard(&mut reader, buf).map_err(|err| ServerError { - message: "failed to read response".into(), - io: Some(Arc::new(err)), + let res = Response::read_postcard(read_postcard, &mut reader, buf).map_err(|err| { + ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)) } })?; - - match frame { - None => Ok(None), - Some(bytes) => { - let resp: Response = postcard::decode_cobs(bytes).map_err(|e| ServerError { - message: format!("failed to decode message: {e}"), - io: None, - })?; - Ok(Some(resp)) - } - } + Ok(res) } diff --git a/crates/proc-macro-api/src/legacy_protocol/json.rs b/crates/proc-macro-api/src/legacy_protocol/json.rs index c8f774031b58..cf8535f77d53 100644 --- a/crates/proc-macro-api/src/legacy_protocol/json.rs +++ b/crates/proc-macro-api/src/legacy_protocol/json.rs @@ -5,7 +5,7 @@ use std::io::{self, BufRead, Write}; pub fn read_json<'a>( inp: &mut impl BufRead, buf: &'a mut String, -) -> io::Result> { +) -> io::Result> { loop { buf.clear(); @@ -28,7 +28,7 @@ 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<()> { +pub fn write_json(out: &mut impl Write, msg: &String) -> io::Result<()> { tracing::debug!("> {}", msg); out.write_all(msg.as_bytes())?; out.write_all(b"\n")?; diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs index d3744dee0ee5..6df184630de7 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -153,7 +153,7 @@ impl ExpnGlobals { pub trait Message: serde::Serialize + DeserializeOwned { fn read( - from_proto: ProtocolRead, + from_proto: ProtocolRead, inp: &mut R, buf: &mut String, ) -> io::Result> { @@ -168,13 +168,13 @@ pub trait Message: serde::Serialize + DeserializeOwned { } }) } - fn write(self, to_proto: ProtocolWrite, out: &mut W) -> io::Result<()> { + fn write(self, to_proto: ProtocolWrite, out: &mut W) -> io::Result<()> { let text = serde_json::to_string(&self)?; to_proto(out, &text) } fn read_postcard( - from_proto: ProtocolReadPostcard, + from_proto: ProtocolRead>, inp: &mut R, buf: &mut Vec, ) -> io::Result> { @@ -186,7 +186,7 @@ pub trait Message: serde::Serialize + DeserializeOwned { fn write_postcard( self, - to_proto: ProtocolWritePostcard, + to_proto: ProtocolWrite>, out: &mut W, ) -> io::Result<()> { let buf = encode_cobs(&self)?; @@ -199,20 +199,12 @@ impl Message for Response {} /// Type alias for a function that reads protocol messages from a buffered input stream. #[allow(type_alias_bounds)] -type ProtocolRead = - for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut String) -> io::Result>; +type ProtocolRead = + for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut Buf) -> io::Result>; /// Type alias for a function that writes protocol messages to an output stream. #[allow(type_alias_bounds)] -type ProtocolWrite = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) -> io::Result<()>; - -/// Type alias for a function that reads protocol postcard messages from a buffered input stream. -#[allow(type_alias_bounds)] -type ProtocolReadPostcard = - for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut Vec) -> io::Result>; -/// Type alias for a function that writes protocol postcard messages to an output stream. -#[allow(type_alias_bounds)] -type ProtocolWritePostcard = - for<'o, 'msg> fn(out: &'o mut W, msg: &'msg [u8]) -> io::Result<()>; +type ProtocolWrite = + for<'o, 'msg> fn(out: &'o mut W, msg: &'msg Buf) -> io::Result<()>; #[cfg(test)] mod tests { diff --git a/crates/proc-macro-api/src/legacy_protocol/postcard.rs b/crates/proc-macro-api/src/legacy_protocol/postcard.rs index eab26439db12..305e4de93415 100644 --- a/crates/proc-macro-api/src/legacy_protocol/postcard.rs +++ b/crates/proc-macro-api/src/legacy_protocol/postcard.rs @@ -5,15 +5,17 @@ use std::io::{self, BufRead, Write}; pub fn read_postcard<'a>( input: &mut impl BufRead, buf: &'a mut Vec, -) -> io::Result> { +) -> io::Result>> { buf.clear(); let n = input.read_until(0, buf)?; if n == 0 { return Ok(None); } - Ok(Some(&mut buf[..])) + Ok(Some(buf)) } -pub fn write_postcard(out: &mut impl Write, msg: &[u8]) -> io::Result<()> { + +#[allow(clippy::ptr_arg)] +pub fn write_postcard(out: &mut impl Write, msg: &Vec) -> io::Result<()> { out.write_all(msg)?; out.flush() } diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 877c45f56cfd..2cdb33ff81eb 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -31,10 +31,9 @@ pub mod version { /// 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; pub const HASHED_AST_ID: u32 = 6; - pub const POSTCARD_WIRE: u32 = 7; /// Current API version of the proc-macro protocol. - pub const CURRENT_API_VERSION: u32 = POSTCARD_WIRE; + pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; } /// Represents different kinds of procedural macros that can be expanded by the external server. @@ -124,7 +123,7 @@ impl ProcMacroClient { Item = (impl AsRef, &'a Option>), > + Clone, ) -> io::Result { - let process = ProcMacroServerProcess::run(process_path, env)?; + let process = ProcMacroServerProcess::run(process_path, env, process::Protocol::default())?; Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) } diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 95b12d0b24e0..7f0cd05c8058 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -28,12 +28,18 @@ pub(crate) struct ProcMacroServerProcess { exited: OnceLock>, } -#[derive(Debug)] -enum Protocol { +#[derive(Debug, Clone)] +pub(crate) enum Protocol { LegacyJson { mode: SpanMode }, Postcard { mode: SpanMode }, } +impl Default for Protocol { + fn default() -> Self { + Protocol::Postcard { mode: SpanMode::Id } + } +} + /// Maintains the state of the proc-macro server process. #[derive(Debug)] struct ProcessSrvState { @@ -49,54 +55,83 @@ impl ProcMacroServerProcess { env: impl IntoIterator< Item = (impl AsRef, &'a Option>), > + Clone, + protocol: Protocol, ) -> io::Result { - let create_srv = || { - let mut process = Process::run(process_path, env.clone())?; + let mut srv = { + let mut process = match Process::run(process_path, env.clone(), &protocol) { + Ok(process) => process, + Err(e) => { + // fallback + if matches!(protocol, Protocol::Postcard { .. }) { + // retry with json + return Self::run( + process_path, + env, + Protocol::LegacyJson { mode: SpanMode::Id }, + ); + } + return Err(e); + } + }; let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); - io::Result::Ok(ProcMacroServerProcess { + ProcMacroServerProcess { state: Mutex::new(ProcessSrvState { process, stdin, stdout }), version: 0, - protocol: Protocol::LegacyJson { mode: SpanMode::Id }, + protocol: protocol.clone(), exited: OnceLock::new(), - }) + } }; - let mut srv = create_srv()?; tracing::info!("sending proc-macro server version check"); - match srv.version_check() { - 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 >= version::RUST_ANALYZER_SPAN_SUPPORT - && let Ok(mode) = srv.enable_rust_analyzer_spans() - { - if srv.version >= version::POSTCARD_WIRE { - srv.protocol = Protocol::Postcard { mode }; - } else { - srv.protocol = Protocol::LegacyJson { mode }; - } - } - tracing::info!("Proc-macro server protocol: {:?}", srv.protocol); - Ok(srv) - } + let version = match srv.version_check() { + Ok(v) => v, Err(e) => { + if matches!(protocol, Protocol::Postcard { .. }) { + // retry with json + return Self::run( + process_path, + env, + Protocol::LegacyJson { mode: SpanMode::Id }, + ); + } + tracing::info!(%e, "proc-macro version check failed"); - Err(io::Error::other(format!("proc-macro server version check failed: {e}"))) + return Err(io::Error::other(format!( + "proc-macro server version check failed: {e}" + ))); } + }; + + if version > version::CURRENT_API_VERSION { + #[allow(clippy::disallowed_methods)] + let process_version = Command::new(process_path) + .arg("--version") + .output() + .map(|out| String::from_utf8_lossy(&out.stdout).trim().to_owned()) + .unwrap_or_else(|_| "unknown version".to_owned()); + + return 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 + ))); + } + + tracing::info!("proc-macro server version: {version}"); + + srv.version = version; + + if version >= version::RUST_ANALYZER_SPAN_SUPPORT + && let Ok(mode) = srv.enable_rust_analyzer_spans() + { + srv.protocol = match protocol { + Protocol::Postcard { .. } => Protocol::Postcard { mode }, + Protocol::LegacyJson { .. } => Protocol::LegacyJson { mode }, + }; } + + tracing::info!("proc-macro server protocol: {:?}", srv.protocol); + Ok(srv) } /// Returns the server error if the process has exited. @@ -148,18 +183,21 @@ impl ProcMacroServerProcess { } } - pub(crate) fn send_task( + pub(crate) fn send_task( &self, serialize_req: impl FnOnce( &mut dyn Write, &mut dyn BufRead, Request, - &mut String, + &mut Buf, ) -> Result, ServerError>, req: Request, - ) -> Result { + ) -> Result + where + Buf: Default, + { let state = &mut *self.state.lock().unwrap(); - let mut buf = String::new(); + let mut buf = Buf::default(); serialize_req(&mut state.stdin, &mut state.stdout, req, &mut buf) .and_then(|res| { res.ok_or_else(|| { @@ -201,55 +239,6 @@ impl ProcMacroServerProcess { } }) } - - pub(crate) fn send_task_bin( - &self, - serialize_req: impl FnOnce( - &mut dyn Write, - &mut dyn BufRead, - Request, - &mut Vec, - ) -> Result, ServerError>, - req: Request, - ) -> Result { - let state = &mut *self.state.lock().unwrap(); - let mut buf = Vec::::new(); - serialize_req(&mut state.stdin, &mut state.stdout, req, &mut buf) - .and_then(|res| { - res.ok_or_else(|| ServerError { - message: "proc-macro server did not respond with data".to_owned(), - io: Some(Arc::new(io::Error::new( - io::ErrorKind::BrokenPipe, - "proc-macro server did not respond with data", - ))), - }) - }) - .map_err(|e| { - if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { - match state.process.child.try_wait() { - Ok(None) | Err(_) => e, - Ok(Some(status)) => { - let mut msg = String::new(); - if !status.success() - && let Some(stderr) = state.process.child.stderr.as_mut() - { - _ = stderr.read_to_string(&mut msg); - } - let server_error = ServerError { - message: format!( - "proc-macro server exited with {status}{}{msg}", - if msg.is_empty() { "" } else { ": " } - ), - io: None, - }; - self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone() - } - } - } else { - e - } - }) - } } /// Manages the execution of the proc-macro server process. @@ -265,8 +254,9 @@ impl Process { env: impl IntoIterator< Item = (impl AsRef, &'a Option>), >, + protocol: &Protocol, ) -> io::Result { - let child = JodChild(mk_child(path, env)?); + let child = JodChild(mk_child(path, env, protocol)?); Ok(Process { child }) } @@ -286,9 +276,15 @@ fn mk_child<'a>( extra_env: impl IntoIterator< Item = (impl AsRef, &'a Option>), >, + protocol: &Protocol, ) -> io::Result { #[allow(clippy::disallowed_methods)] let mut cmd = Command::new(path); + if matches!(protocol, Protocol::LegacyJson { .. }) { + cmd.args(["--format", "json"]); + } else { + cmd.args(["--format", "postcard"]); + } for env in extra_env { match env { (key, Some(val)) => cmd.env(key, val), diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index 9149adb4687e..b6c38da45475 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -31,7 +31,7 @@ fn main() -> std::io::Result<()> { clap::Arg::new("format") .long("format") .action(clap::ArgAction::Set) - .default_value("postcard") + .default_value("json") .value_parser(clap::builder::EnumValueParser::::new()), clap::Arg::new("version") .long("version") From cae902c6e72060e1e27cba1ab73f0fdb8a902efd Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 24 Nov 2025 11:18:14 +0530 Subject: [PATCH 5/5] add codec and framing to abstract encoding and decoding logic from run --- crates/proc-macro-api/src/codec.rs | 12 ++ crates/proc-macro-api/src/framing.rs | 14 ++ crates/proc-macro-api/src/legacy_protocol.rs | 33 ++-- .../src/legacy_protocol/json.rs | 74 ++++++--- .../proc-macro-api/src/legacy_protocol/msg.rs | 56 +------ .../src/legacy_protocol/postcard.rs | 49 +++--- crates/proc-macro-api/src/lib.rs | 11 +- crates/proc-macro-api/src/process.rs | 13 +- crates/proc-macro-srv-cli/Cargo.toml | 2 +- crates/proc-macro-srv-cli/src/main_loop.rs | 148 ++---------------- 10 files changed, 142 insertions(+), 270 deletions(-) create mode 100644 crates/proc-macro-api/src/codec.rs create mode 100644 crates/proc-macro-api/src/framing.rs diff --git a/crates/proc-macro-api/src/codec.rs b/crates/proc-macro-api/src/codec.rs new file mode 100644 index 000000000000..baccaa6be4c2 --- /dev/null +++ b/crates/proc-macro-api/src/codec.rs @@ -0,0 +1,12 @@ +//! Protocol codec + +use std::io; + +use serde::de::DeserializeOwned; + +use crate::framing::Framing; + +pub trait Codec: Framing { + fn encode(msg: &T) -> io::Result; + fn decode(buf: &mut Self::Buf) -> io::Result; +} diff --git a/crates/proc-macro-api/src/framing.rs b/crates/proc-macro-api/src/framing.rs new file mode 100644 index 000000000000..a1e6fc05ca11 --- /dev/null +++ b/crates/proc-macro-api/src/framing.rs @@ -0,0 +1,14 @@ +//! Protocol framing + +use std::io::{self, BufRead, Write}; + +pub trait Framing { + type Buf: Default; + + fn read<'a, R: BufRead>( + inp: &mut R, + buf: &'a mut Self::Buf, + ) -> io::Result>; + + fn write(out: &mut W, buf: &Self::Buf) -> io::Result<()>; +} diff --git a/crates/proc-macro-api/src/legacy_protocol.rs b/crates/proc-macro-api/src/legacy_protocol.rs index 6d521d00cd90..c2b132ddcc1d 100644 --- a/crates/proc-macro-api/src/legacy_protocol.rs +++ b/crates/proc-macro-api/src/legacy_protocol.rs @@ -14,14 +14,15 @@ use span::Span; use crate::{ ProcMacro, ProcMacroKind, ServerError, + codec::Codec, legacy_protocol::{ - json::{read_json, write_json}, + json::JsonProtocol, msg::{ ExpandMacro, ExpandMacroData, ExpnGlobals, FlatTree, Message, Request, Response, ServerConfig, SpanDataIndexMap, deserialize_span_data_index_map, flat::serialize_span_data_index_map, }, - postcard::{read_postcard, write_postcard}, + postcard::PostcardProtocol, }, process::ProcMacroServerProcess, version, @@ -154,42 +155,26 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result, req) } else { - srv.send_task(send_request, req) + srv.send_task(send_request::, req) } } /// Sends a request to the server and reads the response. -fn send_request( +fn send_request( mut writer: &mut dyn Write, mut reader: &mut dyn BufRead, req: Request, - buf: &mut String, + buf: &mut P::Buf, ) -> Result, ServerError> { - req.write(write_json, &mut writer).map_err(|err| ServerError { + req.write::<_, P>(&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 { + let res = Response::read::<_, P>(&mut reader, buf).map_err(|err| ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)), })?; Ok(res) } - -fn send_request_postcard( - mut writer: &mut dyn Write, - mut reader: &mut dyn BufRead, - req: Request, - buf: &mut Vec, -) -> Result, ServerError> { - req.write_postcard(write_postcard, &mut writer).map_err(|err| ServerError { - message: "failed to write request".into(), - io: Some(Arc::new(err)), - })?; - let res = Response::read_postcard(read_postcard, &mut reader, buf).map_err(|err| { - ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)) } - })?; - Ok(res) -} diff --git a/crates/proc-macro-api/src/legacy_protocol/json.rs b/crates/proc-macro-api/src/legacy_protocol/json.rs index cf8535f77d53..1359c0568402 100644 --- a/crates/proc-macro-api/src/legacy_protocol/json.rs +++ b/crates/proc-macro-api/src/legacy_protocol/json.rs @@ -1,36 +1,58 @@ //! 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, -) -> io::Result> { - loop { - buf.clear(); - - inp.read_line(buf)?; - buf.pop(); // Remove trailing '\n' - - if buf.is_empty() { - return Ok(None); - } +use serde::{Serialize, de::DeserializeOwned}; + +use crate::{codec::Codec, framing::Framing}; + +pub struct JsonProtocol; + +impl Framing for JsonProtocol { + type Buf = String; + + fn read<'a, R: BufRead>( + inp: &mut R, + buf: &'a mut String, + ) -> io::Result> { + loop { + buf.clear(); + + inp.read_line(buf)?; + buf.pop(); // Remove trailing '\n' - // Some ill behaved macro try to use stdout for debugging - // We ignore it here - if !buf.starts_with('{') { - tracing::error!("proc-macro tried to print : {}", buf); - continue; + if buf.is_empty() { + return Ok(None); + } + + // Some ill behaved macro try to use stdout for debugging + // We ignore it here + if !buf.starts_with('{') { + tracing::error!("proc-macro tried to print : {}", buf); + continue; + } + + return Ok(Some(buf)); } + } - return Ok(Some(buf)); + fn write(out: &mut W, buf: &String) -> io::Result<()> { + tracing::debug!("> {}", buf); + out.write_all(buf.as_bytes())?; + out.write_all(b"\n")?; + out.flush() } } -/// Writes a JSON message to the output stream. -pub fn write_json(out: &mut impl Write, msg: &String) -> io::Result<()> { - tracing::debug!("> {}", msg); - out.write_all(msg.as_bytes())?; - out.write_all(b"\n")?; - out.flush() +impl Codec for JsonProtocol { + fn encode(msg: &T) -> io::Result { + Ok(serde_json::to_string(msg)?) + } + + fn decode(buf: &mut String) -> io::Result { + let mut deserializer = serde_json::Deserializer::from_str(buf); + // Note that some proc-macro generate very deep syntax tree + // We have to disable the current limit of serde here + deserializer.disable_recursion_limit(); + Ok(T::deserialize(&mut deserializer)?) + } } diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs index 6df184630de7..1c77863aac34 100644 --- a/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -8,10 +8,7 @@ use paths::Utf8PathBuf; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; -use crate::{ - ProcMacroKind, - legacy_protocol::postcard::{decode_cobs, encode_cobs}, -}; +use crate::{ProcMacroKind, codec::Codec}; /// Represents requests sent from the client to the proc-macro-srv. #[derive(Debug, Serialize, Deserialize)] @@ -152,60 +149,21 @@ impl ExpnGlobals { } pub trait Message: serde::Serialize + DeserializeOwned { - fn read( - from_proto: ProtocolRead, - inp: &mut R, - buf: &mut String, - ) -> io::Result> { - Ok(match from_proto(inp, buf)? { + fn read(inp: &mut R, buf: &mut C::Buf) -> io::Result> { + Ok(match C::read(inp, buf)? { None => None, - Some(text) => { - let mut deserializer = serde_json::Deserializer::from_str(text); - // Note that some proc-macro generate very deep syntax tree - // We have to disable the current limit of serde here - deserializer.disable_recursion_limit(); - Some(Self::deserialize(&mut deserializer)?) - } - }) - } - fn write(self, to_proto: ProtocolWrite, out: &mut W) -> io::Result<()> { - let text = serde_json::to_string(&self)?; - to_proto(out, &text) - } - - fn read_postcard( - from_proto: ProtocolRead>, - inp: &mut R, - buf: &mut Vec, - ) -> io::Result> { - Ok(match from_proto(inp, buf)? { - None => None, - Some(buf) => Some(decode_cobs(buf)?), + Some(buf) => C::decode(buf)?, }) } - - fn write_postcard( - self, - to_proto: ProtocolWrite>, - out: &mut W, - ) -> io::Result<()> { - let buf = encode_cobs(&self)?; - to_proto(out, &buf) + fn write(self, out: &mut W) -> io::Result<()> { + let value = C::encode(&self)?; + C::write(out, &value) } } 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 = - for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut Buf) -> io::Result>; -/// Type alias for a function that writes protocol messages to an output stream. -#[allow(type_alias_bounds)] -type ProtocolWrite = - for<'o, 'msg> fn(out: &'o mut W, msg: &'msg Buf) -> io::Result<()>; - #[cfg(test)] mod tests { use intern::{Symbol, sym}; diff --git a/crates/proc-macro-api/src/legacy_protocol/postcard.rs b/crates/proc-macro-api/src/legacy_protocol/postcard.rs index 305e4de93415..c28a9bfe3a1a 100644 --- a/crates/proc-macro-api/src/legacy_protocol/postcard.rs +++ b/crates/proc-macro-api/src/legacy_protocol/postcard.rs @@ -2,28 +2,39 @@ use std::io::{self, BufRead, Write}; -pub fn read_postcard<'a>( - input: &mut impl BufRead, - buf: &'a mut Vec, -) -> io::Result>> { - buf.clear(); - let n = input.read_until(0, buf)?; - if n == 0 { - return Ok(None); +use serde::{Serialize, de::DeserializeOwned}; + +use crate::{codec::Codec, framing::Framing}; + +pub struct PostcardProtocol; + +impl Framing for PostcardProtocol { + type Buf = Vec; + + fn read<'a, R: BufRead>( + inp: &mut R, + buf: &'a mut Vec, + ) -> io::Result>> { + buf.clear(); + let n = inp.read_until(0, buf)?; + if n == 0 { + return Ok(None); + } + Ok(Some(buf)) } - Ok(Some(buf)) -} -#[allow(clippy::ptr_arg)] -pub fn write_postcard(out: &mut impl Write, msg: &Vec) -> io::Result<()> { - out.write_all(msg)?; - out.flush() + fn write(out: &mut W, buf: &Vec) -> io::Result<()> { + out.write_all(buf)?; + out.flush() + } } -pub fn encode_cobs(value: &T) -> io::Result> { - postcard::to_allocvec_cobs(value).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) -} +impl Codec for PostcardProtocol { + fn encode(msg: &T) -> io::Result> { + postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + } -pub fn decode_cobs(bytes: &mut [u8]) -> io::Result { - postcard::from_bytes_cobs(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + fn decode(buf: &mut Self::Buf) -> io::Result { + postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + } } diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 2cdb33ff81eb..a725b94f04b2 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -12,6 +12,8 @@ )] #![allow(internal_features)] +mod codec; +mod framing; pub mod legacy_protocol; mod process; @@ -19,7 +21,8 @@ use paths::{AbsPath, AbsPathBuf}; use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use std::{fmt, io, sync::Arc, time::SystemTime}; -use crate::process::ProcMacroServerProcess; +pub use crate::codec::Codec; +use crate::{legacy_protocol::SpanMode, process::ProcMacroServerProcess}; /// The versions of the server protocol pub mod version { @@ -123,7 +126,11 @@ impl ProcMacroClient { Item = (impl AsRef, &'a Option>), > + Clone, ) -> io::Result { - let process = ProcMacroServerProcess::run(process_path, env, process::Protocol::default())?; + let process = ProcMacroServerProcess::run( + process_path, + env, + process::Protocol::Postcard { mode: SpanMode::Id }, + )?; Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) } diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index 7f0cd05c8058..1365245f9846 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -34,12 +34,6 @@ pub(crate) enum Protocol { Postcard { mode: SpanMode }, } -impl Default for Protocol { - fn default() -> Self { - Protocol::Postcard { mode: SpanMode::Id } - } -} - /// Maintains the state of the proc-macro server process. #[derive(Debug)] struct ProcessSrvState { @@ -122,11 +116,10 @@ impl ProcMacroServerProcess { srv.version = version; if version >= version::RUST_ANALYZER_SPAN_SUPPORT - && let Ok(mode) = srv.enable_rust_analyzer_spans() + && let Ok(new_mode) = srv.enable_rust_analyzer_spans() { - srv.protocol = match protocol { - Protocol::Postcard { .. } => Protocol::Postcard { mode }, - Protocol::LegacyJson { .. } => Protocol::LegacyJson { mode }, + match &mut srv.protocol { + Protocol::Postcard { mode } | Protocol::LegacyJson { mode } => *mode = new_mode, }; } diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index f6022cf2c7bd..aa153897fa96 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -18,7 +18,7 @@ postcard.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} [features] -default = ["postcard"] +default = [] sysroot-abi = ["proc-macro-srv/sysroot-abi", "proc-macro-api/sysroot-abi"] in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"] diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs index b0e7108d20a5..029ab6eca941 100644 --- a/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/crates/proc-macro-srv-cli/src/main_loop.rs @@ -2,13 +2,14 @@ use std::io; use proc_macro_api::{ + Codec, legacy_protocol::{ - json::{read_json, write_json}, + json::JsonProtocol, msg::{ self, ExpandMacroData, ExpnGlobals, Message, SpanMode, SpanTransformer, deserialize_span_data_index_map, serialize_span_data_index_map, }, - postcard::{read_postcard, write_postcard}, + postcard::PostcardProtocol, }, version::CURRENT_API_VERSION, }; @@ -36,12 +37,12 @@ impl SpanTransformer for SpanTrans { pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> { match format { - ProtocolFormat::Json => run_json(), - ProtocolFormat::Postcard => run_postcard(), + ProtocolFormat::Json => run_::(), + ProtocolFormat::Postcard => run_::(), } } -fn run_json() -> io::Result<()> { +fn run_() -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -52,9 +53,9 @@ fn run_json() -> io::Result<()> { } } - let mut buf = String::new(); - let mut read_request = || msg::Request::read(read_json, &mut io::stdin().lock(), &mut buf); - let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock()); + let mut buf = C::Buf::default(); + let mut read_request = || msg::Request::read::<_, C>(&mut io::stdin().lock(), &mut buf); + let write_response = |msg: msg::Response| msg.write::<_, C>(&mut io::stdout().lock()); let env = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env); @@ -170,134 +171,3 @@ fn run_json() -> io::Result<()> { Ok(()) } - -fn run_postcard() -> io::Result<()> { - fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { - match kind { - proc_macro_srv::ProcMacroKind::CustomDerive => { - proc_macro_api::ProcMacroKind::CustomDerive - } - proc_macro_srv::ProcMacroKind::Bang => proc_macro_api::ProcMacroKind::Bang, - proc_macro_srv::ProcMacroKind::Attr => proc_macro_api::ProcMacroKind::Attr, - } - } - - let mut buf = Vec::new(); - let mut read_request = - || msg::Request::read_postcard(read_postcard, &mut io::stdin().lock(), &mut buf); - let write_response = - |msg: msg::Response| msg.write_postcard(write_postcard, &mut io::stdout().lock()); - - let env = proc_macro_srv::EnvSnapshot::default(); - let srv = proc_macro_srv::ProcMacroSrv::new(&env); - - let mut span_mode = msg::SpanMode::Id; - - while let Some(req) = read_request()? { - let res = match req { - msg::Request::ListMacros { dylib_path } => { - msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { - macros.into_iter().map(|(name, kind)| (name, macro_kind_to_api(kind))).collect() - })) - } - msg::Request::ExpandMacro(task) => { - let msg::ExpandMacro { - lib, - env, - current_dir, - data: - msg::ExpandMacroData { - macro_body, - macro_name, - attributes, - has_global_spans: - msg::ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, - span_data_table, - }, - } = *task; - match span_mode { - msg::SpanMode::Id => msg::Response::ExpandMacro({ - let def_site = proc_macro_srv::SpanId(def_site as u32); - let call_site = proc_macro_srv::SpanId(call_site as u32); - let mixed_site = proc_macro_srv::SpanId(mixed_site as u32); - - let macro_body = - macro_body.to_subtree_unresolved::(CURRENT_API_VERSION); - let attributes = attributes - .map(|it| it.to_subtree_unresolved::(CURRENT_API_VERSION)); - - srv.expand( - lib, - &env, - current_dir, - ¯o_name, - macro_body, - attributes, - def_site, - call_site, - mixed_site, - ) - .map(|it| { - msg::FlatTree::new_raw::( - tt::SubtreeView::new(&it), - CURRENT_API_VERSION, - ) - }) - .map_err(|e| e.into_string().unwrap_or_default()) - .map_err(msg::PanicMessage) - }), - msg::SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({ - let mut span_data_table = - msg::deserialize_span_data_index_map(&span_data_table); - - let def_site = span_data_table[def_site]; - let call_site = span_data_table[call_site]; - let mixed_site = span_data_table[mixed_site]; - - let macro_body = - macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); - let attributes = attributes.map(|it| { - it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table) - }); - srv.expand( - lib, - &env, - current_dir, - ¯o_name, - macro_body, - attributes, - def_site, - call_site, - mixed_site, - ) - .map(|it| { - ( - msg::FlatTree::new( - tt::SubtreeView::new(&it), - CURRENT_API_VERSION, - &mut span_data_table, - ), - msg::serialize_span_data_index_map(&span_data_table), - ) - }) - .map(|(tree, span_data_table)| msg::ExpandMacroExtended { - tree, - span_data_table, - }) - .map_err(|e| e.into_string().unwrap_or_default()) - .map_err(msg::PanicMessage) - }), - } - } - msg::Request::ApiVersionCheck {} => msg::Response::ApiVersionCheck(CURRENT_API_VERSION), - msg::Request::SetConfig(config) => { - span_mode = config.span_mode; - msg::Response::SetConfig(config) - } - }; - - write_response(res)?; - } - - Ok(()) -}