diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index bd2bbadea2..8e690f1125 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -94,9 +94,8 @@ pub struct CargoConfig { pub features: CargoFeatures, /// rustc target pub target: Option, - /// Don't load sysroot crates (`std`, `core` & friends). Might be useful - /// when debugging isolated issues. - pub no_sysroot: bool, + /// Sysroot loading behavior + pub sysroot: Option, /// rustc private crate source pub rustc_source: Option, /// crates to disable `#[cfg(test)]` on diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index f0d76aa922..bc37e3d132 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -67,11 +67,14 @@ impl Sysroot { pub fn crates<'a>(&'a self) -> impl Iterator + ExactSizeIterator + 'a { self.crates.iter().map(|(id, _data)| id) } +} +impl Sysroot { pub fn discover(dir: &AbsPath, extra_env: &FxHashMap) -> Result { tracing::debug!("Discovering sysroot for {}", dir.display()); let sysroot_dir = discover_sysroot_dir(dir, extra_env)?; - let sysroot_src_dir = discover_sysroot_src_dir(&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) } @@ -87,6 +90,14 @@ impl Sysroot { .and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) } + pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result { + 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) + } + pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result { let mut sysroot = Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }; @@ -162,23 +173,28 @@ fn discover_sysroot_dir( Ok(AbsPathBuf::assert(PathBuf::from(stdout))) } -fn discover_sysroot_src_dir( +fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option { + if let Ok(path) = env::var("RUST_SRC_PATH") { + if let Ok(path) = AbsPathBuf::try_from(path.as_str()) { + let core = path.join("core"); + if fs::metadata(&core).is_ok() { + tracing::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display()); + return Some(path); + } + tracing::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); + } else { + tracing::debug!("RUST_SRC_PATH is set, but is invalid, ignoring"); + } + } + + get_rust_src(sysroot_path) +} +fn discover_sysroot_src_dir_or_add_component( sysroot_path: &AbsPathBuf, current_dir: &AbsPath, extra_env: &FxHashMap, ) -> 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()))?; - let core = path.join("core"); - if fs::metadata(&core).is_ok() { - tracing::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display()); - return Ok(path); - } - tracing::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); - } - - get_rust_src(sysroot_path) + discover_sysroot_src_dir(sysroot_path) .or_else(|| { let mut rustup = Command::new(toolchain::rustup()); rustup.envs(extra_env); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index d9d3cab456..72ddf80928 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -188,17 +188,26 @@ impl ProjectWorkspace { })?; let cargo = CargoWorkspace::new(meta); - let sysroot = if config.no_sysroot { - None - } else { - Some(Sysroot::discover(cargo_toml.parent(), &config.extra_env).with_context( - || { + let sysroot = match &config.sysroot { + Some(RustcSource::Path(path)) => { + Some(Sysroot::with_sysroot_dir(path.clone()).with_context(|| { format!( + "Failed to find sysroot for Cargo.toml file {}.", + cargo_toml.display() + ) + })?) + } + 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, }; let rustc_dir = match &config.rustc_source { diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 81c393abdb..01fccc83e8 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}; +use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource}; use rayon::prelude::*; use rustc_hash::FxHashSet; use stdx::format_to; @@ -55,7 +55,10 @@ impl flags::AnalysisStats { }; let mut cargo_config = CargoConfig::default(); - cargo_config.no_sysroot = self.no_sysroot; + cargo_config.sysroot = match self.no_sysroot { + true => None, + false => Some(RustcSource::Discover), + }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: !self.disable_build_scripts, with_proc_macro: !self.disable_proc_macros, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index dfef6f014c..577a8640a4 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -94,8 +94,13 @@ config_data! { cargo_features: CargoFeaturesDef = "[]", /// Whether to pass `--no-default-features` to cargo. cargo_noDefaultFeatures: bool = "false", - /// Internal config for debugging, disables loading of sysroot crates. - cargo_noSysroot: bool = "false", + /// Relative path to the sysroot, or "discover" to try to automatically find it via + /// "rustc --print sysroot". + /// + /// Unsetting this disables sysroot loading. + /// + /// This option does not take effect until rust-analyzer is restarted. + cargo_sysroot: Option = "\"discover\"", /// Compilation target override (target triple). cargo_target: Option = "null", /// Unsets `#[cfg(test)]` for the specified crates. @@ -1030,6 +1035,13 @@ impl Config { RustcSource::Path(self.root_path.join(rustc_src)) } }); + let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| { + if sysroot == "discover" { + RustcSource::Discover + } else { + RustcSource::Path(self.root_path.join(sysroot)) + } + }); CargoConfig { features: match &self.data.cargo_features { @@ -1040,7 +1052,7 @@ impl Config { }, }, target: self.data.cargo_target.clone(), - no_sysroot: self.data.cargo_noSysroot, + sysroot, rustc_source, unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()), wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 9032c21d09..fa55f7d90c 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -59,7 +59,7 @@ use std::collections::Spam; "#, ) .with_config(serde_json::json!({ - "cargo": { "noSysroot": false } + "cargo": { "sysroot": "discover" } })) .server() .wait_until_workspace_is_loaded(); @@ -614,7 +614,7 @@ fn main() {{}} librs, libs )) .with_config(serde_json::json!({ - "cargo": { "noSysroot": false } + "cargo": { "sysroot": "discover" } })) .server() .wait_until_workspace_is_loaded(); @@ -742,7 +742,7 @@ fn main() { "buildScripts": { "enable": true }, - "noSysroot": true, + "sysroot": null, } })) .server() @@ -900,7 +900,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { "buildScripts": { "enable": true }, - "noSysroot": true, + "sysroot": null, }, "procMacro": { "enable": true, diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 4fa88c3c6d..7257445dab 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -34,7 +34,7 @@ impl<'a> Project<'a> { config: serde_json::json!({ "cargo": { // Loading standard library is costly, let's ignore it by default - "noSysroot": true, + "sysroot": null, // Can't use test binary as rustc wrapper. "buildScripts": { "useRustcWrapper": false diff --git a/docs/dev/README.md b/docs/dev/README.md index c7f152acc2..4ac75b4bbf 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -98,7 +98,7 @@ After I am done with the fix, I use `cargo xtask install --client` to try the ne If I need to fix something in the `rust-analyzer` crate, I feel sad because it's on the boundary between the two processes, and working there is slow. I usually just `cargo xtask install --server` and poke changes from my live environment. Note that this uses `--release`, which is usually faster overall, because loading stdlib into debug version of rust-analyzer takes a lot of time. -To speed things up, sometimes I open a temporary hello-world project which has `"rust-analyzer.cargo.noSysroot": true` in `.code/settings.json`. +To speed things up, sometimes I open a temporary hello-world project which has `"rust-analyzer.cargo.sysroot": null` in `.code/settings.json`. This flag causes rust-analyzer to skip loading the sysroot, which greatly reduces the amount of things rust-analyzer needs to do, and makes printf's more useful. Note that you should only use the `eprint!` family of macros for debugging: stdout is used for LSP communication, and `print!` would break it. diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index a34f4d5093..acf0aaea85 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -64,10 +64,15 @@ Set this to `"all"` to pass `--all-features` to cargo. -- Whether to pass `--no-default-features` to cargo. -- -[[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`):: +[[rust-analyzer.cargo.sysroot]]rust-analyzer.cargo.sysroot (default: `"discover"`):: + -- -Internal config for debugging, disables loading of sysroot crates. +Relative path to the sysroot, or "discover" to try to automatically find it via +"rustc --print sysroot". + +Unsetting this disables sysroot loading. + +This option does not take effect until rust-analyzer is restarted. -- [[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: + diff --git a/editors/code/package.json b/editors/code/package.json index f8eec9f62e..f1dd3aa79f 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -468,10 +468,13 @@ "default": false, "type": "boolean" }, - "rust-analyzer.cargo.noSysroot": { - "markdownDescription": "Internal config for debugging, disables loading of sysroot crates.", - "default": false, - "type": "boolean" + "rust-analyzer.cargo.sysroot": { + "markdownDescription": "Relative path to the sysroot, or \"discover\" to try to automatically find it via\n\"rustc --print sysroot\".\n\nUnsetting this disables sysroot loading.\n\nThis option does not take effect until rust-analyzer is restarted.", + "default": "discover", + "type": [ + "null", + "string" + ] }, "rust-analyzer.cargo.target": { "markdownDescription": "Compilation target override (target triple).",