Skip to content

Commit fc1f5a4

Browse files
committed
Provide a better error message when mixing dep: with /
1 parent 5181f99 commit fc1f5a4

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

src/cargo/core/summary.rs

+30
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,36 @@ fn build_feature_map(
277277
feature
278278
);
279279
}
280+
281+
// dep: cannot be combined with /
282+
if let Some(stripped_dep) = dep_name.strip_prefix("dep:") {
283+
let has_other_dep = explicitly_listed.contains(stripped_dep);
284+
let is_optional = dep_map
285+
.get(stripped_dep)
286+
.iter()
287+
.flat_map(|d| d.iter())
288+
.any(|d| d.is_optional());
289+
let extra_help = if *weak || has_other_dep || !is_optional {
290+
// In this case, the user should just remove dep:.
291+
// Note that "hiding" an optional dependency
292+
// wouldn't work with just a single `dep:foo?/bar`
293+
// because there would not be any way to enable
294+
// `foo`.
295+
String::new()
296+
} else {
297+
format!(
298+
"\nIf the intent is to avoid creating an implicit feature \
299+
`{stripped_dep}` for an optional dependency, \
300+
then consider replacing this with two values:\n \
301+
\"dep:{stripped_dep}\", \"{stripped_dep}/{dep_feature}\""
302+
)
303+
};
304+
bail!(
305+
"feature `{feature}` includes `{fv}` with both `dep:` and `/`\n\
306+
To fix this, remove the `dep:` prefix.{extra_help}"
307+
)
308+
}
309+
280310
// Validation of the feature name will be performed in the resolver.
281311
if !is_any_dep {
282312
bail!(

tests/testsuite/features_namespaced.rs

+126
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,129 @@ feat3 = ["feat2"]
10731073
)],
10741074
);
10751075
}
1076+
1077+
#[cargo_test]
1078+
fn namespaced_feature_together() {
1079+
// Check for an error when `dep:` is used with `/`
1080+
Package::new("bar", "1.0.0")
1081+
.feature("bar-feat", &[])
1082+
.publish();
1083+
1084+
// Non-optional shouldn't have extra err.
1085+
let p = project()
1086+
.file(
1087+
"Cargo.toml",
1088+
r#"
1089+
[package]
1090+
name = "foo"
1091+
version = "0.1.0"
1092+
1093+
[dependencies]
1094+
bar = "1.0"
1095+
1096+
[features]
1097+
f1 = ["dep:bar/bar-feat"]
1098+
"#,
1099+
)
1100+
.file("src/lib.rs", "")
1101+
.build();
1102+
p.cargo("check")
1103+
.with_status(101)
1104+
.with_stderr(
1105+
"\
1106+
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
1107+
1108+
Caused by:
1109+
feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/`
1110+
To fix this, remove the `dep:` prefix.
1111+
",
1112+
)
1113+
.run();
1114+
1115+
// Weak dependency shouldn't have extra err.
1116+
p.change_file(
1117+
"Cargo.toml",
1118+
r#"
1119+
[package]
1120+
name = "foo"
1121+
version = "0.1.0"
1122+
1123+
[dependencies]
1124+
bar = {version = "1.0", optional = true }
1125+
1126+
[features]
1127+
f1 = ["dep:bar?/bar-feat"]
1128+
"#,
1129+
);
1130+
p.cargo("check")
1131+
.with_status(101)
1132+
.with_stderr(
1133+
"\
1134+
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
1135+
1136+
Caused by:
1137+
feature `f1` includes `dep:bar?/bar-feat` with both `dep:` and `/`
1138+
To fix this, remove the `dep:` prefix.
1139+
",
1140+
)
1141+
.run();
1142+
1143+
// If dep: is already specified, shouldn't have extra err.
1144+
p.change_file(
1145+
"Cargo.toml",
1146+
r#"
1147+
[package]
1148+
name = "foo"
1149+
version = "0.1.0"
1150+
1151+
[dependencies]
1152+
bar = {version = "1.0", optional = true }
1153+
1154+
[features]
1155+
f1 = ["dep:bar", "dep:bar/bar-feat"]
1156+
"#,
1157+
);
1158+
p.cargo("check")
1159+
.with_status(101)
1160+
.with_stderr(
1161+
"\
1162+
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
1163+
1164+
Caused by:
1165+
feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/`
1166+
To fix this, remove the `dep:` prefix.
1167+
",
1168+
)
1169+
.run();
1170+
1171+
// Only when the other 3 cases aren't true should it give some extra help.
1172+
p.change_file(
1173+
"Cargo.toml",
1174+
r#"
1175+
[package]
1176+
name = "foo"
1177+
version = "0.1.0"
1178+
1179+
[dependencies]
1180+
bar = {version = "1.0", optional = true }
1181+
1182+
[features]
1183+
f1 = ["dep:bar/bar-feat"]
1184+
"#,
1185+
);
1186+
p.cargo("check")
1187+
.with_status(101)
1188+
.with_stderr(
1189+
"\
1190+
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
1191+
1192+
Caused by:
1193+
feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/`
1194+
To fix this, remove the `dep:` prefix.
1195+
If the intent is to avoid creating an implicit feature `bar` for an optional \
1196+
dependency, then consider replacing this with two values:
1197+
\"dep:bar\", \"bar/bar-feat\"
1198+
",
1199+
)
1200+
.run();
1201+
}

0 commit comments

Comments
 (0)