diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index f47898b9b2bc..514ba5177e84 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs @@ -44,6 +44,15 @@ impl ops::Index for CargoWorkspace { } } +/// Describes how to set the rustc source directory. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RustcSource { + /// Explicit patch for the rustc source directory. + Path(AbsPathBuf), + /// Try to automatically detect where the rustc source directory is. + Discover, +} + #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CargoConfig { /// Do not activate the `default` feature. @@ -63,8 +72,10 @@ pub struct CargoConfig { /// when debugging isolated issues. pub no_sysroot: bool, - /// rustc private crate source - pub rustc_source: Option, + /// Text-form path to the rustc's private crates source, or "discover" to find them + /// automatically. + /// Keep the root path around so we can reference relative paths to the private crates source. + pub rustc_source: Option, } pub type Package = Idx; diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index d712095a6917..a5b35ed953f4 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs @@ -21,8 +21,8 @@ use rustc_hash::FxHashSet; pub use crate::{ build_data::{BuildDataCollector, BuildDataResult}, cargo_workspace::{ - CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, Target, TargetData, - TargetKind, + CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target, + TargetData, TargetKind, }, project_json::{ProjectJson, ProjectJsonData}, sysroot::Sysroot, diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index ff44dae4a205..3b0ff506d49a 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs @@ -51,11 +51,18 @@ impl Sysroot { pub fn discover(cargo_toml: &AbsPath) -> Result { log::debug!("Discovering sysroot for {}", cargo_toml.display()); let current_dir = cargo_toml.parent().unwrap(); - let sysroot_src_dir = discover_sysroot_src_dir(current_dir)?; + let sysroot_dir = discover_sysroot_dir(current_dir)?; + let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?; let res = Sysroot::load(&sysroot_src_dir)?; Ok(res) } + pub fn discover_rustc(cargo_toml: &AbsPath) -> Option { + log::debug!("Discovering rustc source for {}", cargo_toml.display()); + let current_dir = cargo_toml.parent().unwrap(); + discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) + } + pub fn load(sysroot_src_dir: &AbsPath) -> Result { let mut sysroot = Sysroot { crates: Arena::default() }; @@ -110,7 +117,18 @@ impl Sysroot { } } -fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result { +fn discover_sysroot_dir(current_dir: &AbsPath) -> Result { + let mut rustc = Command::new(toolchain::rustc()); + rustc.current_dir(current_dir).args(&["--print", "sysroot"]); + log::debug!("Discovering sysroot by {:?}", rustc); + let stdout = utf8_stdout(rustc)?; + Ok(AbsPathBuf::assert(PathBuf::from(stdout))) +} + +fn discover_sysroot_src_dir( + sysroot_path: &AbsPathBuf, + current_dir: &AbsPath, +) -> Result { if let Ok(path) = env::var("RUST_SRC_PATH") { let path = AbsPathBuf::try_from(path.as_str()) .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; @@ -122,14 +140,6 @@ fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result { log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); } - let sysroot_path = { - let mut rustc = Command::new(toolchain::rustc()); - rustc.current_dir(current_dir).args(&["--print", "sysroot"]); - log::debug!("Discovering sysroot by {:?}", rustc); - let stdout = utf8_stdout(rustc)?; - AbsPathBuf::assert(PathBuf::from(stdout)) - }; - get_rust_src(&sysroot_path) .or_else(|| { let mut rustup = Command::new(toolchain::rustup()); @@ -149,6 +159,16 @@ try installing the Rust source the same way you installed rustc", }) } +fn get_rustc_src(sysroot_path: &AbsPath) -> Option { + let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml"); + log::debug!("Checking for rustc source code: {}", rustc_src.display()); + if rustc_src.exists() { + Some(rustc_src) + } else { + None + } +} + fn get_rust_src(sysroot_path: &AbsPath) -> Option { // Try the new path first since the old one still exists. let rust_src = sysroot_path.join("lib/rustlib/src/rust"); diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index c3086197678d..0220efdb4e5b 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs @@ -114,6 +114,7 @@ impl ProjectWorkspace { cargo_version ) })?; + let sysroot = if config.no_sysroot { Sysroot::default() } else { @@ -125,7 +126,17 @@ impl ProjectWorkspace { })? }; - let rustc = if let Some(rustc_dir) = &config.rustc_source { + let rustc_dir = if let Some(rustc_source) = &config.rustc_source { + use cargo_workspace::RustcSource; + match rustc_source { + RustcSource::Path(path) => Some(path.clone()), + RustcSource::Discover => Sysroot::discover_rustc(&cargo_toml), + } + } else { + None + }; + + let rustc = if let Some(rustc_dir) = rustc_dir { Some( CargoWorkspace::from_cargo_metadata(&rustc_dir, config, progress) .with_context(|| { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index cc0b22bffb16..f9098968a827 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -18,7 +18,7 @@ use ide_db::helpers::{ }; use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; -use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest}; +use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; use rustc_hash::FxHashSet; use serde::{de::DeserializeOwned, Deserialize}; use vfs::AbsPathBuf; @@ -177,8 +177,9 @@ config_data! { /// tests or binaries.\nFor example, it may be `--release`. runnables_cargoExtraArgs: Vec = "[]", - /// Path to the rust compiler sources, for usage in rustc_private projects. - rustcSource : Option = "null", + /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" + /// to try to automatically find it. + rustcSource : Option = "null", /// Additional arguments to `rustfmt`. rustfmt_extraArgs: Vec = "[]", @@ -473,7 +474,13 @@ impl Config { self.data.cargo_loadOutDirsFromCheck } pub fn cargo(&self) -> CargoConfig { - let rustc_source = self.data.rustcSource.as_ref().map(|it| self.root_path.join(&it)); + let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { + if rustc_src == "discover" { + RustcSource::Discover + } else { + RustcSource::Path(self.root_path.join(rustc_src)) + } + }); CargoConfig { no_default_features: self.data.cargo_noDefaultFeatures, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 55178c84c4cc..f91e04c31d1d 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -105,7 +105,7 @@ [[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`. [[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: - Path to the rust compiler sources, for usage in rustc_private projects. + Path to the rust compiler sources, for usage in rustc_private projects, or "discover" to try to automatically find it. [[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: Additional arguments to `rustfmt`. [[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: diff --git a/editors/code/package.json b/editors/code/package.json index 55825456ec55..defa108cb793 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -707,7 +707,7 @@ } }, "rust-analyzer.rustcSource": { - "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects.", + "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects, or \"discover\" to try to automatically find it.", "default": null, "type": [ "null",