feat: show errors from cargo metadata and initial cargo check in the status bar

closes #3155
This commit is contained in:
Aleksey Kladov 2021-04-06 18:08:05 +03:00
parent 9ec5e6e4fd
commit de33702784
3 changed files with 63 additions and 13 deletions

View file

@ -13,7 +13,7 @@ use cargo_metadata::{BuildScript, Message};
use itertools::Itertools; use itertools::Itertools;
use paths::{AbsPath, AbsPathBuf}; use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use stdx::JodChild; use stdx::{format_to, JodChild};
use crate::{cfg_flag::CfgFlag, CargoConfig}; use crate::{cfg_flag::CfgFlag, CargoConfig};
@ -35,6 +35,7 @@ pub(crate) struct PackageBuildData {
#[derive(Debug, Default, PartialEq, Eq, Clone)] #[derive(Debug, Default, PartialEq, Eq, Clone)]
pub(crate) struct WorkspaceBuildData { pub(crate) struct WorkspaceBuildData {
per_package: FxHashMap<String, PackageBuildData>, per_package: FxHashMap<String, PackageBuildData>,
error: Option<String>,
} }
#[derive(Debug, Default, PartialEq, Eq, Clone)] #[derive(Debug, Default, PartialEq, Eq, Clone)]
@ -94,6 +95,19 @@ impl BuildDataResult {
pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> { pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
self.per_workspace.get(workspace_root) self.per_workspace.get(workspace_root)
} }
pub fn error(&self) -> Option<String> {
let mut buf = String::new();
for (_workspace_root, build_data) in &self.per_workspace {
if let Some(err) = &build_data.error {
format_to!(buf, "cargo check failed:\n{}", err);
}
}
if buf.is_empty() {
return None;
}
Some(buf)
}
} }
impl BuildDataConfig { impl BuildDataConfig {
@ -139,7 +153,7 @@ fn collect_from_workspace(
} }
} }
cmd.stdout(Stdio::piped()).stderr(Stdio::null()).stdin(Stdio::null()); cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
let mut child = cmd.spawn().map(JodChild)?; let mut child = cmd.spawn().map(JodChild)?;
let child_stdout = child.stdout.take().unwrap(); let child_stdout = child.stdout.take().unwrap();
@ -209,6 +223,15 @@ fn collect_from_workspace(
} }
} }
let output = child.into_inner().wait_with_output()?;
if !output.status.success() {
let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
if stderr.is_empty() {
stderr = "cargo check failed".to_string();
}
res.error = Some(stderr)
}
Ok(res) Ok(res)
} }

View file

@ -109,6 +109,11 @@ impl GlobalState {
quiescent: self.is_quiescent(), quiescent: self.is_quiescent(),
message: None, message: None,
}; };
if let Some(error) = self.build_data_error() {
status.health = lsp_ext::Health::Warning;
status.message = Some(error)
}
if !self.config.cargo_autoreload() if !self.config.cargo_autoreload()
&& self.is_quiescent() && self.is_quiescent()
&& self.fetch_workspaces_queue.op_requested() && self.fetch_workspaces_queue.op_requested()
@ -116,9 +121,10 @@ impl GlobalState {
status.health = lsp_ext::Health::Warning; status.health = lsp_ext::Health::Warning;
status.message = Some("Workspace reload required".to_string()) status.message = Some("Workspace reload required".to_string())
} }
if let Some(error) = self.loading_error() {
if let Some(error) = self.fetch_workspace_error() {
status.health = lsp_ext::Health::Error; status.health = lsp_ext::Health::Error;
status.message = Some(format!("Workspace reload failed: {}", error)) status.message = Some(error)
} }
if self.last_reported_status.as_ref() != Some(&status) { if self.last_reported_status.as_ref() != Some(&status) {
@ -217,7 +223,7 @@ impl GlobalState {
let _p = profile::span("GlobalState::switch_workspaces"); let _p = profile::span("GlobalState::switch_workspaces");
log::info!("will switch workspaces"); log::info!("will switch workspaces");
if let Some(error_message) = self.loading_error() { if let Some(error_message) = self.fetch_workspace_error() {
log::error!("failed to switch workspaces: {}", error_message); log::error!("failed to switch workspaces: {}", error_message);
self.show_message(lsp_types::MessageType::Error, error_message); self.show_message(lsp_types::MessageType::Error, error_message);
if !self.workspaces.is_empty() { if !self.workspaces.is_empty() {
@ -225,6 +231,11 @@ impl GlobalState {
} }
} }
if let Some(error_message) = self.build_data_error() {
log::error!("failed to switch build data: {}", error_message);
self.show_message(lsp_types::MessageType::Error, error_message);
}
let workspaces = self let workspaces = self
.fetch_workspaces_queue .fetch_workspaces_queue
.last_op_result() .last_op_result()
@ -343,22 +354,30 @@ impl GlobalState {
log::info!("did switch workspaces"); log::info!("did switch workspaces");
} }
fn loading_error(&self) -> Option<String> { fn fetch_workspace_error(&self) -> Option<String> {
let mut message = None; let mut buf = String::new();
for ws in self.fetch_workspaces_queue.last_op_result() { for ws in self.fetch_workspaces_queue.last_op_result() {
if let Err(err) = ws { if let Err(err) = ws {
let message = message.get_or_insert_with(String::new); stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err);
} }
} }
if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() { if buf.is_empty() {
let message = message.get_or_insert_with(String::new); return None;
stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err);
} }
message Some(buf)
}
fn build_data_error(&self) -> Option<String> {
match self.fetch_build_data_queue.last_op_result() {
Some(Err(err)) => {
Some(format!("rust-analyzer failed to fetch build data: {:#}\n", err))
}
Some(Ok(data)) => data.error(),
None => None,
}
} }
fn reload_flycheck(&mut self) { fn reload_flycheck(&mut self) {

View file

@ -178,6 +178,7 @@ where
start..start + len start..start + len
} }
#[repr(transparent)]
pub struct JodChild(pub process::Child); pub struct JodChild(pub process::Child);
impl ops::Deref for JodChild { impl ops::Deref for JodChild {
@ -200,6 +201,13 @@ impl Drop for JodChild {
} }
} }
impl JodChild {
pub fn into_inner(self) -> process::Child {
// SAFETY: repr transparent
unsafe { std::mem::transmute::<JodChild, process::Child>(self) }
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;