Repository: rust-lang/rust-analyzer
Pull request #18785 (2024-12-29 12:11:53)

Cleanup toolchain info fetching


Patch:
diff --git a/clippy.toml b/clippy.toml
index 8032c775ab..1046cb3d56 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -3,3 +3,7 @@ disallowed-types = [
     { path = "std::collections::HashSet", reason = "use FxHashSet" },
     { path = "std::collections::hash_map::RandomState", reason = "use BuildHasherDefault<FxHasher>"}
 ]
+
+disallowed-methods = [
+    { path = "std::process::Command::new", reason = "use `toolchain::command` instead as it forces the choice of a working directory" },
+]
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 5d19b849a7..318a8d5aa1 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -38,7 +38,7 @@ use crate::{
         attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id},
         diagnostics::DefDiagnostic,
         mod_resolution::ModDir,
-        path_resolution::ReachedFixedPoint,
+        path_resolution::{ReachedFixedPoint, ResolvePathResultPrefixInfo},
         proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
         sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
         ResolveMode,
@@ -797,7 +797,7 @@ impl DefCollector<'_> {
             return PartialResolvedImport::Unresolved;
         }
 
-        if res.prefix_info.differing_crate {
+        if let ResolvePathResultPrefixInfo::DifferingCrate = res.prefix_info {
             return PartialResolvedImport::Resolved(
                 def.filter_visibility(|v| matches!(v, Visibility::Public)),
             );
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index 47c08d3d1d..9573697a87 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -46,11 +46,12 @@ pub(super) struct ResolvePathResult {
     pub(super) prefix_info: ResolvePathResultPrefixInfo,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub struct ResolvePathResultPrefixInfo {
-    pub(crate) differing_crate: bool,
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ResolvePathResultPrefixInfo {
+    None,
+    DifferingCrate,
     /// Path of the form `Enum::Variant` (and not `Variant` alone).
-    pub enum_variant: bool,
+    Enum,
 }
 
 impl ResolvePathResult {
@@ -59,7 +60,7 @@ impl ResolvePathResult {
             PerNs::none(),
             reached_fixedpoint,
             None,
-            ResolvePathResultPrefixInfo::default(),
+            ResolvePathResultPrefixInfo::None,
         )
     }
 
@@ -169,8 +170,17 @@ impl DefMap {
             if result.reached_fixedpoint == ReachedFixedPoint::No {
                 result.reached_fixedpoint = new.reached_fixedpoint;
             }
-            result.prefix_info.differing_crate |= new.prefix_info.differing_crate;
-            result.prefix_info.enum_variant |= new.prefix_info.enum_variant;
+            result.prefix_info = match (result.prefix_info, new.prefix_info) {
+                (ResolvePathResultPrefixInfo::None, it) => it,
+                (ResolvePathResultPrefixInfo::DifferingCrate, _) => {
+                    ResolvePathResultPrefixInfo::DifferingCrate
+                }
+                (
+                    ResolvePathResultPrefixInfo::Enum,
+                    ResolvePathResultPrefixInfo::DifferingCrate,
+                ) => ResolvePathResultPrefixInfo::DifferingCrate,
+                (ResolvePathResultPrefixInfo::Enum, _) => ResolvePathResultPrefixInfo::Enum,
+            };
             result.segment_index = match (result.segment_index, new.segment_index) {
                 (Some(idx), None) => Some(idx),
                 (Some(old), Some(new)) => Some(old.max(new)),
@@ -450,22 +460,13 @@ impl DefMap {
                         // because `macro_use` and other preludes should be taken into account. At
                         // this point, we know we're resolving a multi-segment path so macro kind
                         // expectation is discarded.
-                        let resolution = defp_map.resolve_path_fp_with_macro(
-                            db,
-                            ResolveMode::Other,
-                            module.local_id,
-                            &path,
-                            shadow,
-                            None,
-                        );
+                        let (def, s) =
+                            defp_map.resolve_path(db, module.local_id, &path, shadow, None);
                         return ResolvePathResult::new(
-                            resolution.resolved_def,
+                            def,
                             ReachedFixedPoint::Yes,
-                            resolution.segment_index.map(|s| s + i),
-                            ResolvePathResultPrefixInfo {
-                                differing_crate: true,
-                                enum_variant: resolution.prefix_info.enum_variant,
-                            },
+                            s.map(|s| s + i),
+                            ResolvePathResultPrefixInfo::DifferingCrate,
                         );
                     }
 
@@ -521,10 +522,7 @@ impl DefMap {
                                     res,
                                     ReachedFixedPoint::Yes,
                                     None,
-                                    ResolvePathResultPrefixInfo {
-                                        enum_variant: true,
-                                        ..ResolvePathResultPrefixInfo::default()
-                                    },
+                                    ResolvePathResultPrefixInfo::Enum,
                                 )
                             }
                         }
@@ -532,7 +530,7 @@ impl DefMap {
                             PerNs::types(e.into(), curr.vis, curr.import),
                             ReachedFixedPoint::Yes,
                             Some(i),
-                            ResolvePathResultPrefixInfo::default(),
+                            ResolvePathResultPrefixInfo::None,
                         ),
                     };
                 }
@@ -549,7 +547,7 @@ impl DefMap {
                         PerNs::types(s, curr.vis, curr.import),
                         ReachedFixedPoint::Yes,
                         Some(i),
-                        ResolvePathResultPrefixInfo::default(),
+                        ResolvePathResultPrefixInfo::None,
                     );
                 }
             };
@@ -562,7 +560,7 @@ impl DefMap {
             curr_per_ns,
             ReachedFixedPoint::Yes,
             None,
-            ResolvePathResultPrefixInfo::default(),
+            ResolvePathResultPrefixInfo::None,
         )
     }
 
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 82da57a9bb..4f15c8091c 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -293,7 +293,7 @@ impl Resolver {
                         },
                         None,
                     ),
-                    ResolvePathResultPrefixInfo::default(),
+                    ResolvePathResultPrefixInfo::None,
                 ))
             }
             Path::LangItem(l, Some(_)) => {
@@ -310,7 +310,7 @@ impl Resolver {
                 };
                 return Some((
                     ResolveValueResult::Partial(type_ns, 1, None),
-                    ResolvePathResultPrefixInfo::default(),
+                    ResolvePathResultPrefixInfo::None,
                 ));
             }
         };
@@ -346,7 +346,7 @@ impl Resolver {
                                     ValueNs::LocalBinding(e.binding()),
                                     None,
                                 ),
-                                ResolvePathResultPrefixInfo::default(),
+                                ResolvePathResultPrefixInfo::None,
                             ));
                         }
                     }
@@ -370,7 +370,7 @@ impl Resolver {
                             let val = ValueNs::GenericParam(id);
                             return Some((
                                 ResolveValueResult::ValueNs(val, None),
-                                ResolvePathResultPrefixInfo::default(),
+                                ResolvePathResultPrefixInfo::None,
                             ));
                         }
                     }
@@ -378,7 +378,7 @@ impl Resolver {
                         if *first_name == sym::Self_.clone() {
                             return Some((
                                 ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None),
-                                ResolvePathResultPrefixInfo::default(),
+                                ResolvePathResultPrefixInfo::None,
                             ));
                         }
                     }
@@ -400,7 +400,7 @@ impl Resolver {
                             let ty = TypeNs::GenericParam(id);
                             return Some((
                                 ResolveValueResult::Partial(ty, 1, None),
-                                ResolvePathResultPrefixInfo::default(),
+                                ResolvePathResultPrefixInfo::None,
                             ));
                         }
                     }
@@ -408,7 +408,7 @@ impl Resolver {
                         if *first_name == sym::Self_.clone() {
                             return Some((
                                 ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None),
-                                ResolvePathResultPrefixInfo::default(),
+                                ResolvePathResultPrefixInfo::None,
                             ));
                         }
                     }
@@ -417,7 +417,7 @@ impl Resolver {
                             let ty = TypeNs::AdtSelfType(*adt);
                             return Some((
                                 ResolveValueResult::Partial(ty, 1, None),
-                                ResolvePathResultPrefixInfo::default(),
+                                ResolvePathResultPrefixInfo::None,
                             ));
                         }
                     }
@@ -441,7 +441,7 @@ impl Resolver {
             if let Some(builtin) = BuiltinType::by_name(first_name) {
                 return Some((
                     ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None),
-                    ResolvePathResultPrefixInfo::default(),
+                    ResolvePathResultPrefixInfo::None,
                 ));
             }
         }
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 7a74694887..4bfa51c849 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -32,7 +32,7 @@ use hir_def::{
         WherePredicateTypeTarget,
     },
     lang_item::LangItem,
-    nameres::MacroSubNs,
+    nameres::{MacroSubNs, ResolvePathResultPrefixInfo},
     path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
     resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs},
     type_ref::{
@@ -826,7 +826,7 @@ impl<'a> TyLoweringContext<'a> {
                 (segments.take(unresolved_segment - 1), None)
             }
             ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
-                if prefix_info.enum_variant =>
+                if prefix_info == ResolvePathResultPrefixInfo::Enum =>
             {
                 (segments.strip_last_two(), segments.len().checked_sub(2))
             }
diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index 2b59c1a22f..a6d2ed3223 100644
--- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -575,31 +575,6 @@ fn bar() {
                 // ^^^^^^ 💡 error: generic arguments are not allowed on modules
     let _: &foo::<()>::Trait;
             // ^^^^^^ 💡 error: generic arguments are not allowed on modules
-}
-        "#,
-        );
-    }
-
-    #[test]
-    fn regression_18768() {
-        check_diagnostics(
-            r#"
-//- minicore: result
-//- /foo.rs crate:foo edition:2018
-pub mod lib {
-    mod core {
-        pub use core::*;
-    }
-    pub use self::core::result;
-}
-
-pub mod __private {
-    pub use crate::lib::result::Result::{self, Err, Ok};
-}
-
-//- /bar.rs crate:bar deps:foo edition:2018
-fn bar() {
-    _ = foo::__private::Result::<(), ()>::Ok;
 }
         "#,
         );
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 10a73edd51..f642db6a71 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -253,6 +253,7 @@ fn _format(
     let &crate_id = db.relevant_crates(file_id).iter().next()?;
     let edition = db.crate_graph()[crate_id].edition;
 
+    #[allow(clippy::disallowed_methods)]
     let mut cmd = std::process::Command::new(toolchain::Tool::Rustfmt.path());
     cmd.arg("--edition");
     cmd.arg(edition.to_string());
diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs
index 4045e25fdf..4d62efdd6b 100644
--- a/crates/proc-macro-api/src/process.rs
+++ b/crates/proc-macro-api/src/process.rs
@@ -202,6 +202,7 @@ fn mk_child(
     env: impl IntoIterator<Item = (impl AsRef<std::ffi::OsStr>, impl AsRef<std::ffi::OsStr>)>,
     null_stderr: bool,
 ) -> io::Result<Child> {
+    #[allow(clippy::disallowed_methods)]
     let mut cmd = Command::new(path);
     cmd.envs(env)
         .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
diff --git a/crates/proc-macro-srv/build.rs b/crates/proc-macro-srv/build.rs
index 9a17cfc9f3..07a10aaae5 100644
--- a/crates/proc-macro-srv/build.rs
+++ b/crates/proc-macro-srv/build.rs
@@ -7,6 +7,7 @@ fn main() {
     println!("cargo::rustc-check-cfg=cfg(rust_analyzer)");
 
     let rustc = env::var("RUSTC").expect("proc-macro-srv's build script expects RUSTC to be set");
+    #[allow(clippy::disallowed_methods)]
     let output = Command::new(rustc).arg("--version").output().expect("rustc --version must run");
     let version_string = std::str::from_utf8(&output.stdout[..])
         .expect("rustc --version output must be UTF-8")
diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs
index 524323b973..b0939229f9 100644
--- a/crates/project-model/src/build_dependencies.rs
+++ b/crates/project-model/src/build_dependencies.rs
@@ -172,19 +172,18 @@ impl WorkspaceBuildScripts {
         }
         let res = (|| {
             let target_libdir = (|| {
-                let mut cargo_config = sysroot.tool(Tool::Cargo);
+                let mut cargo_config = sysroot.tool(Tool::Cargo, current_dir);
                 cargo_config.envs(extra_env);
                 cargo_config
-                    .current_dir(current_dir)
                     .args(["rustc", "-Z", "unstable-options", "--print", "target-libdir"])
                     .env("RUSTC_BOOTSTRAP", "1");
-                if let Ok(it) = utf8_stdout(cargo_config) {
+                if let Ok(it) = utf8_stdout(&mut cargo_config) {
                     return Ok(it);
                 }
-                let mut cmd = sysroot.tool(Tool::Rustc);
+                let mut cmd = sysroot.tool(Tool::Rustc, current_dir);
                 cmd.envs(extra_env);
                 cmd.args(["--print", "target-libdir"]);
-                utf8_stdout(cmd)
+                utf8_stdout(&mut cmd)
             })()?;
 
             let target_libdir = AbsPathBuf::try_from(Utf8PathBuf::from(target_libdir))
@@ -390,12 +389,12 @@ impl WorkspaceBuildScripts {
     ) -> io::Result<Command> {
         let mut cmd = match config.run_build_script_command.as_deref() {
             Some([program, args @ ..]) => {
-                let mut cmd = Command::new(program);
+                let mut cmd = toolchain::command(program, current_dir);
                 cmd.args(args);
                 cmd
             }
             _ => {
-                let mut cmd = sysroot.tool(Tool::Cargo);
+                let mut cmd = sysroot.tool(Tool::Cargo, current_dir);
 
                 cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]);
                 cmd.args(&config.extra_args);
@@ -448,7 +447,6 @@ impl WorkspaceBuildScripts {
             }
         };
 
-        cmd.current_dir(current_dir);
         cmd.envs(&config.extra_env);
         if config.wrap_rustc_in_build_scripts {
             // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index 836879c75b..4ffe3a6265 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -294,7 +294,7 @@ impl CargoWorkspace {
         no_deps: bool,
         progress: &dyn Fn(String),
     ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
-        let cargo = sysroot.tool(Tool::Cargo);
+        let cargo = sysroot.tool(Tool::Cargo, current_dir);
         let mut meta = MetadataCommand::new();
         meta.cargo_path(cargo.get_program());
         cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default()));
diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs
index ff9d2035f6..b4714b764a 100644
--- a/crates/project-model/src/env.rs
+++ b/crates/project-model/src/env.rs
@@ -74,10 +74,9 @@ pub(crate) fn cargo_config_env(
     extra_env: &FxHashMap<String, String>,
     sysroot: &Sysroot,
 ) -> FxHashMap<String, String> {
-    let mut cargo_config = sysroot.tool(Tool::Cargo);
+    let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent());
     cargo_config.envs(extra_env);
     cargo_config
-        .current_dir(manifest.parent())
         .args(["-Z", "unstable-options", "config", "get", "env"])
         .env("RUSTC_BOOTSTRAP", "1");
     if manifest.is_rust_manifest() {
@@ -85,7 +84,7 @@ pub(crate) fn cargo_config_env(
     }
     // if successful we receive `env.key.value = "value" per entry
     tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
-    utf8_stdout(cargo_config)
+    utf8_stdout(&mut cargo_config)
         .map(parse_output_cargo_config_env)
         .inspect(|env| {
             tracing::debug!("Discovered cargo config env: {:?}", env);
diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs
index 9a024f6b96..1913db11fa 100644
--- a/crates/project-model/src/lib.rs
+++ b/crates/project-model/src/lib.rs
@@ -15,15 +15,31 @@
 //!   procedural macros).
 //! * Lowering of concrete model to a [`base_db::CrateGraph`]
 
+pub mod project_json;
+pub mod toolchain_info {
+    pub mod rustc_cfg;
+    pub mod target_data_layout;
+    pub mod target_triple;
+
+    use std::path::Path;
+
+    use crate::{ManifestPath, Sysroot};
+
+    #[derive(Copy, Clone)]
+    pub enum QueryConfig<'a> {
+        /// Directly invoke `rustc` to query the desired information.
+        Rustc(&'a Sysroot, &'a Path),
+        /// Attempt to use cargo to query the desired information, honoring cargo configurations.
+        /// If this fails, falls back to invoking `rustc` directly.
+        Cargo(&'a Sysroot, &'a ManifestPath),
+    }
+}
+
 mod build_dependencies;
 mod cargo_workspace;
 mod env;
 mod manifest_path;
-pub mod project_json;
-mod rustc_cfg;
 mod sysroot;
-pub mod target_data_layout;
-mod target_triple;
 mod workspace;
 
 #[cfg(test)]
@@ -182,7 +198,7 @@ impl fmt::Display for ProjectManifest {
     }
 }
 
-fn utf8_stdout(mut cmd: Command) -> anyhow::Result<String> {
+fn utf8_stdout(cmd: &mut Command) -> anyhow::Result<String> {
     let output = cmd.output().with_context(|| format!("{cmd:?} failed"))?;
     if !output.status.success() {
         match String::from_utf8(output.stderr) {
diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs
deleted file mode 100644
index bc1f0e6fbf..0000000000
--- a/crates/project-model/src/rustc_cfg.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-//! Runs `rustc --print cfg` to get built-in cfg flags.
-
-use anyhow::Context;
-use cfg::CfgAtom;
-use intern::Symbol;
-use rustc_hash::FxHashMap;
-use toolchain::Tool;
-
-use crate::{utf8_stdout, ManifestPath, Sysroot};
-
-/// Determines how `rustc --print cfg` is discovered and invoked.
-pub(crate) enum RustcCfgConfig<'a> {
-    /// Use `rustc --print cfg`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::rustc`].
-    Rustc(&'a Sysroot),
-    /// Use `cargo --print cfg`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::cargo`].
-    Cargo(&'a Sysroot, &'a ManifestPath),
-}
-
-pub(crate) fn get(
-    target: Option<&str>,
-    extra_env: &FxHashMap<String, String>,
-    config: RustcCfgConfig<'_>,
-) -> Vec<CfgAtom> {
-    let _p = tracing::info_span!("rustc_cfg::get").entered();
-    let mut res: Vec<_> = Vec::with_capacity(7 * 2 + 1);
-
-    // Some nightly-only cfgs, which are required for stdlib
-    res.push(CfgAtom::Flag(Symbol::intern("target_thread_local")));
-    for key in ["target_has_atomic", "target_has_atomic_load_store"] {
-        for ty in ["8", "16", "32", "64", "cas", "ptr"] {
-            res.push(CfgAtom::KeyValue { key: Symbol::intern(key), value: Symbol::intern(ty) });
-        }
-        res.push(CfgAtom::Flag(Symbol::intern(key)));
-    }
-
-    let rustc_cfgs = get_rust_cfgs(target, extra_env, config);
-
-    let rustc_cfgs = match rustc_cfgs {
-        Ok(cfgs) => cfgs,
-        Err(e) => {
-            tracing::error!(?e, "failed to get rustc cfgs");
-            return res;
-        }
-    };
-
-    let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::<Result<Vec<_>, _>>();
-
-    match rustc_cfgs {
-        Ok(rustc_cfgs) => {
-            tracing::debug!(?rustc_cfgs, "rustc cfgs found");
-            res.extend(rustc_cfgs);
-        }
-        Err(e) => {
-            tracing::error!(?e, "failed to get rustc cfgs")
-        }
-    }
-
-    res
-}
-
-fn get_rust_cfgs(
-    target: Option<&str>,
-    extra_env: &FxHashMap<String, String>,
-    config: RustcCfgConfig<'_>,
-) -> anyhow::Result<String> {
-    let sysroot = match config {
-        RustcCfgConfig::Cargo(sysroot, cargo_toml) => {
-            let mut cmd = sysroot.tool(Tool::Cargo);
-
-            cmd.envs(extra_env);
-            cmd.current_dir(cargo_toml.parent())
-                .args(["rustc", "-Z", "unstable-options", "--print", "cfg"])
-                .env("RUSTC_BOOTSTRAP", "1");
-            if let Some(target) = target {
-                cmd.args(["--target", target]);
-            }
-
-            match utf8_stdout(cmd) {
-                Ok(it) => return Ok(it),
-                Err(e) => {
-                    tracing::warn!("failed to run `cargo rustc --print cfg`, falling back to invoking rustc directly: {e}");
-                    sysroot
-                }
-            }
-        }
-        RustcCfgConfig::Rustc(sysroot) => sysroot,
-    };
-
-    let mut cmd = sysroot.tool(Tool::Rustc);
-    cmd.envs(extra_env);
-    cmd.args(["--print", "cfg", "-O"]);
-    if let Some(target) = target {
-        cmd.args(["--target", target]);
-    }
-
-    utf8_stdout(cmd).context("unable to fetch cfgs via `rustc --print cfg -O`")
-}
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index d8186a23f7..b0fdd3fa41 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -4,7 +4,7 @@
 //! but we can't process `.rlib` and need source code instead. The source code
 //! is typically installed with `rustup component add rust-src` command.
 
-use std::{env, fs, ops, process::Command};
+use std::{env, fs, ops, path::Path, process::Command};
 
 use anyhow::{format_err, Result};
 use base_db::CrateName;
@@ -170,7 +170,7 @@ impl Sysroot {
     }
 
     /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists.
-    pub fn tool(&self, tool: Tool) -> Command {
+    pub fn tool(&self, tool: Tool, current_dir: impl AsRef<Path>) -> Command {
         match self.root() {
             Some(root) => {
                 // special case rustc, we can look that up directly in the sysroot's bin folder
@@ -179,15 +179,15 @@ impl Sysroot {
                     if let Some(path) =
                         probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into())
                     {
-                        return Command::new(path);
+                        return toolchain::command(path, current_dir);
                     }
                 }
 
-                let mut cmd = Command::new(tool.prefer_proxy());
+                let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir);
                 cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(root));
                 cmd
             }
-            _ => Command::new(tool.path()),
+            _ => toolchain::command(tool.path(), current_dir),
         }
     }
 
@@ -436,11 +436,11 @@ fn discover_sysroot_dir(
     current_dir: &AbsPath,
     extra_env: &FxHashMap<String, String>,
 ) -> Result<AbsPathBuf> {
-    let mut rustc = Command::new(Tool::Rustc.path());
+    let mut rustc = toolchain::command(Tool::Rustc.path(), current_dir);
     rustc.envs(extra_env);
     rustc.current_dir(current_dir).args(["--print", "sysroot"]);
     tracing::debug!("Discovering sysroot by {:?}", rustc);
-    let stdout = utf8_stdout(rustc)?;
+    let stdout = utf8_stdout(&mut rustc)?;
     Ok(AbsPathBuf::assert(Utf8PathBuf::from(stdout)))
 }
 
@@ -468,11 +468,11 @@ fn discover_sysroot_src_dir_or_add_component(
 ) -> Result<AbsPathBuf> {
     discover_sysroot_src_dir(sysroot_path)
         .or_else(|| {
-            let mut rustup = Command::new(Tool::Rustup.prefer_proxy());
+            let mut rustup = toolchain::command(Tool::Rustup.prefer_proxy(), current_dir);
             rustup.envs(extra_env);
-            rustup.current_dir(current_dir).args(["component", "add", "rust-src"]);
+            rustup.args(["component", "add", "rust-src"]);
             tracing::info!("adding rust-src component by {:?}", rustup);
-            utf8_stdout(rustup).ok()?;
+            utf8_stdout(&mut rustup).ok()?;
             get_rust_src(sysroot_path)
         })
         .ok_or_else(|| {
diff --git a/crates/project-model/src/target_data_layout.rs b/crates/project-model/src/target_data_layout.rs
deleted file mode 100644
index 8a8a2d3255..0000000000
--- a/crates/project-model/src/target_data_layout.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-//! Runs `rustc --print target-spec-json` to get the target_data_layout.
-
-use rustc_hash::FxHashMap;
-use toolchain::Tool;
-
-use crate::{utf8_stdout, ManifestPath, Sysroot};
-
-/// Determines how `rustc --print target-spec-json` is discovered and invoked.
-pub enum RustcDataLayoutConfig<'a> {
-    /// Use `rustc --print target-spec-json`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::rustc`].
-    Rustc(&'a Sysroot),
-    /// Use `cargo --print target-spec-json`, either from with the binary from the sysroot or by discovering via
-    /// [`toolchain::cargo`].
-    Cargo(&'a Sysroot, &'a ManifestPath),
-}
-
-pub fn get(
-    config: RustcDataLayoutConfig<'_>,
-    target: Option<&str>,
-    extra_env: &FxHashMap<String, String>,
-) -> anyhow::Result<String> {
-    let process = |output: String| {
-        (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))()
-            .ok_or_else(|| {
-                anyhow::format_err!("could not fetch target-spec-json from command output")
-            })
-    };
-    let sysroot = match config {
-        RustcDataLayoutConfig::Cargo(sysroot, cargo_toml) => {
-            let mut cmd = sysroot.tool(Tool::Cargo);
-            cmd.envs(extra_env);
-            cmd.current_dir(cargo_toml.parent())
-                .args([
-                    "rustc",
-                    "-Z",
-                    "unstable-options",
-                    "--print",
-                    "target-spec-json",
-                    "--",
-                    "-Z",
-                    "unstable-options",
-                ])
-                .env("RUSTC_BOOTSTRAP", "1");
-            if let Some(target) = target {
-                cmd.args(["--target", target]);
-            }
-            match utf8_stdout(cmd) {
-                Ok(output) => return process(output),
-                Err(e) => {
-                    tracing::warn!("failed to run `cargo rustc --print target-spec-json`, falling back to invoking rustc directly: {e}");
-                    sysroot
-                }
-            }
-        }
-        RustcDataLayoutConfig::Rustc(sysroot) => sysroot,
-    };
-
-    let mut cmd = Sysroot::tool(sysroot, Tool::Rustc);
-    cmd.envs(extra_env)
-        .args(["-Z", "unstable-options", "--print", "target-spec-json"])
-        .env("RUSTC_BOOTSTRAP", "1");
-    if let Some(target) = target {
-        cmd.args(["--target", target]);
-    }
-    process(utf8_stdout(cmd)?)
-}
diff --git a/crates/project-model/src/toolchain_info/rustc_cfg.rs b/crates/project-model/src/toolchain_info/rustc_cfg.rs
new file mode 100644
index 0000000000..12e674a6c4
--- /dev/null
+++ b/crates/project-model/src/toolchain_info/rustc_cfg.rs
@@ -0,0 +1,78 @@
+//! Get the built-in cfg flags for the to be compile platform.
+
+use anyhow::Context;
+use cfg::CfgAtom;
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{toolchain_info::QueryConfig, utf8_stdout};
+
+/// Uses `rustc --print cfg` to fetch the builtin cfgs.
+pub fn get(
+    config: QueryConfig<'_>,
+    target: Option<&str>,
+    extra_env: &FxHashMap<String, String>,
+) -> Vec<CfgAtom> {
+    let _p = tracing::info_span!("rustc_cfg::get").entered();
+
+    let rustc_cfgs = rustc_print_cfg(target, extra_env, config);
+    let rustc_cfgs = match rustc_cfgs {
+        Ok(cfgs) => cfgs,
+        Err(e) => {
+            tracing::error!(?e, "failed to get rustc cfgs");
+            return vec![];
+        }
+    };
+
+    let rustc_cfgs = rustc_cfgs.lines().map(crate::parse_cfg).collect::<Result<Vec<_>, _>>();
+    match rustc_cfgs {
+        Ok(rustc_cfgs) => {
+            tracing::debug!(?rustc_cfgs, "rustc cfgs found");
+            rustc_cfgs
+        }
+        Err(e) => {
+            tracing::error!(?e, "failed to parse rustc cfgs");
+            vec![]
+        }
+    }
+}
+
+fn rustc_print_cfg(
+    target: Option<&str>,
+    extra_env: &FxHashMap<String, String>,
+    config: QueryConfig<'_>,
+) -> anyhow::Result<String> {
+    const RUSTC_ARGS: [&str; 3] = ["--print", "cfg", "-O"];
+    let (sysroot, current_dir) = match config {
+        QueryConfig::Cargo(sysroot, cargo_toml) => {
+            let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
+            cmd.envs(extra_env);
+            cmd.env("RUSTC_BOOTSTRAP", "1");
+            cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
+            if let Some(target) = target {
+                cmd.args(["--target", target]);
+            }
+
+            match utf8_stdout(&mut cmd) {
+                Ok(it) => return Ok(it),
+                Err(e) => {
+                    tracing::warn!(
+                        %e,
+                        "failed to run `{cmd:?}`, falling back to invoking rustc directly"
+                    );
+                    (sysroot, cargo_toml.parent().as_ref())
+                }
+            }
+        }
+        QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir),
+    };
+
+    let mut cmd = sysroot.tool(Tool::Rustc, current_dir);
+    cmd.envs(extra_env);
+    cmd.args(RUSTC_ARGS);
+    if let Some(target) = target {
+        cmd.args(["--target", target]);
+    }
+
+    utf8_stdout(&mut cmd).with_context(|| format!("unable to fetch cfgs via `{cmd:?}`"))
+}
diff --git a/crates/project-model/src/toolchain_info/target_data_layout.rs b/crates/project-model/src/toolchain_info/target_data_layout.rs
new file mode 100644
index 0000000000..9986c66131
--- /dev/null
+++ b/crates/project-model/src/toolchain_info/target_data_layout.rs
@@ -0,0 +1,57 @@
+//! Runs `rustc --print target-spec-json` to get the target_data_layout.
+
+use anyhow::Context;
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{toolchain_info::QueryConfig, utf8_stdout, Sysroot};
+
+/// Uses `rustc --print target-spec-json`.
+pub fn get(
+    config: QueryConfig<'_>,
+    target: Option<&str>,
+    extra_env: &FxHashMap<String, String>,
+) -> anyhow::Result<String> {
+    const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"];
+    let process = |output: String| {
+        (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))()
+            .ok_or_else(|| {
+                anyhow::format_err!("could not parse target-spec-json from command output")
+            })
+    };
+    let (sysroot, current_dir) = match config {
+        QueryConfig::Cargo(sysroot, cargo_toml) => {
+            let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
+            cmd.envs(extra_env);
+            cmd.env("RUSTC_BOOTSTRAP", "1");
+            cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
+                "--",
+                "-Z",
+                "unstable-options",
+            ]);
+            if let Some(target) = target {
+                cmd.args(["--target", target]);
+            }
+            match utf8_stdout(&mut cmd) {
+                Ok(output) => return process(output),
+                Err(e) => {
+                    tracing::warn!(%e, "failed to run `{cmd:?}`, falling back to invoking rustc directly");
+                    (sysroot, cargo_toml.parent().as_ref())
+                }
+            }
+        }
+        QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir),
+    };
+
+    let mut cmd = Sysroot::tool(sysroot, Tool::Rustc, current_dir);
+    cmd.envs(extra_env)
+        .env("RUSTC_BOOTSTRAP", "1")
+        .args(["-Z", "unstable-options"])
+        .args(RUSTC_ARGS);
+    if let Some(target) = target {
+        cmd.args(["--target", target]);
+    }
+    utf8_stdout(&mut cmd)
+        .with_context(|| format!("unable to fetch target-data-layout via `{cmd:?}`"))
+        .and_then(process)
+}
diff --git a/crates/project-model/src/target_triple.rs b/crates/project-model/src/toolchain_info/target_triple.rs
similarity index 53%
rename from crates/project-model/src/target_triple.rs
rename to crates/project-model/src/toolchain_info/target_triple.rs
index 4a32212097..163884e5e8 100644
--- a/crates/project-model/src/target_triple.rs
+++ b/crates/project-model/src/toolchain_info/target_triple.rs
@@ -1,46 +1,46 @@
-//! Runs `rustc --print -vV` to get the host target.
+//! Functionality to discover the current build target(s).
+use std::path::Path;
+
 use anyhow::Context;
 use rustc_hash::FxHashMap;
 use toolchain::Tool;
 
-use crate::{utf8_stdout, ManifestPath, Sysroot};
-
-pub(super) enum TargetTipleConfig<'a> {
-    #[expect(dead_code)]
-    Rustc(&'a Sysroot),
-    Cargo(&'a Sysroot, &'a ManifestPath),
-}
+use crate::{toolchain_info::QueryConfig, utf8_stdout, ManifestPath, Sysroot};
 
-pub(super) fn get(
-    config: TargetTipleConfig<'_>,
+/// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
+/// For rustc, runs `rustc --print -vV` to get the host target.
+pub fn get(
+    config: QueryConfig<'_>,
     target: Option<&str>,
     extra_env: &FxHashMap<String, String>,
 ) -> anyhow::Result<Vec<String>> {
+    let _p = tracing::info_span!("target_triple::get").entered();
     if let Some(target) = target {
         return Ok(vec![target.to_owned()]);
     }
 
-    let sysroot = match config {
-        TargetTipleConfig::Cargo(sysroot, cargo_toml) => {
+    let (sysroot, current_dir) = match config {
+        QueryConfig::Cargo(sysroot, cargo_toml) => {
             match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
                 Some(it) => return Ok(it),
-                None => sysroot,
+                None => (sysroot, cargo_toml.parent().as_ref()),
             }
         }
-        TargetTipleConfig::Rustc(sysroot) => sysroot,
+        QueryConfig::Rustc(sysroot, current_dir) => (sysroot, current_dir),
     };
-    rustc_discover_host_triple(extra_env, sysroot).map(|it| vec![it])
+    rustc_discover_host_triple(extra_env, sysroot, current_dir).map(|it| vec![it])
 }
 
 fn rustc_discover_host_triple(
     extra_env: &FxHashMap<String, String>,
     sysroot: &Sysroot,
+    current_dir: &Path,
 ) -> anyhow::Result<String> {
-    let mut rustc = sysroot.tool(Tool::Rustc);
-    rustc.envs(extra_env);
-    rustc.arg("-vV");
-    tracing::debug!("Discovering host platform by {:?}", rustc);
-    let stdout = utf8_stdout(rustc).context("Failed to discover host platform")?;
+    let mut cmd = sysroot.tool(Tool::Rustc, current_dir);
+    cmd.envs(extra_env);
+    cmd.arg("-vV");
+    let stdout = utf8_stdout(&mut cmd)
+        .with_context(|| format!("unable to discover host platform via `{cmd:?}`"))?;
     let field = "host: ";
     let target = stdout.lines().find_map(|l| l.strip_prefix(field));
     if let Some(target) = target {
@@ -56,20 +56,18 @@ fn cargo_config_build_target(
     extra_env: &FxHashMap<String, String>,
     sysroot: &Sysroot,
 ) -> Option<Vec<String>> {
-    let mut cargo_config = sysroot.tool(Tool::Cargo);
-    cargo_config.envs(extra_env);
-    cargo_config
-        .current_dir(cargo_toml.parent())
-        .args(["-Z", "unstable-options", "config", "get", "build.target"])
-        .env("RUSTC_BOOTSTRAP", "1");
+    let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent());
+    cmd.envs(extra_env);
+    cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1");
+    cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]);
     // if successful we receive `build.target = "target-triple"`
     // or `build.target = ["<target 1>", ..]`
-    tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
     // this might be `error: config value `build.target` is not set` in which case we
     // don't wanna log the error
-    utf8_stdout(cargo_config).and_then(parse_output_cargo_config_build_target).ok()
+    utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok()
 }
 
+// Parses `"build.target = [target-triple, target-triple, ...]"` or `"build.target = "target-triple"`
 fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
     let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
 
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index d747a8086b..233f94203e 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -25,10 +25,8 @@ use crate::{
     cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource},
     env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
     project_json::{Crate, CrateArrayIdx},
-    rustc_cfg::{self, RustcCfgConfig},
     sysroot::{SysrootCrate, SysrootMode},
-    target_data_layout::{self, RustcDataLayoutConfig},
-    target_triple::{self, TargetTipleConfig},
+    toolchain_info::{rustc_cfg, target_data_layout, target_triple, QueryConfig},
     utf8_stdout, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath,
     Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
 };
@@ -177,10 +175,10 @@ fn get_toolchain_version(
     extra_env: &FxHashMap<String, String>,
     prefix: &str,
 ) -> Result<Option<Version>, anyhow::Error> {
-    let cargo_version = utf8_stdout({
-        let mut cmd = Sysroot::tool(sysroot, tool);
+    let cargo_version = utf8_stdout(&mut {
+        let mut cmd = Sysroot::tool(sysroot, tool, current_dir);
         cmd.envs(extra_env);
-        cmd.arg("--version").current_dir(current_dir);
+        cmd.arg("--version");
         cmd
     })
     .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?;
@@ -221,155 +219,152 @@ impl ProjectWorkspace {
                 ProjectWorkspace::load_detached_file(rust_file, config)?
             }
             ProjectManifest::CargoToml(cargo_toml) => {
-                // FIXME: Split sysroot discovery from sysroot loading, as to load the sysroot we
-                // want to pass the analysis target, but to discover the target we need to know the
-                // sysroot location so we know which cargo to use
-                let sysroot = match (&config.sysroot, &config.sysroot_src) {
-                    (Some(RustLibSource::Discover), None) => Sysroot::discover(
-                        cargo_toml.parent(),
-                        &config.extra_env,
-                        &config.sysroot_query_metadata,
-                    ),
-                    (Some(RustLibSource::Discover), Some(sysroot_src)) => {
-                        Sysroot::discover_with_src_override(
-                            cargo_toml.parent(),
-                            &config.extra_env,
-                            sysroot_src.clone(),
-                            &config.sysroot_query_metadata,
-                        )
-                    }
-                    (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir(
-                        path.clone(),
-                        &config.sysroot_query_metadata,
-                    ),
-                    (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load(
-                        Some(sysroot.clone()),
-                        Some(sysroot_src.clone()),
-                        &config.sysroot_query_metadata,
-                    ),
-                    (None, _) => Sysroot::empty(),
-                };
-                tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot");
-
-                let rustc_dir = match &config.rustc_source {
-                    Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
-                        .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
-                    Some(RustLibSource::Discover) => {
-                        sysroot.discover_rustc_src().ok_or_else(|| {
-                            Some("Failed to discover rustc source for sysroot.".to_owned())
-                        })
-                    }
-                    None => Err(None),
-                };
-                let targets = target_triple::get(
-                    TargetTipleConfig::Cargo(&sysroot, cargo_toml),
-                    config.target.as_deref(),
+                ProjectWorkspace::load_cargo(cargo_toml, config, progress)?
+            }
+        };
+
+        Ok(res)
+    }
+
+    fn load_cargo(
+        cargo_toml: &ManifestPath,
+        config: &CargoConfig,
+        progress: &dyn Fn(String),
+    ) -> Result<ProjectWorkspace, anyhow::Error> {
+        // FIXME: Split sysroot discovery from sysroot loading, as to load the sysroot we
+        // want to pass the analysis target, but to discover the target we need to know the
+        // sysroot location so we know which cargo to use
+        let sysroot = match (&config.sysroot, &config.sysroot_src) {
+            (Some(RustLibSource::Discover), None) => Sysroot::discover(
+                cargo_toml.parent(),
+                &config.extra_env,
+                &config.sysroot_query_metadata,
+            ),
+            (Some(RustLibSource::Discover), Some(sysroot_src)) => {
+                Sysroot::discover_with_src_override(
+                    cargo_toml.parent(),
                     &config.extra_env,
+                    sysroot_src.clone(),
+                    &config.sysroot_query_metadata,
                 )
-                .unwrap_or_default();
-                let rustc =  rustc_dir.and_then(|rustc_dir| {
-                    info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
-                    match CargoWorkspace::fetch_metadata(
-                        &rustc_dir,
+            }
+            (Some(RustLibSource::Path(path)), None) => {
+                Sysroot::discover_sysroot_src_dir(path.clone(), &config.sysroot_query_metadata)
+            }
+            (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load(
+                Some(sysroot.clone()),
+                Some(sysroot_src.clone()),
+                &config.sysroot_query_metadata,
+            ),
+            (None, _) => Sysroot::empty(),
+        };
+        tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.src_root(), root = ?sysroot.root(), "Using sysroot");
+        let rustc_dir = match &config.rustc_source {
+            Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
+                .map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
+            Some(RustLibSource::Discover) => sysroot
+                .discover_rustc_src()
+                .ok_or_else(|| Some("Failed to discover rustc source for sysroot.".to_owned())),
+            None => Err(None),
+        };
+        let targets = target_triple::get(
+            QueryConfig::Cargo(&sysroot, cargo_toml),
+            config.target.as_deref(),
+            &config.extra_env,
+        )
+        .unwrap_or_default();
+        let rustc = rustc_dir.and_then(|rustc_dir| {
+            info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
+            match CargoWorkspace::fetch_metadata(
+                &rustc_dir,
+                cargo_toml.parent(),
+                &CargoMetadataConfig {
+                    features: crate::CargoFeatures::default(),
+                    targets: targets.clone(),
+                    extra_args: config.extra_args.clone(),
+                    extra_env: config.extra_env.clone(),
+                },
+                &sysroot,
+                false,
+                progress,
+            ) {
+                Ok((meta, _error)) => {
+                    let workspace = CargoWorkspace::new(meta, cargo_toml.clone());
+                    let buildscripts = WorkspaceBuildScripts::rustc_crates(
+                        &workspace,
                         cargo_toml.parent(),
-                        &CargoMetadataConfig {
-                            features: crate::CargoFeatures::default(),
-                            targets: targets.clone(),
-                            extra_args: config.extra_args.clone(),
-                            extra_env: config.extra_env.clone(),
-                        },
+                        &config.extra_env,
                         &sysroot,
-                        false,
-                        progress,
-                    ) {
-                        Ok((meta, _error)) => {
-                            let workspace = CargoWorkspace::new(meta, cargo_toml.clone());
-                            let buildscripts = WorkspaceBuildScripts::rustc_crates(
-                                &workspace,
-                                cargo_toml.parent(),
-                                &config.extra_env,
-                                &sysroot
-                            );
-                            Ok(Box::new((workspace, buildscripts)))
-                        }
-                        Err(e) => {
-                            tracing::error!(
-                                %e,
-                                "Failed to read Cargo metadata from rustc source at {rustc_dir}",
-                            );
-                            Err(Some(format!(
-                                "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
-                            )))
-                        }
-                    }
-                });
-
-                let toolchain = get_toolchain_version(
-                    cargo_toml.parent(),
-                    &sysroot,
-                    Tool::Cargo,
-                    &config.extra_env,
-                    "cargo ",
-                )?;
-                let rustc_cfg = rustc_cfg::get(
-                    targets.first().map(Deref::deref),
-                    &config.extra_env,
-                    RustcCfgConfig::Cargo(&sysroot, cargo_toml),
-                );
-
-                let cfg_overrides = config.cfg_overrides.clone();
-                let data_layout = target_data_layout::get(
-                    RustcDataLayoutConfig::Cargo(&sysroot, cargo_toml),
-                    targets.first().map(Deref::deref),
-                    &config.extra_env,
-                );
-                if let Err(e) = &data_layout {
-                    tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace");
+                    );
+                    Ok(Box::new((workspace, buildscripts)))
                 }
-
-                let (meta, error) = CargoWorkspace::fetch_metadata(
-                    cargo_toml,
-                    cargo_toml.parent(),
-                    &CargoMetadataConfig {
-                        features: config.features.clone(),
-                        targets,
-                        extra_args: config.extra_args.clone(),
-                        extra_env: config.extra_env.clone(),
-                    },
-                    &sysroot,
-                        false,
-                        progress,
-                )
-                .with_context(|| {
-                    format!(
-                        "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
-                    )
-                })?;
-                let cargo = CargoWorkspace::new(meta, cargo_toml.clone());
-
-                let cargo_config_extra_env =
-                    cargo_config_env(cargo_toml, &config.extra_env, &sysroot);
-                ProjectWorkspace {
-                    kind: ProjectWorkspaceKind::Cargo {
-                        cargo,
-                        build_scripts: WorkspaceBuildScripts::default(),
-                        rustc,
-                        cargo_config_extra_env,
-                        error: error.map(Arc::new),
-                        set_test: config.set_test,
-                    },
-                    sysroot,
-                    rustc_cfg,
-                    cfg_overrides,
-                    toolchain,
-                    target_layout: data_layout
-                        .map(Arc::from)
-                        .map_err(|it| Arc::from(it.to_string())),
+                Err(e) => {
+                    tracing::error!(
+                        %e,
+                        "Failed to read Cargo metadata from rustc source at {rustc_dir}",
+                    );
+                    Err(Some(format!(
+                        "Failed to read Cargo metadata from rustc source at {rustc_dir}: {e}"
+                    )))
                 }
             }
-        };
-
-        Ok(res)
+        });
+        let toolchain = get_toolchain_version(
+            cargo_toml.parent(),
+            &sysroot,
+            Tool::Cargo,
+            &config.extra_env,
+            "cargo ",
+        )?;
+        let rustc_cfg = rustc_cfg::get(
+            QueryConfig::Cargo(&sysroot, cargo_toml),
+            targets.first().map(Deref::deref),
+            &config.extra_env,
+        );
+        let cfg_overrides = config.cfg_overrides.clone();
+        let data_layout = target_data_layout::get(
+            QueryConfig::Cargo(&sysroot, cargo_toml),
+            targets.first().map(Deref::deref),
+            &config.extra_env,
+        );
+        if let Err(e) = &data_layout {
+            tracing::error!(%e, "failed fetching data layout for {cargo_toml:?} workspace");
+        }
+        let (meta, error) = CargoWorkspace::fetch_metadata(
+            cargo_toml,
+            cargo_toml.parent(),
+            &CargoMetadataConfig {
+                features: config.features.clone(),
+                targets,
+                extra_args: config.extra_args.clone(),
+                extra_env: config.extra_env.clone(),
+            },
+            &sysroot,
+            false,
+            progress,
+        )
+        .with_context(|| {
+            format!(
+                "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
+            )
+        })?;
+        let cargo = CargoWorkspace::new(meta, cargo_toml.clone());
+        let cargo_config_extra_env = cargo_config_env(cargo_toml, &config.extra_env, &sysroot);
+        Ok(ProjectWorkspace {
+            kind: ProjectWorkspaceKind::Cargo {
+                cargo,
+                build_scripts: WorkspaceBuildScripts::default(),
+                rustc,
+                cargo_config_extra_env,
+                error: error.map(Arc::new),
+                set_test: config.set_test,
+            },
+            sysroot,
+            rustc_cfg,
+            cfg_overrides,
+            toolchain,
+            target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+        })
     }
 
     pub fn load_inline(project_json: ProjectJson, config: &CargoConfig) -> ProjectWorkspace {
@@ -378,8 +373,7 @@ impl ProjectWorkspace {
             project_json.sysroot_src.clone(),
             &config.sysroot_query_metadata,
         );
-        let cfg_config = RustcCfgConfig::Rustc(&sysroot);
-        let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot);
+        let query_config = QueryConfig::Rustc(&sysroot, project_json.path().as_ref());
         let toolchain = match get_toolchain_version(
             project_json.path(),
             &sysroot,
@@ -395,8 +389,8 @@ impl ProjectWorkspace {
         };
 
         let target = config.target.as_deref();
-        let rustc_cfg = rustc_cfg::get(target, &config.extra_env, cfg_config);
-        let data_layout = target_data_layout::get(data_layout_config, target, &config.extra_env);
+        let rustc_cfg = rustc_cfg::get(query_config, target, &config.extra_env);
+        let data_layout = target_data_layout::get(query_config, target, &config.extra_env);
         ProjectWorkspace {
             kind: ProjectWorkspaceKind::Json(project_json),
             sysroot,
@@ -432,17 +426,14 @@ impl ProjectWorkspace {
             };
 
         let targets = target_triple::get(
-            TargetTipleConfig::Cargo(&sysroot, detached_file),
+            QueryConfig::Cargo(&sysroot, detached_file),
             config.target.as_deref(),
             &config.extra_env,
         )
         .unwrap_or_default();
-        let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(&sysroot));
-        let data_layout = target_data_layout::get(
-            RustcDataLayoutConfig::Rustc(&sysroot),
-            None,
-            &config.extra_env,
-        );
+        let query_config = QueryConfig::Rustc(&sysroot, dir.as_ref());
+        let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
+        let data_layout = target_data_layout::get(query_config, None, &config.extra_env);
 
         let cargo_script = CargoWorkspace::fetch_metadata(
             detached_file,
@@ -954,7 +945,11 @@ fn project_json_to_crate_graph(
 
                 let target_cfgs = match target.as_deref() {
                     Some(target) => cfg_cache.entry(target).or_insert_with(|| {
-                        rustc_cfg::get(Some(target), extra_env, RustcCfgConfig::Rustc(sysroot))
+                        rustc_cfg::get(
+                            QueryConfig::Rustc(sysroot, project.project_root().as_ref()),
+                            Some(target),
+                            extra_env,
+                        )
                     }),
                     None => &rustc_cfg,
                 };
diff --git a/crates/rust-analyzer/build.rs b/crates/rust-analyzer/build.rs
index 72b741de00..0fd381d612 100644
--- a/crates/rust-analyzer/build.rs
+++ b/crates/rust-analyzer/build.rs
@@ -32,6 +32,7 @@ fn set_rerun() {
 }
 
 fn set_commit_info() {
+    #[allow(clippy::disallowed_methods)]
     let output = match Command::new("git")
         .arg("log")
         .arg("-1")
diff --git a/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/crates/rust-analyzer/src/bin/rustc_wrapper.rs
index 684b3f52af..b9fcd2e187 100644
--- a/crates/rust-analyzer/src/bin/rustc_wrapper.rs
+++ b/crates/rust-analyzer/src/bin/rustc_wrapper.rs
@@ -46,6 +46,7 @@ fn run_rustc_skipping_cargo_checking(
 }
 
 fn run_rustc(rustc_executable: OsString, args: Vec<OsString>) -> io::Result<ExitCode> {
+    #[allow(clippy::disallowed_methods)]
     let mut child = Command::new(rustc_executable)
         .args(args)
         .stdin(Stdio::inherit())
diff --git a/crates/rust-analyzer/src/discover.rs b/crates/rust-analyzer/src/discover.rs
index 96b164228e..0c111319bb 100644
--- a/crates/rust-analyzer/src/discover.rs
+++ b/crates/rust-analyzer/src/discover.rs
@@ -1,6 +1,6 @@
 //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery
 //! via a custom discover command.
-use std::{io, process::Command};
+use std::{io, path::Path};
 
 use crossbeam_channel::Sender;
 use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf};
@@ -43,7 +43,11 @@ impl DiscoverCommand {
     }
 
     /// Spawn the command inside [Discover] and report progress, if any.
-    pub(crate) fn spawn(&self, discover_arg: DiscoverArgument) -> io::Result<DiscoverHandle> {
+    pub(crate) fn spawn(
+        &self,
+        discover_arg: DiscoverArgument,
+        current_dir: &Path,
+    ) -> io::Result<DiscoverHandle> {
         let command = &self.command[0];
         let args = &self.command[1..];
 
@@ -58,7 +62,7 @@ impl DiscoverCommand {
             })
             .collect();
 
-        let mut cmd = Command::new(command);
+        let mut cmd = toolchain::command(command, current_dir);
         cmd.args(args);
 
         Ok(DiscoverHandle {
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index c7bb275c5f..16ed674406 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -444,12 +444,11 @@ impl FlycheckActor {
     ) -> Option<Command> {
         match &self.config {
             FlycheckConfig::CargoCommand { command, options, ansi_color_output } => {
-                let mut cmd = Command::new(Tool::Cargo.path());
+                let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root);
                 if let Some(sysroot_root) = &self.sysroot_root {
                     cmd.env("RUSTUP_TOOLCHAIN", AsRef::<std::path::Path>::as_ref(sysroot_root));
                 }
                 cmd.arg(command);
-                cmd.current_dir(&*self.root);
 
                 match package {
                     Some(pkg) => cmd.arg("-p").arg(pkg),
@@ -486,18 +485,15 @@ impl FlycheckActor {
                 Some(cmd)
             }
             FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => {
-                let mut cmd = Command::new(command);
-                cmd.envs(extra_env);
-
-                match invocation_strategy {
-                    InvocationStrategy::Once => {
-                        cmd.current_dir(&*self.root);
-                    }
+                let root = match invocation_strategy {
+                    InvocationStrategy::Once => &*self.root,
                     InvocationStrategy::PerWorkspace => {
-                        // FIXME: cmd.current_dir(&affected_workspace);
-                        cmd.current_dir(&*self.root);
+                        // FIXME: &affected_workspace
+                        &*self.root
                     }
-                }
+                };
+                let mut cmd = toolchain::command(command, root);
+                cmd.envs(extra_env);
 
                 // If the custom command has a $saved_file placeholder, and
                 // we're saving a file, replace the placeholder in the arguments.
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 8f2bf80ea2..d2ed43e882 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -1,12 +1,7 @@
 //! This module is responsible for implementing handlers for Language Server
 //! Protocol. This module specifically handles requests.
 
-use std::{
-    fs,
-    io::Write as _,
-    ops::Not,
-    process::{self, Stdio},
-};
+use std::{fs, io::Write as _, ops::Not, process::Stdio};
 
 use anyhow::Context;
 
@@ -2243,10 +2238,31 @@ fn run_rustfmt(
     let line_index = snap.file_line_index(file_id)?;
     let source_root_id = snap.analysis.source_root_id(file_id).ok();
 
+    // try to chdir to the file so we can respect `rustfmt.toml`
+    // FIXME: use `rustfmt --config-path` once
+    // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
+    let current_dir = match text_document.uri.to_file_path() {
+        Ok(mut path) => {
+            // pop off file name
+            if path.pop() && path.is_dir() {
+                path
+            } else {
+                std::env::current_dir()?
+            }
+        }
+        Err(_) => {
+            tracing::error!(
+                text_document = ?text_document.uri,
+                "Unable to get path, rustfmt.toml might be ignored"
+            );
+            std::env::current_dir()?
+        }
+    };
+
     let mut command = match snap.config.rustfmt(source_root_id) {
         RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
             // FIXME: Set RUSTUP_TOOLCHAIN
-            let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path());
+            let mut cmd = toolchain::command(toolchain::Tool::Rustfmt.path(), current_dir);
             cmd.envs(snap.config.extra_env(source_root_id));
             cmd.args(extra_args);
 
@@ -2300,9 +2316,9 @@ fn run_rustfmt(
                     } else {
                         cmd
                     };
-                    process::Command::new(cmd_path)
+                    toolchain::command(cmd_path, current_dir)
                 }
-                _ => process::Command::new(cmd),
+                _ => toolchain::command(cmd, current_dir),
             };
 
             cmd.envs(snap.config.extra_env(source_root_id));
@@ -2313,24 +2329,6 @@ fn run_rustfmt(
 
     tracing::debug!(?command, "created format command");
 
-    // try to chdir to the file so we can respect `rustfmt.toml`
-    // FIXME: use `rustfmt --config-path` once
-    // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
-    match text_document.uri.to_file_path() {
-        Ok(mut path) => {
-            // pop off file name
-            if path.pop() && path.is_dir() {
-                command.current_dir(path);
-            }
-        }
-        Err(_) => {
-            tracing::error!(
-                text_document = ?text_document.uri,
-                "Unable to get path, rustfmt.toml might be ignored"
-            );
-        }
-    }
-
     let mut rustfmt = command
         .stdin(Stdio::piped())
         .stdout(Stdio::piped())
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index d97d96d54a..97657b9265 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -744,7 +744,8 @@ impl GlobalState {
                             DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
                         };
 
-                        let handle = discover.spawn(arg).unwrap();
+                        let handle =
+                            discover.spawn(arg, &std::env::current_dir().unwrap()).unwrap();
                         self.discover_handle = Some(handle);
                     }
                 }
diff --git a/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs
index add6b1d5e0..3a05b83e49 100644
--- a/crates/syntax-bridge/src/lib.rs
+++ b/crates/syntax-bridge/src/lib.rs
@@ -1058,7 +1058,7 @@ where
         // We don't do what rustc does exactly, rustc does something clever when the spans have different syntax contexts
         // but this runs afoul of our separation between `span` and `hir-expand`.
         SpanData {
-            range: if a.ctx == b.ctx && a.anchor == b.anchor {
+            range: if a.ctx == b.ctx {
                 TextRange::new(
                     std::cmp::min(a.range.start(), b.range.start()),
                     std::cmp::max(a.range.end(), b.range.end()),
diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs
index a0603e35a0..33578e056e 100644
--- a/crates/toolchain/src/lib.rs
+++ b/crates/toolchain/src/lib.rs
@@ -1,6 +1,12 @@
 //! Discovery of `cargo` & `rustc` executables.
 
-use std::{env, iter, path::PathBuf};
+use std::{
+    env,
+    ffi::OsStr,
+    iter,
+    path::{Path, PathBuf},
+    process::Command,
+};
 
 use camino::{Utf8Path, Utf8PathBuf};
 
@@ -65,6 +71,14 @@ impl Tool {
     }
 }
 
+pub fn command(cmd: impl AsRef<OsStr>, working_directory: impl AsRef<Path>) -> Command {
+    // we are `toolchain::command``
+    #[allow(clippy::disallowed_methods)]
+    let mut cmd = Command::new(cmd);
+    cmd.current_dir(working_directory);
+    cmd
+}
+
 fn invoke(list: &[fn(&str) -> Option<Utf8PathBuf>], executable: &str) -> Utf8PathBuf {
     list.iter().find_map(|it| it(executable)).unwrap_or_else(|| executable.into())
 }
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 5c312da1dd..1e723b90a5 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -9,7 +9,12 @@
 //! `.cargo/config`.
 
 #![warn(rust_2018_idioms, unused_lifetimes)]
-#![allow(clippy::print_stderr, clippy::print_stdout)]
+#![allow(
+    clippy::print_stderr,
+    clippy::print_stdout,
+    clippy::disallowed_methods,
+    clippy::disallowed_types
+)]
 
 mod flags;
 


Test patch:
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index f40d508f75..2649d88ac2 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -1,7 +1,7 @@
 use chalk_ir::{AdtId, TyKind};
 use either::Either;
 use hir_def::db::DefDatabase;
-use project_model::{target_data_layout::RustcDataLayoutConfig, Sysroot};
+use project_model::{toolchain_info::QueryConfig, Sysroot};
 use rustc_hash::FxHashMap;
 use syntax::ToSmolStr;
 use test_fixture::WithFixture;
@@ -17,8 +17,8 @@ use crate::{
 mod closure;
 
 fn current_machine_data_layout() -> String {
-    project_model::target_data_layout::get(
-        RustcDataLayoutConfig::Rustc(&Sysroot::empty()),
+    project_model::toolchain_info::target_data_layout::get(
+        QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()),
         None,
         &FxHashMap::default(),
     )
diff --git a/crates/proc-macro-srv/proc-macro-test/build.rs b/crates/proc-macro-srv/proc-macro-test/build.rs
index ff2f5d1863..d3d58a6df0 100644
--- a/crates/proc-macro-srv/proc-macro-test/build.rs
+++ b/crates/proc-macro-srv/proc-macro-test/build.rs
@@ -7,6 +7,8 @@
 //! a specific rustup toolchain: this allows testing against older ABIs (e.g.
 //! 1.58) and future ABIs (stage1, nightly)
 
+#![allow(clippy::disallowed_methods)]
+
 use std::{
     env,
     path::{Path, PathBuf},
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index db792ade57..dabc71b1b9 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -10,10 +10,10 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
 use itertools::Either;
 use paths::Utf8PathBuf;
 use profile::StopWatch;
-use project_model::target_data_layout::RustcDataLayoutConfig;
+use project_model::toolchain_info::{target_data_layout, QueryConfig};
 use project_model::{
-    target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind,
-    RustLibSource, Sysroot, SysrootQueryMetadata,
+    CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, Sysroot,
+    SysrootQueryMetadata,
 };
 
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
@@ -80,7 +80,7 @@ impl Tester {
             &SysrootQueryMetadata::CargoMetadata(Default::default()),
         );
         let data_layout = target_data_layout::get(
-            RustcDataLayoutConfig::Rustc(&sysroot),
+            QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()),
             None,
             &cargo_config.extra_env,
         );
diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs
index 2fd5254733..503b3ee43a 100644
--- a/crates/rust-analyzer/src/test_runner.rs
+++ b/crates/rust-analyzer/src/test_runner.rs
@@ -1,8 +1,6 @@
 //! This module provides the functionality needed to run `cargo test` in a background
 //! thread and report the result of each test in a channel.
 
-use std::process::Command;
-
 use crossbeam_channel::Sender;
 use paths::AbsPath;
 use serde::Deserialize as _;
@@ -78,7 +76,7 @@ impl CargoTestHandle {
         test_target: TestTarget,
         sender: Sender<CargoTestMessage>,
     ) -> std::io::Result<Self> {
-        let mut cmd = Command::new(Tool::Cargo.path());
+        let mut cmd = toolchain::command(Tool::Cargo.path(), root);
         cmd.env("RUSTC_BOOTSTRAP", "1");
         cmd.arg("test");