Skip to content

Commit 2c712d5

Browse files
committed
patch can conflict on not activated packages
1 parent d84b267 commit 2c712d5

File tree

2 files changed

+125
-17
lines changed

2 files changed

+125
-17
lines changed

src/cargo/core/resolver/mod.rs

+23-17
Original file line numberDiff line numberDiff line change
@@ -811,10 +811,8 @@ impl RemainingCandidates {
811811
}
812812
}
813813

814-
/// Attempts to find a new conflict that allows a `find_candidate` feather then the input one.
814+
/// Attempts to find a new conflict that allows a `find_candidate` better then the input one.
815815
/// It will add the new conflict to the cache if one is found.
816-
///
817-
/// Panics if the input conflict is not all active in `cx`.
818816
fn generalize_conflicting(
819817
cx: &Context,
820818
registry: &mut RegistryQueryer<'_>,
@@ -823,15 +821,12 @@ fn generalize_conflicting(
823821
dep: &Dependency,
824822
conflicting_activations: &ConflictMap,
825823
) -> Option<ConflictMap> {
826-
if conflicting_activations.is_empty() {
827-
return None;
828-
}
829824
// We need to determine the `ContextAge` that this `conflicting_activations` will jump to, and why.
830-
let (backtrack_critical_age, backtrack_critical_id) = conflicting_activations
831-
.keys()
832-
.map(|&c| (cx.is_active(c).expect("not currently active!?"), c))
833-
.max()
834-
.unwrap();
825+
let (backtrack_critical_age, backtrack_critical_id) = shortcircuit_max(
826+
conflicting_activations
827+
.keys()
828+
.map(|&c| cx.is_active(c).map(|a| (a, c))),
829+
)?;
835830
let backtrack_critical_reason: ConflictReason =
836831
conflicting_activations[&backtrack_critical_id].clone();
837832

@@ -923,6 +918,19 @@ fn generalize_conflicting(
923918
None
924919
}
925920

921+
/// Returns Some of the largest item in the iterator.
922+
/// Returns None if any of the items are None or the iterator is empty.
923+
fn shortcircuit_max<I: Ord>(iter: impl Iterator<Item = Option<I>>) -> Option<I> {
924+
let mut out = None;
925+
for i in iter {
926+
if i.is_none() {
927+
return None;
928+
}
929+
out = std::cmp::max(out, i);
930+
}
931+
out
932+
}
933+
926934
/// Looks through the states in `backtrack_stack` for dependencies with
927935
/// remaining candidates. For each one, also checks if rolling back
928936
/// could change the outcome of the failed resolution that caused backtracking
@@ -949,12 +957,10 @@ fn find_candidate(
949957
// the cause of that backtrack, so we do not update it.
950958
let age = if !backtracked {
951959
// we don't have abnormal situations. So we can ask `cx` for how far back we need to go.
952-
let a = cx.is_conflicting(Some(parent.package_id()), conflicting_activations);
953-
// If the `conflicting_activations` does not apply to `cx`, then something went very wrong
954-
// in building it. But we will just fall back to laboriously trying all possibilities witch
955-
// will give us the correct answer so only `assert` if there is a developer to debug it.
956-
debug_assert!(a.is_some());
957-
a
960+
// If the `conflicting_activations` does not apply to `cx`,
961+
// we will just fall back to laboriously trying all possibilities witch
962+
// will give us the correct answer.
963+
cx.is_conflicting(Some(parent.package_id()), conflicting_activations)
958964
} else {
959965
None
960966
};

tests/testsuite/patch.rs

+102
Original file line numberDiff line numberDiff line change
@@ -2541,3 +2541,105 @@ foo v0.1.0 [..]
25412541
))
25422542
.run();
25432543
}
2544+
2545+
// From https://github.com/rust-lang/cargo/issues/7463
2546+
#[cargo_test]
2547+
fn patch_eq_conflict_panic() {
2548+
Package::new("bar", "0.1.0").publish();
2549+
Package::new("bar", "0.1.1").publish();
2550+
let p = project()
2551+
.file(
2552+
"Cargo.toml",
2553+
r#"
2554+
[package]
2555+
name = "foo"
2556+
version = "0.1.0"
2557+
2558+
[dependencies]
2559+
bar = "=0.1.0"
2560+
2561+
[dev-dependencies]
2562+
bar = "=0.1.1"
2563+
2564+
[patch.crates-io]
2565+
bar = {path="bar"}
2566+
"#,
2567+
)
2568+
.file("src/lib.rs", "")
2569+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1"))
2570+
.file("bar/src/lib.rs", "")
2571+
.build();
2572+
2573+
p.cargo("generate-lockfile")
2574+
.with_status(101)
2575+
.with_stderr(
2576+
r#"[UPDATING] `dummy-registry` index
2577+
[ERROR] failed to select a version for `bar`.
2578+
... required by package `foo v0.1.0 ([..])`
2579+
versions that meet the requirements `=0.1.1` are: 0.1.1
2580+
2581+
all possible versions conflict with previously selected packages.
2582+
2583+
previously selected package `bar v0.1.0`
2584+
... which satisfies dependency `bar = "=0.1.0"` of package `foo v0.1.0 ([..])`
2585+
2586+
failed to select a version for `bar` which could resolve this conflict
2587+
"#,
2588+
)
2589+
.run();
2590+
}
2591+
2592+
// From https://github.com/rust-lang/cargo/issues/11336
2593+
#[cargo_test]
2594+
fn mismatched_version2() {
2595+
Package::new("qux", "0.1.0-beta.1").publish();
2596+
Package::new("qux", "0.1.0-beta.2").publish();
2597+
Package::new("bar", "0.1.0")
2598+
.dep("qux", "=0.1.0-beta.1")
2599+
.publish();
2600+
let p = project()
2601+
.file(
2602+
"Cargo.toml",
2603+
r#"
2604+
[package]
2605+
name = "foo"
2606+
version = "0.1.0"
2607+
2608+
[dependencies]
2609+
bar = "0.1.0"
2610+
qux = "0.1.0-beta.2"
2611+
2612+
[patch.crates-io]
2613+
qux = { path = "qux" }
2614+
"#,
2615+
)
2616+
.file("src/lib.rs", "")
2617+
.file(
2618+
"qux/Cargo.toml",
2619+
r#"
2620+
[package]
2621+
name = "qux"
2622+
version = "0.1.0-beta.1"
2623+
"#,
2624+
)
2625+
.file("qux/src/lib.rs", "")
2626+
.build();
2627+
2628+
p.cargo("generate-lockfile")
2629+
.with_status(101)
2630+
.with_stderr(
2631+
r#"[UPDATING] `dummy-registry` index
2632+
[ERROR] failed to select a version for `qux`.
2633+
... required by package `bar v0.1.0`
2634+
... which satisfies dependency `bar = "^0.1.0"` of package `foo v0.1.0 ([..])`
2635+
versions that meet the requirements `=0.1.0-beta.1` are: 0.1.0-beta.1
2636+
2637+
all possible versions conflict with previously selected packages.
2638+
2639+
previously selected package `qux v0.1.0-beta.2`
2640+
... which satisfies dependency `qux = "^0.1.0-beta.2"` of package `foo v0.1.0 ([..])`
2641+
2642+
failed to select a version for `qux` which could resolve this conflict"#,
2643+
)
2644+
.run();
2645+
}

0 commit comments

Comments
 (0)