Skip to content

Commit caba711

Browse files
committed
Auto merge of #7174 - alexcrichton:fix-cycle-check, r=Eh2406
Fix detection of cyclic dependencies through `[patch]` This commit fixes detection of cyclic dependencies through the use of `[patch]` by ensuring that `matches_id` isn't used because it returns a false negative for registry dependencies when the dependency specifications don't match but the resolve edges are still correct. Closes #7163
2 parents d0f8284 + 0bd1d34 commit caba711

File tree

6 files changed

+80
-27
lines changed

6 files changed

+80
-27
lines changed

src/cargo/core/resolver/encode.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ fn encodable_resolve_node(
582582
None => {
583583
let mut deps = resolve
584584
.deps_not_replaced(id)
585-
.map(|id| encodable_package_id(id, state))
585+
.map(|(id, _)| encodable_package_id(id, state))
586586
.collect::<Vec<_>>();
587587
deps.sort();
588588
(None, Some(deps))

src/cargo/core/resolver/mod.rs

+8-18
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ use crate::util::config::Config;
6060
use crate::util::errors::CargoResult;
6161
use crate::util::profile;
6262

63-
use self::context::{Activations, Context};
63+
use self::context::Context;
6464
use self::dep_cache::RegistryQueryer;
6565
use self::types::{ConflictMap, ConflictReason, DepsFrame};
6666
use self::types::{FeaturesSet, RcVecIter, RemainingDeps, ResolverProgress};
@@ -154,7 +154,7 @@ pub fn resolve(
154154
ResolveVersion::default(),
155155
);
156156

157-
check_cycles(&resolve, &cx.activations)?;
157+
check_cycles(&resolve)?;
158158
check_duplicate_pkgs_in_lockfile(&resolve)?;
159159
trace!("resolved: {:?}", resolve);
160160

@@ -1048,27 +1048,21 @@ fn find_candidate(
10481048
None
10491049
}
10501050

1051-
fn check_cycles(resolve: &Resolve, activations: &Activations) -> CargoResult<()> {
1052-
let summaries: HashMap<PackageId, &Summary> = activations
1053-
.values()
1054-
.map(|(s, _)| (s.package_id(), s))
1055-
.collect();
1056-
1051+
fn check_cycles(resolve: &Resolve) -> CargoResult<()> {
10571052
// Sort packages to produce user friendly deterministic errors.
10581053
let mut all_packages: Vec<_> = resolve.iter().collect();
10591054
all_packages.sort_unstable();
10601055
let mut checked = HashSet::new();
10611056
for pkg in all_packages {
10621057
if !checked.contains(&pkg) {
1063-
visit(resolve, pkg, &summaries, &mut HashSet::new(), &mut checked)?
1058+
visit(resolve, pkg, &mut HashSet::new(), &mut checked)?
10641059
}
10651060
}
10661061
return Ok(());
10671062

10681063
fn visit(
10691064
resolve: &Resolve,
10701065
id: PackageId,
1071-
summaries: &HashMap<PackageId, &Summary>,
10721066
visited: &mut HashSet<PackageId>,
10731067
checked: &mut HashSet<PackageId>,
10741068
) -> CargoResult<()> {
@@ -1089,22 +1083,18 @@ fn check_cycles(resolve: &Resolve, activations: &Activations) -> CargoResult<()>
10891083
// visitation list as we can't induce a cycle through transitive
10901084
// dependencies.
10911085
if checked.insert(id) {
1092-
let summary = summaries[&id];
1093-
for dep in resolve.deps_not_replaced(id) {
1094-
let is_transitive = summary
1095-
.dependencies()
1096-
.iter()
1097-
.any(|d| d.matches_id(dep) && d.is_transitive());
1086+
for (dep, listings) in resolve.deps_not_replaced(id) {
1087+
let is_transitive = listings.iter().any(|d| d.is_transitive());
10981088
let mut empty = HashSet::new();
10991089
let visited = if is_transitive {
11001090
&mut *visited
11011091
} else {
11021092
&mut empty
11031093
};
1104-
visit(resolve, dep, summaries, visited, checked)?;
1094+
visit(resolve, dep, visited, checked)?;
11051095

11061096
if let Some(id) = resolve.replacement(dep) {
1107-
visit(resolve, id, summaries, visited, checked)?;
1097+
visit(resolve, id, visited, checked)?;
11081098
}
11091099
}
11101100
}

src/cargo/core/resolver/resolve.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -229,13 +229,17 @@ unable to verify that `{0}` is the same as when the lockfile was generated
229229
}
230230

231231
pub fn deps(&self, pkg: PackageId) -> impl Iterator<Item = (PackageId, &[Dependency])> {
232-
self.graph
233-
.edges(&pkg)
234-
.map(move |&(id, ref deps)| (self.replacement(id).unwrap_or(id), deps.as_slice()))
232+
self.deps_not_replaced(pkg)
233+
.map(move |(id, deps)| (self.replacement(id).unwrap_or(id), deps))
235234
}
236235

237-
pub fn deps_not_replaced<'a>(&'a self, pkg: PackageId) -> impl Iterator<Item = PackageId> + 'a {
238-
self.graph.edges(&pkg).map(|&(id, _)| id)
236+
pub fn deps_not_replaced(
237+
&self,
238+
pkg: PackageId,
239+
) -> impl Iterator<Item = (PackageId, &[Dependency])> {
240+
self.graph
241+
.edges(&pkg)
242+
.map(|(id, deps)| (*id, deps.as_slice()))
239243
}
240244

241245
pub fn replacement(&self, pkg: PackageId) -> Option<PackageId> {

src/cargo/ops/cargo_generate_lockfile.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
154154
return;
155155
}
156156
set.insert(dep);
157-
for dep in resolve.deps_not_replaced(dep) {
157+
for (dep, _) in resolve.deps_not_replaced(dep) {
158158
fill_with_deps(resolve, dep, set, visited);
159159
}
160160
}

src/cargo/ops/resolve.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,11 @@ fn register_previous_locks(
572572
let keep = |id: &PackageId| keep(id) && !avoid_locking.contains(id);
573573

574574
for node in resolve.iter().filter(keep) {
575-
let deps = resolve.deps_not_replaced(node).filter(keep).collect();
575+
let deps = resolve
576+
.deps_not_replaced(node)
577+
.map(|p| p.0)
578+
.filter(keep)
579+
.collect();
576580
registry.register_lock(node, deps);
577581
}
578582

@@ -582,7 +586,7 @@ fn register_previous_locks(
582586
return;
583587
}
584588
debug!("ignoring any lock pointing directly at {}", node);
585-
for dep in resolve.deps_not_replaced(node) {
589+
for (dep, _) in resolve.deps_not_replaced(node) {
586590
add_deps(resolve, dep, set);
587591
}
588592
}

tests/testsuite/patch.rs

+55
Original file line numberDiff line numberDiff line change
@@ -1079,3 +1079,58 @@ fn patch_older() {
10791079
)
10801080
.run();
10811081
}
1082+
1083+
#[cargo_test]
1084+
fn cycle() {
1085+
Package::new("a", "1.0.0").publish();
1086+
Package::new("b", "1.0.0").publish();
1087+
let p = project()
1088+
.file(
1089+
"Cargo.toml",
1090+
r#"
1091+
[workspace]
1092+
members = ["a", "b"]
1093+
1094+
[patch.crates-io]
1095+
a = {path="a"}
1096+
b = {path="b"}
1097+
"#,
1098+
)
1099+
.file(
1100+
"a/Cargo.toml",
1101+
r#"
1102+
[package]
1103+
name = "a"
1104+
version = "1.0.0"
1105+
1106+
[dependencies]
1107+
b = "1.0"
1108+
"#,
1109+
)
1110+
.file("a/src/lib.rs", "")
1111+
.file(
1112+
"b/Cargo.toml",
1113+
r#"
1114+
[package]
1115+
name = "b"
1116+
version = "1.0.0"
1117+
1118+
[dependencies]
1119+
a = "1.0"
1120+
"#,
1121+
)
1122+
.file("b/src/lib.rs", "")
1123+
.build();
1124+
1125+
p.cargo("check")
1126+
.with_status(101)
1127+
.with_stderr(
1128+
"\
1129+
[UPDATING] [..]
1130+
error: cyclic package dependency: [..]
1131+
package `[..]`
1132+
... which is depended on by `[..]`
1133+
",
1134+
)
1135+
.run();
1136+
}

0 commit comments

Comments
 (0)