diff options
author | Jade <[email protected]> | 2021-06-14 05:41:46 +0100 |
---|---|---|
committer | Jade <[email protected]> | 2021-06-19 09:09:19 +0100 |
commit | 8b77e2692cd97552b1b8d66eb51cec69695b3a5b (patch) | |
tree | 82187a911cd5e5acd8ce84f1f375d53185a05c7f /crates/project_model | |
parent | 1f6abb7fbae35dbd036032a6ea2a9e04307f1f55 (diff) |
Implement a config override for the default #[cfg(test)] in cargo crates
Fixes crates which vanish when the 'test' cfg atom is set.
Fix #7243.
Fix #9203.
Fix #7225.
Diffstat (limited to 'crates/project_model')
-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 |
3 files changed, 88 insertions, 55 deletions
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 c2edb3327..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::{CfgAtom, CfgDiff, 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, |
@@ -427,15 +442,16 @@ fn cargo_to_crate_graph( | |||
427 | for pkg in cargo.packages() { | 442 | for pkg in cargo.packages() { |
428 | let mut cfg_options = &cfg_options; | 443 | let mut cfg_options = &cfg_options; |
429 | let mut replaced_cfg_options; | 444 | let mut replaced_cfg_options; |
430 | if cargo[pkg].name == "core" { | 445 | if let Some(overrides) = override_cfg.get(&cargo[pkg].name) { |
431 | // FIXME: in the specific case of libcore in rust-lang/rust (i.e. it is not coming from | 446 | // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen |
432 | // a sysroot), there's a `#![cfg(not(test))]` at the top of it that makes its contents | 447 | // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while |
433 | // get ignored by r-a. We should implement a more general solution for this | 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. | ||
434 | 452 | ||
435 | replaced_cfg_options = cfg_options.clone(); | 453 | replaced_cfg_options = cfg_options.clone(); |
436 | replaced_cfg_options.apply_diff( | 454 | replaced_cfg_options.apply_diff(overrides.clone()); |
437 | CfgDiff::new(Default::default(), vec![CfgAtom::Flag("test".into())]).unwrap(), | ||
438 | ); | ||
439 | cfg_options = &replaced_cfg_options; | 455 | cfg_options = &replaced_cfg_options; |
440 | }; | 456 | }; |
441 | 457 | ||