8570: Flycheck tries to parse both Cargo and Rustc messages. r=rickvanprim a=rickvanprim

This change allows non-Cargo build systems to be used for Flycheck provided they call `rustc` with `--error-format=json` and emit those JSON messages to `stdout`.

Co-authored-by: James Leitch <rickvanprim@gmail.com>
This commit is contained in:
bors[bot] 2021-04-21 21:56:54 +00:00 committed by GitHub
commit 32491c0978
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 22 deletions

1
Cargo.lock generated
View file

@ -396,6 +396,7 @@ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"jod-thread", "jod-thread",
"log", "log",
"serde",
"serde_json", "serde_json",
"stdx", "stdx",
"toolchain", "toolchain",

View file

@ -13,6 +13,7 @@ doctest = false
crossbeam-channel = "0.5.0" crossbeam-channel = "0.5.0"
log = "0.4.8" log = "0.4.8"
cargo_metadata = "0.13" cargo_metadata = "0.13"
serde = { version = "1.0.106", features = ["derive"] }
serde_json = "1.0.48" serde_json = "1.0.48"
jod-thread = "0.1.1" jod-thread = "0.1.1"

View file

@ -4,13 +4,14 @@
use std::{ use std::{
fmt, fmt,
io::{self, BufReader}, io::{self, BufRead, BufReader},
path::PathBuf, path::PathBuf,
process::{self, Command, Stdio}, process::{self, Command, Stdio},
time::Duration, time::Duration,
}; };
use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use serde::Deserialize;
use stdx::JodChild; use stdx::JodChild;
pub use cargo_metadata::diagnostic::{ pub use cargo_metadata::diagnostic::{
@ -128,7 +129,7 @@ struct FlycheckActor {
enum Event { enum Event {
Restart(Restart), Restart(Restart),
CheckEvent(Option<cargo_metadata::Message>), CheckEvent(Option<CargoMessage>),
} }
impl FlycheckActor { impl FlycheckActor {
@ -180,21 +181,16 @@ impl FlycheckActor {
self.progress(Progress::DidFinish(res)); self.progress(Progress::DidFinish(res));
} }
Event::CheckEvent(Some(message)) => match message { Event::CheckEvent(Some(message)) => match message {
cargo_metadata::Message::CompilerArtifact(msg) => { CargoMessage::CompilerArtifact(msg) => {
self.progress(Progress::DidCheckCrate(msg.target.name)); self.progress(Progress::DidCheckCrate(msg.target.name));
} }
cargo_metadata::Message::CompilerMessage(msg) => { CargoMessage::Diagnostic(msg) => {
self.send(Message::AddDiagnostic { self.send(Message::AddDiagnostic {
workspace_root: self.workspace_root.clone(), workspace_root: self.workspace_root.clone(),
diagnostic: msg.message, diagnostic: msg,
}); });
} }
cargo_metadata::Message::BuildScriptExecuted(_)
| cargo_metadata::Message::BuildFinished(_)
| cargo_metadata::Message::TextLine(_)
| _ => {}
}, },
} }
} }
@ -261,7 +257,7 @@ struct CargoHandle {
child: JodChild, child: JodChild,
#[allow(unused)] #[allow(unused)]
thread: jod_thread::JoinHandle<io::Result<bool>>, thread: jod_thread::JoinHandle<io::Result<bool>>,
receiver: Receiver<cargo_metadata::Message>, receiver: Receiver<CargoMessage>,
} }
impl CargoHandle { impl CargoHandle {
@ -294,14 +290,11 @@ impl CargoHandle {
struct CargoActor { struct CargoActor {
child_stdout: process::ChildStdout, child_stdout: process::ChildStdout,
sender: Sender<cargo_metadata::Message>, sender: Sender<CargoMessage>,
} }
impl CargoActor { impl CargoActor {
fn new( fn new(child_stdout: process::ChildStdout, sender: Sender<CargoMessage>) -> CargoActor {
child_stdout: process::ChildStdout,
sender: Sender<cargo_metadata::Message>,
) -> CargoActor {
CargoActor { child_stdout, sender } CargoActor { child_stdout, sender }
} }
fn run(self) -> io::Result<bool> { fn run(self) -> io::Result<bool> {
@ -315,7 +308,7 @@ impl CargoActor {
// erroneus output. // erroneus output.
let stdout = BufReader::new(self.child_stdout); let stdout = BufReader::new(self.child_stdout);
let mut read_at_least_one_message = false; let mut read_at_least_one_message = false;
for message in cargo_metadata::Message::parse_stream(stdout) { for message in stdout.lines() {
let message = match message { let message = match message {
Ok(message) => message, Ok(message) => message,
Err(err) => { Err(err) => {
@ -326,13 +319,44 @@ impl CargoActor {
read_at_least_one_message = true; read_at_least_one_message = true;
// Try to deserialize a message from Cargo or Rustc.
let mut deserializer = serde_json::Deserializer::from_str(&message);
deserializer.disable_recursion_limit();
if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
match message {
// Skip certain kinds of messages to only spend time on what's useful // Skip certain kinds of messages to only spend time on what's useful
match &message { JsonMessage::Cargo(message) => match message {
cargo_metadata::Message::CompilerArtifact(artifact) if artifact.fresh => (), cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
cargo_metadata::Message::BuildScriptExecuted(_) => (), self.sender.send(CargoMessage::CompilerArtifact(artifact)).unwrap()
_ => self.sender.send(message).unwrap(), }
cargo_metadata::Message::CompilerMessage(msg) => {
self.sender.send(CargoMessage::Diagnostic(msg.message)).unwrap()
}
cargo_metadata::Message::CompilerArtifact(_)
| cargo_metadata::Message::BuildScriptExecuted(_)
| cargo_metadata::Message::BuildFinished(_)
| cargo_metadata::Message::TextLine(_)
| _ => (),
},
JsonMessage::Rustc(message) => {
self.sender.send(CargoMessage::Diagnostic(message)).unwrap()
}
}
} }
} }
Ok(read_at_least_one_message) Ok(read_at_least_one_message)
} }
} }
enum CargoMessage {
CompilerArtifact(cargo_metadata::Artifact),
Diagnostic(Diagnostic),
}
#[derive(Deserialize)]
#[serde(untagged)]
enum JsonMessage {
Cargo(cargo_metadata::Message),
Rustc(Diagnostic),
}