Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions crates/proc_macro_api/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ pub enum ErrorCode {
}

pub trait Message: Serialize + DeserializeOwned {
fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> {
Ok(match read_json(inp)? {
fn read(inp: &mut impl BufRead, buf: &mut String) -> io::Result<Option<Self>> {
Ok(match read_json(inp, buf)? {
None => None,
Some(text) => {
let mut deserializer = serde_json::Deserializer::from_str(&text);
Expand All @@ -76,14 +76,29 @@ pub trait Message: Serialize + DeserializeOwned {
impl Message for Request {}
impl Message for Response {}

fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
let mut buf = String::new();
inp.read_line(&mut buf)?;
buf.pop(); // Remove trailing '\n'
Ok(match buf.len() {
0 => None,
_ => Some(buf),
})
fn read_json<'a>(
inp: &mut impl BufRead,
mut buf: &'a mut String,
) -> io::Result<Option<&'a String>> {
loop {
buf.clear();

inp.read_line(&mut buf)?;
buf.pop(); // Remove trailing '\n'

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("{") {
log::error!("proc-macro tried to print : {}", buf);
continue;
}

return Ok(Some(buf));
}
}

fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
Expand Down
7 changes: 5 additions & 2 deletions crates/proc_macro_api/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ impl ProcMacroProcessSrv {
fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
let (mut stdin, mut stdout) = process.stdio().expect("couldn't access child stdio");

let mut buf = String::new();

for Task { req, result_tx } in task_rx {
match send_request(&mut stdin, &mut stdout, req) {
match send_request(&mut stdin, &mut stdout, req, &mut buf) {
Ok(res) => result_tx.send(res).unwrap(),
Err(err) => {
log::error!(
Expand Down Expand Up @@ -152,7 +154,8 @@ fn send_request(
mut writer: &mut impl Write,
mut reader: &mut impl BufRead,
req: Request,
buf: &mut String,
) -> io::Result<Option<Response>> {
req.write(&mut writer)?;
Response::read(&mut reader)
Response::read(&mut reader, buf)
}
7 changes: 4 additions & 3 deletions crates/proc_macro_srv/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ use std::io;

pub fn run() -> io::Result<()> {
let mut srv = ProcMacroSrv::default();
let mut buf = String::new();

while let Some(req) = read_request()? {
while let Some(req) = read_request(&mut buf)? {
let res = match req {
msg::Request::ListMacro(task) => srv.list_macros(&task).map(msg::Response::ListMacro),
msg::Request::ExpansionMacro(task) => {
Expand All @@ -30,8 +31,8 @@ pub fn run() -> io::Result<()> {
Ok(())
}

fn read_request() -> io::Result<Option<msg::Request>> {
msg::Request::read(&mut io::stdin().lock())
fn read_request(buf: &mut String) -> io::Result<Option<msg::Request>> {
msg::Request::read(&mut io::stdin().lock(), buf)
}

fn write_response(msg: msg::Response) -> io::Result<()> {
Expand Down
4 changes: 4 additions & 0 deletions crates/rust-analyzer/tests/rust-analyzer/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,10 @@ pub fn foo(_input: TokenStream) -> TokenStream {
// We hard code the output here for preventing to use any deps
let mut res = TokenStream::new();

// ill behaved proc-macro will use the stdout
// we should ignore it
println!("I am bad guy");

// impl Bar for Foo { fn bar() {} }
let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
let mut fn_stream = TokenStream::new();
Expand Down