Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix panic with doc collision orphan. #9142

Merged
merged 1 commit into from
Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ pub fn create_bcx<'a, 'cfg>(
// TODO: In theory, Cargo should also dedupe the roots, but I'm uncertain
// what heuristics to use in that case.
if build_config.mode == (CompileMode::Doc { deps: true }) {
remove_duplicate_doc(build_config, &mut unit_graph);
remove_duplicate_doc(build_config, &units, &mut unit_graph);
}

if build_config
Expand Down Expand Up @@ -1508,14 +1508,11 @@ fn opt_patterns_and_names(
/// - Different sources. See `collision_doc_sources` test.
///
/// Ideally this would not be necessary.
fn remove_duplicate_doc(build_config: &BuildConfig, unit_graph: &mut UnitGraph) {
// NOTE: There is some risk that this can introduce problems because it
// may create orphans in the unit graph (parts of the tree get detached
// from the roots). I currently can't think of any ways this will cause a
// problem because all other parts of Cargo traverse the graph starting
// from the roots. Perhaps this should scan for detached units and remove
// them too?
//
fn remove_duplicate_doc(
build_config: &BuildConfig,
root_units: &[Unit],
unit_graph: &mut UnitGraph,
) {
// First, create a mapping of crate_name -> Unit so we can see where the
// duplicates are.
let mut all_docs: HashMap<String, Vec<Unit>> = HashMap::new();
Expand Down Expand Up @@ -1601,4 +1598,18 @@ fn remove_duplicate_doc(build_config: &BuildConfig, unit_graph: &mut UnitGraph)
for unit_deps in unit_graph.values_mut() {
unit_deps.retain(|unit_dep| !removed_units.contains(&unit_dep.unit));
}
// Remove any orphan units that were detached from the graph.
let mut visited = HashSet::new();
fn visit(unit: &Unit, graph: &UnitGraph, visited: &mut HashSet<Unit>) {
if !visited.insert(unit.clone()) {
return;
}
for dep in &graph[unit] {
visit(&dep.unit, graph, visited);
}
}
for unit in root_units {
visit(unit, unit_graph, &mut visited);
}
unit_graph.retain(|unit, _| visited.contains(unit));
}
51 changes: 49 additions & 2 deletions tests/testsuite/collisions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
//! Ideally these should never happen, but I don't think we'll ever be able to
//! prevent all collisions.

use cargo_test_support::basic_manifest;
use cargo_test_support::project;
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_manifest, cross_compile, project};
use std::env;

#[cargo_test]
Expand Down Expand Up @@ -431,3 +430,51 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
)
.run();
}

#[cargo_test]
fn collision_doc_target() {
// collision in doc with --target, doesn't fail due to orphans
if cross_compile::disabled() {
return;
}

Package::new("orphaned", "1.0.0").publish();
Package::new("bar", "1.0.0")
.dep("orphaned", "1.0")
.publish();
Package::new("bar", "2.0.0").publish();
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.1.0"

[dependencies]
bar2 = { version = "2.0", package="bar" }
bar = "1.0"
"#,
)
.file("src/lib.rs", "")
.build();

p.cargo("doc --target")
.arg(cross_compile::alternate())
.with_stderr_unordered(
"\
[UPDATING] [..]
[DOWNLOADING] crates ...
[DOWNLOADED] orphaned v1.0.0 [..]
[DOWNLOADED] bar v2.0.0 [..]
[DOWNLOADED] bar v1.0.0 [..]
[CHECKING] orphaned v1.0.0
[DOCUMENTING] bar v2.0.0
[CHECKING] bar v2.0.0
[CHECKING] bar v1.0.0
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
",
)
.run();
}