about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-15 10:36:50 +0000
committerbors <bors@rust-lang.org>2023-03-15 10:36:50 +0000
commit1787c14e725e4ac416828f986b095a23ecd11494 (patch)
tree61fc3d87c3bef5429c6c7138f6403b29bca5b513
parent579797f0eca3963f9c9d23c29def3da42e8c944a (diff)
parentd9c7d28e0d306ef3f259fe06b75385867335906e (diff)
downloadrust-1787c14e725e4ac416828f986b095a23ecd11494.tar.gz
rust-1787c14e725e4ac416828f986b095a23ecd11494.zip
Auto merge of #14358 - Veykril:err-reporting, r=Veykril
Report sysroot and rustc crate loading errors

Also aggregates the warnings and errors so we don't discard previous ones.
cc https://github.com/rust-lang/rust-analyzer/issues/14327
-rw-r--r--crates/project-model/src/cargo_workspace.rs6
-rw-r--r--crates/project-model/src/lib.rs2
-rw-r--r--crates/project-model/src/tests.rs6
-rw-r--r--crates/project-model/src/workspace.rs209
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs4
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs4
-rw-r--r--crates/rust-analyzer/src/cli/lsif.rs4
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs4
-rw-r--r--crates/rust-analyzer/src/cli/ssr.rs4
-rw-r--r--crates/rust-analyzer/src/config.rs10
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs40
-rw-r--r--crates/rust-analyzer/src/main_loop.rs17
-rw-r--r--crates/rust-analyzer/src/reload.rs41
13 files changed, 194 insertions, 157 deletions
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index 732adc50b50..01162b1a8ba 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -50,7 +50,7 @@ impl ops::Index<Target> for CargoWorkspace {
 
 /// Describes how to set the rustc source directory.
 #[derive(Clone, Debug, PartialEq, Eq)]
-pub enum RustcSource {
+pub enum RustLibSource {
     /// Explicit path for the rustc source directory.
     Path(AbsPathBuf),
     /// Try to automatically detect where the rustc source directory is.
@@ -95,10 +95,10 @@ pub struct CargoConfig {
     /// rustc target
     pub target: Option<String>,
     /// Sysroot loading behavior
-    pub sysroot: Option<RustcSource>,
+    pub sysroot: Option<RustLibSource>,
     pub sysroot_src: Option<AbsPathBuf>,
     /// rustc private crate source
-    pub rustc_source: Option<RustcSource>,
+    pub rustc_source: Option<RustLibSource>,
     /// crates to disable `#[cfg(test)]` on
     pub unset_test_crates: UnsetTestCrates,
     /// Invoke `cargo check` through the RUSTC_WRAPPER.
diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs
index 9b6a71db811..70cb71ae3bd 100644
--- a/crates/project-model/src/lib.rs
+++ b/crates/project-model/src/lib.rs
@@ -44,7 +44,7 @@ pub use crate::{
     build_scripts::WorkspaceBuildScripts,
     cargo_workspace::{
         CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
-        RustcSource, Target, TargetData, TargetKind, UnsetTestCrates,
+        RustLibSource, Target, TargetData, TargetKind, UnsetTestCrates,
     },
     manifest_path::ManifestPath,
     project_json::{ProjectJson, ProjectJsonData},
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index 749eee531ee..3754accbb03 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -24,8 +24,8 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr
     let project_workspace = ProjectWorkspace::Cargo {
         cargo: cargo_workspace,
         build_scripts: WorkspaceBuildScripts::default(),
-        sysroot: None,
-        rustc: None,
+        sysroot: Err(None),
+        rustc: Err(None),
         rustc_cfg: Vec::new(),
         cfg_overrides,
         toolchain: None,
@@ -37,7 +37,7 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr
 fn load_rust_project(file: &str) -> CrateGraph {
     let data = get_test_json_file(file);
     let project = rooted_project_json(data);
-    let sysroot = Some(get_fake_sysroot());
+    let sysroot = Ok(get_fake_sysroot());
     let project_workspace = ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new() };
     to_crate_graph(project_workspace)
 }
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 816950b22f4..d1e53e12eeb 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -17,7 +17,7 @@ use stdx::{always, hash::NoHashHashMap};
 
 use crate::{
     build_scripts::BuildScriptOutput,
-    cargo_workspace::{DepKind, PackageData, RustcSource},
+    cargo_workspace::{DepKind, PackageData, RustLibSource},
     cfg_flag::CfgFlag,
     rustc_cfg,
     sysroot::SysrootCrate,
@@ -69,8 +69,8 @@ pub enum ProjectWorkspace {
     Cargo {
         cargo: CargoWorkspace,
         build_scripts: WorkspaceBuildScripts,
-        sysroot: Option<Sysroot>,
-        rustc: Option<(CargoWorkspace, WorkspaceBuildScripts)>,
+        sysroot: Result<Sysroot, Option<String>>,
+        rustc: Result<(CargoWorkspace, WorkspaceBuildScripts), Option<String>>,
         /// Holds cfg flags for the current target. We get those by running
         /// `rustc --print cfg`.
         ///
@@ -82,7 +82,7 @@ pub enum ProjectWorkspace {
         target_layout: Result<String, String>,
     },
     /// Project workspace was manually specified using a `rust-project.json` file.
-    Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
+    Json { project: ProjectJson, sysroot: Result<Sysroot, Option<String>>, rustc_cfg: Vec<CfgFlag> },
     // FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
     // That's not the end user experience we should strive for.
     // Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
@@ -93,7 +93,11 @@ pub enum ProjectWorkspace {
     // //
     /// Project with a set of disjoint files, not belonging to any particular workspace.
     /// Backed by basic sysroot crates for basic completion and highlighting.
-    DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
+    DetachedFiles {
+        files: Vec<AbsPathBuf>,
+        sysroot: Result<Sysroot, Option<String>>,
+        rustc_cfg: Vec<CfgFlag>,
+    },
 }
 
 impl fmt::Debug for ProjectWorkspace {
@@ -113,7 +117,7 @@ impl fmt::Debug for ProjectWorkspace {
                 .debug_struct("Cargo")
                 .field("root", &cargo.workspace_root().file_name())
                 .field("n_packages", &cargo.packages().len())
-                .field("sysroot", &sysroot.is_some())
+                .field("sysroot", &sysroot.is_ok())
                 .field(
                     "n_rustc_compiler_crates",
                     &rustc.as_ref().map_or(0, |(rc, _)| rc.packages().len()),
@@ -126,7 +130,7 @@ impl fmt::Debug for ProjectWorkspace {
             ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
                 let mut debug_struct = f.debug_struct("Json");
                 debug_struct.field("n_crates", &project.n_crates());
-                if let Some(sysroot) = sysroot {
+                if let Ok(sysroot) = sysroot {
                     debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
                 }
                 debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
@@ -135,7 +139,7 @@ impl fmt::Debug for ProjectWorkspace {
             ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
                 .debug_struct("DetachedFiles")
                 .field("n_files", &files.len())
-                .field("sysroot", &sysroot.is_some())
+                .field("sysroot", &sysroot.is_ok())
                 .field("n_rustc_cfg", &rustc_cfg.len())
                 .finish(),
         }
@@ -191,96 +195,81 @@ impl ProjectWorkspace {
                 let cargo = CargoWorkspace::new(meta);
 
                 let sysroot = match (&config.sysroot, &config.sysroot_src) {
-                    (Some(RustcSource::Path(path)), None) => {
-                        match Sysroot::with_sysroot_dir(path.clone()) {
-                            Ok(it) => Some(it),
-                            Err(e) => {
-                                tracing::error!(%e, "Failed to find sysroot at {}.", path.display());
-                                None
-                            }
-                        }
+                    (Some(RustLibSource::Path(path)), None) => {
+                        Sysroot::with_sysroot_dir(path.clone()).map_err(|e| {
+                          Some(format!("Failed to find sysroot at {}:{e}", path.display()))
+                        })
                     }
-                    (Some(RustcSource::Discover), None) => {
-                        match Sysroot::discover(cargo_toml.parent(), &config.extra_env) {
-                            Ok(it) => Some(it),
-                            Err(e) => {
-                                tracing::error!(
-                                    %e,
-                                    "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
-                                    cargo_toml.display()
-                                );
-                                None
-                            }
-                        }
+                    (Some(RustLibSource::Discover), None) => {
+                        Sysroot::discover(cargo_toml.parent(), &config.extra_env).map_err(|e| {
+                            Some(format!("Failed to find sysroot for Cargo.toml file {}. Is rust-src installed? {e}", cargo_toml.display()))
+                        })
                     }
-                    (Some(RustcSource::Path(sysroot)), Some(sysroot_src)) => {
-                        Some(Sysroot::load(sysroot.clone(), sysroot_src.clone()))
+                    (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => {
+                        Ok(Sysroot::load(sysroot.clone(), sysroot_src.clone()))
                     }
-                    (Some(RustcSource::Discover), Some(sysroot_src)) => {
-                        match Sysroot::discover_with_src_override(
+                    (Some(RustLibSource::Discover), Some(sysroot_src)) => {
+                        Sysroot::discover_with_src_override(
                             cargo_toml.parent(),
                             &config.extra_env,
                             sysroot_src.clone(),
-                        ) {
-                            Ok(it) => Some(it),
-                            Err(e) => {
-                                tracing::error!(
-                                    %e,
-                                    "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
-                                    cargo_toml.display()
-                                );
-                                None
-                            }
-                        }
+                        ).map_err(|e| {
+                            Some(format!("Failed to find sysroot for Cargo.toml file {}. Is rust-src installed? {e}", cargo_toml.display()))
+                        })
                     }
-                    (None, _) => None,
+                    (None, _) => Err(None),
                 };
 
-                if let Some(sysroot) = &sysroot {
+                if let Ok(sysroot) = &sysroot {
                     tracing::info!(workspace = %cargo_toml.display(), src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
                 }
 
                 let rustc_dir = match &config.rustc_source {
-                    Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
-                    Some(RustcSource::Discover) => {
-                        sysroot.as_ref().and_then(Sysroot::discover_rustc)
+                    Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
+                        .map_err(|p| {
+                            Some(format!("rustc source path is not absolute: {}", p.display()))
+                        }),
+                    Some(RustLibSource::Discover) => {
+                        sysroot.as_ref().ok().and_then(Sysroot::discover_rustc).ok_or_else(|| {
+                            Some(format!("Failed to discover rustc source for sysroot."))
+                        })
                     }
-                    None => None,
+                    None => Err(None),
                 };
 
-                let rustc = match rustc_dir {
-                    Some(rustc_dir) => {
-                        tracing::info!(workspace = %cargo_toml.display(), rustc_dir = %rustc_dir.display(), "Using rustc source");
-                        match CargoWorkspace::fetch_metadata(
-                            &rustc_dir,
-                            cargo_toml.parent(),
-                            &CargoConfig {
-                                features: crate::CargoFeatures::default(),
-                                ..config.clone()
-                            },
-                            progress,
-                        ) {
-                            Ok(meta) => {
-                                let workspace = CargoWorkspace::new(meta);
-                                let buildscripts = WorkspaceBuildScripts::rustc_crates(
-                                    &workspace,
-                                    cargo_toml.parent(),
-                                    &config.extra_env,
-                                );
-                                Some((workspace, buildscripts))
-                            }
-                            Err(e) => {
-                                tracing::error!(
-                                    %e,
-                                    "Failed to read Cargo metadata from rustc source at {}",
-                                    rustc_dir.display()
-                                );
-                                None
-                            }
+                let rustc =  rustc_dir.and_then(|rustc_dir| {
+                    tracing::info!(workspace = %cargo_toml.display(), rustc_dir = %rustc_dir.display(), "Using rustc source");
+                    match CargoWorkspace::fetch_metadata(
+                        &rustc_dir,
+                        cargo_toml.parent(),
+                        &CargoConfig {
+                            features: crate::CargoFeatures::default(),
+                            ..config.clone()
+                        },
+                        progress,
+                    ) {
+                        Ok(meta) => {
+                            let workspace = CargoWorkspace::new(meta);
+                            let buildscripts = WorkspaceBuildScripts::rustc_crates(
+                                &workspace,
+                                cargo_toml.parent(),
+                                &config.extra_env,
+                            );
+                            Ok((workspace, buildscripts))
+                        }
+                        Err(e) => {
+                            tracing::error!(
+                                %e,
+                                "Failed to read Cargo metadata from rustc source at {}",
+                                rustc_dir.display()
+                            );
+                            Err(Some(format!(
+                                "Failed to read Cargo metadata from rustc source at {}: {e}",
+                                rustc_dir.display())
+                            ))
                         }
                     }
-                    None => None,
-                };
+                });
 
                 let rustc_cfg =
                     rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
@@ -316,12 +305,12 @@ impl ProjectWorkspace {
         extra_env: &FxHashMap<String, String>,
     ) -> ProjectWorkspace {
         let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
-            (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)),
+            (Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)),
             (Some(sysroot), None) => {
                 // assume sysroot is structured like rustup's and guess `sysroot_src`
                 let sysroot_src =
                     sysroot.join("lib").join("rustlib").join("src").join("rust").join("library");
-                Some(Sysroot::load(sysroot, sysroot_src))
+                Ok(Sysroot::load(sysroot, sysroot_src))
             }
             (None, Some(sysroot_src)) => {
                 // assume sysroot is structured like rustup's and guess `sysroot`
@@ -329,11 +318,11 @@ impl ProjectWorkspace {
                 for _ in 0..5 {
                     sysroot.pop();
                 }
-                Some(Sysroot::load(sysroot, sysroot_src))
+                Ok(Sysroot::load(sysroot, sysroot_src))
             }
-            (None, None) => None,
+            (None, None) => Err(None),
         };
-        if let Some(sysroot) = &sysroot {
+        if let Ok(sysroot) = &sysroot {
             tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
         }
 
@@ -346,33 +335,23 @@ impl ProjectWorkspace {
         config: &CargoConfig,
     ) -> Result<ProjectWorkspace> {
         let sysroot = match &config.sysroot {
-            Some(RustcSource::Path(path)) => match Sysroot::with_sysroot_dir(path.clone()) {
-                Ok(it) => Some(it),
-                Err(e) => {
-                    tracing::error!(%e, "Failed to find sysroot at {}.", path.display());
-                    None
-                }
-            },
-            Some(RustcSource::Discover) => {
+            Some(RustLibSource::Path(path)) => Sysroot::with_sysroot_dir(path.clone())
+                .map_err(|e| Some(format!("Failed to find sysroot at {}:{e}", path.display()))),
+            Some(RustLibSource::Discover) => {
                 let dir = &detached_files
                     .first()
                     .and_then(|it| it.parent())
                     .ok_or_else(|| format_err!("No detached files to load"))?;
-                match Sysroot::discover(dir, &config.extra_env) {
-                    Ok(it) => Some(it),
-                    Err(e) => {
-                        tracing::error!(
-                            %e,
-                            "Failed to find sysroot for {}. Is rust-src installed?",
-                            dir.display()
-                        );
-                        None
-                    }
-                }
+                Sysroot::discover(dir, &config.extra_env).map_err(|e| {
+                    Some(format!(
+                        "Failed to find sysroot for {}. Is rust-src installed? {e}",
+                        dir.display()
+                    ))
+                })
             }
-            None => None,
+            None => Err(None),
         };
-        if let Some(sysroot) = &sysroot {
+        if let Ok(sysroot) = &sysroot {
             tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
         }
         let rustc_cfg = rustc_cfg::get(None, None, &Default::default());
@@ -463,8 +442,8 @@ impl ProjectWorkspace {
 
     pub fn find_sysroot_proc_macro_srv(&self) -> Option<AbsPathBuf> {
         match self {
-            ProjectWorkspace::Cargo { sysroot: Some(sysroot), .. }
-            | ProjectWorkspace::Json { sysroot: Some(sysroot), .. } => {
+            ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
+            | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } => {
                 let standalone_server_name =
                     format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
                 ["libexec", "lib"]
@@ -480,7 +459,7 @@ impl ProjectWorkspace {
     /// The return type contains the path and whether or not
     /// the root is a member of the current workspace
     pub fn to_roots(&self) -> Vec<PackageRoot> {
-        let mk_sysroot = |sysroot: Option<&Sysroot>, project_root: Option<&AbsPath>| {
+        let mk_sysroot = |sysroot: Result<&Sysroot, _>, project_root: Option<&AbsPath>| {
             sysroot.map(|sysroot| PackageRoot {
                 // mark the sysroot as mutable if it is located inside of the project
                 is_local: project_root
@@ -603,7 +582,7 @@ impl ProjectWorkspace {
                 load_proc_macro,
                 load,
                 project,
-                sysroot.as_ref(),
+                sysroot.as_ref().ok(),
                 extra_env,
                 Err("rust-project.json projects have no target layout set".into()),
             ),
@@ -619,9 +598,9 @@ impl ProjectWorkspace {
             } => cargo_to_crate_graph(
                 load_proc_macro,
                 load,
-                rustc,
+                rustc.as_ref().ok(),
                 cargo,
-                sysroot.as_ref(),
+                sysroot.as_ref().ok(),
                 rustc_cfg.clone(),
                 cfg_overrides,
                 build_scripts,
@@ -635,7 +614,7 @@ impl ProjectWorkspace {
                     rustc_cfg.clone(),
                     load,
                     files,
-                    sysroot,
+                    sysroot.as_ref().ok(),
                     Err("detached file projects have no target layout set".into()),
                 )
             }
@@ -797,7 +776,7 @@ fn project_json_to_crate_graph(
 fn cargo_to_crate_graph(
     load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
-    rustc: &Option<(CargoWorkspace, WorkspaceBuildScripts)>,
+    rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
     cargo: &CargoWorkspace,
     sysroot: Option<&Sysroot>,
     rustc_cfg: Vec<CfgFlag>,
@@ -974,7 +953,7 @@ fn detached_files_to_crate_graph(
     rustc_cfg: Vec<CfgFlag>,
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     detached_files: &[AbsPathBuf],
-    sysroot: &Option<Sysroot>,
+    sysroot: Option<&Sysroot>,
     target_layout: TargetLayoutLoadResult,
 ) -> CrateGraph {
     let _p = profile::span("detached_files_to_crate_graph");
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 8187b1ba4e9..6ce1de5d32b 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -24,7 +24,7 @@ use ide_db::base_db::{
 use itertools::Itertools;
 use oorandom::Rand32;
 use profile::{Bytes, StopWatch};
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
 use rayon::prelude::*;
 use rustc_hash::FxHashSet;
 use stdx::format_to;
@@ -57,7 +57,7 @@ impl flags::AnalysisStats {
         let mut cargo_config = CargoConfig::default();
         cargo_config.sysroot = match self.no_sysroot {
             true => None,
-            false => Some(RustcSource::Discover),
+            false => Some(RustLibSource::Discover),
         };
         let no_progress = &|_| ();
 
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index 0721d486ef1..4006d023def 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -1,7 +1,7 @@
 //! Analyze all modules in a project for diagnostics. Exits with a non-zero
 //! status code if any errors are found.
 
-use project_model::{CargoConfig, RustcSource};
+use project_model::{CargoConfig, RustLibSource};
 use rustc_hash::FxHashSet;
 
 use hir::{db::HirDatabase, Crate, Module};
@@ -16,7 +16,7 @@ use crate::cli::{
 impl flags::Diagnostics {
     pub fn run(self) -> anyhow::Result<()> {
         let mut cargo_config = CargoConfig::default();
-        cargo_config.sysroot = Some(RustcSource::Discover);
+        cargo_config.sysroot = Some(RustLibSource::Discover);
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: !self.disable_build_scripts,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs
index 9b5451496c6..7f5d0844967 100644
--- a/crates/rust-analyzer/src/cli/lsif.rs
+++ b/crates/rust-analyzer/src/cli/lsif.rs
@@ -13,7 +13,7 @@ use ide_db::LineIndexDatabase;
 use ide_db::base_db::salsa::{self, ParallelDatabase};
 use ide_db::line_index::WideEncoding;
 use lsp_types::{self, lsif};
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
 use vfs::{AbsPathBuf, Vfs};
 
 use crate::cli::load_cargo::ProcMacroServerChoice;
@@ -290,7 +290,7 @@ impl flags::Lsif {
         eprintln!("Generating LSIF started...");
         let now = Instant::now();
         let mut cargo_config = CargoConfig::default();
-        cargo_config.sysroot = Some(RustcSource::Discover);
+        cargo_config.sysroot = Some(RustLibSource::Discover);
         let no_progress = &|_| ();
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: true,
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index df5c26cf77a..3e5e40750e9 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -15,7 +15,7 @@ use ide::{
     TokenStaticData,
 };
 use ide_db::LineIndexDatabase;
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
 use scip::types as scip_types;
 use std::env;
 
@@ -30,7 +30,7 @@ impl flags::Scip {
         eprintln!("Generating SCIP start...");
         let now = Instant::now();
         let mut cargo_config = CargoConfig::default();
-        cargo_config.sysroot = Some(RustcSource::Discover);
+        cargo_config.sysroot = Some(RustLibSource::Discover);
 
         let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}"));
         let load_cargo_config = LoadCargoConfig {
diff --git a/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs
index 35a874f8920..82a769347df 100644
--- a/crates/rust-analyzer/src/cli/ssr.rs
+++ b/crates/rust-analyzer/src/cli/ssr.rs
@@ -1,7 +1,7 @@
 //! Applies structured search replace rules from the command line.
 
 use ide_ssr::MatchFinder;
-use project_model::{CargoConfig, RustcSource};
+use project_model::{CargoConfig, RustLibSource};
 
 use crate::cli::{
     flags,
@@ -13,7 +13,7 @@ impl flags::Ssr {
     pub fn run(self) -> Result<()> {
         use ide_db::base_db::SourceDatabaseExt;
         let mut cargo_config = CargoConfig::default();
-        cargo_config.sysroot = Some(RustcSource::Discover);
+        cargo_config.sysroot = Some(RustLibSource::Discover);
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 05ad7ab4c4a..c35cce103fa 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -22,7 +22,7 @@ use ide_db::{
 use itertools::Itertools;
 use lsp_types::{ClientCapabilities, MarkupKind};
 use project_model::{
-    CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource,
+    CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
     UnsetTestCrates,
 };
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -1137,16 +1137,16 @@ impl Config {
     pub fn cargo(&self) -> CargoConfig {
         let rustc_source = self.data.rustc_source.as_ref().map(|rustc_src| {
             if rustc_src == "discover" {
-                RustcSource::Discover
+                RustLibSource::Discover
             } else {
-                RustcSource::Path(self.root_path.join(rustc_src))
+                RustLibSource::Path(self.root_path.join(rustc_src))
             }
         });
         let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| {
             if sysroot == "discover" {
-                RustcSource::Discover
+                RustLibSource::Discover
             } else {
-                RustcSource::Path(self.root_path.join(sysroot))
+                RustLibSource::Path(self.root_path.join(sysroot))
             }
         });
         let sysroot_src =
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 30f1c53c198..12e5caf2cc9 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -36,11 +36,41 @@ impl Progress {
 }
 
 impl GlobalState {
-    pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
-        let message = message;
-        self.send_notification::<lsp_types::notification::ShowMessage>(
-            lsp_types::ShowMessageParams { typ, message },
-        )
+    pub(crate) fn show_message(
+        &mut self,
+        typ: lsp_types::MessageType,
+        message: String,
+        show_open_log_button: bool,
+    ) {
+        match self.config.open_server_logs() && show_open_log_button  {
+            true => self.send_request::<lsp_types::request::ShowMessageRequest>(
+                lsp_types::ShowMessageRequestParams {
+                    typ,
+                    message,
+                    actions: Some(vec![lsp_types::MessageActionItem {
+                        title: "Open server logs".to_owned(),
+                        properties: Default::default(),
+                    }]),
+                },
+                |this, resp| {
+                    let lsp_server::Response { error: None, result: Some(result), .. } = resp
+                    else { return };
+                    if let Ok(Some(_item)) = crate::from_json::<
+                        <lsp_types::request::ShowMessageRequest as lsp_types::request::Request>::Result,
+                    >(
+                        lsp_types::request::ShowMessageRequest::METHOD, &result
+                    ) {
+                        this.send_notification::<lsp_ext::OpenServerLogs>(());
+                    }
+                },
+            ),
+            false => self.send_notification::<lsp_types::notification::ShowMessage>(
+                lsp_types::ShowMessageParams {
+                    typ,
+                    message,
+                },
+            ),
+        }
     }
 
     /// Sends a notification to the client containing the error `message`.
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index dd0804b4398..2752e710342 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -406,9 +406,19 @@ impl GlobalState {
 
             if self.config.server_status_notification() {
                 self.send_notification::<lsp_ext::ServerStatusNotification>(status);
-            } else if let (lsp_ext::Health::Error, Some(message)) = (status.health, &status.message)
-            {
-                self.show_and_log_error(message.clone(), None);
+            } else if let (health, Some(message)) = (status.health, &status.message) {
+                let open_log_button = tracing::enabled!(tracing::Level::ERROR)
+                    && (self.fetch_build_data_error().is_err()
+                        || self.fetch_workspace_error().is_err());
+                self.show_message(
+                    match health {
+                        lsp_ext::Health::Ok => lsp_types::MessageType::INFO,
+                        lsp_ext::Health::Warning => lsp_types::MessageType::WARNING,
+                        lsp_ext::Health::Error => lsp_types::MessageType::ERROR,
+                    },
+                    message.clone(),
+                    open_log_button,
+                );
             }
         }
     }
@@ -919,6 +929,7 @@ impl GlobalState {
                                         this.show_message(
                                             lsp_types::MessageType::WARNING,
                                             error.to_string(),
+                                            false,
                                         );
                                     }
                                     this.update_configuration(config);
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 28d37f5685a..1a6e1af2eb7 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -90,38 +90,55 @@ impl GlobalState {
             quiescent: self.is_quiescent(),
             message: None,
         };
+        let mut message = String::new();
 
         if self.proc_macro_changed {
             status.health = lsp_ext::Health::Warning;
-            status.message =
-                Some("Reload required due to source changes of a procedural macro.".into())
+            message.push_str("Reload required due to source changes of a procedural macro.\n\n");
         }
         if let Err(_) = self.fetch_build_data_error() {
             status.health = lsp_ext::Health::Warning;
-            status.message =
-                Some("Failed to run build scripts of some packages, check the logs.".to_string());
+            message.push_str("Failed to run build scripts of some packages.\n\n");
         }
         if !self.config.cargo_autoreload()
             && self.is_quiescent()
             && self.fetch_workspaces_queue.op_requested()
         {
             status.health = lsp_ext::Health::Warning;
-            status.message = Some("Workspace reload required".to_string())
+            message.push_str("Auto-reloading is disabled and the workspace has changed, a manual workspace reload is required.\n\n");
         }
-
-        if let Err(_) = self.fetch_workspace_error() {
-            status.health = lsp_ext::Health::Error;
-            status.message = Some("Failed to load workspaces".to_string())
-        }
-
         if self.config.linked_projects().is_empty()
             && self.config.detached_files().is_empty()
             && self.config.notifications().cargo_toml_not_found
         {
             status.health = lsp_ext::Health::Warning;
-            status.message = Some("Failed to discover workspace".to_string())
+            message.push_str("Failed to discover workspace.\n\n");
+        }
+
+        for ws in self.workspaces.iter() {
+            let (ProjectWorkspace::Cargo { sysroot, .. }
+            | ProjectWorkspace::Json { sysroot, .. }
+            | ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws;
+            if let Err(Some(e)) = sysroot {
+                status.health = lsp_ext::Health::Warning;
+                message.push_str(e);
+                message.push_str("\n\n");
+            }
+            if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws {
+                status.health = lsp_ext::Health::Warning;
+                message.push_str(e);
+                message.push_str("\n\n");
+            }
         }
 
+        if let Err(_) = self.fetch_workspace_error() {
+            status.health = lsp_ext::Health::Error;
+            message.push_str("Failed to load workspaces.\n\n");
+        }
+
+        if !message.is_empty() {
+            status.message = Some(message.trim_end().to_owned());
+        }
         status
     }