From dbc2c2b57d7de77cfea67a6a83c304929eb11e64 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 5 Jun 2019 09:10:10 -0700 Subject: [PATCH] Don't synthesize feature diretives for non-optional deps Currently when Cargo is invoked on the command like `cargo build --features foo/bar` then it will actually always compile the current crate with `feature = "foo"` even if `foo` is a non-optional dependency. This isn't intended because the crate doesn't actually have a `foo` feature as so no directive should be emitted or passed to the compiler. This was discovered in rust-lang/rust where Cargo is being built with the `rustc-workspace-hack` feature but when the RLS depends on Cargo it doesn't enable the same feature. This feature, however, doesn't actually exist for Cargo! --- src/cargo/core/resolver/dep_cache.rs | 14 +++++++++- tests/testsuite/features.rs | 41 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/resolver/dep_cache.rs b/src/cargo/core/resolver/dep_cache.rs index a6ae59132d5..58b52a617d5 100644 --- a/src/cargo/core/resolver/dep_cache.rs +++ b/src/cargo/core/resolver/dep_cache.rs @@ -415,7 +415,19 @@ impl Requirements<'_> { } fn require_crate_feature(&mut self, package: InternedString, feat: InternedString) { - self.used.insert(package); + // If `package` is indeed an optional dependency then we activate the + // feature named `package`, but otherwise if `package` is a required + // dependency then there's no feature associated with it. + if let Some(dep) = self + .summary + .dependencies() + .iter() + .find(|p| p.name_in_toml() == package) + { + if dep.is_optional() { + self.used.insert(package); + } + } self.deps .entry(package) .or_insert((false, BTreeSet::new())) diff --git a/tests/testsuite/features.rs b/tests/testsuite/features.rs index 35a4fa9d247..3c38143ec44 100644 --- a/tests/testsuite/features.rs +++ b/tests/testsuite/features.rs @@ -1870,3 +1870,44 @@ fn warn_if_default_features() { "#.trim(), ).run(); } + +#[test] +fn no_feature_for_non_optional_dep() { + let p = project() + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + + [dependencies] + bar = { path = "bar" } + "#, + ) + .file( + "src/main.rs", + r#" + #[cfg(not(feature = "bar"))] + fn main() { + } + "#, + ) + .file( + "bar/Cargo.toml", + r#" + [project] + name = "bar" + version = "0.0.1" + authors = [] + + [features] + a = [] + "#, + ) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("build --features bar/a").run(); +}