aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/cfg/src/lib.rs17
-rw-r--r--crates/project_model/src/cargo_workspace.rs17
-rw-r--r--crates/project_model/src/lib.rs2
-rw-r--r--crates/project_model/src/workspace.rs124
-rw-r--r--crates/rust-analyzer/src/config.rs4
-rw-r--r--docs/user/generated_config.adoc5
-rw-r--r--editors/code/package.json10
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
3mod cfg_expr; 3mod cfg_expr;
4mod dnf; 4mod dnf;
@@ -52,6 +52,7 @@ impl CfgOptions {
52 } 52 }
53} 53}
54 54
55#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct CfgDiff { 56pub 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
61impl CfgDiff { 62impl 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
3use std::iter;
3use std::path::PathBuf; 4use std::path::PathBuf;
4use std::{convert::TryInto, ops, process::Command, sync::Arc}; 5use std::{convert::TryInto, ops, process::Command, sync::Arc};
5 6
@@ -12,6 +13,7 @@ use rustc_hash::FxHashMap;
12use serde::Deserialize; 13use serde::Deserialize;
13use serde_json::from_value; 14use serde_json::from_value;
14 15
16use crate::CfgOverrides;
15use crate::{build_data::BuildDataConfig, utf8_stdout}; 17use 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
86impl 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
81pub type Package = Idx<PackageData>; 98pub 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
47pub use proc_macro_api::ProcMacroClient; 47pub 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};
7use anyhow::{format_err, Context, Result}; 7use anyhow::{format_err, Context, Result};
8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; 8use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
9use cargo_workspace::DepKind; 9use cargo_workspace::DepKind;
10use cfg::CfgOptions; 10use cfg::{CfgDiff, CfgOptions};
11use paths::{AbsPath, AbsPathBuf}; 11use paths::{AbsPath, AbsPathBuf};
12use proc_macro_api::ProcMacroClient; 12use proc_macro_api::ProcMacroClient;
13use rustc_hash::{FxHashMap, FxHashSet}; 13use rustc_hash::{FxHashMap, FxHashSet};
@@ -22,6 +22,8 @@ use crate::{
22 Sysroot, TargetKind, 22 Sysroot, TargetKind,
23}; 23};
24 24
25pub 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
399fn cargo_to_crate_graph( 413fn 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--
40Activate all available features (`--all-features`). 40Activate all available features (`--all-features`).
41-- 41--
42[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
43+
44--
45Unsets `#[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": [],