mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 14:51:48 +00:00
internal: Show more project building errors to the user
This commit is contained in:
parent
cf9c825c9e
commit
b23b276310
8 changed files with 82 additions and 51 deletions
|
@ -119,7 +119,9 @@ fn setup_logging(log_file: Option<&Path>) -> Result<()> {
|
|||
None => None,
|
||||
};
|
||||
let filter = env::var("RA_LOG").ok();
|
||||
logger::Logger::new(log_file, filter.as_deref()).install()?;
|
||||
// deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually useful
|
||||
// information in there for debugging
|
||||
logger::Logger::new(log_file, filter.as_deref().or(Some("error"))).install()?;
|
||||
|
||||
profile::init();
|
||||
|
||||
|
|
|
@ -190,6 +190,7 @@ impl GlobalState {
|
|||
|
||||
for file in changed_files {
|
||||
if !file.is_created_or_deleted() {
|
||||
// FIXME: https://github.com/rust-analyzer/rust-analyzer/issues/11357
|
||||
let crates = self.analysis_host.raw_database().relevant_crates(file.file_id);
|
||||
let crate_graph = self.analysis_host.raw_database().crate_graph();
|
||||
|
||||
|
@ -255,6 +256,7 @@ impl GlobalState {
|
|||
let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
|
||||
self.send(request.into());
|
||||
}
|
||||
|
||||
pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
|
||||
let handler = self
|
||||
.req_queue
|
||||
|
@ -281,6 +283,7 @@ impl GlobalState {
|
|||
.incoming
|
||||
.register(request.id.clone(), (request.method.clone(), request_received));
|
||||
}
|
||||
|
||||
pub(crate) fn respond(&mut self, response: lsp_server::Response) {
|
||||
if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
|
||||
if let Some(err) = &response.error {
|
||||
|
@ -294,6 +297,7 @@ impl GlobalState {
|
|||
self.send(response.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
|
||||
if let Some(response) = self.req_queue.incoming.cancel(request_id) {
|
||||
self.send(response.into());
|
||||
|
@ -307,7 +311,7 @@ impl GlobalState {
|
|||
|
||||
impl Drop for GlobalState {
|
||||
fn drop(&mut self) {
|
||||
self.analysis_host.request_cancellation()
|
||||
self.analysis_host.request_cancellation();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,26 @@ impl GlobalState {
|
|||
)
|
||||
}
|
||||
|
||||
/// Sends a notification to the client containing the error `message`.
|
||||
/// If `additional_info` is [`Some`], appends a note to the notification telling to check the logs.
|
||||
/// This will always log `message` + `additional_info` to the server's error log.
|
||||
pub(crate) fn show_and_log_error(&mut self, message: String, additional_info: Option<String>) {
|
||||
let mut message = message;
|
||||
match additional_info {
|
||||
Some(additional_info) => {
|
||||
tracing::error!("{}\n\n{}", &message, &additional_info);
|
||||
if tracing::enabled!(tracing::Level::ERROR) {
|
||||
message.push_str("\n\nCheck the server logs for additional info.");
|
||||
}
|
||||
}
|
||||
None => tracing::error!("{}", &message),
|
||||
}
|
||||
|
||||
self.send_notification::<lsp_types::notification::ShowMessage>(
|
||||
lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message },
|
||||
)
|
||||
}
|
||||
|
||||
/// rust-analyzer is resilient -- if it fails, this doesn't usually affect
|
||||
/// the user experience. Part of that is that we deliberately hide panics
|
||||
/// from the user.
|
||||
|
|
|
@ -111,10 +111,7 @@ impl GlobalState {
|
|||
&& self.config.detached_files().is_empty()
|
||||
&& self.config.notifications().cargo_toml_not_found
|
||||
{
|
||||
self.show_message(
|
||||
lsp_types::MessageType::ERROR,
|
||||
"rust-analyzer failed to discover workspace".to_string(),
|
||||
);
|
||||
self.show_and_log_error("rust-analyzer failed to discover workspace".to_string(), None);
|
||||
};
|
||||
|
||||
if self.config.did_save_text_document_dynamic_registration() {
|
||||
|
@ -406,9 +403,9 @@ impl GlobalState {
|
|||
flycheck::Progress::DidCancel => (Progress::End, None),
|
||||
flycheck::Progress::DidFinish(result) => {
|
||||
if let Err(err) = result {
|
||||
self.show_message(
|
||||
lsp_types::MessageType::ERROR,
|
||||
format!("cargo check failed: {}", err),
|
||||
self.show_and_log_error(
|
||||
"cargo check failed".to_string(),
|
||||
Some(err.to_string()),
|
||||
);
|
||||
}
|
||||
(Progress::End, None)
|
||||
|
@ -564,7 +561,6 @@ impl GlobalState {
|
|||
if self.workspaces.is_empty() && !self.is_quiescent() {
|
||||
self.respond(lsp_server::Response::new_err(
|
||||
req.id,
|
||||
// FIXME: i32 should impl From<ErrorCode> (from() guarantees lossless conversion)
|
||||
lsp_server::ErrorCode::ContentModified as i32,
|
||||
"waiting for cargo metadata or cargo check".to_owned(),
|
||||
));
|
||||
|
|
|
@ -72,9 +72,10 @@ impl GlobalState {
|
|||
status.message =
|
||||
Some("Reload required due to source changes of a procedural macro.".into())
|
||||
}
|
||||
if let Some(error) = self.fetch_build_data_error() {
|
||||
if let Err(_) = self.fetch_build_data_error() {
|
||||
status.health = lsp_ext::Health::Warning;
|
||||
status.message = Some(error)
|
||||
status.message =
|
||||
Some("Failed to run build scripts of some packages, check the logs.".to_string());
|
||||
}
|
||||
if !self.config.cargo_autoreload()
|
||||
&& self.is_quiescent()
|
||||
|
@ -84,7 +85,7 @@ impl GlobalState {
|
|||
status.message = Some("Workspace reload required".to_string())
|
||||
}
|
||||
|
||||
if let Some(error) = self.fetch_workspace_error() {
|
||||
if let Err(error) = self.fetch_workspace_error() {
|
||||
status.health = lsp_ext::Health::Error;
|
||||
status.message = Some(error)
|
||||
}
|
||||
|
@ -167,8 +168,8 @@ impl GlobalState {
|
|||
let _p = profile::span("GlobalState::switch_workspaces");
|
||||
tracing::info!("will switch workspaces");
|
||||
|
||||
if let Some(error_message) = self.fetch_workspace_error() {
|
||||
tracing::error!("failed to switch workspaces: {}", error_message);
|
||||
if let Err(error_message) = self.fetch_workspace_error() {
|
||||
self.show_and_log_error(error_message, None);
|
||||
if !self.workspaces.is_empty() {
|
||||
// It only makes sense to switch to a partially broken workspace
|
||||
// if we don't have any workspace at all yet.
|
||||
|
@ -176,8 +177,11 @@ impl GlobalState {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(error_message) = self.fetch_build_data_error() {
|
||||
tracing::error!("failed to switch build data: {}", error_message);
|
||||
if let Err(error) = self.fetch_build_data_error() {
|
||||
self.show_and_log_error(
|
||||
"rust-analyzer failed to run build scripts".to_string(),
|
||||
Some(error),
|
||||
);
|
||||
}
|
||||
|
||||
let workspaces = self
|
||||
|
@ -277,20 +281,18 @@ impl GlobalState {
|
|||
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
|
||||
|
||||
if self.proc_macro_client.is_none() {
|
||||
self.proc_macro_client = match self.config.proc_macro_srv() {
|
||||
None => None,
|
||||
Some((path, args)) => match ProcMacroServer::spawn(path.clone(), args) {
|
||||
Ok(it) => Some(it),
|
||||
if let Some((path, args)) = self.config.proc_macro_srv() {
|
||||
match ProcMacroServer::spawn(path.clone(), args) {
|
||||
Ok(it) => self.proc_macro_client = Some(it),
|
||||
Err(err) => {
|
||||
tracing::error!(
|
||||
"Failed to run proc_macro_srv from path {}, error: {:?}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let watch = match files_config.watcher {
|
||||
|
@ -348,7 +350,7 @@ impl GlobalState {
|
|||
tracing::info!("did switch workspaces");
|
||||
}
|
||||
|
||||
fn fetch_workspace_error(&self) -> Option<String> {
|
||||
fn fetch_workspace_error(&self) -> Result<(), String> {
|
||||
let mut buf = String::new();
|
||||
|
||||
for ws in self.fetch_workspaces_queue.last_op_result() {
|
||||
|
@ -358,35 +360,30 @@ impl GlobalState {
|
|||
}
|
||||
|
||||
if buf.is_empty() {
|
||||
return None;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Some(buf)
|
||||
Err(buf)
|
||||
}
|
||||
|
||||
fn fetch_build_data_error(&self) -> Option<String> {
|
||||
let mut buf = "rust-analyzer failed to run build scripts:\n".to_string();
|
||||
let mut has_errors = false;
|
||||
fn fetch_build_data_error(&self) -> Result<(), String> {
|
||||
let mut buf = String::new();
|
||||
|
||||
for ws in &self.fetch_build_data_queue.last_op_result().1 {
|
||||
match ws {
|
||||
Ok(data) => {
|
||||
if let Some(err) = data.error() {
|
||||
has_errors = true;
|
||||
stdx::format_to!(buf, "{:#}\n", err);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
has_errors = true;
|
||||
stdx::format_to!(buf, "{:#}\n", err);
|
||||
}
|
||||
Ok(data) => match data.error() {
|
||||
Some(stderr) => stdx::format_to!(buf, "{:#}\n", stderr),
|
||||
_ => (),
|
||||
},
|
||||
// io errors
|
||||
Err(err) => stdx::format_to!(buf, "{:#}\n", err),
|
||||
}
|
||||
}
|
||||
|
||||
if has_errors {
|
||||
Some(buf)
|
||||
if buf.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
None
|
||||
Err(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue