From b2598f4801364216e452c0eaef32931d49f38514 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 27 Jan 2023 13:49:28 +0100 Subject: [PATCH] Don't fail workspace loading if sysroot can't be found --- crates/project-model/src/sysroot.rs | 16 +-- crates/project-model/src/tests.rs | 2 +- crates/project-model/src/workspace.rs | 149 ++++++++++++++++++-------- crates/rust-analyzer/src/reload.rs | 33 +----- 4 files changed, 121 insertions(+), 79 deletions(-) diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 046786580e..e1dde12bad 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -70,6 +70,10 @@ impl Sysroot { pub fn crates(&self) -> impl Iterator + ExactSizeIterator + '_ { self.crates.iter().map(|(id, _data)| id) } + + pub fn is_empty(&self) -> bool { + self.crates.is_empty() + } } impl Sysroot { @@ -79,8 +83,7 @@ impl Sysroot { let sysroot_dir = discover_sysroot_dir(dir, extra_env)?; let sysroot_src_dir = discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?; - let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; - Ok(res) + Ok(Sysroot::load(sysroot_dir, sysroot_src_dir)) } pub fn discover_rustc( @@ -97,11 +100,10 @@ impl Sysroot { let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| { format_err!("can't load standard library from sysroot {}", sysroot_dir.display()) })?; - let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; - Ok(res) + Ok(Sysroot::load(sysroot_dir, sysroot_src_dir)) } - pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result { + pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Sysroot { let mut sysroot = Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }; @@ -152,14 +154,14 @@ impl Sysroot { } else { "" }; - anyhow::bail!( + tracing::error!( "could not find libcore in sysroot path `{}`{}", sysroot.src_root.as_path().display(), var_note, ); } - Ok(sysroot) + sysroot } fn by_name(&self, name: &str) -> Option { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 19ee685691..505b660f34 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -81,7 +81,7 @@ fn get_fake_sysroot() -> Sysroot { // fake sysroot, so we give them both the same path: let sysroot_dir = AbsPathBuf::assert(sysroot_path); let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(sysroot_dir, sysroot_src_dir).unwrap() + Sysroot::load(sysroot_dir, sysroot_src_dir) } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 755bf95199..d784d3d0e9 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -63,7 +63,7 @@ pub struct PackageRoot { pub exclude: Vec, } -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone)] pub enum ProjectWorkspace { /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. Cargo { @@ -83,7 +83,6 @@ pub enum ProjectWorkspace { }, /// Project workspace was manually specified using a `rust-project.json` file. Json { project: ProjectJson, sysroot: Option, rustc_cfg: Vec }, - // 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. @@ -163,7 +162,7 @@ impl ProjectWorkspace { project_json, config.target.as_deref(), &config.extra_env, - )? + ) } ProjectManifest::CargoToml(cargo_toml) => { let cargo_version = utf8_stdout({ @@ -193,20 +192,27 @@ impl ProjectWorkspace { let sysroot = match &config.sysroot { Some(RustcSource::Path(path)) => { - Some(Sysroot::with_sysroot_dir(path.clone()).with_context(|| { - format!("Failed to find sysroot at {}.", path.display()) - })?) + 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) => { + 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(RustcSource::Discover) => Some( - Sysroot::discover(cargo_toml.parent(), &config.extra_env).with_context( - || { - format!( - "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", - cargo_toml.display() - ) - }, - )?, - ), None => None, }; if let Some(sysroot) = &sysroot { @@ -225,18 +231,22 @@ impl ProjectWorkspace { } let rustc = match rustc_dir { - Some(rustc_dir) => Some({ - let meta = CargoWorkspace::fetch_metadata( - &rustc_dir, - cargo_toml.parent(), - config, - progress, - ) - .with_context(|| { - "Failed to read Cargo metadata for Rust sources".to_string() - })?; - CargoWorkspace::new(meta) - }), + Some(rustc_dir) => match CargoWorkspace::fetch_metadata( + &rustc_dir, + cargo_toml.parent(), + config, + progress, + ) { + Ok(meta) => Some(CargoWorkspace::new(meta)), + Err(e) => { + tracing::error!( + %e, + "Failed to read Cargo metadata from rustc source at {}", + rustc_dir.display() + ); + None + } + }, None => None, }; @@ -272,15 +282,14 @@ impl ProjectWorkspace { project_json: ProjectJson, target: Option<&str>, extra_env: &FxHashMap, - ) -> Result { + ) -> 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)) => Some(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)?) + Some(Sysroot::load(sysroot, sysroot_src)) } (None, Some(sysroot_src)) => { // assume sysroot is structured like rustup's and guess `sysroot` @@ -288,7 +297,7 @@ impl ProjectWorkspace { for _ in 0..5 { sysroot.pop(); } - Some(Sysroot::load(sysroot, sysroot_src)?) + Some(Sysroot::load(sysroot, sysroot_src)) } (None, None) => None, }; @@ -297,7 +306,7 @@ impl ProjectWorkspace { } let rustc_cfg = rustc_cfg::get(None, target, extra_env); - Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) + ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg } } pub fn load_detached_files( @@ -305,18 +314,29 @@ impl ProjectWorkspace { config: &CargoConfig, ) -> Result { let sysroot = match &config.sysroot { - Some(RustcSource::Path(path)) => Some( - Sysroot::with_sysroot_dir(path.clone()) - .with_context(|| format!("Failed to find sysroot at {}.", path.display()))?, - ), + 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) => { let dir = &detached_files .first() .and_then(|it| it.parent()) .ok_or_else(|| format_err!("No detached files to load"))?; - Some(Sysroot::discover(dir, &config.extra_env).with_context(|| { - format!("Failed to find sysroot in {}. Is rust-src installed?", dir.display()) - })?) + 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 + } + } } None => None, }; @@ -541,7 +561,7 @@ impl ProjectWorkspace { load_proc_macro, load, project, - sysroot, + sysroot.as_ref(), extra_env, Err("rust-project.json projects have no target layout set".into()), ), @@ -585,6 +605,49 @@ impl ProjectWorkspace { } crate_graph } + + pub fn eq_ignore_build_data(&self, other: &Self) -> bool { + match (self, other) { + ( + Self::Cargo { + cargo, + sysroot, + rustc, + rustc_cfg, + cfg_overrides, + toolchain, + build_scripts: _, + target_layout: _, + }, + Self::Cargo { + cargo: o_cargo, + sysroot: o_sysroot, + rustc: o_rustc, + rustc_cfg: o_rustc_cfg, + cfg_overrides: o_cfg_overrides, + toolchain: o_toolchain, + build_scripts: _, + target_layout: _, + }, + ) => { + cargo == o_cargo + && rustc == o_rustc + && rustc_cfg == o_rustc_cfg + && cfg_overrides == o_cfg_overrides + && toolchain == o_toolchain + && sysroot == o_sysroot + } + ( + Self::Json { project, sysroot, rustc_cfg }, + Self::Json { project: o_project, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg }, + ) => project == o_project && rustc_cfg == o_rustc_cfg && sysroot == o_sysroot, + ( + Self::DetachedFiles { files, sysroot, rustc_cfg }, + Self::DetachedFiles { files: o_files, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg }, + ) => files == o_files && sysroot == o_sysroot && rustc_cfg == o_rustc_cfg, + _ => false, + } + } } fn project_json_to_crate_graph( @@ -592,7 +655,7 @@ fn project_json_to_crate_graph( load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, project: &ProjectJson, - sysroot: &Option, + sysroot: Option<&Sysroot>, extra_env: &FxHashMap, target_layout: TargetLayoutLoadResult, ) -> CrateGraph { diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 3d7342d191..a33b8e14cf 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -148,11 +148,11 @@ impl GlobalState { ) } LinkedProject::InlineJsonProject(it) => { - project_model::ProjectWorkspace::load_inline( + Ok(project_model::ProjectWorkspace::load_inline( it.clone(), cargo_config.target.as_deref(), &cargo_config.extra_env, - ) + )) } }) .collect::>(); @@ -212,35 +212,11 @@ impl GlobalState { let workspaces = workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::>(); - fn eq_ignore_build_data<'a>( - left: &'a ProjectWorkspace, - right: &'a ProjectWorkspace, - ) -> bool { - let key = |p: &'a ProjectWorkspace| match p { - ProjectWorkspace::Cargo { - cargo, - sysroot, - rustc, - rustc_cfg, - cfg_overrides, - - build_scripts: _, - toolchain: _, - target_layout: _, - } => Some((cargo, sysroot, rustc, rustc_cfg, cfg_overrides)), - _ => None, - }; - match (key(left), key(right)) { - (Some(lk), Some(rk)) => lk == rk, - _ => left == right, - } - } - let same_workspaces = workspaces.len() == self.workspaces.len() && workspaces .iter() .zip(self.workspaces.iter()) - .all(|(l, r)| eq_ignore_build_data(l, r)); + .all(|(l, r)| l.eq_ignore_build_data(r)); if same_workspaces { let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result(); @@ -270,7 +246,8 @@ impl GlobalState { // Here, we completely changed the workspace (Cargo.toml edit), so // we don't care about build-script results, they are stale. - self.workspaces = Arc::new(workspaces) + // FIXME: can we abort the build scripts here? + self.workspaces = Arc::new(workspaces); } if let FilesWatcher::Client = self.config.files().watcher {