about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/flycheck/Cargo.toml1
-rw-r--r--crates/flycheck/src/lib.rs10
-rw-r--r--crates/project-model/src/build_scripts.rs2
-rw-r--r--crates/project-model/src/cargo_workspace.rs36
-rw-r--r--crates/project-model/src/rustc_cfg.rs18
-rw-r--r--crates/project-model/src/sysroot.rs19
-rw-r--r--crates/project-model/src/tests.rs22
-rw-r--r--crates/project-model/src/workspace.rs24
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs3
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs4
-rw-r--r--crates/rust-analyzer/src/cli/lsif.rs3
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs23
-rw-r--r--crates/rust-analyzer/src/handlers.rs2
-rw-r--r--crates/rust-analyzer/src/reload.rs7
-rw-r--r--docs/user/generated_config.adoc11
-rw-r--r--editors/code/package.json10
18 files changed, 155 insertions, 43 deletions
diff --git a/Cargo.lock b/Cargo.lock
index eaadcc8b8c6..216cf51447f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -394,6 +394,7 @@ dependencies = [
  "crossbeam-channel",
  "jod-thread",
  "paths",
+ "rustc-hash",
  "serde",
  "serde_json",
  "stdx",
diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml
index d3d180ece51..688e790c536 100644
--- a/crates/flycheck/Cargo.toml
+++ b/crates/flycheck/Cargo.toml
@@ -13,6 +13,7 @@ doctest = false
 crossbeam-channel = "0.5.5"
 tracing = "0.1.35"
 cargo_metadata = "0.15.0"
+rustc-hash = "1.1.0"
 serde = { version = "1.0.137", features = ["derive"] }
 serde_json = "1.0.81"
 jod-thread = "0.1.2"
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index d9f4ef5b7ff..fdc03f4053a 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -12,6 +12,7 @@ use std::{
 
 use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
 use paths::AbsPathBuf;
+use rustc_hash::FxHashMap;
 use serde::Deserialize;
 use stdx::{process::streaming_output, JodChild};
 
@@ -30,10 +31,12 @@ pub enum FlycheckConfig {
         all_features: bool,
         features: Vec<String>,
         extra_args: Vec<String>,
+        extra_env: FxHashMap<String, String>,
     },
     CustomCommand {
         command: String,
         args: Vec<String>,
+        extra_env: FxHashMap<String, String>,
     },
 }
 
@@ -41,7 +44,7 @@ impl fmt::Display for FlycheckConfig {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
-            FlycheckConfig::CustomCommand { command, args } => {
+            FlycheckConfig::CustomCommand { command, args, .. } => {
                 write!(f, "{} {}", command, args.join(" "))
             }
         }
@@ -256,6 +259,7 @@ impl FlycheckActor {
                 all_features,
                 extra_args,
                 features,
+                extra_env,
             } => {
                 let mut cmd = Command::new(toolchain::cargo());
                 cmd.arg(command);
@@ -281,11 +285,13 @@ impl FlycheckActor {
                     }
                 }
                 cmd.args(extra_args);
+                cmd.envs(extra_env);
                 cmd
             }
-            FlycheckConfig::CustomCommand { command, args } => {
+            FlycheckConfig::CustomCommand { command, args, extra_env } => {
                 let mut cmd = Command::new(command);
                 cmd.args(args);
+                cmd.envs(extra_env);
                 cmd
             }
         };
diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs
index 84e772d1684..837ea016193 100644
--- a/crates/project-model/src/build_scripts.rs
+++ b/crates/project-model/src/build_scripts.rs
@@ -43,10 +43,12 @@ impl WorkspaceBuildScripts {
         if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
             let mut cmd = Command::new(program);
             cmd.args(args);
+            cmd.envs(&config.extra_env);
             return cmd;
         }
 
         let mut cmd = Command::new(toolchain::cargo());
+        cmd.envs(&config.extra_env);
 
         cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
 
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index eed955b42da..736d80041bd 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -2,6 +2,7 @@
 
 use std::iter;
 use std::path::PathBuf;
+use std::str::from_utf8;
 use std::{ops, process::Command};
 
 use anyhow::{Context, Result};
@@ -98,6 +99,8 @@ pub struct CargoConfig {
     pub wrap_rustc_in_build_scripts: bool,
 
     pub run_build_script_command: Option<Vec<String>>,
+
+    pub extra_env: FxHashMap<String, String>,
 }
 
 impl CargoConfig {
@@ -263,8 +266,8 @@ impl CargoWorkspace {
         let target = config
             .target
             .clone()
-            .or_else(|| cargo_config_build_target(cargo_toml))
-            .or_else(|| rustc_discover_host_triple(cargo_toml));
+            .or_else(|| cargo_config_build_target(cargo_toml, config))
+            .or_else(|| rustc_discover_host_triple(cargo_toml, config));
 
         let mut meta = MetadataCommand::new();
         meta.cargo_path(toolchain::cargo());
@@ -292,8 +295,27 @@ impl CargoWorkspace {
         // unclear whether cargo itself supports it.
         progress("metadata".to_string());
 
-        let meta =
-            meta.exec().with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?;
+        fn exec_with_env(
+            command: &cargo_metadata::MetadataCommand,
+            extra_env: &FxHashMap<String, String>,
+        ) -> Result<cargo_metadata::Metadata, cargo_metadata::Error> {
+            let mut command = command.cargo_command();
+            command.envs(extra_env);
+            let output = command.output()?;
+            if !output.status.success() {
+                return Err(cargo_metadata::Error::CargoMetadata {
+                    stderr: String::from_utf8(output.stderr)?,
+                });
+            }
+            let stdout = from_utf8(&output.stdout)?
+                .lines()
+                .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)
     }
@@ -463,8 +485,9 @@ impl CargoWorkspace {
     }
 }
 
-fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
+fn rustc_discover_host_triple(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<String> {
     let mut rustc = Command::new(toolchain::rustc());
+    rustc.envs(&config.extra_env);
     rustc.current_dir(cargo_toml.parent()).arg("-vV");
     tracing::debug!("Discovering host platform by {:?}", rustc);
     match utf8_stdout(rustc) {
@@ -486,8 +509,9 @@ fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
     }
 }
 
-fn cargo_config_build_target(cargo_toml: &ManifestPath) -> Option<String> {
+fn cargo_config_build_target(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<String> {
     let mut cargo_config = Command::new(toolchain::cargo());
+    cargo_config.envs(&config.extra_env);
     cargo_config
         .current_dir(cargo_toml.parent())
         .args(&["-Z", "unstable-options", "config", "get", "build.target"])
diff --git a/crates/project-model/src/rustc_cfg.rs b/crates/project-model/src/rustc_cfg.rs
index 17e244d0649..486cb143b80 100644
--- a/crates/project-model/src/rustc_cfg.rs
+++ b/crates/project-model/src/rustc_cfg.rs
@@ -4,9 +4,13 @@ use std::process::Command;
 
 use anyhow::Result;
 
-use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath};
+use crate::{cfg_flag::CfgFlag, utf8_stdout, CargoConfig, ManifestPath};
 
-pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Vec<CfgFlag> {
+pub(crate) fn get(
+    cargo_toml: Option<&ManifestPath>,
+    target: Option<&str>,
+    config: &CargoConfig,
+) -> Vec<CfgFlag> {
     let _p = profile::span("rustc_cfg::get");
     let mut res = Vec::with_capacity(6 * 2 + 1);
 
@@ -18,7 +22,7 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
         }
     }
 
-    match get_rust_cfgs(cargo_toml, target) {
+    match get_rust_cfgs(cargo_toml, target, config) {
         Ok(rustc_cfgs) => {
             tracing::debug!(
                 "rustc cfgs found: {:?}",
@@ -35,9 +39,14 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
     res
 }
 
-fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Result<String> {
+fn get_rust_cfgs(
+    cargo_toml: Option<&ManifestPath>,
+    target: Option<&str>,
+    config: &CargoConfig,
+) -> Result<String> {
     if let Some(cargo_toml) = cargo_toml {
         let mut cargo_config = Command::new(toolchain::cargo());
+        cargo_config.envs(&config.extra_env);
         cargo_config
             .current_dir(cargo_toml.parent())
             .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"])
@@ -52,6 +61,7 @@ fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Res
     }
     // using unstable cargo features failed, fall back to using plain rustc
     let mut cmd = Command::new(toolchain::rustc());
+    cmd.envs(&config.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 362bb0f5e79..3282719fef3 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -10,7 +10,7 @@ use anyhow::{format_err, Result};
 use la_arena::{Arena, Idx};
 use paths::{AbsPath, AbsPathBuf};
 
-use crate::{utf8_stdout, ManifestPath};
+use crate::{utf8_stdout, CargoConfig, ManifestPath};
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Sysroot {
@@ -67,18 +67,20 @@ impl Sysroot {
         self.crates.iter().map(|(id, _data)| id)
     }
 
-    pub fn discover(dir: &AbsPath) -> Result<Sysroot> {
+    pub fn discover(dir: &AbsPath, config: &CargoConfig) -> Result<Sysroot> {
         tracing::debug!("Discovering sysroot for {}", dir.display());
-        let sysroot_dir = discover_sysroot_dir(dir)?;
-        let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?;
+        let sysroot_dir = discover_sysroot_dir(dir, config)?;
+        let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir, config)?;
         let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?;
         Ok(res)
     }
 
-    pub fn discover_rustc(cargo_toml: &ManifestPath) -> Option<ManifestPath> {
+    pub fn discover_rustc(cargo_toml: &ManifestPath, config: &CargoConfig) -> Option<ManifestPath> {
         tracing::debug!("Discovering rustc source for {}", cargo_toml.display());
         let current_dir = cargo_toml.parent();
-        discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
+        discover_sysroot_dir(current_dir, config)
+            .ok()
+            .and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
     }
 
     pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
@@ -144,8 +146,9 @@ impl Sysroot {
     }
 }
 
-fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
+fn discover_sysroot_dir(current_dir: &AbsPath, config: &CargoConfig) -> Result<AbsPathBuf> {
     let mut rustc = Command::new(toolchain::rustc());
+    rustc.envs(&config.extra_env);
     rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
     tracing::debug!("Discovering sysroot by {:?}", rustc);
     let stdout = utf8_stdout(rustc)?;
@@ -155,6 +158,7 @@ fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
 fn discover_sysroot_src_dir(
     sysroot_path: &AbsPathBuf,
     current_dir: &AbsPath,
+    config: &CargoConfig,
 ) -> Result<AbsPathBuf> {
     if let Ok(path) = env::var("RUST_SRC_PATH") {
         let path = AbsPathBuf::try_from(path.as_str())
@@ -170,6 +174,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.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 9ccb6e9101e..bea624bd541 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -10,8 +10,8 @@ use paths::{AbsPath, AbsPathBuf};
 use serde::de::DeserializeOwned;
 
 use crate::{
-    CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
-    WorkspaceBuildScripts,
+    CargoConfig, CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace,
+    Sysroot, WorkspaceBuildScripts,
 };
 
 fn load_cargo(file: &str) -> CrateGraph {
@@ -92,13 +92,17 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
 }
 
 fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
-    project_workspace.to_crate_graph(&mut |_, _| Ok(Vec::new()), &mut {
-        let mut counter = 0;
-        move |_path| {
-            counter += 1;
-            Some(FileId(counter))
-        }
-    })
+    project_workspace.to_crate_graph(
+        &mut |_, _| Ok(Vec::new()),
+        &mut {
+            let mut counter = 0;
+            move |_path| {
+                counter += 1;
+                Some(FileId(counter))
+            }
+        },
+        &CargoConfig::default(),
+    )
 }
 
 fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) {
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 818bbed6af2..bc4ab45daef 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -156,11 +156,12 @@ impl ProjectWorkspace {
                 })?;
                 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())?
+                ProjectWorkspace::load_inline(project_json, config.target.as_deref(), config)?
             }
             ProjectManifest::CargoToml(cargo_toml) => {
                 let cargo_version = utf8_stdout({
                     let mut cmd = Command::new(toolchain::cargo());
+                    cmd.envs(&config.extra_env);
                     cmd.arg("--version");
                     cmd
                 })?;
@@ -186,7 +187,7 @@ impl ProjectWorkspace {
                 let sysroot = if config.no_sysroot {
                     None
                 } else {
-                    Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
+                    Some(Sysroot::discover(cargo_toml.parent(), config).with_context(|| {
                         format!(
                             "Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
                             cargo_toml.display()
@@ -196,7 +197,7 @@ impl ProjectWorkspace {
 
                 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),
+                    Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml, config),
                     None => None,
                 };
 
@@ -216,7 +217,7 @@ impl ProjectWorkspace {
                     None => None,
                 };
 
-                let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
+                let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), config);
 
                 let cfg_overrides = config.cfg_overrides();
                 ProjectWorkspace::Cargo {
@@ -237,6 +238,7 @@ impl ProjectWorkspace {
     pub fn load_inline(
         project_json: ProjectJson,
         target: Option<&str>,
+        config: &CargoConfig,
     ) -> Result<ProjectWorkspace> {
         let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
             (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
@@ -258,7 +260,7 @@ impl ProjectWorkspace {
             (None, None) => None,
         };
 
-        let rustc_cfg = rustc_cfg::get(None, target);
+        let rustc_cfg = rustc_cfg::get(None, target, config);
         Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
     }
 
@@ -268,8 +270,9 @@ impl ProjectWorkspace {
                 .first()
                 .and_then(|it| it.parent())
                 .ok_or_else(|| format_err!("No detached files to load"))?,
+            &CargoConfig::default(),
         )?;
-        let rustc_cfg = rustc_cfg::get(None, None);
+        let rustc_cfg = rustc_cfg::get(None, None, &CargoConfig::default());
         Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
     }
 
@@ -416,6 +419,7 @@ impl ProjectWorkspace {
         &self,
         load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
         load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+        config: &CargoConfig,
     ) -> CrateGraph {
         let _p = profile::span("ProjectWorkspace::to_crate_graph");
 
@@ -426,6 +430,7 @@ impl ProjectWorkspace {
                 load,
                 project,
                 sysroot,
+                config,
             ),
             ProjectWorkspace::Cargo {
                 cargo,
@@ -464,6 +469,7 @@ fn project_json_to_crate_graph(
     load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
     project: &ProjectJson,
     sysroot: &Option<Sysroot>,
+    config: &CargoConfig,
 ) -> CrateGraph {
     let mut crate_graph = CrateGraph::default();
     let sysroot_deps = sysroot
@@ -489,9 +495,9 @@ 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)))
-                }
+                Some(target) => cfg_cache
+                    .entry(target)
+                    .or_insert_with(|| rustc_cfg::get(None, Some(target), config)),
                 None => &rustc_cfg,
             };
 
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index f52e1e75127..80128e43fd3 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -80,7 +80,8 @@ impl flags::AnalysisStats {
             Some(build_scripts_sw.elapsed())
         };
 
-        let (host, vfs, _proc_macro) = load_workspace(workspace, &load_cargo_config)?;
+        let (host, vfs, _proc_macro) =
+            load_workspace(workspace, &cargo_config, &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 5d1c013c327..88953096e2b 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -38,7 +38,7 @@ pub fn load_workspace_at(
         workspace.set_build_scripts(build_scripts)
     }
 
-    load_workspace(workspace, load_config)
+    load_workspace(workspace, cargo_config, load_config)
 }
 
 // Note: Since this function is used by external tools that use rust-analyzer as a library
@@ -48,6 +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,
     load_config: &LoadCargoConfig,
 ) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
     let (sender, receiver) = unbounded();
@@ -75,6 +76,7 @@ pub fn load_workspace(
             vfs.set_file_contents(path.clone(), contents);
             vfs.file_id(&path)
         },
+        cargo_config,
     );
 
     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 491c55a04f8..79577bf78c8 100644
--- a/crates/rust-analyzer/src/cli/lsif.rs
+++ b/crates/rust-analyzer/src/cli/lsif.rs
@@ -299,7 +299,8 @@ impl flags::Lsif {
 
         let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
 
-        let (host, vfs, _proc_macro) = load_workspace(workspace, &load_cargo_config)?;
+        let (host, vfs, _proc_macro) =
+            load_workspace(workspace, &cargo_config, &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 65cc993c45e..05c16bb39e3 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -40,7 +40,7 @@ impl flags::Scip {
 
         let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
 
-        let (host, vfs, _) = load_workspace(workspace, &load_cargo_config)?;
+        let (host, vfs, _) = load_workspace(workspace, &cargo_config, &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 3cafbc74335..9ef79e6f381 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -84,6 +84,9 @@ config_data! {
         /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
         /// avoid checking unnecessary things.
         cargo_buildScripts_useRustcWrapper: bool = "true",
+        /// Extra environment variables that will be set when running cargo, rustc
+        /// or other commands within the workspace. Useful for setting RUSTFLAGS.
+        cargo_extraEnv: FxHashMap<String, String> = "{}",
         /// List of features to activate.
         ///
         /// Set this to `"all"` to pass `--all-features` to cargo.
@@ -105,6 +108,8 @@ config_data! {
         checkOnSave_enable: bool                         = "true",
         /// Extra arguments for `cargo check`.
         checkOnSave_extraArgs: Vec<String>               = "[]",
+        /// Extra environment variables that will be set when running `cargo check`.
+        checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
         /// List of features to activate. Defaults to
         /// `#rust-analyzer.cargo.features#`.
         ///
@@ -956,6 +961,16 @@ impl Config {
         }
     }
 
+    pub fn extra_env(&self) -> &FxHashMap<String, String> {
+        &self.data.cargo_extraEnv
+    }
+
+    pub fn check_on_save_extra_env(&self) -> FxHashMap<String, String> {
+        let mut extra_env = self.data.cargo_extraEnv.clone();
+        extra_env.extend(self.data.checkOnSave_extraEnv.clone());
+        extra_env
+    }
+
     pub fn lru_capacity(&self) -> Option<usize> {
         self.data.lru_capacity
     }
@@ -1025,6 +1040,7 @@ impl Config {
             unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
             wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
             run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
+            extra_env: self.data.cargo_extraEnv.clone(),
         }
     }
 
@@ -1050,7 +1066,11 @@ impl Config {
             Some(args) if !args.is_empty() => {
                 let mut args = args.clone();
                 let command = args.remove(0);
-                FlycheckConfig::CustomCommand { command, args }
+                FlycheckConfig::CustomCommand {
+                    command,
+                    args,
+                    extra_env: self.check_on_save_extra_env(),
+                }
             }
             Some(_) | None => FlycheckConfig::CargoCommand {
                 command: self.data.checkOnSave_command.clone(),
@@ -1078,6 +1098,7 @@ impl Config {
                     CargoFeatures::Listed(it) => it,
                 },
                 extra_args: self.data.checkOnSave_extraArgs.clone(),
+                extra_env: self.check_on_save_extra_env(),
             },
         };
         Some(flycheck_config)
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 7dfa32cf374..8c3ea77d061 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1789,6 +1789,7 @@ fn run_rustfmt(
     let mut command = match snap.config.rustfmt() {
         RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
             let mut cmd = process::Command::new(toolchain::rustfmt());
+            cmd.envs(snap.config.extra_env());
             cmd.args(extra_args);
             // try to chdir to the file so we can respect `rustfmt.toml`
             // FIXME: use `rustfmt --config-path` once
@@ -1846,6 +1847,7 @@ fn run_rustfmt(
         }
         RustfmtConfig::CustomCommand { command, args } => {
             let mut cmd = process::Command::new(command);
+            cmd.envs(snap.config.extra_env());
             cmd.args(args);
             cmd
         }
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index e47f70fff39..4cf5de46c48 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -143,6 +143,7 @@ impl GlobalState {
                             project_model::ProjectWorkspace::load_inline(
                                 it.clone(),
                                 cargo_config.target.as_deref(),
+                                &cargo_config,
                             )
                         }
                     })
@@ -398,7 +399,11 @@ impl GlobalState {
                         dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
                     )
                 };
-                crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load));
+                crate_graph.extend(ws.to_crate_graph(
+                    &mut load_proc_macro,
+                    &mut load,
+                    &self.config.cargo(),
+                ));
             }
             crate_graph
         };
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 62671459d7b..996d4c023d7 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -46,6 +46,12 @@ cargo check --quiet --workspace --message-format=json --all-targets
 Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
 avoid checking unnecessary things.
 --
+[[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`)::
++
+--
+Extra environment variables that will be set when running cargo, rustc
+or other commands within the workspace. Useful for setting RUSTFLAGS.
+--
 [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
 +
 --
@@ -93,6 +99,11 @@ Run specified `cargo check` command for diagnostics on save.
 --
 Extra arguments for `cargo check`.
 --
+[[rust-analyzer.checkOnSave.extraEnv]]rust-analyzer.checkOnSave.extraEnv (default: `{}`)::
++
+--
+Extra environment variables that will be set when running `cargo check`.
+--
 [[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index e2cc2c16c92..94b41c049bc 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -437,6 +437,11 @@
                     "default": true,
                     "type": "boolean"
                 },
+                "rust-analyzer.cargo.extraEnv": {
+                    "markdownDescription": "Extra environment variables that will be set when running cargo, rustc\nor other commands within the workspace. Useful for setting RUSTFLAGS.",
+                    "default": {},
+                    "type": "object"
+                },
                 "rust-analyzer.cargo.features": {
                     "markdownDescription": "List of features to activate.\n\nSet this to `\"all\"` to pass `--all-features` to cargo.",
                     "default": [],
@@ -509,6 +514,11 @@
                         "type": "string"
                     }
                 },
+                "rust-analyzer.checkOnSave.extraEnv": {
+                    "markdownDescription": "Extra environment variables that will be set when running `cargo check`.",
+                    "default": {},
+                    "type": "object"
+                },
                 "rust-analyzer.checkOnSave.features": {
                     "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.\n\nSet to `\"all\"` to pass `--all-features` to Cargo.",
                     "default": null,