about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-12-24 17:21:46 +0100
committerLukas Wirth <lukastw97@gmail.com>2024-12-24 17:49:07 +0100
commit4be8178a76c1fe9fb295642a19b4da7c54849eed (patch)
treec6f08d16c55fbf4e25418d2614a61ef71dc3b19a
parenteb80a6bd48c3809e95222ddd9249af387705c98e (diff)
downloadrust-4be8178a76c1fe9fb295642a19b4da7c54849eed.tar.gz
rust-4be8178a76c1fe9fb295642a19b4da7c54849eed.zip
Cleanup target fetching for cargo metadata
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs105
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/lib.rs16
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs31
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/target_triple.rs82
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs83
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs11
10 files changed, 209 insertions, 140 deletions
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index ba4946bf0b9..836879c75bf 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -13,8 +13,8 @@ use serde_json::from_value;
 use span::Edition;
 use toolchain::Tool;
 
-use crate::{utf8_stdout, ManifestPath, Sysroot, SysrootQueryMetadata};
 use crate::{CfgOverrides, InvocationStrategy};
+use crate::{ManifestPath, Sysroot, SysrootQueryMetadata};
 
 /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
 /// workspace. It pretty closely mirrors `cargo metadata` output.
@@ -251,6 +251,18 @@ impl TargetKind {
     }
 }
 
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
+pub struct CargoMetadataConfig {
+    /// List of features to activate.
+    pub features: CargoFeatures,
+    /// rustc targets
+    pub targets: Vec<String>,
+    /// Extra args to pass to the cargo command.
+    pub extra_args: Vec<String>,
+    /// Extra env vars to set when invoking the cargo command
+    pub extra_env: FxHashMap<String, String>,
+}
+
 // Deserialize helper for the cargo metadata
 #[derive(Deserialize, Default)]
 struct PackageMetadata {
@@ -265,7 +277,7 @@ impl CargoWorkspace {
     pub fn fetch_metadata(
         cargo_toml: &ManifestPath,
         current_dir: &AbsPath,
-        config: &CargoConfig,
+        config: &CargoMetadataConfig,
         sysroot: &Sysroot,
         locked: bool,
         progress: &dyn Fn(String),
@@ -276,14 +288,12 @@ impl CargoWorkspace {
     fn fetch_metadata_(
         cargo_toml: &ManifestPath,
         current_dir: &AbsPath,
-        config: &CargoConfig,
+        config: &CargoMetadataConfig,
         sysroot: &Sysroot,
         locked: bool,
         no_deps: bool,
         progress: &dyn Fn(String),
     ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
-        let targets = find_list_of_build_targets(config, cargo_toml, sysroot);
-
         let cargo = sysroot.tool(Tool::Cargo);
         let mut meta = MetadataCommand::new();
         meta.cargo_path(cargo.get_program());
@@ -319,12 +329,9 @@ impl CargoWorkspace {
             }
         }
 
-        if !targets.is_empty() {
-            other_options.append(
-                &mut targets
-                    .into_iter()
-                    .flat_map(|target| ["--filter-platform".to_owned(), target])
-                    .collect(),
+        if !config.targets.is_empty() {
+            other_options.extend(
+                config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
             );
         }
         // The manifest is a rust file, so this means its a script manifest
@@ -596,79 +603,3 @@ impl CargoWorkspace {
         self.is_virtual_workspace
     }
 }
-
-fn find_list_of_build_targets(
-    config: &CargoConfig,
-    cargo_toml: &ManifestPath,
-    sysroot: &Sysroot,
-) -> Vec<String> {
-    if let Some(target) = &config.target {
-        return [target.into()].to_vec();
-    }
-
-    let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env, sysroot);
-    if !build_targets.is_empty() {
-        return build_targets;
-    }
-
-    rustc_discover_host_triple(cargo_toml, &config.extra_env, sysroot).into_iter().collect()
-}
-
-fn rustc_discover_host_triple(
-    cargo_toml: &ManifestPath,
-    extra_env: &FxHashMap<String, String>,
-    sysroot: &Sysroot,
-) -> Option<String> {
-    let mut rustc = sysroot.tool(Tool::Rustc);
-    rustc.envs(extra_env);
-    rustc.current_dir(cargo_toml.parent()).arg("-vV");
-    tracing::debug!("Discovering host platform by {:?}", rustc);
-    match utf8_stdout(rustc) {
-        Ok(stdout) => {
-            let field = "host: ";
-            let target = stdout.lines().find_map(|l| l.strip_prefix(field));
-            if let Some(target) = target {
-                Some(target.to_owned())
-            } else {
-                // If we fail to resolve the host platform, it's not the end of the world.
-                tracing::info!("rustc -vV did not report host platform, got:\n{}", stdout);
-                None
-            }
-        }
-        Err(e) => {
-            tracing::warn!("Failed to discover host platform: {}", e);
-            None
-        }
-    }
-}
-
-fn cargo_config_build_target(
-    cargo_toml: &ManifestPath,
-    extra_env: &FxHashMap<String, String>,
-    sysroot: &Sysroot,
-) -> 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");
-    // if successful we receive `build.target = "target-triple"`
-    // or `build.target = ["<target 1>", ..]`
-    tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
-    utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default()
-}
-
-fn parse_output_cargo_config_build_target(stdout: String) -> Vec<String> {
-    let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
-
-    if !trimmed.starts_with('[') {
-        return [trimmed.to_owned()].to_vec();
-    }
-
-    let res = serde_json::from_str(trimmed);
-    if let Err(e) = &res {
-        tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e);
-    }
-    res.unwrap_or_default()
-}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index da8afc5d3a1..9a024f6b962 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -23,6 +23,7 @@ pub mod project_json;
 mod rustc_cfg;
 mod sysroot;
 pub mod target_data_layout;
+mod target_triple;
 mod workspace;
 
 #[cfg(test)]
@@ -42,8 +43,8 @@ use rustc_hash::FxHashSet;
 pub use crate::{
     build_dependencies::WorkspaceBuildScripts,
     cargo_workspace::{
-        CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
-        RustLibSource, Target, TargetData, TargetKind,
+        CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData,
+        PackageDependency, RustLibSource, Target, TargetData, TargetKind,
     },
     manifest_path::ManifestPath,
     project_json::{ProjectJson, ProjectJsonData},
@@ -241,9 +242,14 @@ fn parse_cfg(s: &str) -> Result<cfg::CfgAtom, String> {
     Ok(res)
 }
 
-#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum SysrootQueryMetadata {
-    #[default]
-    CargoMetadata,
+    CargoMetadata(CargoMetadataConfig),
     None,
 }
+
+impl Default for SysrootQueryMetadata {
+    fn default() -> Self {
+        SysrootQueryMetadata::CargoMetadata(Default::default())
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 8426e689a64..d8186a23f76 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -14,7 +14,10 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::FxHashMap;
 use toolchain::{probe_for_binary, Tool};
 
-use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, SysrootQueryMetadata};
+use crate::{
+    cargo_workspace::CargoMetadataConfig, utf8_stdout, CargoWorkspace, ManifestPath,
+    SysrootQueryMetadata,
+};
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Sysroot {
@@ -126,7 +129,7 @@ impl Sysroot {
     pub fn discover(
         dir: &AbsPath,
         extra_env: &FxHashMap<String, String>,
-        sysroot_query_metadata: SysrootQueryMetadata,
+        sysroot_query_metadata: &SysrootQueryMetadata,
     ) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(dir, extra_env);
         let sysroot_src_dir = sysroot_dir.as_ref().ok().map(|sysroot_dir| {
@@ -139,7 +142,7 @@ impl Sysroot {
         current_dir: &AbsPath,
         extra_env: &FxHashMap<String, String>,
         sysroot_src_dir: AbsPathBuf,
-        sysroot_query_metadata: SysrootQueryMetadata,
+        sysroot_query_metadata: &SysrootQueryMetadata,
     ) -> Sysroot {
         let sysroot_dir = discover_sysroot_dir(current_dir, extra_env);
         Sysroot::load_core_check(
@@ -151,7 +154,7 @@ impl Sysroot {
 
     pub fn discover_sysroot_src_dir(
         sysroot_dir: AbsPathBuf,
-        sysroot_query_metadata: SysrootQueryMetadata,
+        sysroot_query_metadata: &SysrootQueryMetadata,
     ) -> Sysroot {
         let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir)
             .ok_or_else(|| format_err!("can't find standard library sources in {sysroot_dir}"));
@@ -205,7 +208,7 @@ impl Sysroot {
     pub fn load(
         sysroot_dir: Option<AbsPathBuf>,
         sysroot_src_dir: Option<AbsPathBuf>,
-        sysroot_query_metadata: SysrootQueryMetadata,
+        sysroot_query_metadata: &SysrootQueryMetadata,
     ) -> Sysroot {
         Self::load_core_check(sysroot_dir.map(Ok), sysroot_src_dir.map(Ok), sysroot_query_metadata)
     }
@@ -213,7 +216,7 @@ impl Sysroot {
     fn load_core_check(
         sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
         sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
-        sysroot_query_metadata: SysrootQueryMetadata,
+        sysroot_query_metadata: &SysrootQueryMetadata,
     ) -> Sysroot {
         let mut sysroot = Self::load_(sysroot_dir, sysroot_src_dir, sysroot_query_metadata);
         if sysroot.error.is_none() {
@@ -241,7 +244,7 @@ impl Sysroot {
     fn load_(
         sysroot_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
         sysroot_src_dir: Option<Result<AbsPathBuf, anyhow::Error>>,
-        sysroot_query_metadata: SysrootQueryMetadata,
+        sysroot_query_metadata: &SysrootQueryMetadata,
     ) -> Sysroot {
         let sysroot_dir = match sysroot_dir {
             Some(Ok(sysroot_dir)) => Some(sysroot_dir),
@@ -274,13 +277,16 @@ impl Sysroot {
                 }
             }
         };
-        if sysroot_query_metadata == SysrootQueryMetadata::CargoMetadata {
+        if let SysrootQueryMetadata::CargoMetadata(cargo_config) = sysroot_query_metadata {
             let library_manifest =
                 ManifestPath::try_from(sysroot_src_dir.join("Cargo.toml")).unwrap();
             if fs::metadata(&library_manifest).is_ok() {
-                if let Some(sysroot) =
-                    Self::load_library_via_cargo(library_manifest, &sysroot_dir, &sysroot_src_dir)
-                {
+                if let Some(sysroot) = Self::load_library_via_cargo(
+                    library_manifest,
+                    &sysroot_dir,
+                    &sysroot_src_dir,
+                    cargo_config,
+                ) {
                     return sysroot;
                 }
             }
@@ -341,9 +347,10 @@ impl Sysroot {
         library_manifest: ManifestPath,
         sysroot_dir: &Option<AbsPathBuf>,
         sysroot_src_dir: &AbsPathBuf,
+        cargo_config: &CargoMetadataConfig,
     ) -> Option<Sysroot> {
         tracing::debug!("Loading library metadata: {library_manifest}");
-        let mut cargo_config = CargoConfig::default();
+        let mut cargo_config = cargo_config.clone();
         // the sysroot uses `public-dependency`, so we make cargo think it's a nightly
         cargo_config.extra_env.insert(
             "__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS".to_owned(),
diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_triple.rs b/src/tools/rust-analyzer/crates/project-model/src/target_triple.rs
new file mode 100644
index 00000000000..92e0c49885f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/target_triple.rs
@@ -0,0 +1,82 @@
+//! Runs `rustc --print -vV` to get the host target.
+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),
+}
+
+pub(super) fn get(
+    config: TargetTipleConfig<'_>,
+    target: Option<&str>,
+    extra_env: &FxHashMap<String, String>,
+) -> anyhow::Result<Vec<String>> {
+    if let Some(target) = target {
+        return Ok(vec![target.to_owned()]);
+    }
+
+    let sysroot = match config {
+        TargetTipleConfig::Cargo(sysroot, cargo_toml) => {
+            match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
+                Ok(it) => return Ok(it),
+                Err(e) => {
+                    tracing::warn!("failed to run `cargo rustc --print cfg`, falling back to invoking rustc directly: {e}");
+                    sysroot
+                }
+            }
+        }
+        TargetTipleConfig::Rustc(sysroot) => sysroot,
+    };
+    rustc_discover_host_triple(extra_env, sysroot).map(|it| vec![it])
+}
+
+fn rustc_discover_host_triple(
+    extra_env: &FxHashMap<String, String>,
+    sysroot: &Sysroot,
+) -> 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 field = "host: ";
+    let target = stdout.lines().find_map(|l| l.strip_prefix(field));
+    if let Some(target) = target {
+        Ok(target.to_owned())
+    } else {
+        // If we fail to resolve the host platform, it's not the end of the world.
+        Err(anyhow::format_err!("rustc -vV did not report host platform, got:\n{}", stdout))
+    }
+}
+
+fn cargo_config_build_target(
+    cargo_toml: &ManifestPath,
+    extra_env: &FxHashMap<String, String>,
+    sysroot: &Sysroot,
+) -> anyhow::Result<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");
+    // if successful we receive `build.target = "target-triple"`
+    // or `build.target = ["<target 1>", ..]`
+    tracing::debug!("Discovering cargo config target by {:?}", cargo_config);
+    utf8_stdout(cargo_config).and_then(parse_output_cargo_config_build_target)
+}
+
+fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
+    let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
+
+    if !trimmed.starts_with('[') {
+        return Ok([trimmed.to_owned()].to_vec());
+    }
+
+    serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index f3cf2d83eac..9fc2d6116a4 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -117,7 +117,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(Some(sysroot_dir), Some(sysroot_src_dir), SysrootQueryMetadata::CargoMetadata)
+    Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir), &SysrootQueryMetadata::default())
 }
 
 fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
@@ -232,7 +232,7 @@ fn smoke_test_real_sysroot_cargo() {
     let sysroot = Sysroot::discover(
         AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
         &Default::default(),
-        SysrootQueryMetadata::CargoMetadata,
+        &SysrootQueryMetadata::default(),
     );
     assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_)));
     let project_workspace = ProjectWorkspace {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 71ddee30910..d747a8086b4 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,7 +2,7 @@
 //! metadata` or `rust-project.json`) into representation stored in the salsa
 //! database -- `CrateGraph`.
 
-use std::{collections::VecDeque, fmt, fs, iter, sync};
+use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync};
 
 use anyhow::Context;
 use base_db::{
@@ -22,12 +22,13 @@ use triomphe::Arc;
 
 use crate::{
     build_dependencies::BuildScriptOutput,
-    cargo_workspace::{DepKind, PackageData, RustLibSource},
+    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},
     utf8_stdout, CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath,
     Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
 };
@@ -220,28 +221,31 @@ 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,
+                        &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,
+                            &config.sysroot_query_metadata,
                         )
                     }
                     (Some(RustLibSource::Path(path)), None) => Sysroot::discover_sysroot_src_dir(
                         path.clone(),
-                        config.sysroot_query_metadata,
+                        &config.sysroot_query_metadata,
                     ),
                     (Some(RustLibSource::Path(sysroot)), Some(sysroot_src)) => Sysroot::load(
                         Some(sysroot.clone()),
                         Some(sysroot_src.clone()),
-                        config.sysroot_query_metadata,
+                        &config.sysroot_query_metadata,
                     ),
                     (None, _) => Sysroot::empty(),
                 };
@@ -257,15 +261,22 @@ impl ProjectWorkspace {
                     }
                     None => Err(None),
                 };
-
+                let targets = target_triple::get(
+                    TargetTipleConfig::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(),
-                        &CargoConfig {
+                        &CargoMetadataConfig {
                             features: crate::CargoFeatures::default(),
-                            ..config.clone()
+                            targets: targets.clone(),
+                            extra_args: config.extra_args.clone(),
+                            extra_env: config.extra_env.clone(),
                         },
                         &sysroot,
                         false,
@@ -301,7 +312,7 @@ impl ProjectWorkspace {
                     "cargo ",
                 )?;
                 let rustc_cfg = rustc_cfg::get(
-                    config.target.as_deref(),
+                    targets.first().map(Deref::deref),
                     &config.extra_env,
                     RustcCfgConfig::Cargo(&sysroot, cargo_toml),
                 );
@@ -309,7 +320,7 @@ impl ProjectWorkspace {
                 let cfg_overrides = config.cfg_overrides.clone();
                 let data_layout = target_data_layout::get(
                     RustcDataLayoutConfig::Cargo(&sysroot, cargo_toml),
-                    config.target.as_deref(),
+                    targets.first().map(Deref::deref),
                     &config.extra_env,
                 );
                 if let Err(e) = &data_layout {
@@ -319,7 +330,12 @@ impl ProjectWorkspace {
                 let (meta, error) = CargoWorkspace::fetch_metadata(
                     cargo_toml,
                     cargo_toml.parent(),
-                    config,
+                    &CargoMetadataConfig {
+                        features: config.features.clone(),
+                        targets,
+                        extra_args: config.extra_args.clone(),
+                        extra_env: config.extra_env.clone(),
+                    },
                     &sysroot,
                         false,
                         progress,
@@ -360,7 +376,7 @@ impl ProjectWorkspace {
         let sysroot = Sysroot::load(
             project_json.sysroot.clone(),
             project_json.sysroot_src.clone(),
-            config.sysroot_query_metadata,
+            &config.sysroot_query_metadata,
         );
         let cfg_config = RustcCfgConfig::Rustc(&sysroot);
         let data_layout_config = RustcDataLayoutConfig::Rustc(&sysroot);
@@ -398,10 +414,10 @@ impl ProjectWorkspace {
         let dir = detached_file.parent();
         let sysroot = match &config.sysroot {
             Some(RustLibSource::Path(path)) => {
-                Sysroot::discover_sysroot_src_dir(path.clone(), config.sysroot_query_metadata)
+                Sysroot::discover_sysroot_src_dir(path.clone(), &config.sysroot_query_metadata)
             }
             Some(RustLibSource::Discover) => {
-                Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata)
+                Sysroot::discover(dir, &config.extra_env, &config.sysroot_query_metadata)
             }
             None => Sysroot::empty(),
         };
@@ -415,6 +431,12 @@ impl ProjectWorkspace {
                 }
             };
 
+        let targets = target_triple::get(
+            TargetTipleConfig::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),
@@ -422,16 +444,27 @@ impl ProjectWorkspace {
             &config.extra_env,
         );
 
-        let cargo_script =
-            CargoWorkspace::fetch_metadata(detached_file, dir, config, &sysroot, false, &|_| ())
-                .ok()
-                .map(|(ws, error)| {
-                    (
-                        CargoWorkspace::new(ws, detached_file.clone()),
-                        WorkspaceBuildScripts::default(),
-                        error.map(Arc::new),
-                    )
-                });
+        let cargo_script = CargoWorkspace::fetch_metadata(
+            detached_file,
+            dir,
+            &CargoMetadataConfig {
+                features: config.features.clone(),
+                targets,
+                extra_args: config.extra_args.clone(),
+                extra_env: config.extra_env.clone(),
+            },
+            &sysroot,
+            false,
+            &|_| (),
+        )
+        .ok()
+        .map(|(ws, error)| {
+            (
+                CargoWorkspace::new(ws, detached_file.clone()),
+                WorkspaceBuildScripts::default(),
+                error.map(Arc::new),
+            )
+        });
 
         let cargo_config_extra_env = cargo_config_env(detached_file, &config.extra_env, &sysroot);
         Ok(ProjectWorkspace {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index caa42dd62cd..1a9cdef256d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -10,7 +10,7 @@ extern crate rustc_driver as _;
 
 mod rustc_wrapper;
 
-use std::{env, fs, path::PathBuf, process::ExitCode, sync::Arc, thread::sleep};
+use std::{env, fs, path::PathBuf, process::ExitCode, sync::Arc};
 
 use anyhow::Context;
 use lsp_server::Connection;
@@ -100,7 +100,7 @@ fn wait_for_debugger() {
         // SAFETY: WinAPI generated code that is defensively marked `unsafe` but
         // in practice can not be used in an unsafe way.
         while unsafe { IsDebuggerPresent() } == 0 {
-            sleep(std::time::Duration::from_millis(100));
+            std::thread::sleep(std::time::Duration::from_millis(100));
         }
     }
     #[cfg(not(target_os = "windows"))]
@@ -109,7 +109,7 @@ fn wait_for_debugger() {
         let mut d = 4;
         while d == 4 {
             d = 4;
-            sleep(std::time::Duration::from_millis(100));
+            std::thread::sleep(std::time::Duration::from_millis(100));
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 66cd2e424e3..9b428871c40 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -33,7 +33,10 @@ use itertools::Itertools;
 use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
 use oorandom::Rand32;
 use profile::{Bytes, StopWatch};
-use project_model::{CargoConfig, CfgOverrides, ProjectManifest, ProjectWorkspace, RustLibSource};
+use project_model::{
+    CargoConfig, CargoMetadataConfig, CfgOverrides, ProjectManifest, ProjectWorkspace,
+    RustLibSource,
+};
 use rayon::prelude::*;
 use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::{AstNode, SyntaxNode};
@@ -68,7 +71,9 @@ impl flags::AnalysisStats {
             },
             sysroot_query_metadata: match self.no_query_sysroot_metadata {
                 true => project_model::SysrootQueryMetadata::None,
-                false => project_model::SysrootQueryMetadata::CargoMetadata,
+                false => project_model::SysrootQueryMetadata::CargoMetadata(
+                    CargoMetadataConfig::default(),
+                ),
             },
             all_targets: true,
             set_test: !self.no_test,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 6483afc85b2..db792ade574 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -77,7 +77,7 @@ impl Tester {
         let sysroot = Sysroot::discover(
             tmp_file.parent().unwrap(),
             &cargo_config.extra_env,
-            SysrootQueryMetadata::CargoMetadata,
+            &SysrootQueryMetadata::CargoMetadata(Default::default()),
         );
         let data_layout = target_data_layout::get(
             RustcDataLayoutConfig::Rustc(&sysroot),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index b06117f7383..4160dd16bec 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -21,8 +21,8 @@ use ide_db::{
 use itertools::Itertools;
 use paths::{Utf8Path, Utf8PathBuf};
 use project_model::{
-    CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand,
-    ProjectManifest, RustLibSource,
+    CargoConfig, CargoFeatures, CargoMetadataConfig, ProjectJson, ProjectJsonData,
+    ProjectJsonFromCommand, ProjectManifest, RustLibSource,
 };
 use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
@@ -1862,7 +1862,12 @@ impl Config {
             sysroot,
             sysroot_query_metadata: match self.cargo_sysrootQueryMetadata(None) {
                 SysrootQueryMetadata::CargoMetadata => {
-                    project_model::SysrootQueryMetadata::CargoMetadata
+                    project_model::SysrootQueryMetadata::CargoMetadata(CargoMetadataConfig {
+                        features: Default::default(),
+                        targets: self.cargo_target(source_root).clone().into_iter().collect(),
+                        extra_args: Default::default(),
+                        extra_env: Default::default(),
+                    })
                 }
                 SysrootQueryMetadata::None => project_model::SysrootQueryMetadata::None,
             },