feat: include full path in the cyclic deps error

This commit is contained in:
Aleksey Kladov 2021-08-18 15:04:16 +03:00
parent 8d7eea9e3a
commit 5192ddfa2e

View file

@ -247,12 +247,17 @@ impl CrateGraph {
to: CrateId, to: CrateId,
) -> Result<(), CyclicDependenciesError> { ) -> Result<(), CyclicDependenciesError> {
let _p = profile::span("add_dep"); let _p = profile::span("add_dep");
if self.dfs_find(from, to, &mut FxHashSet::default()) {
return Err(CyclicDependenciesError { // Check if adding a dep from `from` to `to` creates a cycle. To figure
from: (from, self[from].display_name.clone()), // that out, look for a path in the *opposite* direction, from `to` to
to: (to, self[to].display_name.clone()), // `from`.
}); if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) {
let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
let err = CyclicDependenciesError { path };
assert!(err.from().0 == from && err.to().0 == to);
return Err(err);
} }
self.arena.get_mut(&from).unwrap().add_dep(name, to); self.arena.get_mut(&from).unwrap().add_dep(name, to);
Ok(()) Ok(())
} }
@ -361,22 +366,29 @@ impl CrateGraph {
start start
} }
fn dfs_find(&self, target: CrateId, from: CrateId, visited: &mut FxHashSet<CrateId>) -> bool { fn find_path(
&self,
visited: &mut FxHashSet<CrateId>,
from: CrateId,
to: CrateId,
) -> Option<Vec<CrateId>> {
if !visited.insert(from) { if !visited.insert(from) {
return false; return None;
} }
if target == from { if from == to {
return true; return Some(vec![to]);
} }
for dep in &self[from].dependencies { for dep in &self[from].dependencies {
let crate_id = dep.crate_id; let crate_id = dep.crate_id;
if self.dfs_find(target, crate_id, visited) { if let Some(mut path) = self.find_path(visited, crate_id, to) {
return true; path.push(from);
return Some(path);
} }
} }
false
None
} }
// Work around for https://github.com/rust-analyzer/rust-analyzer/issues/6038. // Work around for https://github.com/rust-analyzer/rust-analyzer/issues/6038.
@ -481,8 +493,16 @@ impl std::error::Error for ParseEditionError {}
#[derive(Debug)] #[derive(Debug)]
pub struct CyclicDependenciesError { pub struct CyclicDependenciesError {
from: (CrateId, Option<CrateDisplayName>), path: Vec<(CrateId, Option<CrateDisplayName>)>,
to: (CrateId, Option<CrateDisplayName>), }
impl CyclicDependenciesError {
fn from(&self) -> &(CrateId, Option<CrateDisplayName>) {
self.path.first().unwrap()
}
fn to(&self) -> &(CrateId, Option<CrateDisplayName>) {
self.path.last().unwrap()
}
} }
impl fmt::Display for CyclicDependenciesError { impl fmt::Display for CyclicDependenciesError {
@ -491,7 +511,14 @@ impl fmt::Display for CyclicDependenciesError {
Some(it) => format!("{}({:?})", it, id), Some(it) => format!("{}({:?})", it, id),
None => format!("{:?}", id), None => format!("{:?}", id),
}; };
write!(f, "cyclic deps: {} -> {}", render(&self.from), render(&self.to)) let path = self.path.iter().rev().map(render).collect::<Vec<String>>().join(" -> ");
write!(
f,
"cyclic deps: {} -> {}, alternative path: {}",
render(&self.from()),
render(&self.to()),
path
)
} }
} }