From de3370278468e5135e4990fc14562e5ce523ef37 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 6 Apr 2021 18:08:05 +0300 Subject: feat: show errors from `cargo metadata` and initial `cargo check` in the status bar closes #3155 --- crates/project_model/src/build_data.rs | 27 ++++++++++++++++++++-- crates/rust-analyzer/src/reload.rs | 41 +++++++++++++++++++++++++--------- crates/stdx/src/lib.rs | 8 +++++++ 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs index fdf4b2d55..0d4d39fef 100644 --- a/crates/project_model/src/build_data.rs +++ b/crates/project_model/src/build_data.rs @@ -13,7 +13,7 @@ use cargo_metadata::{BuildScript, Message}; use itertools::Itertools; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::FxHashMap; -use stdx::JodChild; +use stdx::{format_to, JodChild}; use crate::{cfg_flag::CfgFlag, CargoConfig}; @@ -35,6 +35,7 @@ pub(crate) struct PackageBuildData { #[derive(Debug, Default, PartialEq, Eq, Clone)] pub(crate) struct WorkspaceBuildData { per_package: FxHashMap, + error: Option, } #[derive(Debug, Default, PartialEq, Eq, Clone)] @@ -94,6 +95,19 @@ impl BuildDataResult { pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> { self.per_workspace.get(workspace_root) } + pub fn error(&self) -> Option { + 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 { @@ -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 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) } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 301c7003b..d0cc1b61a 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -109,6 +109,11 @@ impl GlobalState { quiescent: self.is_quiescent(), 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() && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() @@ -116,9 +121,10 @@ impl GlobalState { status.health = lsp_ext::Health::Warning; 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.message = Some(format!("Workspace reload failed: {}", error)) + status.message = Some(error) } if self.last_reported_status.as_ref() != Some(&status) { @@ -217,7 +223,7 @@ impl GlobalState { let _p = profile::span("GlobalState::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); self.show_message(lsp_types::MessageType::Error, error_message); 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 .fetch_workspaces_queue .last_op_result() @@ -343,22 +354,30 @@ impl GlobalState { log::info!("did switch workspaces"); } - fn loading_error(&self) -> Option { - let mut message = None; + fn fetch_workspace_error(&self) -> Option { + let mut buf = String::new(); for ws in self.fetch_workspaces_queue.last_op_result() { if let Err(err) = ws { - let message = message.get_or_insert_with(String::new); - stdx::format_to!(message, "rust-analyzer failed to load workspace: {:#}\n", err); + stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err); } } - if let Some(Err(err)) = self.fetch_build_data_queue.last_op_result() { - let message = message.get_or_insert_with(String::new); - stdx::format_to!(message, "rust-analyzer failed to fetch build data: {:#}\n", err); + if buf.is_empty() { + return None; } - message + Some(buf) + } + + fn build_data_error(&self) -> Option { + 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) { diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index d26be4853..b0a18d58d 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -178,6 +178,7 @@ where start..start + len } +#[repr(transparent)] pub struct JodChild(pub process::Child); 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::(self) } + } +} + #[cfg(test)] mod tests { use super::*; -- cgit v1.2.3