Warn on missing required-features

Co-authored-by: Eric Huss <eric@huss.org>
This commit is contained in:
Alex Tokarev 2020-02-28 21:55:18 +03:00
parent a93b317273
commit 19a9579031
3 changed files with 126 additions and 9 deletions

View File

@ -34,7 +34,7 @@ use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
use crate::core::profiles::{Profiles, UnitFor};
use crate::core::resolver::features::{self, FeaturesFor};
use crate::core::resolver::{HasDevUnits, Resolve, ResolveOpts};
use crate::core::{Package, PackageSet, Target};
use crate::core::{FeatureValue, Package, PackageSet, Shell, Summary, Target};
use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
use crate::ops;
use crate::ops::resolve::WorkspaceResolve;
@ -417,6 +417,7 @@ pub fn create_bcx<'a, 'cfg>(
&build_config.requested_kinds,
build_config.mode,
&resolve,
&workspace_resolve,
&resolved_features,
&pkg_set,
&profiles,
@ -701,6 +702,7 @@ fn generate_targets(
requested_kinds: &[CompileKind],
mode: CompileMode,
resolve: &Resolve,
workspace_resolve: &Option<Resolve>,
resolved_features: &features::ResolvedFeatures,
package_set: &PackageSet<'_>,
profiles: &Profiles,
@ -929,6 +931,13 @@ fn generate_targets(
{
let unavailable_features = match target.required_features() {
Some(rf) => {
warn_on_missing_features(
workspace_resolve,
rf,
pkg.summary(),
&mut config.shell(),
)?;
let features = features_map.entry(pkg).or_insert_with(|| {
resolve_all_features(resolve, resolved_features, package_set, pkg.package_id())
});
@ -958,6 +967,68 @@ fn generate_targets(
Ok(units.into_iter().collect())
}
fn warn_on_missing_features(
resolve: &Option<Resolve>,
required_features: &[String],
summary: &Summary,
shell: &mut Shell,
) -> CargoResult<()> {
let resolve = match resolve {
None => return Ok(()),
Some(resolve) => resolve,
};
for feature in required_features {
match FeatureValue::new(feature.into(), summary) {
// No need to do anything here, since the feature must exist to be parsed as such
FeatureValue::Feature(_) => {}
// Possibly mislabeled feature that was not found
FeatureValue::Crate(krate) => {
if !summary
.dependencies()
.iter()
.any(|dep| dep.name_in_toml() == krate && dep.is_optional())
{
shell.warn(format!(
"feature `{}` is not present in [features] section.",
krate
))?;
}
}
// Handling of dependent_crate/dependent_crate_feature syntax
FeatureValue::CrateFeature(krate, feature) => {
match resolve
.deps(summary.package_id())
.find(|(_dep_id, deps)| deps.iter().any(|dep| dep.name_in_toml() == krate))
{
Some((dep_id, _deps)) => {
let dep_summary = resolve.summary(dep_id);
if !dep_summary.features().contains_key(&feature)
&& !dep_summary
.dependencies()
.iter()
.any(|dep| dep.name_in_toml() == feature && dep.is_optional())
{
shell.warn(format!(
"feature `{}` does not exist in package `{}`.",
feature, dep_id
))?;
}
}
None => {
shell.warn(format!(
"dependency `{}` specified in required-features as `{}/{}` \
does not exist.",
krate, krate, feature
))?;
}
}
}
}
}
Ok(())
}
/// Gets all of the features enabled for a package, plus its dependencies'
/// features.
///

View File

@ -595,6 +595,9 @@ fn bench_autodiscover_2015() {
version = "0.0.1"
authors = []
edition = "2015"
[features]
magic = []
[[bench]]
name = "bench_magic"

View File

@ -291,7 +291,7 @@ fn invalid9() {
.build();
p.cargo("build --features bar")
.with_stderr(
.with_stderr(
"\
error: Package `foo v0.0.1 ([..])` does not have feature `bar`. It has a required dependency with that name, but only optional dependencies can be used as features.
",
@ -1514,14 +1514,14 @@ fn namespaced_shadowed_dep() {
.build();
p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr(
"\
"\
[ERROR] failed to parse manifest at `[..]`
Caused by:
Feature `baz` includes the optional dependency of the same name, but this is left implicit in the features included by this feature.
Consider adding `crate:baz` to this feature's requirements.
",
)
)
.run();
}
@ -1550,7 +1550,7 @@ fn namespaced_shadowed_non_optional() {
.build();
p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr(
"\
"\
[ERROR] failed to parse manifest at `[..]`
Caused by:
@ -1558,7 +1558,7 @@ Caused by:
Additionally, the dependency must be marked as optional to be included in the feature definition.
Consider adding `crate:baz` to this feature's requirements and marking the dependency as `optional = true`
",
)
)
.run();
}
@ -1587,15 +1587,14 @@ fn namespaced_implicit_non_optional() {
.build();
p.cargo("build").masquerade_as_nightly_cargo().with_status(101).with_stderr(
"\
"\
[ERROR] failed to parse manifest at `[..]`
Caused by:
Feature `bar` includes `baz` which is not defined as a feature.
A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition
",
).run(
);
).run();
}
#[cargo_test]
@ -2190,3 +2189,47 @@ fn registry_summary_order_doesnt_matter() {
.with_stdout("it works")
.run();
}
#[cargo_test]
fn nonexistent_required_features() {
Package::new("required_dependency", "0.1.0")
.feature("simple", &[])
.publish();
Package::new("optional_dependency", "0.2.0")
.feature("optional", &[])
.publish();
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"
[features]
existing = []
fancy = ["optional_dependency"]
[dependencies]
required_dependency = { version = "0.1", optional = false}
optional_dependency = { version = "0.2", optional = true}
[[example]]
name = "ololo"
required-features = ["not_present",
"existing",
"fancy",
"required_dependency/not_existing",
"required_dependency/simple",
"optional_dependency/optional",
"not_specified_dependency/some_feature"]"#,
)
.file("src/main.rs", "fn main() {}")
.file("examples/ololo.rs", "fn main() {}")
.build();
p.cargo("build --examples")
.with_stderr_contains(
r#"warning: feature `not_present` is not present in [features] section.
warning: feature `not_existing` does not exist in package `required_dependency v0.1.0`.
warning: dependency `not_specified_dependency` specified in required-features as `not_specified_dependency/some_feature` does not exist."#,
)
.run();
}