diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 30709c968da..495119d5519 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs @@ -86,7 +86,7 @@ impl CfgOptions { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct CfgDiff { // Invariants: No duplicates, no atom that's both in `enable` and `disable`. enable: Vec, diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index e3fdeb448db..671760d4deb 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -1,6 +1,5 @@ //! See [`CargoWorkspace`]. -use std::iter; use std::path::PathBuf; use std::str::from_utf8; use std::{ops, process::Command}; @@ -58,20 +57,6 @@ pub enum RustLibSource { Discover, } -/// Crates to disable `#[cfg(test)]` on. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum UnsetTestCrates { - None, - Only(Vec), - All, -} - -impl Default for UnsetTestCrates { - fn default() -> Self { - Self::None - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum CargoFeatures { All, @@ -100,8 +85,7 @@ pub struct CargoConfig { pub sysroot_src: Option, /// rustc private crate source pub rustc_source: Option, - /// crates to disable `#[cfg(test)]` on - pub unset_test_crates: UnsetTestCrates, + pub cfg_overrides: CfgOverrides, /// Invoke `cargo check` through the RUSTC_WRAPPER. pub wrap_rustc_in_build_scripts: bool, /// The command to run instead of `cargo check` for building build scripts. @@ -114,27 +98,6 @@ pub struct CargoConfig { pub invocation_location: InvocationLocation, } -impl CargoConfig { - pub fn cfg_overrides(&self) -> CfgOverrides { - match &self.unset_test_crates { - UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()), - UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective( - unset_test_crates - .iter() - .cloned() - .zip(iter::repeat_with(|| { - cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]) - .unwrap() - })) - .collect(), - ), - UnsetTestCrates::All => CfgOverrides::Wildcard( - cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(), - ), - } - } -} - pub type Package = Idx; pub type Target = Idx; diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 70cb71ae3bd..61acc646f81 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -44,7 +44,7 @@ pub use crate::{ build_scripts::WorkspaceBuildScripts, cargo_workspace::{ CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency, - RustLibSource, Target, TargetData, TargetKind, UnsetTestCrates, + RustLibSource, Target, TargetData, TargetKind, }, manifest_path::ManifestPath, project_json::{ProjectJson, ProjectJsonData}, diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index c3c654ddb6f..7815b9dda77 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -158,9 +158,10 @@ fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) { #[test] fn cargo_hello_world_project_model_with_wildcard_overrides() { - let cfg_overrides = CfgOverrides::Wildcard( - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), - ); + let cfg_overrides = CfgOverrides { + global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + selective: Default::default(), + }; let (crate_graph, _proc_macros) = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); check_crate_graph( @@ -173,14 +174,13 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() { #[test] fn cargo_hello_world_project_model_with_selective_overrides() { - let cfg_overrides = { - CfgOverrides::Selective( - std::iter::once(( - "libc".to_owned(), - CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), - )) - .collect(), - ) + let cfg_overrides = CfgOverrides { + global: Default::default(), + selective: std::iter::once(( + "libc".to_owned(), + CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), + )) + .collect(), }; let (crate_graph, _proc_macros) = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 4adaa6d86ee..a695bc1cca7 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -28,29 +28,17 @@ use crate::{ }; /// A set of cfg-overrides per crate. -/// -/// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates, -/// without having to first obtain a list of all crates. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum CfgOverrides { - /// A single global set of overrides matching all crates. - Wildcard(CfgDiff), +#[derive(Default, Debug, Clone, Eq, PartialEq)] +pub struct CfgOverrides { + /// A global set of overrides matching all crates. + pub global: CfgDiff, /// A set of overrides matching specific crates. - Selective(FxHashMap), -} - -impl Default for CfgOverrides { - fn default() -> Self { - Self::Selective(FxHashMap::default()) - } + pub selective: FxHashMap, } impl CfgOverrides { pub fn len(&self) -> usize { - match self { - CfgOverrides::Wildcard(_) => 1, - CfgOverrides::Selective(hash_map) => hash_map.len(), - } + self.global.len() + self.selective.iter().map(|(_, it)| it.len()).sum::() } } @@ -292,7 +280,7 @@ impl ProjectWorkspace { let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env); - let cfg_overrides = config.cfg_overrides(); + let cfg_overrides = config.cfg_overrides.clone(); let data_layout = target_data_layout::get( Some(&cargo_toml), config.target.as_deref(), @@ -886,12 +874,10 @@ fn cargo_to_crate_graph( cfg_options.insert_atom("test".into()); } - let overrides = match override_cfg { - CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff), - CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name), + if !override_cfg.global.is_empty() { + cfg_options.apply_diff(override_cfg.global.clone()); }; - - if let Some(overrides) = overrides { + if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) { // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while // working on rust-lang/rust as that's the only time it appears outside sysroot). @@ -899,7 +885,7 @@ fn cargo_to_crate_graph( // A more ideal solution might be to reanalyze crates based on where the cursor is and // figure out the set of cfgs that would have to apply to make it active. - cfg_options.apply_diff(overrides.clone()); + cfg_options.apply_diff(diff.clone()); }; cfg_options }); @@ -1109,14 +1095,10 @@ fn handle_rustc_crates( let mut cfg_options = cfg_options.clone(); - let overrides = match override_cfg { - CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff), - CfgOverrides::Selective(cfg_overrides) => { - cfg_overrides.get(&rustc_workspace[pkg].name) - } + if !override_cfg.global.is_empty() { + cfg_options.apply_diff(override_cfg.global.clone()); }; - - if let Some(overrides) = overrides { + if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) { // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while // working on rust-lang/rust as that's the only time it appears outside sysroot). @@ -1124,7 +1106,7 @@ fn handle_rustc_crates( // A more ideal solution might be to reanalyze crates based on where the cursor is and // figure out the set of cfgs that would have to apply to make it active. - cfg_options.apply_diff(overrides.clone()); + cfg_options.apply_diff(diff.clone()); }; for &tgt in rustc_workspace[pkg].targets.iter() { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index b1d019cb113..649ac90fc6e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -9,6 +9,7 @@ use std::{fmt, iter, ops::Not, path::PathBuf}; +use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, @@ -23,7 +24,6 @@ use itertools::Itertools; use lsp_types::{ClientCapabilities, MarkupKind}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, - UnsetTestCrates, }; use rustc_hash::{FxHashMap, FxHashSet}; use serde::{de::DeserializeOwned, Deserialize}; @@ -101,6 +101,8 @@ config_data! { /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to /// avoid checking unnecessary things. cargo_buildScripts_useRustcWrapper: bool = "true", + /// List of cfg options to enable with the given values. + cargo_cfgs: FxHashMap = "{}", /// Extra arguments that are passed to every cargo invocation. cargo_extraArgs: Vec = "[]", /// Extra environment variables that will be set when running cargo, rustc @@ -128,7 +130,7 @@ config_data! { // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work // than `checkOnSave_target` cargo_target: Option = "null", - /// Unsets `#[cfg(test)]` for the specified crates. + /// Unsets the implicit `#[cfg(test)]` for the specified crates. cargo_unsetTest: Vec = "[\"core\"]", /// Run the check command for diagnostics on save. @@ -1189,7 +1191,34 @@ impl Config { sysroot, sysroot_src, rustc_source, - unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()), + cfg_overrides: project_model::CfgOverrides { + global: CfgDiff::new( + self.data + .cargo_cfgs + .iter() + .map(|(key, val)| { + if val.is_empty() { + CfgAtom::Flag(key.into()) + } else { + CfgAtom::KeyValue { key: key.into(), value: val.into() } + } + }) + .collect(), + vec![], + ) + .unwrap(), + selective: self + .data + .cargo_unsetTest + .iter() + .map(|it| { + ( + it.clone(), + CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(), + ) + }) + .collect(), + }, wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper, invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy { InvocationStrategy::Once => project_model::InvocationStrategy::Once, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index c2f8c6c754f..dc97366eef1 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -71,6 +71,11 @@ 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.cfgs]]rust-analyzer.cargo.cfgs (default: `{}`):: ++ +-- +List of cfg options to enable with the given values. +-- [[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`):: + -- @@ -120,7 +125,7 @@ Compilation target override (target triple). [[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`):: + -- -Unsets `#[cfg(test)]` for the specified crates. +Unsets the implicit `#[cfg(test)]` for the specified crates. -- [[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`):: + diff --git a/editors/code/package.json b/editors/code/package.json index 390508b883e..aa63c40c0d2 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -553,6 +553,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.cargo.cfgs": { + "markdownDescription": "List of cfg options to enable with the given values.", + "default": {}, + "type": "object" + }, "rust-analyzer.cargo.extraArgs": { "markdownDescription": "Extra arguments that are passed to every cargo invocation.", "default": [], @@ -617,7 +622,7 @@ ] }, "rust-analyzer.cargo.unsetTest": { - "markdownDescription": "Unsets `#[cfg(test)]` for the specified crates.", + "markdownDescription": "Unsets the implicit `#[cfg(test)]` for the specified crates.", "default": [ "core" ],