Unnamed repository; edit this file 'description' to name the repository.
Fix [env] in .cargo/config.toml overriding process environment variables
cargo_config_env() only checked extra_env (rust-analyzer.cargo.extraEnv)
when deciding whether to skip an existing variable, but never checked the
actual process environment via std::env::var. This caused config.toml
values to unconditionally override real environment variables even without
force = true, diverging from Cargo's behavior.
Additionally, plain string entries (e.g. `KEY = "value"`) skipped the
force check entirely.
When a process env var takes precedence, its value is now inserted into
the Env so that env!/option_env! resolution stays correct.
Fixes rust-lang/rust-analyzer#21994
| -rw-r--r-- | crates/project-model/src/env.rs | 27 |
1 files changed, 20 insertions, 7 deletions
diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs index 8375215136..1a660fbf5b 100644 --- a/crates/project-model/src/env.rs +++ b/crates/project-model/src/env.rs @@ -79,18 +79,31 @@ pub(crate) fn cargo_config_env( for (key, entry) in env_toml { let key = key.as_ref().as_ref(); let value = match entry.as_ref() { - DeValue::String(s) => String::from(s.clone()), + DeValue::String(s) => { + // Plain string entries have no `force` option, so they should not + // override existing environment variables (matching Cargo behavior). + if extra_env.get(key).is_some_and(Option::is_some) { + continue; + } + if let Ok(val) = std::env::var(key) { val } else { String::from(s.clone()) } + } DeValue::Table(entry) => { // Each entry MUST have a `value` key. let Some(map) = entry.get("value").and_then(|v| v.as_ref().as_str()) else { continue; }; - // If the entry already exists in the environment AND the `force` key is not set to - // true, then don't overwrite the value. - if extra_env.get(key).is_some_and(Option::is_some) - && !entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false) - { - continue; + let is_forced = + entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false); + // If the entry already exists in the environment AND the `force` key is not set + // to true, use the existing value instead of the config value. + if !is_forced { + if extra_env.get(key).is_some_and(Option::is_some) { + continue; + } + if let Ok(val) = std::env::var(key) { + env.insert(key, val); + continue; + } } if let Some(base) = entry.get("relative").and_then(|v| { |