Skip to content

Commit f461453

Browse files
committed
Fix bug when with resolver = "1" non-virtual package was allowing unknown features
1 parent f2b5271 commit f461453

File tree

3 files changed

+88
-28
lines changed

3 files changed

+88
-28
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ clap = "2.31.2"
6767
unicode-width = "0.1.5"
6868
openssl = { version = '0.10.11', optional = true }
6969
im-rc = "15.0.0"
70+
itertools = "0.10.0"
7071

7172
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
7273
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`

src/cargo/core/workspace.rs

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::slice;
77

88
use anyhow::{bail, Context as _};
99
use glob::glob;
10+
use itertools::Itertools;
1011
use log::debug;
1112
use url::Url;
1213

@@ -1071,7 +1072,7 @@ impl<'cfg> Workspace<'cfg> {
10711072
if self.allows_new_cli_feature_behavior() {
10721073
self.members_with_features_new(specs, cli_features)
10731074
} else {
1074-
Ok(self.members_with_features_old(specs, cli_features))
1075+
self.members_with_features_old(specs, cli_features)
10751076
}
10761077
}
10771078

@@ -1196,7 +1197,7 @@ impl<'cfg> Workspace<'cfg> {
11961197
&self,
11971198
specs: &[PackageIdSpec],
11981199
cli_features: &CliFeatures,
1199-
) -> Vec<(&Package, CliFeatures)> {
1200+
) -> CargoResult<Vec<(&Package, CliFeatures)>> {
12001201
// Split off any features with the syntax `member-name/feature-name` into a map
12011202
// so that those features can be applied directly to those workspace-members.
12021203
let mut member_specific_features: HashMap<InternedString, BTreeSet<FeatureValue>> =
@@ -1215,29 +1216,29 @@ impl<'cfg> Workspace<'cfg> {
12151216
} => panic!("unexpected dep: syntax {}", feature),
12161217
FeatureValue::DepFeature {
12171218
dep_name,
1218-
dep_feature,
1219+
dep_feature: _,
12191220
dep_prefix: _,
12201221
weak: _,
12211222
} => {
1222-
// I think weak can be ignored here.
1223-
// * With `--features member?/feat -p member`, the ? doesn't
1224-
// really mean anything (either the member is built or it isn't).
1225-
// * With `--features nonmember?/feat`, cwd_features will
1226-
// handle processing it correctly.
1223+
// Check if `dep_name` is member of the workspace or package.
1224+
// Weak can be ignored for this moment.
12271225
let is_member = self.members().any(|member| member.name() == *dep_name);
12281226
if is_member && specs.iter().any(|spec| spec.name() == *dep_name) {
12291227
member_specific_features
12301228
.entry(*dep_name)
12311229
.or_default()
1232-
.insert(FeatureValue::Feature(*dep_feature));
1230+
.insert(feature.clone());
12331231
} else {
1232+
// With `--features nonmember?/feat`, cwd_features will
1233+
// handle processing it correctly.
12341234
cwd_features.insert(feature.clone());
12351235
}
12361236
}
12371237
}
12381238
}
12391239

1240-
let ms = self.members().filter_map(|member| {
1240+
let mut result = Vec::new();
1241+
for member in self.members() {
12411242
let member_id = member.package_id();
12421243
match self.current_opt() {
12431244
// The features passed on the command-line only apply to
@@ -1248,13 +1249,29 @@ impl<'cfg> Workspace<'cfg> {
12481249
all_features: cli_features.all_features,
12491250
uses_default_features: cli_features.uses_default_features,
12501251
};
1251-
Some((member, feats))
1252+
1253+
// If any member specific features were passed to non-virtual package, it's error
1254+
if !member_specific_features.is_empty() {
1255+
let invalid: Vec<_> = member_specific_features
1256+
.values()
1257+
.map(|set| set.iter())
1258+
.flatten()
1259+
.map(|feature| feature.to_string())
1260+
.sorted()
1261+
.collect();
1262+
1263+
bail!(
1264+
"Member specific features with `pkg/feat` syntax are dissalowed outside of workspace with `resolver = \"1\", remove: {}",
1265+
invalid.join(", ")
1266+
);
1267+
}
1268+
1269+
result.push((member, feats))
12521270
}
12531271
_ => {
12541272
// Ignore members that are not enabled on the command-line.
12551273
if specs.iter().any(|spec| spec.matches(member_id)) {
1256-
// -p for a workspace member that is not the "current"
1257-
// one.
1274+
// -p for a workspace member that is not the "current" one.
12581275
//
12591276
// The odd behavior here is due to backwards
12601277
// compatibility. `--features` and
@@ -1266,20 +1283,36 @@ impl<'cfg> Workspace<'cfg> {
12661283
features: Rc::new(
12671284
member_specific_features
12681285
.remove(member.name().as_str())
1269-
.unwrap_or_default(),
1286+
.unwrap_or_default()
1287+
.into_iter()
1288+
.map(|feature| match feature {
1289+
// I think weak can be ignored here.
1290+
// With `--features member?/feat -p member`, the ? doesn't
1291+
// really mean anything (either the member is built or it isn't).
1292+
FeatureValue::DepFeature {
1293+
dep_name: _,
1294+
dep_feature,
1295+
dep_prefix: false,
1296+
weak: _,
1297+
} => FeatureValue::new(dep_feature),
1298+
// Member specific features by definition contain only `FeatureValue::DepFeature`
1299+
_ => unreachable!(),
1300+
})
1301+
.collect(),
12701302
),
12711303
uses_default_features: true,
12721304
all_features: cli_features.all_features,
12731305
};
1274-
Some((member, feats))
1306+
1307+
result.push((member, feats))
12751308
} else {
12761309
// This member was not requested on the command-line, skip.
1277-
None
12781310
}
12791311
}
12801312
}
1281-
});
1282-
ms.collect()
1313+
}
1314+
1315+
Ok(result)
12831316
}
12841317
}
12851318

tests/testsuite/package_features.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ fn other_member_from_current() {
215215
name = "bar"
216216
version = "0.1.0"
217217
218-
[features]
218+
[features]
219219
f1 = []
220220
f2 = []
221221
f3 = []
@@ -273,8 +273,8 @@ fn other_member_from_current() {
273273
}
274274

275275
#[cargo_test]
276-
fn virtual_typo_member_feature_default_resolver() {
277-
project()
276+
fn feature_default_resolver() {
277+
let p = project()
278278
.file(
279279
"Cargo.toml",
280280
r#"
@@ -283,17 +283,37 @@ fn virtual_typo_member_feature_default_resolver() {
283283
version = "0.1.0"
284284
285285
[features]
286-
deny-warnings = []
286+
test = []
287287
"#,
288288
)
289-
.file("src/lib.rs", "")
290-
.build()
291-
.cargo("check --features a/deny-warning")
289+
.file(
290+
"src/main.rs",
291+
r#"
292+
fn main() {
293+
if cfg!(feature = "test") {
294+
println!("feature set");
295+
}
296+
}
297+
"#,
298+
)
299+
.build();
300+
301+
p.cargo("check --features testt")
292302
.masquerade_as_nightly_cargo()
293303
.with_status(101)
294-
.with_stderr(
295-
"[ERROR] none of the selected packages contains these features: a/deny-warning",
296-
)
304+
.with_stderr("[ERROR] Package `a[..]` does not have the feature `testt`")
305+
.run();
306+
307+
p.cargo("run --features test")
308+
.masquerade_as_nightly_cargo()
309+
.with_status(0)
310+
.with_stdout("feature set")
311+
.run();
312+
313+
p.cargo("run --features a/test")
314+
.masquerade_as_nightly_cargo()
315+
.with_status(101)
316+
.with_stderr("[ERROR] Member specific features with `pkg/feat` syntax are dissalowed outside of workspace with `resolver = \"1\", remove: a/test")
297317
.run();
298318
}
299319

@@ -483,6 +503,12 @@ fn resolver1_member_features() {
483503
.cwd("member2")
484504
.with_stdout("m1-feature set")
485505
.run();
506+
507+
p.cargo("check -p member1 --features member1/m2-feature")
508+
.cwd("member2")
509+
.with_status(101)
510+
.with_stderr("[ERROR] Package `member1[..]` does not have the feature `m2-feature`")
511+
.run();
486512
}
487513

488514
#[cargo_test]

0 commit comments

Comments
 (0)