about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEmil Lauridsen <mine809@gmail.com>2020-01-29 13:40:27 +0100
committerEmil Lauridsen <mine809@gmail.com>2020-01-29 13:40:27 +0100
commit8ffbe86dfd24ffcc11ec37bceca9102260d59db2 (patch)
tree9b21617545fc358ebf2a47c8f28bf663a5a9afbc
parent6fd29651b4f2bca8a36685a49d35cd349692984a (diff)
downloadrust-8ffbe86dfd24ffcc11ec37bceca9102260d59db2.tar.gz
rust-8ffbe86dfd24ffcc11ec37bceca9102260d59db2.zip
Parse cargo output a line at a time.
We previously used serde's stream deserializer to read json blobs from
the cargo output. It has an issue though: If the deserializer encounters
invalid input, it gets stuck reporting the same error again and again
because it is unable to foward over the input until it reaches a new
valid object.

Reading a line at a time and manually deserializing fixes this issue,
because cargo makes sure to only outpu one json blob per line, so should
we encounter invalid input, we can just skip a line and continue.

The main reason this would happen is stray printf-debugging in
procedural macros, so we still report that an error occured, but we
handle it gracefully now.

Fixes #2935
-rw-r--r--crates/ra_cargo_watch/Cargo.toml1
-rw-r--r--crates/ra_cargo_watch/src/lib.rs26
2 files changed, 22 insertions, 5 deletions
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml
index 49e06e0d3c7..dd814fc9d9a 100644
--- a/crates/ra_cargo_watch/Cargo.toml
+++ b/crates/ra_cargo_watch/Cargo.toml
@@ -11,6 +11,7 @@ log = "0.4.3"
 cargo_metadata = "0.9.1"
 jod-thread = "0.1.0"
 parking_lot = "0.10.0"
+serde_json = "1.0.45"
 
 [dev-dependencies]
 insta = "0.13.0"
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
index 9af9c347dfa..e015692fa98 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -9,7 +9,7 @@ use lsp_types::{
 };
 use std::{
     collections::HashMap,
-    io::BufReader,
+    io::{BufRead, BufReader},
     path::PathBuf,
     process::{Command, Stdio},
     sync::Arc,
@@ -350,13 +350,29 @@ impl WatchThread {
             // which will break out of the loop, and continue the shutdown
             let _ = message_send.send(CheckEvent::Begin);
 
-            for message in
-                cargo_metadata::parse_messages(BufReader::new(command.stdout.take().unwrap()))
-            {
+            // We manually read a line at a time, instead of using serde's
+            // stream deserializers, because the deserializer cannot recover
+            // from an error, resulting in it getting stuck, because we try to
+            // be resillient against failures.
+            //
+            // Because cargo only outputs one JSON object per line, we can
+            // simply skip a line if it doesn't parse, which just ignores any
+            // erroneus output.
+            let stdout = BufReader::new(command.stdout.take().unwrap());
+            for line in stdout.lines() {
+                let line = match line {
+                    Ok(line) => line,
+                    Err(err) => {
+                        log::error!("Couldn't read line from cargo: {:?}", err);
+                        continue;
+                    }
+                };
+
+                let message = serde_json::from_str::<cargo_metadata::Message>(&line);
                 let message = match message {
                     Ok(message) => message,
                     Err(err) => {
-                        log::error!("Invalid json from cargo check, ignoring: {}", err);
+                        log::error!("Invalid json from cargo check, ignoring ({}): {} ", err, line);
                         continue;
                     }
                 };