Skip to content

Commit cb533ae

Browse files
committed
Take feature namespace into account while building summary (fixes #1286)
Here's an attempt at a table to cover the different cases: Feature Old (must be in features table) Continue Namespaced (might be stray value) In features table: Check that Crate dependency is in the list -> Non-optional dependency: Bail [PREVIOUSLY: bailed for non-optional dependency] -> Optional dependency: Insert feature of this name -> Else: Bail [PREVIOUSLY: bailed for unknown dependency or feature] Crate Old (might be stray value) Non-optional dependency: Bail No dependency found: Bail Namespaced Non-optional dependency: Bail No dependency found: Bail CrateFeature Old No dependency found: Bail Namespaced No dependency found: Bail
1 parent a9f163e commit cb533ae

File tree

2 files changed

+168
-37
lines changed

2 files changed

+168
-37
lines changed

src/cargo/core/summary.rs

Lines changed: 165 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl Summary {
3838
namespaced_features: bool,
3939
) -> CargoResult<Summary> {
4040
for dep in dependencies.iter() {
41-
if features.get(&*dep.name()).is_some() {
41+
if !namespaced_features && features.get(&*dep.name()).is_some() {
4242
bail!(
4343
"Features and dependencies cannot have the \
4444
same name: `{}`",
@@ -52,7 +52,7 @@ impl Summary {
5252
)
5353
}
5454
}
55-
let feature_map = build_feature_map(features, &dependencies)?;
55+
let feature_map = build_feature_map(features, &dependencies, namespaced_features)?;
5656
Ok(Summary {
5757
inner: Rc::new(Inner {
5858
package_id: pkg_id,
@@ -137,6 +137,7 @@ impl PartialEq for Summary {
137137
fn build_feature_map(
138138
features: BTreeMap<String, Vec<String>>,
139139
dependencies: &[Dependency],
140+
namespaced: bool,
140141
) -> CargoResult<FeatureMap> {
141142
use self::FeatureValue::*;
142143
let dep_map: HashMap<_, _> = dependencies
@@ -146,52 +147,169 @@ fn build_feature_map(
146147

147148
let mut map = BTreeMap::new();
148149
for (feature, list) in features.iter() {
150+
// If namespaced features is active and the key is the same as that of an
151+
// optional dependency, that dependency must be included in the values.
152+
// Thus, if a `feature` is found that has the same name as a dependency, we
153+
// (a) bail out if the dependency is non-optional, and (b) we track if the
154+
// feature requirements include the dependency `crate:feature` in the list.
155+
// This is done with the `dependency_found` variable, which can only be
156+
// false if features are namespaced and the current feature key is the same
157+
// as the name of an optional dependency. If so, it gets set to true during
158+
// iteration over the list if the dependency is found in the list.
159+
let mut dependency_found = if namespaced {
160+
match dep_map.get(feature.as_str()) {
161+
Some(ref dep_data) => {
162+
if !dep_data.is_optional() {
163+
bail!(
164+
"Feature `{}` includes the dependency of the same name, but this is \
165+
left implicit in the features included by this feature.\n\
166+
Additionally, the dependency must be marked as optional to be \
167+
included in the feature definition.\n\
168+
Consider adding `crate:{}` to this feature's requirements \
169+
and marking the dependency as `optional = true`",
170+
feature,
171+
feature
172+
)
173+
} else {
174+
false
175+
}
176+
}
177+
None => true,
178+
}
179+
} else {
180+
true
181+
};
182+
149183
let mut values = vec![];
150184
for dep in list {
151-
let val = FeatureValue::build(InternedString::new(dep), |fs| features.contains_key(fs));
152-
if let &Feature(_) = &val {
153-
values.push(val);
154-
continue;
155-
}
185+
let val = FeatureValue::build(
186+
InternedString::new(dep),
187+
|fs| features.contains_key(fs),
188+
namespaced,
189+
);
156190

157191
// Find data for the referenced dependency...
158192
let dep_data = {
159193
match val {
160-
Feature(_) => None,
161-
Crate(ref dep_name) | CrateFeature(ref dep_name, _) => {
194+
Feature(ref dep_name) | Crate(ref dep_name) | CrateFeature(ref dep_name, _) => {
162195
dep_map.get(dep_name.as_str())
163196
}
164197
}
165198
};
199+
let is_optional_dep = dep_data.map_or(false, |d| d.is_optional());
200+
if let FeatureValue::Crate(ref dep_name) = val {
201+
// If we have a dependency value, check if this is the dependency named
202+
// the same as the feature that we were looking for.
203+
if !dependency_found && feature == dep_name.as_str() {
204+
dependency_found = true;
205+
}
206+
}
166207

167-
match (&val, dep_data) {
168-
(&Crate(ref dep), Some(d)) => {
169-
if !d.is_optional() {
170-
bail!(
171-
"Feature `{}` depends on `{}` which is not an \
172-
optional dependency.\nConsider adding \
173-
`optional = true` to the dependency",
174-
feature,
175-
dep
176-
)
208+
match (&val, dep_data.is_some(), is_optional_dep) {
209+
// The value is a feature. If features are namespaced, this just means
210+
// it's not prefixed with `crate:`, so we have to check whether the
211+
// feature actually exist. If the feature is not defined *and* an optional
212+
// dependency of the same name exists, the feature is defined implicitly
213+
// here by adding it to the feature map, pointing to the dependency.
214+
// If features are not namespaced, it's been validated as a feature already
215+
// while instantiating the `FeatureValue` in `FeatureValue::build()`, so
216+
// we don't have to do so here.
217+
(&Feature(feat), _, true) => {
218+
if namespaced && !features.contains_key(&*feat) {
219+
map.insert(feat.to_string(), vec![FeatureValue::Crate(feat)]);
177220
}
178221
}
179-
(&CrateFeature(ref dep_name, _), None) => bail!(
222+
// If features are namespaced and the value is not defined as a feature
223+
// and there is no optional dependency of the same name, error out.
224+
// If features are not namespaced, there must be an existing feature
225+
// here (checked by `FeatureValue::build()`), so it will always be defined.
226+
(&Feature(feat), dep_exists, false) => {
227+
if namespaced && !features.contains_key(&*feat) {
228+
if dep_exists {
229+
bail!(
230+
"Feature `{}` includes `{}` which is not defined as a feature.\n\
231+
A non-optional dependency of the same name is defined; consider \
232+
adding `optional = true` to its definition",
233+
feature,
234+
feat
235+
)
236+
} else {
237+
bail!(
238+
"Feature `{}` includes `{}` which is not defined as a feature",
239+
feature,
240+
feat
241+
)
242+
}
243+
}
244+
}
245+
// The value is a dependency. If features are namespaced, it is explicitly
246+
// tagged as such (`crate:value`). If features are not namespaced, any value
247+
// not recognized as a feature is pegged as a `Crate`. Here we handle the case
248+
// where the dependency exists but is non-optional. It branches on namespaced
249+
// just to provide the correct string for the crate dependency in the error.
250+
(&Crate(ref dep), true, false) => if namespaced {
251+
bail!(
252+
"Feature `{}` includes `crate:{}` which is not an \
253+
optional dependency.\nConsider adding \
254+
`optional = true` to the dependency",
255+
feature,
256+
dep
257+
)
258+
} else {
259+
bail!(
260+
"Feature `{}` depends on `{}` which is not an \
261+
optional dependency.\nConsider adding \
262+
`optional = true` to the dependency",
263+
feature,
264+
dep
265+
)
266+
},
267+
// If namespaced, the value was tagged as a dependency; if not namespaced,
268+
// this could be anything not defined as a feature. This handles the case
269+
// where no such dependency is actually defined; again, the branch on
270+
// namespaced here is just to provide the correct string in the error.
271+
(&Crate(ref dep), false, _) => if namespaced {
272+
bail!(
273+
"Feature `{}` includes `crate:{}` which is not a known \
274+
dependency",
275+
feature,
276+
dep
277+
)
278+
} else {
279+
bail!(
280+
"Feature `{}` includes `{}` which is neither a dependency nor \
281+
another feature",
282+
feature,
283+
dep
284+
)
285+
},
286+
(&Crate(_), true, true) => {}
287+
// If the value is a feature for one of the dependencies, bail out if no such
288+
// dependency is actually defined in the manifest.
289+
(&CrateFeature(ref dep, _), false, _) => bail!(
180290
"Feature `{}` requires a feature of `{}` which is not a \
181291
dependency",
182292
feature,
183-
dep_name
184-
),
185-
(&Crate(ref dep), None) => bail!(
186-
"Feature `{}` includes `{}` which is neither \
187-
a dependency nor another feature",
188-
feature,
189293
dep
190294
),
191-
(&CrateFeature(_, _), Some(_)) | (&Feature(_), _) => {}
295+
(&CrateFeature(_, _), true, _) => {}
192296
}
193297
values.push(val);
194298
}
299+
300+
if !dependency_found {
301+
// If we have not found the dependency of the same-named feature, we should
302+
// bail here.
303+
bail!(
304+
"Feature `{}` includes the optional dependency of the \
305+
same name, but this is left implicit in the features \
306+
included by this feature.\nConsider adding \
307+
`crate:{}` to this feature's requirements.",
308+
feature,
309+
feature
310+
)
311+
}
312+
195313
map.insert(feature.clone(), values);
196314
}
197315
Ok(map)
@@ -212,30 +330,42 @@ pub enum FeatureValue {
212330
}
213331

214332
impl FeatureValue {
215-
fn build<T>(feature: InternedString, is_feature: T) -> FeatureValue
333+
fn build<T>(feature: InternedString, is_feature: T, namespaced: bool) -> FeatureValue
216334
where
217335
T: Fn(&str) -> bool,
218336
{
219-
match feature.find('/') {
220-
Some(pos) => {
337+
match (feature.find('/'), namespaced) {
338+
(Some(pos), _) => {
221339
let (dep, dep_feat) = feature.split_at(pos);
222340
let dep_feat = &dep_feat[1..];
223341
FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat))
224342
}
225-
None if is_feature(&feature) => FeatureValue::Feature(feature),
226-
None => FeatureValue::Crate(feature),
343+
(None, true) if feature.starts_with("crate:") => {
344+
FeatureValue::Crate(InternedString::new(&feature[6..]))
345+
}
346+
(None, true) => FeatureValue::Feature(feature),
347+
(None, false) if is_feature(&feature) => FeatureValue::Feature(feature),
348+
(None, false) => FeatureValue::Crate(feature),
227349
}
228350
}
229351

230352
pub fn new(feature: InternedString, s: &Summary) -> FeatureValue {
231-
Self::build(feature, |fs| s.features().contains_key(fs))
353+
Self::build(
354+
feature,
355+
|fs| s.features().contains_key(fs),
356+
s.namespaced_features(),
357+
)
232358
}
233359

234-
pub fn to_string(&self) -> String {
360+
pub fn to_string(&self, s: &Summary) -> String {
235361
use self::FeatureValue::*;
236362
match *self {
237363
Feature(ref f) => f.to_string(),
238-
Crate(ref c) => c.to_string(),
364+
Crate(ref c) => if s.namespaced_features() {
365+
format!("crate:{}", &c)
366+
} else {
367+
c.to_string()
368+
},
239369
CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"),
240370
}
241371
}

src/cargo/ops/registry.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,14 @@ fn transmit(
214214
return Ok(());
215215
}
216216

217-
let string_features = pkg.summary()
217+
let summary = pkg.summary();
218+
let string_features = summary
218219
.features()
219220
.iter()
220221
.map(|(feat, values)| {
221222
(
222223
feat.clone(),
223-
values.iter().map(|fv| fv.to_string()).collect(),
224+
values.iter().map(|fv| fv.to_string(&summary)).collect(),
224225
)
225226
})
226227
.collect::<BTreeMap<String, Vec<String>>>();

0 commit comments

Comments
 (0)