@@ -247,12 +247,17 @@ impl CrateGraph {
247
247
to : CrateId ,
248
248
) -> Result < ( ) , CyclicDependenciesError > {
249
249
let _p = profile:: span ( "add_dep" ) ;
250
- if self . dfs_find ( from, to, & mut FxHashSet :: default ( ) ) {
251
- return Err ( CyclicDependenciesError {
252
- from : ( from, self [ from] . display_name . clone ( ) ) ,
253
- to : ( to, self [ to] . display_name . clone ( ) ) ,
254
- } ) ;
250
+
251
+ // Check if adding a dep from `from` to `to` creates a cycle. To figure
252
+ // that out, look for a path in the *opposite* direction, from `to` to
253
+ // `from`.
254
+ if let Some ( path) = self . find_path ( & mut FxHashSet :: default ( ) , to, from) {
255
+ let path = path. into_iter ( ) . map ( |it| ( it, self [ it] . display_name . clone ( ) ) ) . collect ( ) ;
256
+ let err = CyclicDependenciesError { path } ;
257
+ assert ! ( err. from( ) . 0 == from && err. to( ) . 0 == to) ;
258
+ return Err ( err) ;
255
259
}
260
+
256
261
self . arena . get_mut ( & from) . unwrap ( ) . add_dep ( name, to) ;
257
262
Ok ( ( ) )
258
263
}
@@ -361,22 +366,29 @@ impl CrateGraph {
361
366
start
362
367
}
363
368
364
- fn dfs_find ( & self , target : CrateId , from : CrateId , visited : & mut FxHashSet < CrateId > ) -> bool {
369
+ fn find_path (
370
+ & self ,
371
+ visited : & mut FxHashSet < CrateId > ,
372
+ from : CrateId ,
373
+ to : CrateId ,
374
+ ) -> Option < Vec < CrateId > > {
365
375
if !visited. insert ( from) {
366
- return false ;
376
+ return None ;
367
377
}
368
378
369
- if target == from {
370
- return true ;
379
+ if from == to {
380
+ return Some ( vec ! [ to ] ) ;
371
381
}
372
382
373
383
for dep in & self [ from] . dependencies {
374
384
let crate_id = dep. crate_id ;
375
- if self . dfs_find ( target, crate_id, visited) {
376
- return true ;
385
+ if let Some ( mut path) = self . find_path ( visited, crate_id, to) {
386
+ path. push ( from) ;
387
+ return Some ( path) ;
377
388
}
378
389
}
379
- false
390
+
391
+ None
380
392
}
381
393
382
394
// Work around for https://github.com/rust-analyzer/rust-analyzer/issues/6038.
@@ -481,8 +493,16 @@ impl std::error::Error for ParseEditionError {}
481
493
482
494
#[ derive( Debug ) ]
483
495
pub struct CyclicDependenciesError {
484
- from : ( CrateId , Option < CrateDisplayName > ) ,
485
- to : ( CrateId , Option < CrateDisplayName > ) ,
496
+ path : Vec < ( CrateId , Option < CrateDisplayName > ) > ,
497
+ }
498
+
499
+ impl CyclicDependenciesError {
500
+ fn from ( & self ) -> & ( CrateId , Option < CrateDisplayName > ) {
501
+ self . path . first ( ) . unwrap ( )
502
+ }
503
+ fn to ( & self ) -> & ( CrateId , Option < CrateDisplayName > ) {
504
+ self . path . last ( ) . unwrap ( )
505
+ }
486
506
}
487
507
488
508
impl fmt:: Display for CyclicDependenciesError {
@@ -491,7 +511,14 @@ impl fmt::Display for CyclicDependenciesError {
491
511
Some ( it) => format ! ( "{}({:?})" , it, id) ,
492
512
None => format ! ( "{:?}" , id) ,
493
513
} ;
494
- write ! ( f, "cyclic deps: {} -> {}" , render( & self . from) , render( & self . to) )
514
+ let path = self . path . iter ( ) . rev ( ) . map ( render) . collect :: < Vec < String > > ( ) . join ( " -> " ) ;
515
+ write ! (
516
+ f,
517
+ "cyclic deps: {} -> {}, alternative path: {}" ,
518
+ render( & self . from( ) ) ,
519
+ render( & self . to( ) ) ,
520
+ path
521
+ )
495
522
}
496
523
}
497
524
0 commit comments