mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
More helpful missing feature error message
This commit is contained in:
parent
a0acc29f7b
commit
e8b28cf87e
@ -1363,12 +1363,12 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_unknown_features_error(
|
fn missing_feature_spelling_suggestions(
|
||||||
&self,
|
&self,
|
||||||
specs: &[PackageIdSpec],
|
selected_members: &[&Package],
|
||||||
cli_features: &CliFeatures,
|
cli_features: &CliFeatures,
|
||||||
found_features: &BTreeSet<FeatureValue>,
|
found_features: &BTreeSet<FeatureValue>,
|
||||||
) -> CargoResult<()> {
|
) -> Vec<String> {
|
||||||
// Keeps track of which features were contained in summary of `member` to suggest similar features in errors
|
// Keeps track of which features were contained in summary of `member` to suggest similar features in errors
|
||||||
let mut summary_features: Vec<InternedString> = Default::default();
|
let mut summary_features: Vec<InternedString> = Default::default();
|
||||||
|
|
||||||
@ -1387,10 +1387,7 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
let mut optional_dependency_names_per_member: BTreeMap<&Package, BTreeSet<InternedString>> =
|
let mut optional_dependency_names_per_member: BTreeMap<&Package, BTreeSet<InternedString>> =
|
||||||
Default::default();
|
Default::default();
|
||||||
|
|
||||||
for member in self
|
for &member in selected_members {
|
||||||
.members()
|
|
||||||
.filter(|m| specs.iter().any(|spec| spec.matches(m.package_id())))
|
|
||||||
{
|
|
||||||
// Only include features this member defines.
|
// Only include features this member defines.
|
||||||
let summary = member.summary();
|
let summary = member.summary();
|
||||||
|
|
||||||
@ -1428,7 +1425,7 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
edit_distance(a.as_str(), b.as_str(), 3).is_some()
|
edit_distance(a.as_str(), b.as_str(), 3).is_some()
|
||||||
};
|
};
|
||||||
|
|
||||||
let suggestions: Vec<_> = cli_features
|
cli_features
|
||||||
.features
|
.features
|
||||||
.difference(found_features)
|
.difference(found_features)
|
||||||
.map(|feature| match feature {
|
.map(|feature| match feature {
|
||||||
@ -1522,8 +1519,15 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
})
|
})
|
||||||
.sorted()
|
.sorted()
|
||||||
.take(5)
|
.take(5)
|
||||||
.collect();
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn report_unknown_features_error(
|
||||||
|
&self,
|
||||||
|
specs: &[PackageIdSpec],
|
||||||
|
cli_features: &CliFeatures,
|
||||||
|
found_features: &BTreeSet<FeatureValue>,
|
||||||
|
) -> CargoResult<()> {
|
||||||
let unknown: Vec<_> = cli_features
|
let unknown: Vec<_> = cli_features
|
||||||
.features
|
.features
|
||||||
.difference(found_features)
|
.difference(found_features)
|
||||||
@ -1531,20 +1535,72 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
.sorted()
|
.sorted()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if suggestions.is_empty() {
|
let (selected_members, unselected_members): (Vec<_>, Vec<_>) = self
|
||||||
bail!(
|
.members()
|
||||||
"none of the selected packages contains these features: {}",
|
.partition(|member| specs.iter().any(|spec| spec.matches(member.package_id())));
|
||||||
unknown.join(", ")
|
|
||||||
);
|
let missing_packages_with_the_features = unselected_members
|
||||||
|
.into_iter()
|
||||||
|
.filter(|member| {
|
||||||
|
unknown
|
||||||
|
.iter()
|
||||||
|
.any(|feature| member.summary().features().contains_key(&**feature))
|
||||||
|
})
|
||||||
|
.map(|m| m.name())
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
let these_features = if unknown.len() == 1 {
|
||||||
|
"this feature"
|
||||||
} else {
|
} else {
|
||||||
bail!(
|
"these features"
|
||||||
"none of the selected packages contains these features: {}, did you mean: {}?",
|
};
|
||||||
unknown.join(", "),
|
let mut msg = if let [singular] = &selected_members[..] {
|
||||||
suggestions.join(", ")
|
format!(
|
||||||
|
"the package '{}' does not contain {these_features}: {}",
|
||||||
|
singular.name(),
|
||||||
|
unknown.join(", ")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let names = selected_members.iter().map(|m| m.name()).join(", ");
|
||||||
|
format!("none of the selected packages contains {these_features}: {}\nselected packages: {names}", unknown.join(", "))
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::fmt::Write;
|
||||||
|
if !missing_packages_with_the_features.is_empty() {
|
||||||
|
write!(
|
||||||
|
&mut msg,
|
||||||
|
"\nhelp: package{} with the missing feature{}: {}",
|
||||||
|
if missing_packages_with_the_features.len() != 1 {
|
||||||
|
"s"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
if unknown.len() != 1 { "s" } else { "" },
|
||||||
|
missing_packages_with_the_features.join(", ")
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
let suggestions = self.missing_feature_spelling_suggestions(
|
||||||
|
&selected_members,
|
||||||
|
cli_features,
|
||||||
|
found_features,
|
||||||
);
|
);
|
||||||
|
if !suggestions.is_empty() {
|
||||||
|
write!(
|
||||||
|
&mut msg,
|
||||||
|
"\nhelp: there {}: {}",
|
||||||
|
if suggestions.len() == 1 {
|
||||||
|
"is a similarly named feature"
|
||||||
|
} else {
|
||||||
|
"are similarly named features"
|
||||||
|
},
|
||||||
|
suggestions.join(", ")
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bail!("{msg}")
|
||||||
|
}
|
||||||
|
|
||||||
/// New command-line feature selection behavior with resolver = "2" or the
|
/// New command-line feature selection behavior with resolver = "2" or the
|
||||||
/// root of a virtual workspace. See `allows_new_cli_feature_behavior`.
|
/// root of a virtual workspace. See `allows_new_cli_feature_behavior`.
|
||||||
fn members_with_features_new(
|
fn members_with_features_new(
|
||||||
|
@ -75,7 +75,9 @@ fn virtual_no_default_features() {
|
|||||||
p.cargo("check --features foo")
|
p.cargo("check --features foo")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
[ERROR] none of the selected packages contains these features: foo, did you mean: f1?
|
[ERROR] none of the selected packages contains this feature: foo
|
||||||
|
selected packages: a, b
|
||||||
|
[HELP] there is a similarly named feature: f1
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
@ -83,7 +85,9 @@ fn virtual_no_default_features() {
|
|||||||
p.cargo("check --features a/dep1,b/f1,b/f2,f2")
|
p.cargo("check --features a/dep1,b/f1,b/f2,f2")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
[ERROR] none of the selected packages contains these features: b/f2, f2, did you mean: f1?
|
[ERROR] none of the selected packages contains these features: b/f2, f2
|
||||||
|
selected packages: a, b
|
||||||
|
[HELP] there is a similarly named feature: f1
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
@ -91,7 +95,9 @@ fn virtual_no_default_features() {
|
|||||||
p.cargo("check --features a/dep,b/f1,b/f2,f2")
|
p.cargo("check --features a/dep,b/f1,b/f2,f2")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
[ERROR] none of the selected packages contains these features: a/dep, b/f2, f2, did you mean: a/dep1, f1?
|
[ERROR] none of the selected packages contains these features: a/dep, b/f2, f2
|
||||||
|
selected packages: a, b
|
||||||
|
[HELP] there are similarly named features: a/dep1, f1
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
@ -99,7 +105,18 @@ fn virtual_no_default_features() {
|
|||||||
p.cargo("check --features a/dep,a/dep1")
|
p.cargo("check --features a/dep,a/dep1")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
[ERROR] none of the selected packages contains these features: a/dep, did you mean: b/f1?
|
[ERROR] none of the selected packages contains this feature: a/dep
|
||||||
|
selected packages: a, b
|
||||||
|
[HELP] there is a similarly named feature: b/f1
|
||||||
|
|
||||||
|
"#]])
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("check -p b --features=dep1")
|
||||||
|
.with_status(101)
|
||||||
|
.with_stderr_data(str![[r#"
|
||||||
|
[ERROR] the package 'b' does not contain this feature: dep1
|
||||||
|
[HELP] package with the missing feature: a
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
@ -126,7 +143,8 @@ fn virtual_typo_member_feature() {
|
|||||||
.cargo("check --features a/deny-warning")
|
.cargo("check --features a/deny-warning")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_data(str![[r#"
|
.with_stderr_data(str![[r#"
|
||||||
[ERROR] none of the selected packages contains these features: a/deny-warning, did you mean: a/deny-warnings?
|
[ERROR] the package 'a' does not contain this feature: a/deny-warning
|
||||||
|
[HELP] there is a similarly named feature: a/deny-warnings
|
||||||
|
|
||||||
"#]])
|
"#]])
|
||||||
.run();
|
.run();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user