mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 04:23:25 +00:00
Merge #9227
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:
commit
b48aba0090
7 changed files with 130 additions and 49 deletions
|
@ -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()
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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() => {
|
||||||
|
|
|
@ -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: `[]`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
|
@ -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": [],
|
||||||
|
|
Loading…
Reference in a new issue