Allow unsetting default cfgs

This commit is contained in:
Lukas Wirth 2025-02-27 16:49:37 +01:00
parent 505b52da5f
commit 5e18ad0770
10 changed files with 96 additions and 64 deletions

View File

@ -18,6 +18,25 @@ pub enum CfgAtom {
KeyValue { key: Symbol, value: Symbol },
}
impl PartialOrd for CfgAtom {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for CfgAtom {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
(CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
(CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
(CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
(CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
}
}
}
}
impl fmt::Display for CfgAtom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {

View File

@ -66,9 +66,9 @@ impl DnfExpr {
}
}
res.enabled.sort_unstable_by(compare);
res.enabled.sort_unstable();
res.enabled.dedup();
res.disabled.sort_unstable_by(compare);
res.disabled.sort_unstable();
res.disabled.dedup();
Some(res)
}
@ -114,25 +114,14 @@ impl DnfExpr {
};
// Undo the FxHashMap randomization for consistent output.
diff.enable.sort_unstable_by(compare);
diff.disable.sort_unstable_by(compare);
diff.enable.sort_unstable();
diff.disable.sort_unstable();
Some(diff)
})
}
}
fn compare(a: &CfgAtom, b: &CfgAtom) -> std::cmp::Ordering {
match (a, b) {
(CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
(CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
(CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
(CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
}
}
}
impl fmt::Display for DnfExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.conjunctions.len() != 1 {

View File

@ -148,16 +148,20 @@ pub struct 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();
if enable.iter().chain(disable.iter()).any(|item| !occupied.insert(item)) {
// was present
return None;
/// Create a new CfgDiff.
pub fn new(mut enable: Vec<CfgAtom>, mut disable: Vec<CfgAtom>) -> CfgDiff {
enable.sort();
enable.dedup();
disable.sort();
disable.dedup();
for i in (0..enable.len()).rev() {
if let Some(j) = disable.iter().position(|atom| *atom == enable[i]) {
enable.remove(i);
disable.remove(j);
}
}
Some(CfgDiff { enable, disable })
CfgDiff { enable, disable }
}
/// Returns the total number of atoms changed by this diff.

View File

@ -166,7 +166,7 @@ fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
#[test]
fn cargo_hello_world_project_model_with_wildcard_overrides() {
let cfg_overrides = CfgOverrides {
global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]),
selective: Default::default(),
};
let (crate_graph, _proc_macros) =
@ -185,7 +185,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
global: Default::default(),
selective: std::iter::once((
"libc".to_owned(),
CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]),
))
.collect(),
};

View File

@ -1522,7 +1522,7 @@ fn extend_crate_graph_with_sysroot(
) -> (SysrootPublicDeps, Option<CrateId>) {
let mut pub_deps = vec![];
let mut libproc_macro = None;
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]);
for (cid, c) in sysroot_crate_graph.iter_mut() {
// uninject `test` flag so `core` keeps working.
Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
@ -1596,8 +1596,7 @@ fn sysroot_to_crate_graph(
CfgAtom::Flag(sym::miri.clone()),
],
vec![],
)
.unwrap(),
),
..Default::default()
},
&WorkspaceBuildScripts::default(),
@ -1620,8 +1619,7 @@ fn sysroot_to_crate_graph(
CfgAtom::Flag(sym::miri.clone()),
],
vec![],
)
.unwrap(),
),
..Default::default()
},
false,

View File

@ -69,7 +69,7 @@ impl flags::AnalysisStats {
all_targets: true,
set_test: !self.no_test,
cfg_overrides: CfgOverrides {
global: CfgDiff::new(vec![CfgAtom::Flag(hir::sym::miri.clone())], vec![]).unwrap(),
global: CfgDiff::new(vec![CfgAtom::Flag(hir::sym::miri.clone())], vec![]),
selective: Default::default(),
},
..Default::default()

View File

@ -18,7 +18,7 @@ use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
SnippetCap,
};
use itertools::Itertools;
use itertools::{Either, Itertools};
use paths::{Utf8Path, Utf8PathBuf};
use project_model::{
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand,
@ -589,6 +589,10 @@ config_data! {
/// avoid checking unnecessary things.
cargo_buildScripts_useRustcWrapper: bool = true,
/// List of cfg options to enable with the given values.
///
/// To enable a name without a value, use `"key"`.
/// To enable a name with a value, use `"key=value"`.
/// To disable, prefix the entry with a `!`.
cargo_cfgs: Vec<String> = {
vec!["debug_assertions".into(), "miri".into()]
},
@ -1980,27 +1984,35 @@ impl Config {
rustc_source,
extra_includes,
cfg_overrides: project_model::CfgOverrides {
global: CfgDiff::new(
self.cargo_cfgs(source_root)
.iter()
global: {
let (enabled, disabled): (Vec<_>, Vec<_>) =
self.cargo_cfgs(source_root).iter().partition_map(|s| {
s.strip_prefix("!").map_or(Either::Left(s), Either::Right)
});
CfgDiff::new(
enabled
.into_iter()
// parse any cfg setting formatted as key=value or just key (without value)
.filter_map(|s| {
let mut sp = s.splitn(2, "=");
let key = sp.next();
let val = sp.next();
key.map(|key| (key, val))
})
.map(|(key, val)| match val {
Some(val) => CfgAtom::KeyValue {
.map(|s| match s.split_once("=") {
Some((key, val)) => CfgAtom::KeyValue {
key: Symbol::intern(key),
value: Symbol::intern(val),
},
None => CfgAtom::Flag(Symbol::intern(key)),
None => CfgAtom::Flag(Symbol::intern(s)),
})
.collect(),
disabled
.into_iter()
.map(|s| match s.split_once("=") {
Some((key, val)) => CfgAtom::KeyValue {
key: Symbol::intern(key),
value: Symbol::intern(val),
},
None => CfgAtom::Flag(Symbol::intern(s)),
})
.collect(),
vec![],
)
.unwrap(),
},
selective: Default::default(),
},
wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(source_root),

View File

@ -244,8 +244,14 @@ struct FlycheckActor {
/// The receiver side of the channel mentioned above.
command_receiver: Option<Receiver<CargoCheckMessage>>,
diagnostics_cleared_for: FxHashSet<Arc<PackageId>>,
diagnostics_cleared_for_all: bool,
diagnostics_received: bool,
diagnostics_received: DiagnosticsReceived,
}
#[derive(PartialEq)]
enum DiagnosticsReceived {
Yes,
No,
YesAndClearedForAll,
}
#[allow(clippy::large_enum_variant)]
@ -276,8 +282,7 @@ impl FlycheckActor {
command_handle: None,
command_receiver: None,
diagnostics_cleared_for: Default::default(),
diagnostics_cleared_for_all: false,
diagnostics_received: false,
diagnostics_received: DiagnosticsReceived::No,
}
}
@ -354,7 +359,7 @@ impl FlycheckActor {
error
);
}
if !self.diagnostics_received {
if self.diagnostics_received == DiagnosticsReceived::No {
tracing::trace!(flycheck_id = self.id, "clearing diagnostics");
// We finished without receiving any diagnostics.
// Clear everything for good measure
@ -396,7 +401,7 @@ impl FlycheckActor {
package_id = package_id.as_ref().map(|it| &it.repr),
"diagnostic received"
);
self.diagnostics_received = true;
self.diagnostics_received = DiagnosticsReceived::Yes;
if let Some(package_id) = &package_id {
if self.diagnostics_cleared_for.insert(package_id.clone()) {
tracing::trace!(
@ -409,8 +414,10 @@ impl FlycheckActor {
package_id: Some(package_id.clone()),
});
}
} else if !self.diagnostics_cleared_for_all {
self.diagnostics_cleared_for_all = true;
} else if self.diagnostics_received
!= DiagnosticsReceived::YesAndClearedForAll
{
self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll;
self.send(FlycheckMessage::ClearDiagnostics {
id: self.id,
package_id: None,
@ -445,8 +452,7 @@ impl FlycheckActor {
fn clear_diagnostics_state(&mut self) {
self.diagnostics_cleared_for.clear();
self.diagnostics_cleared_for_all = false;
self.diagnostics_received = false;
self.diagnostics_received = DiagnosticsReceived::No;
}
/// Construct a `Command` object for checking the user's code. If the user

View File

@ -102,6 +102,10 @@ Default:
List of cfg options to enable with the given values.
To enable a name without a value, use `"key"`.
To enable a name with a value, use `"key=value"`.
To disable, prefix the entry with a `!`.
**rust-analyzer.cargo.extraArgs** (default: [])

View File

@ -818,7 +818,7 @@
"title": "cargo",
"properties": {
"rust-analyzer.cargo.cfgs": {
"markdownDescription": "List of cfg options to enable with the given values.",
"markdownDescription": "List of cfg options to enable with the given values.\n\nTo enable a name without a value, use `\"key\"`.\nTo enable a name with a value, use `\"key=value\"`.\nTo disable, prefix the entry with a `!`.",
"default": [
"debug_assertions",
"miri"