Skip to content

Commit 5d4402c

Browse files
committedMar 1, 2018
fix the todo's
needs a test where we have an activation_error the then try activate something that dose not work and backtrack to where we had the activation_error then: - Hit fast backtracking that go past the crate with the missing features - Or give a bad error message that does not mention the activation_error. The test will pass, but there is code that is not yet justified by tests
1 parent 3300728 commit 5d4402c

File tree

2 files changed

+94
-30
lines changed

2 files changed

+94
-30
lines changed
 

‎src/cargo/core/resolver/mod.rs

+81-24
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ fn activate(cx: &mut Context,
414414
parent: Option<&Summary>,
415415
candidate: Candidate,
416416
method: &Method)
417-
-> CargoResult<Option<(DepsFrame, Duration)>> {
417+
-> ActivateResult<Option<(DepsFrame, Duration)>> {
418418
if let Some(parent) = parent {
419419
cx.resolve_graph.push(GraphNode::Link(parent.package_id().clone(),
420420
candidate.summary.package_id().clone()));
@@ -536,14 +536,40 @@ impl Ord for DepsFrame {
536536
enum ConflictReason {
537537
Semver,
538538
Links(String),
539+
MissingFeatures(String),
540+
}
541+
542+
enum ActivateError {
543+
Error(::failure::Error),
544+
Conflict(PackageId, ConflictReason),
545+
}
546+
type ActivateResult<T> = Result<T, ActivateError>;
547+
548+
impl From<::failure::Error> for ActivateError {
549+
fn from(t: ::failure::Error) -> Self {
550+
ActivateError::Error(t)
551+
}
552+
}
553+
554+
impl From<(PackageId, ConflictReason)> for ActivateError {
555+
fn from(t: (PackageId, ConflictReason)) -> Self {
556+
ActivateError::Conflict(t.0, t.1)
557+
}
539558
}
540559

541560
impl ConflictReason {
542561
fn is_links(&self) -> bool {
543-
match *self {
544-
ConflictReason::Semver => false,
545-
ConflictReason::Links(_) => true,
562+
if let ConflictReason::Links(_) = *self {
563+
return true;
564+
}
565+
false
566+
}
567+
568+
fn is_missing_features(&self) -> bool {
569+
if let ConflictReason::MissingFeatures(_) = *self {
570+
return true;
546571
}
572+
false
547573
}
548574
}
549575

@@ -556,6 +582,7 @@ struct BacktrackFrame<'a> {
556582
parent: Summary,
557583
dep: Dependency,
558584
features: Rc<Vec<String>>,
585+
conflicting_activations: HashMap<PackageId, ConflictReason>,
559586
}
560587

561588
#[derive(Clone)]
@@ -652,9 +679,17 @@ fn activate_deps_loop<'a>(
652679
summary: summary.clone(),
653680
replace: None,
654681
};
655-
let res = activate(&mut cx, registry, None, candidate, method)?;
656-
if let Some((frame, _)) = res {
657-
remaining_deps.push(frame);
682+
let res = activate(&mut cx, registry, None, candidate, method);
683+
match res {
684+
Ok(Some((frame, _))) => remaining_deps.push(frame),
685+
Ok(None) => (),
686+
Err(ActivateError::Error(e)) => return Err(e),
687+
Err(ActivateError::Conflict(id, reason)) => {
688+
match reason {
689+
ConflictReason::MissingFeatures(features) => bail!("Package `{}` does not have these features: `{}`", id, features),
690+
_ => panic!("bad error from activate"),
691+
}
692+
}
658693
}
659694
}
660695

@@ -713,6 +748,7 @@ fn activate_deps_loop<'a>(
713748

714749
let mut remaining_candidates = RemainingCandidates::new(&candidates);
715750
let mut successfully_activated = false;
751+
let mut conflicting_activations = HashMap::new();
716752

717753
while !successfully_activated {
718754
let next = remaining_candidates.next(cx.prev_active(&dep), &cx.links);
@@ -729,7 +765,8 @@ fn activate_deps_loop<'a>(
729765
// This means that we're going to attempt to activate each candidate in
730766
// turn. We could possibly fail to activate each candidate, so we try
731767
// each one in turn.
732-
let (candidate, has_another) = next.or_else(|mut conflicting| {
768+
let (candidate, has_another) = next.or_else(|conflicting| {
769+
conflicting_activations.extend(conflicting);
733770
// This dependency has no valid candidate. Backtrack until we
734771
// find a dependency that does have a candidate to try, and try
735772
// to activate that one. This resets the `remaining_deps` to
@@ -744,14 +781,16 @@ fn activate_deps_loop<'a>(
744781
&mut dep,
745782
&mut features,
746783
&mut remaining_candidates,
747-
&mut conflicting,
784+
&mut conflicting_activations,
748785
).ok_or_else(|| {
786+
// if we hit an activation error and we are out of other combinations
787+
// then just report that error
749788
activation_error(
750789
&cx,
751790
registry,
752791
&parent,
753792
&dep,
754-
&conflicting,
793+
&conflicting_activations,
755794
&candidates,
756795
config,
757796
)
@@ -770,6 +809,7 @@ fn activate_deps_loop<'a>(
770809
parent: Summary::clone(&parent),
771810
dep: Dependency::clone(&dep),
772811
features: Rc::clone(&features),
812+
conflicting_activations: conflicting_activations.clone(),
773813
});
774814
}
775815

@@ -781,12 +821,14 @@ fn activate_deps_loop<'a>(
781821
trace!("{}[{}]>{} trying {}", parent.name(), cur, dep.name(), candidate.summary.version());
782822
let res = activate(&mut cx, registry, Some(&parent), candidate, &method);
783823
successfully_activated = res.is_ok();
784-
// TODO: disable fast-backtracking
785-
// TODO: save that disable status in backtrack frame
786-
// TODO: integrate with error messages
787-
if let Ok(Some((frame, dur))) = res {
788-
remaining_deps.push(frame);
789-
deps_time += dur;
824+
match res {
825+
Ok(Some((frame, dur))) => {
826+
remaining_deps.push(frame);
827+
deps_time += dur;
828+
}
829+
Ok(None) => (),
830+
Err(ActivateError::Error(e)) => return Err(e),
831+
Err(ActivateError::Conflict(id, reason)) => { conflicting_activations.insert(id, reason); },
790832
}
791833
}
792834
}
@@ -834,6 +876,7 @@ fn find_candidate<'a>(
834876
*dep = frame.dep;
835877
*features = frame.features;
836878
*remaining_candidates = frame.remaining_candidates;
879+
*conflicting_activations = frame.conflicting_activations;
837880
return Some((candidate, has_another));
838881
}
839882
}
@@ -875,9 +918,9 @@ fn activation_error(cx: &Context,
875918

876919
let mut conflicting_activations: Vec<_> = conflicting_activations.iter().collect();
877920
conflicting_activations.sort_unstable();
878-
let (links_errors, other_errors): (Vec<_>, Vec<_>) = conflicting_activations.drain(..).rev().partition(|&(_, r)| r.is_links());
921+
let (links_errors, mut other_errors): (Vec<_>, Vec<_>) = conflicting_activations.drain(..).rev().partition(|&(_, r)| r.is_links());
879922

880-
for &(p, r) in &links_errors {
923+
for &(p, r) in links_errors.iter() {
881924
if let ConflictReason::Links(ref link) = *r {
882925
msg.push_str("\n\nthe package `");
883926
msg.push_str(dep.name());
@@ -890,12 +933,27 @@ fn activation_error(cx: &Context,
890933
msg.push_str(&describe_path(p));
891934
}
892935

936+
let (features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors.drain(..).partition(|&(_, r)| r.is_missing_features());
937+
938+
for &(p, r) in features_errors.iter() {
939+
if let ConflictReason::MissingFeatures(ref features) = *r {
940+
msg.push_str("\n\nthe package `");
941+
msg.push_str(dep.name());
942+
msg.push_str("` depends on `");
943+
msg.push_str(p.name());
944+
msg.push_str("`, with features: `");
945+
msg.push_str(features);
946+
msg.push_str("` but it does not have these features.\n");
947+
}
948+
msg.push_str(&describe_path(p));
949+
}
950+
893951
if links_errors.is_empty() {
894952
msg.push_str("\n\nall possible versions conflict with \
895953
previously selected packages.");
896954
}
897955

898-
for &(p, _) in &other_errors {
956+
for &(p, _) in other_errors.iter() {
899957
msg.push_str("\n\n previously selected ");
900958
msg.push_str(&describe_path(p));
901959
}
@@ -1158,7 +1216,7 @@ impl<'a> Context<'a> {
11581216
fn build_deps(&mut self,
11591217
registry: &mut Registry,
11601218
candidate: &Summary,
1161-
method: &Method) -> CargoResult<Vec<DepInfo>> {
1219+
method: &Method) -> ActivateResult<Vec<DepInfo>> {
11621220
// First, figure out our set of dependencies based on the requested set
11631221
// of features. This also calculates what features we're going to enable
11641222
// for our own dependencies.
@@ -1275,7 +1333,7 @@ impl<'a> Context<'a> {
12751333
fn resolve_features<'b>(&mut self,
12761334
s: &'b Summary,
12771335
method: &'b Method)
1278-
-> CargoResult<Vec<(Dependency, Vec<String>)>> {
1336+
-> ActivateResult<Vec<(Dependency, Vec<String>)>> {
12791337
let dev_deps = match *method {
12801338
Method::Everything => true,
12811339
Method::Required { dev_deps, .. } => dev_deps,
@@ -1310,7 +1368,7 @@ impl<'a> Context<'a> {
13101368
base.extend(dep.features().iter().cloned());
13111369
for feature in base.iter() {
13121370
if feature.contains('/') {
1313-
bail!("feature names may not contain slashes: `{}`", feature);
1371+
return Err(format_err!("feature names may not contain slashes: `{}`", feature).into());
13141372
}
13151373
}
13161374
ret.push((dep.clone(), base));
@@ -1324,8 +1382,7 @@ impl<'a> Context<'a> {
13241382
.map(|s| &s[..])
13251383
.collect::<Vec<&str>>();
13261384
let features = unknown.join(", ");
1327-
bail!("Package `{}` does not have these features: `{}`",
1328-
s.package_id(), features)
1385+
return Err((s.package_id().clone(), ConflictReason::MissingFeatures(features)))?;
13291386
}
13301387

13311388
// Record what list of features is active for this package.

‎tests/testsuite/features.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,17 @@ fn invalid4() {
109109

110110
assert_that(p.cargo("build"),
111111
execs().with_status(101).with_stderr("\
112-
[ERROR] Package `bar v0.0.1 ([..])` does not have these features: `bar`
113-
"));
112+
error: failed to select a version for `bar`.
113+
... required by package `foo v0.0.1 ([..])`
114+
versions that meet the requirements `*` are: 0.0.1
115+
116+
the package `bar` depends on `bar`, with features: `bar` but it does not have these features.
117+
package `bar v0.0.1 ([..])`
118+
... which is depended on by `foo v0.0.1 ([..])`
119+
120+
all possible versions conflict with previously selected packages.
121+
122+
failed to select a version for `bar` which could resolve this conflict"));
114123

115124
p.change_file("Cargo.toml", r#"
116125
[project]
@@ -121,8 +130,7 @@ fn invalid4() {
121130

122131
assert_that(p.cargo("build").arg("--features").arg("test"),
123132
execs().with_status(101).with_stderr("\
124-
[ERROR] Package `foo v0.0.1 ([..])` does not have these features: `test`
125-
"));
133+
error: Package `foo v0.0.1 ([..])` does not have these features: `test`"));
126134
}
127135

128136
#[test]
@@ -1052,8 +1060,7 @@ fn dep_feature_in_cmd_line() {
10521060
// Trying to enable features of transitive dependencies is an error
10531061
assert_that(p.cargo("build").arg("--features").arg("bar/some-feat"),
10541062
execs().with_status(101).with_stderr("\
1055-
[ERROR] Package `foo v0.0.1 ([..])` does not have these features: `bar`
1056-
"));
1063+
error: Package `foo v0.0.1 ([..])` does not have these features: `bar`"));
10571064

10581065
// Hierarchical feature specification should still be disallowed
10591066
assert_that(p.cargo("build").arg("--features").arg("derived/bar/some-feat"),

0 commit comments

Comments
 (0)
Please sign in to comment.