diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 8e50fe878ae..bd2bbadea23 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -272,8 +272,8 @@ pub fn fetch_metadata( let target = config .target .clone() - .or_else(|| cargo_config_build_target(cargo_toml, config)) - .or_else(|| rustc_discover_host_triple(cargo_toml, config)); + .or_else(|| cargo_config_build_target(cargo_toml, &config.extra_env)) + .or_else(|| rustc_discover_host_triple(cargo_toml, &config.extra_env)); let mut meta = MetadataCommand::new(); meta.cargo_path(toolchain::cargo()); @@ -304,12 +304,9 @@ pub fn fetch_metadata( // unclear whether cargo itself supports it. progress("metadata".to_string()); - fn exec_with_env( - command: &cargo_metadata::MetadataCommand, - extra_env: &FxHashMap, - ) -> Result { - let mut command = command.cargo_command(); - command.envs(extra_env); + (|| -> Result { + let mut command = meta.cargo_command(); + command.envs(&config.extra_env); let output = command.output()?; if !output.status.success() { return Err(cargo_metadata::Error::CargoMetadata { @@ -321,12 +318,8 @@ fn exec_with_env( .find(|line| line.starts_with('{')) .ok_or(cargo_metadata::Error::NoJson)?; cargo_metadata::MetadataCommand::parse(stdout) - } - - let meta = exec_with_env(&meta, &config.extra_env) - .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?; - - Ok(meta) + })() + .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command())) } pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace { @@ -395,32 +388,14 @@ pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace { } let resolve = meta.resolve.expect("metadata executed with deps"); for mut node in resolve.nodes { - let source = match pkg_by_id.get(&node.id) { - Some(&src) => src, - // FIXME: replace this and a similar branch below with `.unwrap`, once - // https://github.com/rust-lang/cargo/issues/7841 - // is fixed and hits stable (around 1.43-is probably?). - None => { - tracing::error!("Node id do not match in cargo metadata, ignoring {}", node.id); - continue; - } - }; + let &source = pkg_by_id.get(&node.id).unwrap(); node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg)); - for (dep_node, kind) in node + let dependencies = node .deps .iter() - .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind))) - { - let pkg = match pkg_by_id.get(&dep_node.pkg) { - Some(&pkg) => pkg, - None => { - tracing::error!( - "Dep node id do not match in cargo metadata, ignoring {}", - dep_node.pkg - ); - continue; - } - }; + .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind))); + for (dep_node, kind) in dependencies { + let &pkg = pkg_by_id.get(&dep_node.pkg).unwrap(); let dep = PackageDependency { name: dep_node.name.clone(), pkg, kind }; packages[source].dependencies.push(dep); } @@ -465,10 +440,7 @@ pub fn parent_manifests(&self, manifest_path: &ManifestPath) -> Option>(); @@ -494,9 +466,12 @@ fn is_unique(&self, name: &str) -> bool { } } -fn rustc_discover_host_triple(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option { +fn rustc_discover_host_triple( + cargo_toml: &ManifestPath, + extra_env: &FxHashMap, +) -> Option { let mut rustc = Command::new(toolchain::rustc()); - rustc.envs(&config.extra_env); + rustc.envs(extra_env); rustc.current_dir(cargo_toml.parent()).arg("-vV"); tracing::debug!("Discovering host platform by {:?}", rustc); match utf8_stdout(rustc) { @@ -518,9 +493,12 @@ fn rustc_discover_host_triple(cargo_toml: &ManifestPath, config: &CargoConfig) - } } -fn cargo_config_build_target(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option { +fn cargo_config_build_target( + cargo_toml: &ManifestPath, + extra_env: &FxHashMap, +) -> Option { let mut cargo_config = Command::new(toolchain::cargo()); - cargo_config.envs(&config.extra_env); + cargo_config.envs(extra_env); cargo_config .current_dir(cargo_toml.parent()) .args(&["-Z", "unstable-options", "config", "get", "build.target"]) diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index 63d1d0ace96..5133a14d532 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -110,14 +110,17 @@ pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { .collect::>(), } } + /// Returns the number of crates in the project. pub fn n_crates(&self) -> usize { self.crates.len() } + /// Returns an iterator over the crates in the project. pub fn crates(&self) -> impl Iterator + '_ { self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate)) } + /// Returns the path to the project's root folder. pub fn path(&self) -> &AbsPath { &self.project_root diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs index 486cb143b80..32313618366 100644 --- a/crates/project-model/src/rustc_cfg.rs +++ b/crates/project-model/src/rustc_cfg.rs @@ -3,13 +3,14 @@ use std::process::Command; use anyhow::Result; +use rustc_hash::FxHashMap; -use crate::{cfg_flag::CfgFlag, utf8_stdout, CargoConfig, ManifestPath}; +use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath}; pub(crate) fn get( cargo_toml: Option<&ManifestPath>, target: Option<&str>, - config: &CargoConfig, + extra_env: &FxHashMap, ) -> Vec { let _p = profile::span("rustc_cfg::get"); let mut res = Vec::with_capacity(6 * 2 + 1); @@ -22,7 +23,7 @@ pub(crate) fn get( } } - match get_rust_cfgs(cargo_toml, target, config) { + match get_rust_cfgs(cargo_toml, target, extra_env) { Ok(rustc_cfgs) => { tracing::debug!( "rustc cfgs found: {:?}", @@ -42,11 +43,11 @@ pub(crate) fn get( fn get_rust_cfgs( cargo_toml: Option<&ManifestPath>, target: Option<&str>, - config: &CargoConfig, + extra_env: &FxHashMap, ) -> Result { if let Some(cargo_toml) = cargo_toml { let mut cargo_config = Command::new(toolchain::cargo()); - cargo_config.envs(&config.extra_env); + cargo_config.envs(extra_env); cargo_config .current_dir(cargo_toml.parent()) .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"]) @@ -61,7 +62,7 @@ fn get_rust_cfgs( } // using unstable cargo features failed, fall back to using plain rustc let mut cmd = Command::new(toolchain::rustc()); - cmd.envs(&config.extra_env); + cmd.envs(extra_env); cmd.args(&["--print", "cfg", "-O"]); if let Some(target) = target { cmd.args(&["--target", target]); diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 3282719fef3..f0d76aa922f 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -9,8 +9,9 @@ use anyhow::{format_err, Result}; use la_arena::{Arena, Idx}; use paths::{AbsPath, AbsPathBuf}; +use rustc_hash::FxHashMap; -use crate::{utf8_stdout, CargoConfig, ManifestPath}; +use crate::{utf8_stdout, ManifestPath}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Sysroot { @@ -67,18 +68,21 @@ pub fn crates<'a>(&'a self) -> impl Iterator + ExactSizeIte self.crates.iter().map(|(id, _data)| id) } - pub fn discover(dir: &AbsPath, config: &CargoConfig) -> Result { + pub fn discover(dir: &AbsPath, extra_env: &FxHashMap) -> Result { tracing::debug!("Discovering sysroot for {}", dir.display()); - let sysroot_dir = discover_sysroot_dir(dir, config)?; - let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir, config)?; + let sysroot_dir = discover_sysroot_dir(dir, extra_env)?; + let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir, extra_env)?; let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; Ok(res) } - pub fn discover_rustc(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option { + pub fn discover_rustc( + cargo_toml: &ManifestPath, + extra_env: &FxHashMap, + ) -> Option { tracing::debug!("Discovering rustc source for {}", cargo_toml.display()); let current_dir = cargo_toml.parent(); - discover_sysroot_dir(current_dir, config) + discover_sysroot_dir(current_dir, extra_env) .ok() .and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) } @@ -146,9 +150,12 @@ fn by_name(&self, name: &str) -> Option { } } -fn discover_sysroot_dir(current_dir: &AbsPath, config: &CargoConfig) -> Result { +fn discover_sysroot_dir( + current_dir: &AbsPath, + extra_env: &FxHashMap, +) -> Result { let mut rustc = Command::new(toolchain::rustc()); - rustc.envs(&config.extra_env); + rustc.envs(extra_env); rustc.current_dir(current_dir).args(&["--print", "sysroot"]); tracing::debug!("Discovering sysroot by {:?}", rustc); let stdout = utf8_stdout(rustc)?; @@ -158,7 +165,7 @@ fn discover_sysroot_dir(current_dir: &AbsPath, config: &CargoConfig) -> Result, ) -> Result { if let Ok(path) = env::var("RUST_SRC_PATH") { let path = AbsPathBuf::try_from(path.as_str()) @@ -174,7 +181,7 @@ fn discover_sysroot_src_dir( get_rust_src(sysroot_path) .or_else(|| { let mut rustup = Command::new(toolchain::rustup()); - rustup.envs(&config.extra_env); + rustup.envs(extra_env); rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); utf8_stdout(rustup).ok()?; get_rust_src(sysroot_path) diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index bea624bd541..813f0a7ce9f 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -10,8 +10,8 @@ use serde::de::DeserializeOwned; use crate::{ - CargoConfig, CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, - Sysroot, WorkspaceBuildScripts, + CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot, + WorkspaceBuildScripts, }; fn load_cargo(file: &str) -> CrateGraph { @@ -101,7 +101,7 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph { Some(FileId(counter)) } }, - &CargoConfig::default(), + &Default::default(), ) } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index bc4ab45daef..c749a3b6566 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -156,7 +156,11 @@ pub fn load( })?; let project_location = project_json.parent().to_path_buf(); let project_json = ProjectJson::new(&project_location, data); - ProjectWorkspace::load_inline(project_json, config.target.as_deref(), config)? + ProjectWorkspace::load_inline( + project_json, + config.target.as_deref(), + &config.extra_env, + )? } ProjectManifest::CargoToml(cargo_toml) => { let cargo_version = utf8_stdout({ @@ -187,17 +191,21 @@ pub fn load( let sysroot = if config.no_sysroot { None } else { - Some(Sysroot::discover(cargo_toml.parent(), config).with_context(|| { - format!( + Some(Sysroot::discover(cargo_toml.parent(), &config.extra_env).with_context( + || { + format!( "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?", cargo_toml.display() ) - })?) + }, + )?) }; let rustc_dir = match &config.rustc_source { Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(), - Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml, config), + Some(RustcSource::Discover) => { + Sysroot::discover_rustc(&cargo_toml, &config.extra_env) + } None => None, }; @@ -217,7 +225,8 @@ pub fn load( None => None, }; - let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), config); + let rustc_cfg = + rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); let cfg_overrides = config.cfg_overrides(); ProjectWorkspace::Cargo { @@ -238,7 +247,7 @@ pub fn load( pub fn load_inline( project_json: ProjectJson, target: Option<&str>, - config: &CargoConfig, + extra_env: &FxHashMap, ) -> Result { let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?), @@ -260,7 +269,7 @@ pub fn load_inline( (None, None) => None, }; - let rustc_cfg = rustc_cfg::get(None, target, config); + let rustc_cfg = rustc_cfg::get(None, target, extra_env); Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) } @@ -270,9 +279,9 @@ pub fn load_detached_files(detached_files: Vec) -> Result ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, - config: &CargoConfig, + extra_env: &FxHashMap, ) -> CrateGraph { let _p = profile::span("ProjectWorkspace::to_crate_graph"); @@ -430,7 +439,7 @@ pub fn to_crate_graph( load, project, sysroot, - config, + extra_env, ), ProjectWorkspace::Cargo { cargo, @@ -469,7 +478,7 @@ fn project_json_to_crate_graph( load: &mut dyn FnMut(&AbsPath) -> Option, project: &ProjectJson, sysroot: &Option, - config: &CargoConfig, + extra_env: &FxHashMap, ) -> CrateGraph { let mut crate_graph = CrateGraph::default(); let sysroot_deps = sysroot @@ -497,7 +506,7 @@ fn project_json_to_crate_graph( let target_cfgs = match krate.target.as_deref() { Some(target) => cfg_cache .entry(target) - .or_insert_with(|| rustc_cfg::get(None, Some(target), config)), + .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)), None => &rustc_cfg, }; diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 80128e43fd3..81c393abdb3 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -81,7 +81,7 @@ pub fn run(self, verbosity: Verbosity) -> Result<()> { }; let (host, vfs, _proc_macro) = - load_workspace(workspace, &cargo_config, &load_cargo_config)?; + load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?; let db = host.raw_database(); eprint!("{:<20} {}", "Database loaded:", db_load_sw.elapsed()); eprint!(" (metadata {}", metadata_time); diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 88953096e2b..e07d9054237 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -6,7 +6,7 @@ use crossbeam_channel::{unbounded, Receiver}; use hir::db::DefDatabase; use ide::{AnalysisHost, Change}; -use ide_db::base_db::CrateGraph; +use ide_db::{base_db::CrateGraph, FxHashMap}; use proc_macro_api::ProcMacroServer; use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace}; use vfs::{loader::Handle, AbsPath, AbsPathBuf}; @@ -38,7 +38,7 @@ pub fn load_workspace_at( workspace.set_build_scripts(build_scripts) } - load_workspace(workspace, cargo_config, load_config) + load_workspace(workspace, &cargo_config.extra_env, load_config) } // Note: Since this function is used by external tools that use rust-analyzer as a library @@ -48,7 +48,7 @@ pub fn load_workspace_at( // these tools need access to `ProjectWorkspace`, too, which `load_workspace_at` hides. pub fn load_workspace( ws: ProjectWorkspace, - cargo_config: &CargoConfig, + extra_env: &FxHashMap, load_config: &LoadCargoConfig, ) -> Result<(AnalysisHost, vfs::Vfs, Option)> { let (sender, receiver) = unbounded(); @@ -76,7 +76,7 @@ pub fn load_workspace( vfs.set_file_contents(path.clone(), contents); vfs.file_id(&path) }, - cargo_config, + extra_env, ); let project_folders = ProjectFolders::new(&[ws], &[]); diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 79577bf78c8..748306ea57d 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -300,7 +300,7 @@ pub fn run(self) -> Result<()> { let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; let (host, vfs, _proc_macro) = - load_workspace(workspace, &cargo_config, &load_cargo_config)?; + load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?; let db = host.raw_database(); let analysis = host.analysis(); diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 05c16bb39e3..2c29b3ee3a6 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -40,7 +40,8 @@ pub fn run(self) -> Result<()> { let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?; - let (host, vfs, _) = load_workspace(workspace, &cargo_config, &load_cargo_config)?; + let (host, vfs, _) = + load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?; let db = host.raw_database(); let analysis = host.analysis(); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 0d0e246029b..5d99d2fb119 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -110,6 +110,7 @@ struct ConfigData { /// Extra arguments for `cargo check`. checkOnSave_extraArgs: Vec = "[]", /// Extra environment variables that will be set when running `cargo check`. + /// Extends `#rust-analyzer.cargo.extraEnv#`. checkOnSave_extraEnv: FxHashMap = "{}", /// List of features to activate. Defaults to /// `#rust-analyzer.cargo.features#`. diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 4cf5de46c48..e7f7972e9ab 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -143,7 +143,7 @@ pub(crate) fn fetch_workspaces(&mut self, cause: Cause) { project_model::ProjectWorkspace::load_inline( it.clone(), cargo_config.target.as_deref(), - &cargo_config, + &cargo_config.extra_env, ) } }) @@ -402,7 +402,7 @@ fn eq_ignore_build_data<'a>( crate_graph.extend(ws.to_crate_graph( &mut load_proc_macro, &mut load, - &self.config.cargo(), + &self.config.cargo().extra_env, )); } crate_graph diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 996d4c023d7..a34f4d5093e 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -103,6 +103,7 @@ Extra arguments for `cargo check`. + -- Extra environment variables that will be set when running `cargo check`. +Extends `#rust-analyzer.cargo.extraEnv#`. -- [[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: + diff --git a/editors/code/package.json b/editors/code/package.json index 94b41c049bc..f8eec9f62e5 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -515,7 +515,7 @@ } }, "rust-analyzer.checkOnSave.extraEnv": { - "markdownDescription": "Extra environment variables that will be set when running `cargo check`.", + "markdownDescription": "Extra environment variables that will be set when running `cargo check`.\nExtends `#rust-analyzer.cargo.extraEnv#`.", "default": {}, "type": "object" },