From 740eb6b59fa663e9f1b721d7f6995dcec4af4bc9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 16 Jan 2026 01:16:57 +0530 Subject: [PATCH 1/6] remove non-describing field annotation from bidirectional message definition --- .../proc-macro-api/src/bidirectional_protocol/msg.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index c56ed5191694..2644cd406b63 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -84,18 +84,14 @@ pub struct ExpandMacroData { pub macro_body: FlatTree, pub macro_name: String, pub attributes: Option, - #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")] #[serde(default)] pub has_global_spans: ExpnGlobals, - - #[serde(skip_serializing_if = "Vec::is_empty")] #[serde(default)] pub span_data_table: Vec, } #[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)] pub struct ExpnGlobals { - #[serde(skip_serializing)] #[serde(default)] pub serialize: bool, pub def_site: usize, @@ -103,10 +99,4 @@ pub struct ExpnGlobals { pub mixed_site: usize, } -impl ExpnGlobals { - fn skip_serializing_if(&self) -> bool { - !self.serialize - } -} - impl Message for BidirectionalMessage {} From ffa2dadf0eeab0a484688373cffe9f1939173084 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 18 Jan 2026 19:34:03 +0530 Subject: [PATCH 2/6] remove serialize from Expn Globals --- .../crates/proc-macro-api/src/bidirectional_protocol.rs | 8 +------- .../proc-macro-api/src/bidirectional_protocol/msg.rs | 2 -- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index b5f43e1d3726..a13bff7d7d02 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -23,7 +23,6 @@ use crate::{ }, process::ProcMacroServerProcess, transport::codec::postcard::PostcardProtocol, - version, }; pub mod msg; @@ -159,12 +158,7 @@ pub(crate) fn expand( macro_name: proc_macro.name.to_string(), attributes: attr .map(|subtree| FlatTree::from_subtree(subtree, version, &mut span_data_table)), - has_global_spans: ExpnGlobals { - serialize: version >= version::HAS_GLOBAL_SPANS, - def_site, - call_site, - mixed_site, - }, + has_global_spans: ExpnGlobals { def_site, call_site, mixed_site }, span_data_table: if process.rust_analyzer_spans() { serialize_span_data_index_map(&span_data_table) } else { diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index 2644cd406b63..d030498e59c4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -92,8 +92,6 @@ pub struct ExpandMacroData { #[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)] pub struct ExpnGlobals { - #[serde(default)] - pub serialize: bool, pub def_site: usize, pub call_site: usize, pub mixed_site: usize, From 8fd55694389332c648a76c0ad533ba9e01ecc1fe Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 18 Jan 2026 20:23:30 +0530 Subject: [PATCH 3/6] add bidirectional flow --- .../proc-macro-srv-cli/tests/common/utils.rs | 570 +++++++++++------- 1 file changed, 357 insertions(+), 213 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 722e92eec7e5..63b3a74aa4e8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -1,213 +1,357 @@ -use std::{ - collections::VecDeque, - io::{self, BufRead, Read, Write}, - sync::{Arc, Condvar, Mutex}, - thread, -}; - -use paths::Utf8PathBuf; -use proc_macro_api::{ - legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap}, - transport::codec::json::JsonProtocol, -}; -use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange}; -use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder}; - -/// Shared state for an in-memory byte channel. -#[derive(Default)] -struct ChannelState { - buffer: VecDeque, - closed: bool, -} - -type InMemoryChannel = Arc<(Mutex, Condvar)>; - -/// Writer end of an in-memory channel. -pub(crate) struct ChannelWriter { - state: InMemoryChannel, -} - -impl Write for ChannelWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - let (lock, cvar) = &*self.state; - let mut state = lock.lock().unwrap(); - if state.closed { - return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed")); - } - state.buffer.extend(buf); - cvar.notify_all(); - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Drop for ChannelWriter { - fn drop(&mut self) { - let (lock, cvar) = &*self.state; - let mut state = lock.lock().unwrap(); - state.closed = true; - cvar.notify_all(); - } -} - -/// Reader end of an in-memory channel. -pub(crate) struct ChannelReader { - state: InMemoryChannel, - internal_buf: Vec, -} - -impl Read for ChannelReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let (lock, cvar) = &*self.state; - let mut state = lock.lock().unwrap(); - - while state.buffer.is_empty() && !state.closed { - state = cvar.wait(state).unwrap(); - } - - if state.buffer.is_empty() && state.closed { - return Ok(0); - } - - let to_read = buf.len().min(state.buffer.len()); - for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) { - *dst = src; - } - Ok(to_read) - } -} - -impl BufRead for ChannelReader { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - let (lock, cvar) = &*self.state; - let mut state = lock.lock().unwrap(); - - while state.buffer.is_empty() && !state.closed { - state = cvar.wait(state).unwrap(); - } - - self.internal_buf.clear(); - self.internal_buf.extend(&state.buffer); - Ok(&self.internal_buf) - } - - fn consume(&mut self, amt: usize) { - let (lock, _) = &*self.state; - let mut state = lock.lock().unwrap(); - let to_drain = amt.min(state.buffer.len()); - drop(state.buffer.drain(..to_drain)); - } -} - -/// Creates a connected pair of channels for bidirectional communication. -fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) { - // Channel for client -> server communication - let client_to_server = Arc::new(( - Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), - Condvar::new(), - )); - let client_writer = ChannelWriter { state: client_to_server.clone() }; - let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() }; - - // Channel for server -> client communication - let server_to_client = Arc::new(( - Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), - Condvar::new(), - )); - - let server_writer = ChannelWriter { state: server_to_client.clone() }; - let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() }; - - (client_writer, client_reader, server_writer, server_reader) -} - -pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { - let path = proc_macro_test::PROC_MACRO_TEST_LOCATION; - if path.is_empty() { - panic!("proc-macro-test dylib not available (requires nightly toolchain)"); - } - path.into() -} - -/// Runs a test with the server in a background thread. -pub(crate) fn with_server(test_fn: F) -> R -where - F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R, -{ - let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) = - create_channel_pair(); - - let server_handle = thread::spawn(move || { - proc_macro_srv_cli::main_loop::run( - &mut server_reader, - &mut server_writer, - proc_macro_api::ProtocolFormat::JsonLegacy, - ) - }); - - let result = test_fn(&mut client_writer, &mut client_reader); - - // Close the client writer to signal the server to stop - drop(client_writer); - - // Wait for server to finish - match server_handle.join() { - Ok(Ok(())) => {} - Ok(Err(e)) => { - // IO error from server is expected when client disconnects - if matches!( - e.kind(), - io::ErrorKind::BrokenPipe - | io::ErrorKind::UnexpectedEof - | io::ErrorKind::InvalidData - ) { - panic!("Server error: {e}"); - } - } - Err(e) => std::panic::resume_unwind(e), - } - - result -} - -/// Sends a request and reads the response using JSON protocol. -pub(crate) fn request( - writer: &mut dyn Write, - reader: &mut dyn BufRead, - request: Request, -) -> Response { - request.write::(writer).expect("failed to write request"); - - let mut buf = String::new(); - Response::read::(reader, &mut buf) - .expect("failed to read response") - .expect("no response received") -} - -/// Creates a simple empty token tree suitable for testing. -pub(crate) fn create_empty_token_tree( - version: u32, - span_data_table: &mut SpanDataIndexMap, -) -> FlatTree { - let anchor = SpanAnchor { - file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), - ast_id: span::ROOT_ERASED_FILE_AST_ID, - }; - let span = Span { - range: TextRange::empty(0.into()), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }; - - let builder = TopSubtreeBuilder::new(Delimiter { - open: span, - close: span, - kind: DelimiterKind::Invisible, - }); - let tt = builder.build(); - - FlatTree::from_subtree(tt.view(), version, span_data_table) -} +use std::{ + collections::VecDeque, + io::{self, BufRead, Read, Write}, + sync::{Arc, Condvar, Mutex}, + thread, +}; + +use paths::Utf8PathBuf; +use proc_macro_api::{ + ServerError, + bidirectional_protocol::msg::{ + BidirectionalMessage, Request as BiRequest, Response as BiResponse, SubRequest, SubResponse, + }, + legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap}, + transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, +}; +use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange}; +use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder}; + +/// Shared state for an in-memory byte channel. +#[derive(Default)] +struct ChannelState { + buffer: VecDeque, + closed: bool, +} + +type InMemoryChannel = Arc<(Mutex, Condvar)>; + +/// Writer end of an in-memory channel. +pub(crate) struct ChannelWriter { + state: InMemoryChannel, +} + +impl Write for ChannelWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + if state.closed { + return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed")); + } + state.buffer.extend(buf); + cvar.notify_all(); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Drop for ChannelWriter { + fn drop(&mut self) { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + state.closed = true; + cvar.notify_all(); + } +} + +/// Reader end of an in-memory channel. +pub(crate) struct ChannelReader { + state: InMemoryChannel, + internal_buf: Vec, +} + +impl Read for ChannelReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + if state.buffer.is_empty() && state.closed { + return Ok(0); + } + + let to_read = buf.len().min(state.buffer.len()); + for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) { + *dst = src; + } + Ok(to_read) + } +} + +impl BufRead for ChannelReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + self.internal_buf.clear(); + self.internal_buf.extend(&state.buffer); + Ok(&self.internal_buf) + } + + fn consume(&mut self, amt: usize) { + let (lock, _) = &*self.state; + let mut state = lock.lock().unwrap(); + let to_drain = amt.min(state.buffer.len()); + drop(state.buffer.drain(..to_drain)); + } +} + +/// Creates a connected pair of channels for bidirectional communication. +fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) { + // Channel for client -> server communication + let client_to_server = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + let client_writer = ChannelWriter { state: client_to_server.clone() }; + let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() }; + + // Channel for server -> client communication + let server_to_client = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + + let server_writer = ChannelWriter { state: server_to_client.clone() }; + let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() }; + + (client_writer, client_reader, server_writer, server_reader) +} + +pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { + let path = proc_macro_test::PROC_MACRO_TEST_LOCATION; + if path.is_empty() { + panic!("proc-macro-test dylib not available (requires nightly toolchain)"); + } + path.into() +} + +/// Creates a simple empty token tree suitable for testing. +pub(crate) fn create_empty_token_tree( + version: u32, + span_data_table: &mut SpanDataIndexMap, +) -> FlatTree { + let anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), + ast_id: span::ROOT_ERASED_FILE_AST_ID, + }; + let span = Span { + range: TextRange::empty(0.into()), + anchor, + ctx: SyntaxContext::root(Edition::CURRENT), + }; + + let builder = TopSubtreeBuilder::new(Delimiter { + open: span, + close: span, + kind: DelimiterKind::Invisible, + }); + let tt = builder.build(); + + FlatTree::from_subtree(tt.view(), version, span_data_table) +} + +pub(crate) fn with_server(format: proc_macro_api::ProtocolFormat, test_fn: F) -> R +where + F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R, +{ + let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) = + create_channel_pair(); + + let server_handle = thread::spawn(move || { + proc_macro_srv_cli::main_loop::run(&mut server_reader, &mut server_writer, format) + }); + + let result = test_fn(&mut client_writer, &mut client_reader); + + drop(client_writer); + + match server_handle.join() { + Ok(Ok(())) => {} + Ok(Err(e)) => { + if !matches!( + e.kind(), + io::ErrorKind::BrokenPipe + | io::ErrorKind::UnexpectedEof + | io::ErrorKind::InvalidData + ) { + panic!("Server error: {e}"); + } + } + Err(e) => std::panic::resume_unwind(e), + } + + result +} + +trait TestProtocol { + type Request; + type Response; + + fn send(&self, writer: &mut dyn Write, req: Self::Request); + fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response; +} + +struct JsonLegacy; + +impl TestProtocol for JsonLegacy { + type Request = Request; + type Response = Response; + + fn send(&self, writer: &mut dyn Write, req: Request) { + req.write::(writer).expect("failed to write request"); + } + + fn drive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response { + let mut buf = String::new(); + Response::read::(reader, &mut buf) + .expect("failed to read response") + .expect("no response received") + } +} + +struct PostcardBidirectional +where + F: Fn(SubRequest) -> Result, +{ + callback: F, +} + +impl TestProtocol for PostcardBidirectional +where + F: Fn(SubRequest) -> Result, +{ + type Request = BiRequest; + type Response = BiResponse; + + fn send(&self, writer: &mut dyn Write, req: BiRequest) { + let msg = BidirectionalMessage::Request(req); + msg.write::(writer).expect("failed to write request"); + } + + fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse { + let mut buf = Vec::new(); + + loop { + let msg = BidirectionalMessage::read::(reader, &mut buf) + .expect("failed to read message") + .expect("no message received"); + + match msg { + BidirectionalMessage::Response(resp) => return resp, + BidirectionalMessage::SubRequest(sr) => { + let reply = (self.callback)(sr).expect("subrequest callback failed"); + let msg = BidirectionalMessage::SubResponse(reply); + msg.write::(writer).expect("failed to write subresponse"); + } + other => panic!("unexpected message: {other:?}"), + } + } + } +} + +pub(crate) fn request( + writer: &mut dyn Write, + reader: &mut dyn BufRead, + request: impl Into, + callback: Option<&dyn Fn(SubRequest) -> Result>, +) -> AutoResponse { + let protocol = match callback { + None => AutoProtocol::Legacy(JsonLegacy), + Some(cb) => AutoProtocol::Bidirectional(PostcardBidirectional { callback: cb }), + }; + + protocol.send(writer, request.into()); + protocol.drive(reader, writer) +} + +enum AutoProtocol +where + F: Fn(SubRequest) -> Result, +{ + Legacy(JsonLegacy), + Bidirectional(PostcardBidirectional), +} + +impl TestProtocol for AutoProtocol +where + F: Fn(SubRequest) -> Result, +{ + type Request = AutoRequest; + type Response = AutoResponse; + + fn send(&self, writer: &mut dyn Write, req: AutoRequest) { + match (self, req) { + (AutoProtocol::Legacy(p), AutoRequest::Legacy(r)) => { + p.send(writer, r); + } + (AutoProtocol::Bidirectional(p), AutoRequest::Bidirectional(r)) => { + p.send(writer, r); + } + (AutoProtocol::Legacy(_), AutoRequest::Bidirectional(_)) => { + panic!("bidirectional request used with legacy protocol"); + } + (AutoProtocol::Bidirectional(_), AutoRequest::Legacy(_)) => { + panic!("legacy request used with bidirectional protocol"); + } + } + } + + fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> AutoResponse { + match self { + AutoProtocol::Legacy(p) => AutoResponse::Legacy(p.drive(reader, writer)), + AutoProtocol::Bidirectional(p) => AutoResponse::Bidirectional(p.drive(reader, writer)), + } + } +} + +pub(crate) enum AutoRequest { + Legacy(Request), + Bidirectional(BiRequest), +} + +#[derive(Debug)] +pub(crate) enum AutoResponse { + Legacy(Response), + Bidirectional(BiResponse), +} + +impl From for AutoRequest { + fn from(req: Request) -> AutoRequest { + AutoRequest::Legacy(req) + } +} + +impl From for AutoRequest { + fn from(req: BiRequest) -> AutoRequest { + AutoRequest::Bidirectional(req) + } +} + +impl From for Response { + fn from(res: AutoResponse) -> Response { + match res { + AutoResponse::Legacy(res) => res, + _ => panic!("Should be legacy response"), + } + } +} + +impl From for BiResponse { + fn from(res: AutoResponse) -> BiResponse { + match res { + AutoResponse::Bidirectional(res) => res, + _ => panic!("Should be bidirectional response"), + } + } +} From ee35fd6cb206e00b0e43b7e676a1aed8eb013684 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 18 Jan 2026 20:23:40 +0530 Subject: [PATCH 4/6] add bidirectional test --- .../tests/bidirectional_postcard.rs | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs new file mode 100644 index 000000000000..08e44bad3723 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -0,0 +1,223 @@ +#![cfg(feature = "sysroot-abi")] + +mod common { + pub(crate) mod utils; +} + +use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server}; +use expect_test::expect; +use proc_macro_api::{ + ProtocolFormat::BidirectionalPostcardPrototype, + bidirectional_protocol::{ + msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response}, + reject_subrequests, + }, + legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode}, + version::CURRENT_API_VERSION, +}; + +#[test] +fn test_bidi_version_check_bidirectional() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let response = + request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into(); + + match response { + Response::ApiVersionCheck(version) => { + assert_eq!(version, CURRENT_API_VERSION); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_list_macros() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + let response = + request(writer, reader, Request::ListMacros { dylib_path }, Some(&reject_subrequests)) + .into(); + + let Response::ListMacros(Ok(macros)) = response else { + panic!("expected successful ListMacros response"); + }; + + let mut macro_list: Vec<_> = + macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); + macro_list.sort(); + let macro_list_str = macro_list.join("\n"); + + expect![[r#" + DeriveEmpty [CustomDerive] + DeriveError [CustomDerive] + DerivePanic [CustomDerive] + DeriveReemit [CustomDerive] + attr_error [Attr] + attr_noop [Attr] + attr_panic [Attr] + fn_like_clone_tokens [Bang] + fn_like_error [Bang] + fn_like_mk_idents [Bang] + fn_like_mk_literals [Bang] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_span_join [Bang] + fn_like_span_line_column [Bang] + fn_like_span_ops [Bang]"#]] + .assert_eq(¯o_list_str); + }); +} + +#[test] +fn test_bidi_list_macros_invalid_path() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let response = request( + writer, + reader, + Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + Some(&reject_subrequests), + ) + .into(); + + match response { + Response::ListMacros(Err(e)) => assert!( + e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), + "{e}" + ), + other => panic!("expected error response, got: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_set_config() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::Id }; + let response = + request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into(); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::Id); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_set_config_rust_analyzer_mode() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; + let response = + request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into(); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_expand_macro_panic() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = + common::utils::create_empty_token_tree(CURRENT_API_VERSION, &mut span_data_table); + + let request1 = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "fn_like_panic".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, request1, Some(&reject_subrequests)).into(); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + assert!(msg.contains("fn_like_panic"), "panic message should mention macro name"); + } + other => panic!("expected panic response, got: {other:?}"), + } + }); +} + +#[test] +fn test_bidi_basic_call_flow() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let response1 = + request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into(); + assert!(matches!(response1, Response::ApiVersionCheck(_))); + + let response2 = request( + writer, + reader, + Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }), + Some(&reject_subrequests), + ) + .into(); + assert!(matches!(response2, Response::SetConfig(_))); + + let response3 = request( + writer, + reader, + Request::ListMacros { dylib_path: dylib_path.clone() }, + Some(&reject_subrequests), + ) + .into(); + assert!(matches!(response3, Response::ListMacros(Ok(_)))); + }); +} + +#[test] +fn test_bidi_expand_nonexistent_macro() { + with_server(BidirectionalPostcardPrototype, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = + request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into(); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "NonexistentMacro".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, expand_request, Some(&reject_subrequests)).into(); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) + } + other => panic!("expected error for nonexistent macro, got: {other:?}"), + } + }); +} From a151d7dc5617ecd8c0cf8fbb688654c66e9776b6 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 18 Jan 2026 20:23:49 +0530 Subject: [PATCH 5/6] adapt json test --- .../proc-macro-srv-cli/tests/legacy_json.rs | 457 +++++++++--------- 1 file changed, 233 insertions(+), 224 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs index 1fa886219a8a..8daee7b2bceb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -1,224 +1,233 @@ -//! Integration tests for the proc-macro-srv-cli main loop. -//! -//! These tests exercise the full client-server RPC procedure using in-memory -//! channels without needing to spawn the actual server and client processes. - -#![cfg(feature = "sysroot-abi")] - -mod common { - pub(crate) mod utils; -} - -use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server}; -use expect_test::expect; -use proc_macro_api::{ - legacy_protocol::msg::{ - ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig, - SpanDataIndexMap, SpanMode, - }, - version::CURRENT_API_VERSION, -}; - -#[test] -fn test_version_check() { - with_server(|writer, reader| { - let response = request(writer, reader, Request::ApiVersionCheck {}); - - match response { - Response::ApiVersionCheck(version) => { - assert_eq!(version, CURRENT_API_VERSION); - } - other => panic!("unexpected response: {other:?}"), - } - }); -} - -#[test] -fn test_list_macros() { - with_server(|writer, reader| { - let dylib_path = proc_macro_test_dylib_path(); - let response = request(writer, reader, Request::ListMacros { dylib_path }); - - let Response::ListMacros(Ok(macros)) = response else { - panic!("expected successful ListMacros response"); - }; - - let mut macro_list: Vec<_> = - macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); - macro_list.sort(); - let macro_list_str = macro_list.join("\n"); - - expect![[r#" - DeriveEmpty [CustomDerive] - DeriveError [CustomDerive] - DerivePanic [CustomDerive] - DeriveReemit [CustomDerive] - attr_error [Attr] - attr_noop [Attr] - attr_panic [Attr] - fn_like_clone_tokens [Bang] - fn_like_error [Bang] - fn_like_mk_idents [Bang] - fn_like_mk_literals [Bang] - fn_like_noop [Bang] - fn_like_panic [Bang] - fn_like_span_join [Bang] - fn_like_span_line_column [Bang] - fn_like_span_ops [Bang]"#]] - .assert_eq(¯o_list_str); - }); -} - -#[test] -fn test_list_macros_invalid_path() { - with_server(|writer, reader| { - let response = request( - writer, - reader, - Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, - ); - - match response { - Response::ListMacros(Err(e)) => assert!( - e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), - "{e}" - ), - other => panic!("expected error response, got: {other:?}"), - } - }); -} - -#[test] -fn test_set_config() { - with_server(|writer, reader| { - let config = ServerConfig { span_mode: SpanMode::Id }; - let response = request(writer, reader, Request::SetConfig(config)); - - match response { - Response::SetConfig(returned_config) => { - assert_eq!(returned_config.span_mode, SpanMode::Id); - } - other => panic!("unexpected response: {other:?}"), - } - }); -} - -#[test] -fn test_set_config_rust_analyzer_mode() { - with_server(|writer, reader| { - let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; - let response = request(writer, reader, Request::SetConfig(config)); - - match response { - Response::SetConfig(returned_config) => { - assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); - } - other => panic!("unexpected response: {other:?}"), - } - }); -} - -#[test] -fn test_expand_macro_panic() { - with_server(|writer, reader| { - let dylib_path = proc_macro_test_dylib_path(); - - let version_response = request(writer, reader, Request::ApiVersionCheck {}); - let Response::ApiVersionCheck(version) = version_response else { - panic!("expected version check response"); - }; - - let mut span_data_table = SpanDataIndexMap::default(); - let macro_body = create_empty_token_tree(version, &mut span_data_table); - - let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { - lib: dylib_path, - env: vec![], - current_dir: None, - data: ExpandMacroData { - macro_body, - macro_name: "fn_like_panic".to_owned(), - attributes: None, - has_global_spans: ExpnGlobals { - serialize: version >= 3, - def_site: 0, - call_site: 0, - mixed_site: 0, - }, - span_data_table: vec![], - }, - })); - - let response = request(writer, reader, expand_request); - - match response { - Response::ExpandMacro(Err(PanicMessage(msg))) => { - assert!(msg.contains("fn_like_panic"), "panic message should mention the macro"); - } - Response::ExpandMacro(Ok(_)) => { - panic!("expected panic, but macro succeeded"); - } - other => panic!("unexpected response: {other:?}"), - } - }); -} - -#[test] -fn test_basic_call_flow() { - with_server(|writer, reader| { - let dylib_path = proc_macro_test_dylib_path(); - - let response1 = request(writer, reader, Request::ApiVersionCheck {}); - assert!(matches!(response1, Response::ApiVersionCheck(_))); - - let response2 = - request(writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id })); - assert!(matches!(response2, Response::SetConfig(_))); - - let response3 = - request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }); - assert!(matches!(response3, Response::ListMacros(Ok(_)))); - }); -} - -#[test] -fn test_expand_nonexistent_macro() { - with_server(|writer, reader| { - let dylib_path = proc_macro_test_dylib_path(); - - let version_response = request(writer, reader, Request::ApiVersionCheck {}); - let Response::ApiVersionCheck(version) = version_response else { - panic!("expected version check response"); - }; - - let mut span_data_table = SpanDataIndexMap::default(); - let macro_body = create_empty_token_tree(version, &mut span_data_table); - - let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { - lib: dylib_path, - env: vec![], - current_dir: None, - data: ExpandMacroData { - macro_body, - macro_name: "NonexistentMacro".to_owned(), - attributes: None, - has_global_spans: ExpnGlobals { - serialize: version >= 3, - def_site: 0, - call_site: 0, - mixed_site: 0, - }, - span_data_table: vec![], - }, - })); - - let response = request(writer, reader, expand_request); - - match response { - Response::ExpandMacro(Err(PanicMessage(msg))) => { - expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) - } - other => panic!("expected error for nonexistent macro, got: {other:?}"), - } - }); -} +//! Integration tests for the proc-macro-srv-cli main loop. +//! +//! These tests exercise the full client-server RPC procedure using in-memory +//! channels without needing to spawn the actual server and client processes. + +#![cfg(feature = "sysroot-abi")] + +mod common { + pub(crate) mod utils; +} + +use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server}; +use expect_test::expect; +use proc_macro_api::{ + ProtocolFormat::JsonLegacy, + legacy_protocol::msg::{ + ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig, + SpanDataIndexMap, SpanMode, + }, + version::CURRENT_API_VERSION, +}; + +#[test] +fn test_version_check() { + with_server(JsonLegacy, |writer, reader| { + let response = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + + match response { + Response::ApiVersionCheck(version) => { + assert_eq!(version, CURRENT_API_VERSION); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_list_macros() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + let response = request(writer, reader, Request::ListMacros { dylib_path }, None).into(); + + let Response::ListMacros(Ok(macros)) = response else { + panic!("expected successful ListMacros response"); + }; + + let mut macro_list: Vec<_> = + macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); + macro_list.sort(); + let macro_list_str = macro_list.join("\n"); + + expect![[r#" + DeriveEmpty [CustomDerive] + DeriveError [CustomDerive] + DerivePanic [CustomDerive] + DeriveReemit [CustomDerive] + attr_error [Attr] + attr_noop [Attr] + attr_panic [Attr] + fn_like_clone_tokens [Bang] + fn_like_error [Bang] + fn_like_mk_idents [Bang] + fn_like_mk_literals [Bang] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_span_join [Bang] + fn_like_span_line_column [Bang] + fn_like_span_ops [Bang]"#]] + .assert_eq(¯o_list_str); + }); +} + +#[test] +fn test_list_macros_invalid_path() { + with_server(JsonLegacy, |writer, reader| { + let response = request( + writer, + reader, + Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + None, + ) + .into(); + + match response { + Response::ListMacros(Err(e)) => assert!( + e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), + "{e}" + ), + other => panic!("expected error response, got: {other:?}"), + } + }); +} + +#[test] +fn test_set_config() { + with_server(JsonLegacy, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::Id }; + let response = request(writer, reader, Request::SetConfig(config), None).into(); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::Id); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_set_config_rust_analyzer_mode() { + with_server(JsonLegacy, |writer, reader| { + let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; + let response = request(writer, reader, Request::SetConfig(config), None).into(); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_expand_macro_panic() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "fn_like_panic".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, expand_request, None).into(); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + assert!(msg.contains("fn_like_panic"), "panic message should mention the macro"); + } + Response::ExpandMacro(Ok(_)) => { + panic!("expected panic, but macro succeeded"); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_basic_call_flow() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let response1 = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + assert!(matches!(response1, Response::ApiVersionCheck(_))); + + let response2 = request( + writer, + reader, + Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }), + None, + ) + .into(); + assert!(matches!(response2, Response::SetConfig(_))); + + let response3 = + request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, None) + .into(); + assert!(matches!(response3, Response::ListMacros(Ok(_)))); + }); +} + +#[test] +fn test_expand_nonexistent_macro() { + with_server(JsonLegacy, |writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "NonexistentMacro".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, expand_request, None).into(); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) + } + other => panic!("expected error for nonexistent macro, got: {other:?}"), + } + }); +} From 095b0138028d51e45ca987e0b0b1dcf4e16a876d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 18 Jan 2026 20:38:58 +0530 Subject: [PATCH 6/6] rename send and drive to request and receive and remove auto* --- .../tests/bidirectional_postcard.rs | 48 +++---- .../proc-macro-srv-cli/tests/common/utils.rs | 118 ++++-------------- .../proc-macro-srv-cli/tests/legacy_json.rs | 37 +++--- 3 files changed, 67 insertions(+), 136 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index 08e44bad3723..33ca1d791de7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -4,7 +4,9 @@ mod common { pub(crate) mod utils; } -use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server}; +use common::utils::{ + create_empty_token_tree, proc_macro_test_dylib_path, request_bidirectional, with_server, +}; use expect_test::expect; use proc_macro_api::{ ProtocolFormat::BidirectionalPostcardPrototype, @@ -20,7 +22,7 @@ use proc_macro_api::{ fn test_bidi_version_check_bidirectional() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let response = - request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into(); + request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); match response { Response::ApiVersionCheck(version) => { @@ -35,9 +37,12 @@ fn test_bidi_version_check_bidirectional() { fn test_bidi_list_macros() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let response = - request(writer, reader, Request::ListMacros { dylib_path }, Some(&reject_subrequests)) - .into(); + let response = request_bidirectional( + writer, + reader, + Request::ListMacros { dylib_path }, + &reject_subrequests, + ); let Response::ListMacros(Ok(macros)) = response else { panic!("expected successful ListMacros response"); @@ -72,13 +77,12 @@ fn test_bidi_list_macros() { #[test] fn test_bidi_list_macros_invalid_path() { with_server(BidirectionalPostcardPrototype, |writer, reader| { - let response = request( + let response = request_bidirectional( writer, reader, Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, - Some(&reject_subrequests), - ) - .into(); + reject_subrequests, + ); match response { Response::ListMacros(Err(e)) => assert!( @@ -95,7 +99,7 @@ fn test_bidi_set_config() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let config = ServerConfig { span_mode: SpanMode::Id }; let response = - request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into(); + request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests); match response { Response::SetConfig(returned_config) => { @@ -111,7 +115,7 @@ fn test_bidi_set_config_rust_analyzer_mode() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; let response = - request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into(); + request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests); match response { Response::SetConfig(returned_config) => { @@ -144,7 +148,7 @@ fn test_bidi_expand_macro_panic() { }, })); - let response = request(writer, reader, request1, Some(&reject_subrequests)).into(); + let response = request_bidirectional(writer, reader, request1, reject_subrequests); match response { Response::ExpandMacro(Err(PanicMessage(msg))) => { @@ -161,25 +165,23 @@ fn test_bidi_basic_call_flow() { let dylib_path = proc_macro_test_dylib_path(); let response1 = - request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into(); + request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); assert!(matches!(response1, Response::ApiVersionCheck(_))); - let response2 = request( + let response2 = request_bidirectional( writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }), - Some(&reject_subrequests), - ) - .into(); + reject_subrequests, + ); assert!(matches!(response2, Response::SetConfig(_))); - let response3 = request( + let response3 = request_bidirectional( writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, - Some(&reject_subrequests), - ) - .into(); + reject_subrequests, + ); assert!(matches!(response3, Response::ListMacros(Ok(_)))); }); } @@ -190,7 +192,7 @@ fn test_bidi_expand_nonexistent_macro() { let dylib_path = proc_macro_test_dylib_path(); let version_response = - request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into(); + request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; @@ -211,7 +213,7 @@ fn test_bidi_expand_nonexistent_macro() { }, })); - let response = request(writer, reader, expand_request, Some(&reject_subrequests)).into(); + let response = request_bidirectional(writer, reader, expand_request, reject_subrequests); match response { Response::ExpandMacro(Err(PanicMessage(msg))) => { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 63b3a74aa4e8..85c394734b33 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -198,21 +198,22 @@ trait TestProtocol { type Request; type Response; - fn send(&self, writer: &mut dyn Write, req: Self::Request); - fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response; + fn request(&self, writer: &mut dyn Write, req: Self::Request); + fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response; } +#[allow(dead_code)] struct JsonLegacy; impl TestProtocol for JsonLegacy { type Request = Request; type Response = Response; - fn send(&self, writer: &mut dyn Write, req: Request) { + fn request(&self, writer: &mut dyn Write, req: Request) { req.write::(writer).expect("failed to write request"); } - fn drive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response { + fn receive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response { let mut buf = String::new(); Response::read::(reader, &mut buf) .expect("failed to read response") @@ -220,6 +221,7 @@ impl TestProtocol for JsonLegacy { } } +#[allow(dead_code)] struct PostcardBidirectional where F: Fn(SubRequest) -> Result, @@ -234,12 +236,12 @@ where type Request = BiRequest; type Response = BiResponse; - fn send(&self, writer: &mut dyn Write, req: BiRequest) { + fn request(&self, writer: &mut dyn Write, req: BiRequest) { let msg = BidirectionalMessage::Request(req); msg.write::(writer).expect("failed to write request"); } - fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse { + fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse { let mut buf = Vec::new(); loop { @@ -260,98 +262,28 @@ where } } -pub(crate) fn request( +#[allow(dead_code)] +pub(crate) fn request_legacy( writer: &mut dyn Write, reader: &mut dyn BufRead, - request: impl Into, - callback: Option<&dyn Fn(SubRequest) -> Result>, -) -> AutoResponse { - let protocol = match callback { - None => AutoProtocol::Legacy(JsonLegacy), - Some(cb) => AutoProtocol::Bidirectional(PostcardBidirectional { callback: cb }), - }; - - protocol.send(writer, request.into()); - protocol.drive(reader, writer) + request: Request, +) -> Response { + let protocol = JsonLegacy; + protocol.request(writer, request); + protocol.receive(reader, writer) } -enum AutoProtocol +#[allow(dead_code)] +pub(crate) fn request_bidirectional( + writer: &mut dyn Write, + reader: &mut dyn BufRead, + request: BiRequest, + callback: F, +) -> BiResponse where F: Fn(SubRequest) -> Result, { - Legacy(JsonLegacy), - Bidirectional(PostcardBidirectional), -} - -impl TestProtocol for AutoProtocol -where - F: Fn(SubRequest) -> Result, -{ - type Request = AutoRequest; - type Response = AutoResponse; - - fn send(&self, writer: &mut dyn Write, req: AutoRequest) { - match (self, req) { - (AutoProtocol::Legacy(p), AutoRequest::Legacy(r)) => { - p.send(writer, r); - } - (AutoProtocol::Bidirectional(p), AutoRequest::Bidirectional(r)) => { - p.send(writer, r); - } - (AutoProtocol::Legacy(_), AutoRequest::Bidirectional(_)) => { - panic!("bidirectional request used with legacy protocol"); - } - (AutoProtocol::Bidirectional(_), AutoRequest::Legacy(_)) => { - panic!("legacy request used with bidirectional protocol"); - } - } - } - - fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> AutoResponse { - match self { - AutoProtocol::Legacy(p) => AutoResponse::Legacy(p.drive(reader, writer)), - AutoProtocol::Bidirectional(p) => AutoResponse::Bidirectional(p.drive(reader, writer)), - } - } -} - -pub(crate) enum AutoRequest { - Legacy(Request), - Bidirectional(BiRequest), -} - -#[derive(Debug)] -pub(crate) enum AutoResponse { - Legacy(Response), - Bidirectional(BiResponse), -} - -impl From for AutoRequest { - fn from(req: Request) -> AutoRequest { - AutoRequest::Legacy(req) - } -} - -impl From for AutoRequest { - fn from(req: BiRequest) -> AutoRequest { - AutoRequest::Bidirectional(req) - } -} - -impl From for Response { - fn from(res: AutoResponse) -> Response { - match res { - AutoResponse::Legacy(res) => res, - _ => panic!("Should be legacy response"), - } - } -} - -impl From for BiResponse { - fn from(res: AutoResponse) -> BiResponse { - match res { - AutoResponse::Bidirectional(res) => res, - _ => panic!("Should be bidirectional response"), - } - } + let protocol = PostcardBidirectional { callback }; + protocol.request(writer, request); + protocol.receive(reader, writer) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs index 8daee7b2bceb..c0dbfd1679f7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -9,7 +9,9 @@ mod common { pub(crate) mod utils; } -use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server}; +use common::utils::{ + create_empty_token_tree, proc_macro_test_dylib_path, request_legacy, with_server, +}; use expect_test::expect; use proc_macro_api::{ ProtocolFormat::JsonLegacy, @@ -23,7 +25,7 @@ use proc_macro_api::{ #[test] fn test_version_check() { with_server(JsonLegacy, |writer, reader| { - let response = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + let response = request_legacy(writer, reader, Request::ApiVersionCheck {}); match response { Response::ApiVersionCheck(version) => { @@ -38,7 +40,7 @@ fn test_version_check() { fn test_list_macros() { with_server(JsonLegacy, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let response = request(writer, reader, Request::ListMacros { dylib_path }, None).into(); + let response = request_legacy(writer, reader, Request::ListMacros { dylib_path }); let Response::ListMacros(Ok(macros)) = response else { panic!("expected successful ListMacros response"); @@ -73,13 +75,11 @@ fn test_list_macros() { #[test] fn test_list_macros_invalid_path() { with_server(JsonLegacy, |writer, reader| { - let response = request( + let response = request_legacy( writer, reader, Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, - None, - ) - .into(); + ); match response { Response::ListMacros(Err(e)) => assert!( @@ -95,7 +95,7 @@ fn test_list_macros_invalid_path() { fn test_set_config() { with_server(JsonLegacy, |writer, reader| { let config = ServerConfig { span_mode: SpanMode::Id }; - let response = request(writer, reader, Request::SetConfig(config), None).into(); + let response = request_legacy(writer, reader, Request::SetConfig(config)); match response { Response::SetConfig(returned_config) => { @@ -110,7 +110,7 @@ fn test_set_config() { fn test_set_config_rust_analyzer_mode() { with_server(JsonLegacy, |writer, reader| { let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; - let response = request(writer, reader, Request::SetConfig(config), None).into(); + let response = request_legacy(writer, reader, Request::SetConfig(config)); match response { Response::SetConfig(returned_config) => { @@ -126,7 +126,7 @@ fn test_expand_macro_panic() { with_server(JsonLegacy, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {}); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; @@ -152,7 +152,7 @@ fn test_expand_macro_panic() { }, })); - let response = request(writer, reader, expand_request, None).into(); + let response = request_legacy(writer, reader, expand_request); match response { Response::ExpandMacro(Err(PanicMessage(msg))) => { @@ -171,21 +171,18 @@ fn test_basic_call_flow() { with_server(JsonLegacy, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let response1 = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + let response1 = request_legacy(writer, reader, Request::ApiVersionCheck {}); assert!(matches!(response1, Response::ApiVersionCheck(_))); - let response2 = request( + let response2 = request_legacy( writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }), - None, - ) - .into(); + ); assert!(matches!(response2, Response::SetConfig(_))); let response3 = - request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, None) - .into(); + request_legacy(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }); assert!(matches!(response3, Response::ListMacros(Ok(_)))); }); } @@ -195,7 +192,7 @@ fn test_expand_nonexistent_macro() { with_server(JsonLegacy, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into(); + let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {}); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; @@ -221,7 +218,7 @@ fn test_expand_nonexistent_macro() { }, })); - let response = request(writer, reader, expand_request, None).into(); + let response = request_legacy(writer, reader, expand_request); match response { Response::ExpandMacro(Err(PanicMessage(msg))) => {