diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 5e06010c67..8b4d6d9941 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -1077,6 +1077,8 @@ fn cargo_to_crate_graph( } } + let mut delayed_dev_deps = vec![]; + // Now add a dep edge from all targets of upstream to the lib // target of downstream. for pkg in cargo.packages() { @@ -1092,20 +1094,18 @@ fn cargo_to_crate_graph( } // If the dependency is a dev-dependency with both crates being member libraries of - // the workspace we discard the edge. The reason can be read up on in + // the workspace we delay adding the edge. The reason can be read up on in // https://github.com/rust-lang/rust-analyzer/issues/14167 - // but in short, such an edge usually causes some form of cycle in the crate graph - // wrt to unit tests. Something we cannot reasonable support. + // but in short, such an edge is able to cause some form of cycle in the crate graph + // for normal dependencies. If we do run into a cycle like this, we want to prefer + // the non dev-dependency edge, and so the easiest way to do that is by adding the + // dev-dependency edges last. if dep.kind == DepKind::Dev && matches!(kind, TargetKind::Lib { .. }) && cargo[dep.pkg].is_member && cargo[pkg].is_member { - tracing::warn!( - "Discarding dev-dependency edge from library target `{}` to library target `{}` to prevent potential cycles", - cargo[dep.pkg].name, - cargo[pkg].name - ); + delayed_dev_deps.push((from, name.clone(), to)); continue; } @@ -1114,6 +1114,10 @@ fn cargo_to_crate_graph( } } + for (from, name, to) in delayed_dev_deps { + add_dep(crate_graph, from, name, to); + } + if has_private { // If the user provided a path to rustc sources, we add all the rustc_private crates // and create dependencies on them for the crates which opt-in to that