9227: Add a config setting to disable the 'test' cfg in specified crates r=matklad a=lf-

If you are opening libcore from rust-lang/rust as opposed to e.g.
goto definition from some other crate which would use the sysroot
instance of libcore, a `#![cfg(not(test))]` would previously have made
all the code excluded from the module tree, breaking the editor
experience.

Core does not need to ever be edited with `#[cfg(test)]` enabled,
as the tests are in another crate.

This PR puts in a slight hack that checks for the crate name "core" and
turns off `#[cfg(test)]` for that crate.

Fixes #9203 
Fixes #9226 

Co-authored-by: Jade <software@lfcode.ca>
This commit is contained in:
bors[bot] 2021-06-21 13:41:27 +00:00 committed by GitHub
commit b48aba0090
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 130 additions and 49 deletions

View file

@ -1,4 +1,4 @@
//! cfg defines conditional compiling options, `cfg` attibute parser and evaluator //! cfg defines conditional compiling options, `cfg` attribute parser and evaluator
mod cfg_expr; mod cfg_expr;
mod dnf; mod dnf;
@ -52,6 +52,7 @@ impl CfgOptions {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff { pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`. // Invariants: No duplicates, no atom that's both in `enable` and `disable`.
enable: Vec<CfgAtom>, enable: Vec<CfgAtom>,
@ -59,6 +60,20 @@ pub struct CfgDiff {
} }
impl CfgDiff { impl CfgDiff {
/// Create a new CfgDiff. Will return None if the same item appears more than once in the set
/// of both.
pub fn new(enable: Vec<CfgAtom>, disable: Vec<CfgAtom>) -> Option<CfgDiff> {
let mut occupied = FxHashSet::default();
for item in enable.iter().chain(disable.iter()) {
if !occupied.insert(item) {
// was present
return None;
}
}
Some(CfgDiff { enable, disable })
}
/// Returns the total number of atoms changed by this diff. /// Returns the total number of atoms changed by this diff.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.enable.len() + self.disable.len() self.enable.len() + self.disable.len()

View file

@ -1,5 +1,6 @@
//! See [`CargoWorkspace`]. //! See [`CargoWorkspace`].
use std::iter;
use std::path::PathBuf; use std::path::PathBuf;
use std::{convert::TryInto, ops, process::Command, sync::Arc}; use std::{convert::TryInto, ops, process::Command, sync::Arc};
@ -12,6 +13,7 @@ use rustc_hash::FxHashMap;
use serde::Deserialize; use serde::Deserialize;
use serde_json::from_value; use serde_json::from_value;
use crate::CfgOverrides;
use crate::{build_data::BuildDataConfig, utf8_stdout}; use crate::{build_data::BuildDataConfig, utf8_stdout};
/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
@ -76,6 +78,21 @@ pub struct CargoConfig {
/// rustc private crate source /// rustc private crate source
pub rustc_source: Option<RustcSource>, pub rustc_source: Option<RustcSource>,
/// crates to disable `#[cfg(test)]` on
pub unset_test_crates: Vec<String>,
}
impl CargoConfig {
pub fn cfg_overrides(&self) -> CfgOverrides {
self.unset_test_crates
.iter()
.cloned()
.zip(iter::repeat_with(|| {
cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap()
}))
.collect()
}
} }
pub type Package = Idx<PackageData>; pub type Package = Idx<PackageData>;

View file

@ -41,7 +41,7 @@ pub use crate::{
}, },
project_json::{ProjectJson, ProjectJsonData}, project_json::{ProjectJson, ProjectJsonData},
sysroot::Sysroot, sysroot::Sysroot,
workspace::{PackageRoot, ProjectWorkspace}, workspace::{CfgOverrides, PackageRoot, ProjectWorkspace},
}; };
pub use proc_macro_api::ProcMacroClient; pub use proc_macro_api::ProcMacroClient;

View file

@ -7,7 +7,7 @@ use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
use anyhow::{format_err, Context, Result}; use anyhow::{format_err, Context, Result};
use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
use cargo_workspace::DepKind; use cargo_workspace::DepKind;
use cfg::CfgOptions; use cfg::{CfgDiff, CfgOptions};
use paths::{AbsPath, AbsPathBuf}; use paths::{AbsPath, AbsPathBuf};
use proc_macro_api::ProcMacroClient; use proc_macro_api::ProcMacroClient;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
@ -22,6 +22,8 @@ use crate::{
Sysroot, TargetKind, Sysroot, TargetKind,
}; };
pub type CfgOverrides = FxHashMap<String, CfgDiff>;
/// `PackageRoot` describes a package root folder. /// `PackageRoot` describes a package root folder.
/// Which may be an external dependency, or a member of /// Which may be an external dependency, or a member of
/// the current workspace. /// the current workspace.
@ -46,6 +48,7 @@ pub enum ProjectWorkspace {
/// FIXME: make this a per-crate map, as, eg, build.rs might have a /// FIXME: make this a per-crate map, as, eg, build.rs might have a
/// different target. /// different target.
rustc_cfg: Vec<CfgFlag>, rustc_cfg: Vec<CfgFlag>,
cfg_overrides: CfgOverrides,
}, },
/// Project workspace was manually specified using a `rust-project.json` file. /// Project workspace was manually specified using a `rust-project.json` file.
Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> }, Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
@ -67,7 +70,7 @@ impl fmt::Debug for ProjectWorkspace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Make sure this isn't too verbose. // Make sure this isn't too verbose.
match self { match self {
ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => f
.debug_struct("Cargo") .debug_struct("Cargo")
.field("root", &cargo.workspace_root().file_name()) .field("root", &cargo.workspace_root().file_name())
.field("n_packages", &cargo.packages().len()) .field("n_packages", &cargo.packages().len())
@ -77,6 +80,7 @@ impl fmt::Debug for ProjectWorkspace {
&rustc.as_ref().map_or(0, |rc| rc.packages().len()), &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
) )
.field("n_rustc_cfg", &rustc_cfg.len()) .field("n_rustc_cfg", &rustc_cfg.len())
.field("n_cfg_overrides", &cfg_overrides.len())
.finish(), .finish(),
ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
let mut debug_struct = f.debug_struct("Json"); let mut debug_struct = f.debug_struct("Json");
@ -164,7 +168,9 @@ impl ProjectWorkspace {
}; };
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());
ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg }
let cfg_overrides = config.cfg_overrides();
ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides }
} }
}; };
@ -213,43 +219,45 @@ impl ProjectWorkspace {
}) })
})) }))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _ } => cargo ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg: _, cfg_overrides: _ } => {
.packages() cargo
.map(|pkg| { .packages()
let is_member = cargo[pkg].is_member; .map(|pkg| {
let pkg_root = cargo[pkg].root().to_path_buf(); let is_member = cargo[pkg].is_member;
let pkg_root = cargo[pkg].root().to_path_buf();
let mut include = vec![pkg_root.clone()]; let mut include = vec![pkg_root.clone()];
include.extend( include.extend(
build_data build_data
.and_then(|it| it.get(cargo.workspace_root())) .and_then(|it| it.get(cargo.workspace_root()))
.and_then(|map| map.get(&cargo[pkg].id)) .and_then(|map| map.get(&cargo[pkg].id))
.and_then(|it| it.out_dir.clone()), .and_then(|it| it.out_dir.clone()),
); );
let mut exclude = vec![pkg_root.join(".git")]; let mut exclude = vec![pkg_root.join(".git")];
if is_member { if is_member {
exclude.push(pkg_root.join("target")); exclude.push(pkg_root.join("target"));
} else { } else {
exclude.push(pkg_root.join("tests")); exclude.push(pkg_root.join("tests"));
exclude.push(pkg_root.join("examples")); exclude.push(pkg_root.join("examples"));
exclude.push(pkg_root.join("benches")); exclude.push(pkg_root.join("benches"));
} }
PackageRoot { is_member, include, exclude } PackageRoot { is_member, include, exclude }
})
.chain(sysroot.crates().map(|krate| PackageRoot {
is_member: false,
include: vec![sysroot[krate].root_dir().to_path_buf()],
exclude: Vec::new(),
}))
.chain(rustc.into_iter().flat_map(|rustc| {
rustc.packages().map(move |krate| PackageRoot {
is_member: false,
include: vec![rustc[krate].root().to_path_buf()],
exclude: Vec::new(),
}) })
})) .chain(sysroot.crates().map(|krate| PackageRoot {
.collect(), is_member: false,
include: vec![sysroot[krate].root_dir().to_path_buf()],
exclude: Vec::new(),
}))
.chain(rustc.into_iter().flat_map(|rustc| {
rustc.packages().map(move |krate| PackageRoot {
is_member: false,
include: vec![rustc[krate].root().to_path_buf()],
exclude: Vec::new(),
})
}))
.collect()
}
ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
.into_iter() .into_iter()
.map(|detached_file| PackageRoot { .map(|detached_file| PackageRoot {
@ -299,16 +307,22 @@ impl ProjectWorkspace {
project, project,
sysroot, sysroot,
), ),
ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => cargo_to_crate_graph( ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg, cfg_overrides } => {
rustc_cfg.clone(), cargo_to_crate_graph(
&proc_macro_loader, rustc_cfg.clone(),
load, cfg_overrides,
cargo, &proc_macro_loader,
build_data.and_then(|it| it.get(cargo.workspace_root())), load,
sysroot, cargo,
rustc, build_data.and_then(|it| it.get(cargo.workspace_root())),
rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())), sysroot,
), rustc,
rustc
.as_ref()
.zip(build_data)
.and_then(|(it, map)| map.get(it.workspace_root())),
)
}
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot) detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
} }
@ -398,6 +412,7 @@ fn project_json_to_crate_graph(
fn cargo_to_crate_graph( fn cargo_to_crate_graph(
rustc_cfg: Vec<CfgFlag>, rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
cargo: &CargoWorkspace, cargo: &CargoWorkspace,
@ -425,6 +440,21 @@ fn cargo_to_crate_graph(
let mut has_private = false; let mut has_private = false;
// Next, create crates for each package, target pair // Next, create crates for each package, target pair
for pkg in cargo.packages() { for pkg in cargo.packages() {
let mut cfg_options = &cfg_options;
let mut replaced_cfg_options;
if let Some(overrides) = override_cfg.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).
//
// 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.
replaced_cfg_options = cfg_options.clone();
replaced_cfg_options.apply_diff(overrides.clone());
cfg_options = &replaced_cfg_options;
};
has_private |= cargo[pkg].metadata.rustc_private; has_private |= cargo[pkg].metadata.rustc_private;
let mut lib_tgt = None; let mut lib_tgt = None;
for &tgt in cargo[pkg].targets.iter() { for &tgt in cargo[pkg].targets.iter() {

View file

@ -55,6 +55,8 @@ config_data! {
cargo_autoreload: bool = "true", cargo_autoreload: bool = "true",
/// Activate all available features (`--all-features`). /// Activate all available features (`--all-features`).
cargo_allFeatures: bool = "false", cargo_allFeatures: bool = "false",
/// Unsets `#[cfg(test)]` for the specified crates.
cargo_unsetTest: Vec<String> = "[\"core\"]",
/// List of features to activate. /// List of features to activate.
cargo_features: Vec<String> = "[]", cargo_features: Vec<String> = "[]",
/// Run build scripts (`build.rs`) for more precise code analysis. /// Run build scripts (`build.rs`) for more precise code analysis.
@ -595,8 +597,10 @@ impl Config {
target: self.data.cargo_target.clone(), target: self.data.cargo_target.clone(),
rustc_source, rustc_source,
no_sysroot: self.data.cargo_noSysroot, no_sysroot: self.data.cargo_noSysroot,
unset_test_crates: self.data.cargo_unsetTest.clone(),
} }
} }
pub fn rustfmt(&self) -> RustfmtConfig { pub fn rustfmt(&self) -> RustfmtConfig {
match &self.data.rustfmt_overrideCommand { match &self.data.rustfmt_overrideCommand {
Some(args) if !args.is_empty() => { Some(args) if !args.is_empty() => {

View file

@ -39,6 +39,11 @@ Automatically refresh project info via `cargo metadata` on
-- --
Activate all available features (`--all-features`). Activate all available features (`--all-features`).
-- --
[[rust-analyzer.cargo.unsetTest]]rust-analyzer.cargo.unsetTest (default: `["core"]`)::
+
--
Unsets `#[cfg(test)]` for the specified crates.
--
[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
+ +
-- --

View file

@ -452,6 +452,16 @@
"default": false, "default": false,
"type": "boolean" "type": "boolean"
}, },
"rust-analyzer.cargo.unsetTest": {
"markdownDescription": "Unsets `#[cfg(test)]` for the specified crates.",
"default": [
"core"
],
"type": "array",
"items": {
"type": "string"
}
},
"rust-analyzer.cargo.features": { "rust-analyzer.cargo.features": {
"markdownDescription": "List of features to activate.", "markdownDescription": "List of features to activate.",
"default": [], "default": [],