diff options
-rw-r--r-- | crates/cfg/src/lib.rs | 17 | ||||
-rw-r--r-- | crates/project_model/src/cargo_workspace.rs | 17 | ||||
-rw-r--r-- | crates/project_model/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/project_model/src/workspace.rs | 124 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 4 | ||||
-rw-r--r-- | docs/user/generated_config.adoc | 5 | ||||
-rw-r--r-- | editors/code/package.json | 10 |
7 files changed, 130 insertions, 49 deletions
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs index 03b8dd767..916d39a0b 100644 --- a/crates/cfg/src/lib.rs +++ b/crates/cfg/src/lib.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | //! cfg defines conditional compiling options, `cfg` attibute parser and evaluator | 1 | //! cfg defines conditional compiling options, `cfg` attribute parser and evaluator |
2 | 2 | ||
3 | mod cfg_expr; | 3 | mod cfg_expr; |
4 | mod dnf; | 4 | mod dnf; |
@@ -52,6 +52,7 @@ impl CfgOptions { | |||
52 | } | 52 | } |
53 | } | 53 | } |
54 | 54 | ||
55 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
55 | pub struct CfgDiff { | 56 | pub struct CfgDiff { |
56 | // Invariants: No duplicates, no atom that's both in `enable` and `disable`. | 57 | // Invariants: No duplicates, no atom that's both in `enable` and `disable`. |
57 | enable: Vec<CfgAtom>, | 58 | enable: Vec<CfgAtom>, |
@@ -59,6 +60,20 @@ pub struct CfgDiff { | |||
59 | } | 60 | } |
60 | 61 | ||
61 | impl CfgDiff { | 62 | impl CfgDiff { |
63 | /// Create a new CfgDiff. Will return None if the same item appears more than once in the set | ||
64 | /// of both. | ||
65 | pub fn new(enable: Vec<CfgAtom>, disable: Vec<CfgAtom>) -> Option<CfgDiff> { | ||
66 | let mut occupied = FxHashSet::default(); | ||
67 | for item in enable.iter().chain(disable.iter()) { | ||
68 | if !occupied.insert(item) { | ||
69 | // was present | ||
70 | return None; | ||
71 | } | ||
72 | } | ||
73 | |||
74 | Some(CfgDiff { enable, disable }) | ||
75 | } | ||
76 | |||
62 | /// Returns the total number of atoms changed by this diff. | 77 | /// Returns the total number of atoms changed by this diff. |
63 | pub fn len(&self) -> usize { | 78 | pub fn len(&self) -> usize { |
64 | self.enable.len() + self.disable.len() | 79 | self.enable.len() + self.disable.len() |
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index ac079f83e..0935ea967 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! See [`CargoWorkspace`]. | 1 | //! See [`CargoWorkspace`]. |
2 | 2 | ||
3 | use std::iter; | ||
3 | use std::path::PathBuf; | 4 | use std::path::PathBuf; |
4 | use std::{convert::TryInto, ops, process::Command, sync::Arc}; | 5 | use std::{convert::TryInto, ops, process::Command, sync::Arc}; |
5 | 6 | ||
@@ -12,6 +13,7 @@ use rustc_hash::FxHashMap; | |||
12 | use serde::Deserialize; | 13 | use serde::Deserialize; |
13 | use serde_json::from_value; | 14 | use serde_json::from_value; |
14 | 15 | ||
16 | use crate::CfgOverrides; | ||
15 | use crate::{build_data::BuildDataConfig, utf8_stdout}; | 17 | use crate::{build_data::BuildDataConfig, utf8_stdout}; |
16 | 18 | ||
17 | /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo | 19 | /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo |
@@ -76,6 +78,21 @@ pub struct CargoConfig { | |||
76 | 78 | ||
77 | /// rustc private crate source | 79 | /// rustc private crate source |
78 | pub rustc_source: Option<RustcSource>, | 80 | pub rustc_source: Option<RustcSource>, |
81 | |||
82 | /// crates to disable `#[cfg(test)]` on | ||
83 | pub unset_test_crates: Vec<String>, | ||
84 | } | ||
85 | |||
86 | impl CargoConfig { | ||
87 | pub fn cfg_overrides(&self) -> CfgOverrides { | ||
88 | self.unset_test_crates | ||
89 | .iter() | ||
90 | .cloned() | ||
91 | .zip(iter::repeat_with(|| { | ||
92 | cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap() | ||
93 | })) | ||
94 | .collect() | ||
95 | } | ||
79 | } | 96 | } |
80 | 97 | ||
81 | pub type Package = Idx<PackageData>; | 98 | pub type Package = Idx<PackageData>; |
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs index 8c6cf94c2..1d408dff2 100644 --- a/crates/project_model/src/lib.rs +++ b/crates/project_model/src/lib.rs | |||
@@ -41,7 +41,7 @@ pub use crate::{ | |||
41 | }, | 41 | }, |
42 | project_json::{ProjectJson, ProjectJsonData}, | 42 | project_json::{ProjectJson, ProjectJsonData}, |
43 | sysroot::Sysroot, | 43 | sysroot::Sysroot, |
44 | workspace::{PackageRoot, ProjectWorkspace}, | 44 | workspace::{CfgOverrides, PackageRoot, ProjectWorkspace}, |
45 | }; | 45 | }; |
46 | 46 | ||
47 | pub use proc_macro_api::ProcMacroClient; | 47 | pub use proc_macro_api::ProcMacroClient; |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index ef0f3c9e4..d8217f714 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -7,7 +7,7 @@ use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; | |||
7 | use anyhow::{format_err, Context, Result}; | 7 | use anyhow::{format_err, Context, Result}; |
8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; | 8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; |
9 | use cargo_workspace::DepKind; | 9 | use cargo_workspace::DepKind; |
10 | use cfg::CfgOptions; | 10 | use cfg::{CfgDiff, CfgOptions}; |
11 | use paths::{AbsPath, AbsPathBuf}; | 11 | use paths::{AbsPath, AbsPathBuf}; |
12 | use proc_macro_api::ProcMacroClient; | 12 | use proc_macro_api::ProcMacroClient; |
13 | use rustc_hash::{FxHashMap, FxHashSet}; | 13 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -22,6 +22,8 @@ use crate::{ | |||
22 | Sysroot, TargetKind, | 22 | Sysroot, TargetKind, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | pub type CfgOverrides = FxHashMap<String, CfgDiff>; | ||
26 | |||
25 | /// `PackageRoot` describes a package root folder. | 27 | /// `PackageRoot` describes a package root folder. |
26 | /// Which may be an external dependency, or a member of | 28 | /// Which may be an external dependency, or a member of |
27 | /// the current workspace. | 29 | /// the current workspace. |
@@ -46,6 +48,7 @@ pub enum ProjectWorkspace { | |||
46 | /// FIXME: make this a per-crate map, as, eg, build.rs might have a | 48 | /// FIXME: make this a per-crate map, as, eg, build.rs might have a |
47 | /// different target. | 49 | /// different target. |
48 | rustc_cfg: Vec<CfgFlag>, | 50 | rustc_cfg: Vec<CfgFlag>, |
51 | cfg_overrides: CfgOverrides, | ||
49 | }, | 52 | }, |
50 | /// Project workspace was manually specified using a `rust-project.json` file. | 53 | /// Project workspace was manually specified using a `rust-project.json` file. |
51 | Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> }, | 54 | Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> }, |
@@ -67,7 +70,7 @@ impl fmt::Debug for ProjectWorkspace { | |||
67 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 70 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
68 | // Make sure this isn't too verbose. | 71 | // Make sure this isn't too verbose. |
69 | match self { | 72 | match self { |
70 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f | 73 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => f |
71 | .debug_struct("Cargo") | 74 | .debug_struct("Cargo") |
72 | .field("root", &cargo.workspace_root().file_name()) | 75 | .field("root", &cargo.workspace_root().file_name()) |
73 | .field("n_packages", &cargo.packages().len()) | 76 | .field("n_packages", &cargo.packages().len()) |
@@ -77,6 +80,7 @@ impl fmt::Debug for ProjectWorkspace { | |||
77 | &rustc.as_ref().map_or(0, |rc| rc.packages().len()), | 80 | &rustc.as_ref().map_or(0, |rc| rc.packages().len()), |
78 | ) | 81 | ) |
79 | .field("n_rustc_cfg", &rustc_cfg.len()) | 82 | .field("n_rustc_cfg", &rustc_cfg.len()) |
83 | .field("n_cfg_overrides", &cfg_overrides.len()) | ||
80 | .finish(), | 84 | .finish(), |
81 | ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { | 85 | ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { |
82 | let mut debug_struct = f.debug_struct("Json"); | 86 | let mut debug_struct = f.debug_struct("Json"); |
@@ -164,7 +168,9 @@ impl ProjectWorkspace { | |||
164 | }; | 168 | }; |
165 | 169 | ||
166 | let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref()); | 170 | let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref()); |
167 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } | 171 | |
172 | let cfg_overrides = config.cfg_overrides(); | ||
173 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } | ||
168 | } | 174 | } |
169 | }; | 175 | }; |
170 | 176 | ||
@@ -213,43 +219,45 @@ impl ProjectWorkspace { | |||
213 | }) | 219 | }) |
214 | })) | 220 | })) |
215 | .collect::<Vec<_>>(), | 221 | .collect::<Vec<_>>(), |
216 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo | 222 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _, cfg_overrides: _ } => { |
217 | .packages() | 223 | cargo |
218 | .map(|pkg| { | 224 | .packages() |
219 | let is_member = cargo[pkg].is_member; | 225 | .map(|pkg| { |
220 | let pkg_root = cargo[pkg].root().to_path_buf(); | 226 | let is_member = cargo[pkg].is_member; |
221 | 227 | let pkg_root = cargo[pkg].root().to_path_buf(); | |
222 | let mut include = vec![pkg_root.clone()]; | 228 | |
223 | include.extend( | 229 | let mut include = vec![pkg_root.clone()]; |
224 | build_data | 230 | include.extend( |
225 | .and_then(|it| it.get(cargo.workspace_root())) | 231 | build_data |
226 | .and_then(|map| map.get(&cargo[pkg].id)) | 232 | .and_then(|it| it.get(cargo.workspace_root())) |
227 | .and_then(|it| it.out_dir.clone()), | 233 | .and_then(|map| map.get(&cargo[pkg].id)) |
228 | ); | 234 | .and_then(|it| it.out_dir.clone()), |
235 | ); | ||
229 | 236 | ||
230 | let mut exclude = vec![pkg_root.join(".git")]; | 237 | let mut exclude = vec![pkg_root.join(".git")]; |
231 | if is_member { | 238 | if is_member { |
232 | exclude.push(pkg_root.join("target")); | 239 | exclude.push(pkg_root.join("target")); |
233 | } else { | 240 | } else { |
234 | exclude.push(pkg_root.join("tests")); | 241 | exclude.push(pkg_root.join("tests")); |
235 | exclude.push(pkg_root.join("examples")); | 242 | exclude.push(pkg_root.join("examples")); |
236 | exclude.push(pkg_root.join("benches")); | 243 | exclude.push(pkg_root.join("benches")); |
237 | } | 244 | } |
238 | PackageRoot { is_member, include, exclude } | 245 | PackageRoot { is_member, include, exclude } |
239 | }) | 246 | }) |
240 | .chain(sysroot.crates().map(|krate| PackageRoot { | 247 | .chain(sysroot.crates().map(|krate| PackageRoot { |
241 | is_member: false, | ||
242 | include: vec![sysroot[krate].root_dir().to_path_buf()], | ||
243 | exclude: Vec::new(), | ||
244 | })) | ||
245 | .chain(rustc.into_iter().flat_map(|rustc| { | ||
246 | rustc.packages().map(move |krate| PackageRoot { | ||
247 | is_member: false, | 248 | is_member: false, |
248 | include: vec![rustc[krate].root().to_path_buf()], | 249 | include: vec![sysroot[krate].root_dir().to_path_buf()], |
249 | exclude: Vec::new(), | 250 | exclude: Vec::new(), |
250 | }) | 251 | })) |
251 | })) | 252 | .chain(rustc.into_iter().flat_map(|rustc| { |
252 | .collect(), | 253 | rustc.packages().map(move |krate| PackageRoot { |
254 | is_member: false, | ||
255 | include: vec![rustc[krate].root().to_path_buf()], | ||
256 | exclude: Vec::new(), | ||
257 | }) | ||
258 | })) | ||
259 | .collect() | ||
260 | } | ||
253 | ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files | 261 | ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files |
254 | .into_iter() | 262 | .into_iter() |
255 | .map(|detached_file| PackageRoot { | 263 | .map(|detached_file| PackageRoot { |
@@ -299,16 +307,22 @@ impl ProjectWorkspace { | |||
299 | project, | 307 | project, |
300 | sysroot, | 308 | sysroot, |
301 | ), | 309 | ), |
302 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph( | 310 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => { |
303 | rustc_cfg.clone(), | 311 | cargo_to_crate_graph( |
304 | &proc_macro_loader, | 312 | rustc_cfg.clone(), |
305 | load, | 313 | cfg_overrides, |
306 | cargo, | 314 | &proc_macro_loader, |
307 | build_data.and_then(|it| it.get(cargo.workspace_root())), | 315 | load, |
308 | sysroot, | 316 | cargo, |
309 | rustc, | 317 | build_data.and_then(|it| it.get(cargo.workspace_root())), |
310 | rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())), | 318 | sysroot, |
311 | ), | 319 | rustc, |
320 | rustc | ||
321 | .as_ref() | ||
322 | .zip(build_data) | ||
323 | .and_then(|(it, map)| map.get(it.workspace_root())), | ||
324 | ) | ||
325 | } | ||
312 | ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { | 326 | ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { |
313 | detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot) | 327 | detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot) |
314 | } | 328 | } |
@@ -398,6 +412,7 @@ fn project_json_to_crate_graph( | |||
398 | 412 | ||
399 | fn cargo_to_crate_graph( | 413 | fn cargo_to_crate_graph( |
400 | rustc_cfg: Vec<CfgFlag>, | 414 | rustc_cfg: Vec<CfgFlag>, |
415 | override_cfg: &CfgOverrides, | ||
401 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | 416 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, |
402 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, | 417 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
403 | cargo: &CargoWorkspace, | 418 | cargo: &CargoWorkspace, |
@@ -425,6 +440,21 @@ fn cargo_to_crate_graph( | |||
425 | let mut has_private = false; | 440 | let mut has_private = false; |
426 | // Next, create crates for each package, target pair | 441 | // Next, create crates for each package, target pair |
427 | for pkg in cargo.packages() { | 442 | for pkg in cargo.packages() { |
443 | let mut cfg_options = &cfg_options; | ||
444 | let mut replaced_cfg_options; | ||
445 | if let Some(overrides) = override_cfg.get(&cargo[pkg].name) { | ||
446 | // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen | ||
447 | // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while | ||
448 | // working on rust-lang/rust as that's the only time it appears outside sysroot). | ||
449 | // | ||
450 | // A more ideal solution might be to reanalyze crates based on where the cursor is and | ||
451 | // figure out the set of cfgs that would have to apply to make it active. | ||
452 | |||
453 | replaced_cfg_options = cfg_options.clone(); | ||
454 | replaced_cfg_options.apply_diff(overrides.clone()); | ||
455 | cfg_options = &replaced_cfg_options; | ||
456 | }; | ||
457 | |||
428 | has_private |= cargo[pkg].metadata.rustc_private; | 458 | has_private |= cargo[pkg].metadata.rustc_private; |
429 | let mut lib_tgt = None; | 459 | let mut lib_tgt = None; |
430 | for &tgt in cargo[pkg].targets.iter() { | 460 | for &tgt in cargo[pkg].targets.iter() { |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 16c295639..7e0276c10 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -55,6 +55,8 @@ config_data! { | |||
55 | cargo_autoreload: bool = "true", | 55 | cargo_autoreload: bool = "true", |
56 | /// Activate all available features (`--all-features`). | 56 | /// Activate all available features (`--all-features`). |
57 | cargo_allFeatures: bool = "false", | 57 | cargo_allFeatures: bool = "false", |
58 | /// Unsets `#[cfg(test)]` for the specified crates. | ||
59 | cargo_unsetTest: Vec<String> = "[\"core\"]", | ||
58 | /// List of features to activate. | 60 | /// List of features to activate. |
59 | cargo_features: Vec<String> = "[]", | 61 | cargo_features: Vec<String> = "[]", |
60 | /// Run build scripts (`build.rs`) for more precise code analysis. | 62 | /// Run build scripts (`build.rs`) for more precise code analysis. |
@@ -595,8 +597,10 @@ impl Config { | |||
595 | target: self.data.cargo_target.clone(), | 597 | target: self.data.cargo_target.clone(), |
596 | rustc_source, | 598 | rustc_source, |
597 | no_sysroot: self.data.cargo_noSysroot, | 599 | no_sysroot: self.data.cargo_noSysroot, |
600 | unset_test_crates: self.data.cargo_unsetTest.clone(), | ||
598 | } | 601 | } |
599 | } | 602 | } |
603 | |||
600 | pub fn rustfmt(&self) -> RustfmtConfig { | 604 | pub fn rustfmt(&self) -> RustfmtConfig { |
601 | match &self.data.rustfmt_overrideCommand { | 605 | match &self.data.rustfmt_overrideCommand { |
602 | Some(args) if !args.is_empty() => { | 606 | Some(args) if !args.is_empty() => { |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 18ea77266..58cb46974 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -39,6 +39,11 @@ Automatically refresh project info via `cargo metadata` on | |||
39 | -- | 39 | -- |
40 | Activate all available features (`--all-features`). | 40 | Activate all available features (`--all-features`). |
41 | -- | 41 | -- |
42 | [[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`):: | ||
43 | + | ||
44 | -- | ||
45 | Unsets `#[cfg(test)]` for the specified crates. | ||
46 | -- | ||
42 | [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: | 47 | [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: |
43 | + | 48 | + |
44 | -- | 49 | -- |
diff --git a/editors/code/package.json b/editors/code/package.json index c077bd2c0..b20a39a95 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -452,6 +452,16 @@ | |||
452 | "default": false, | 452 | "default": false, |
453 | "type": "boolean" | 453 | "type": "boolean" |
454 | }, | 454 | }, |
455 | "rust-analyzer.cargo.unsetTest": { | ||
456 | "markdownDescription": "Unsets `#[cfg(test)]` for the specified crates.", | ||
457 | "default": [ | ||
458 | "core" | ||
459 | ], | ||
460 | "type": "array", | ||
461 | "items": { | ||
462 | "type": "string" | ||
463 | } | ||
464 | }, | ||
455 | "rust-analyzer.cargo.features": { | 465 | "rust-analyzer.cargo.features": { |
456 | "markdownDescription": "List of features to activate.", | 466 | "markdownDescription": "List of features to activate.", |
457 | "default": [], | 467 | "default": [], |