@@ -313,7 +313,17 @@ impl_eq_hash!(TypeAlias; name, ty);
313313#[ derive( Debug ) ]
314314pub enum C3Error {
315315 CycleDetected ( Vec < String > ) ,
316- InconsistentLinearization { module : String } ,
316+ /// Error for inconsistent MRO.
317+ /// This can happen if the dependency graph has a shape that makes the
318+ /// order of parent classes ambiguous.
319+ /// Example: A depends on B and C, and B also depends on C.
320+ /// The linearization of A is A + merge(linearization(B), linearization(C), [B, C]).
321+ /// If B appears before C in one parent's linearization but C appears before B
322+ /// in another's, the merge will fail.
323+ InconsistentLinearization {
324+ module : String ,
325+ conflicts : Vec < Vec < String > > ,
326+ } ,
317327}
318328
319329impl fmt:: Display for C3Error {
@@ -322,8 +332,22 @@ impl fmt::Display for C3Error {
322332 C3Error :: CycleDetected ( cycle) => {
323333 write ! ( f, "Circular dependency detected: {:?}" , cycle. join( " -> " ) )
324334 }
325- C3Error :: InconsistentLinearization { module } => {
326- write ! ( f, "Inconsistent resolution order for module '{:?}'" , module)
335+ C3Error :: InconsistentLinearization { module, conflicts } => {
336+ writeln ! ( f, "Inconsistent resolution order for module '{}'" , module) ?;
337+ writeln ! (
338+ f,
339+ "The compiler could not resolve the following conflicting import constraints:"
340+ ) ?;
341+
342+ // Loop through the matrix and print each conflicting sequence
343+ for conflict in conflicts {
344+ writeln ! ( f, " [{}]" , conflict. join( ", " ) ) ?;
345+ }
346+
347+ write ! (
348+ f,
349+ "Try reordering your `use` statements to avoid cross-wiring."
350+ )
327351 }
328352 }
329353 }
@@ -489,12 +513,26 @@ impl ProjectGraph {
489513 seqs. push ( line) ;
490514 }
491515
492- seqs. push ( parents. clone ( ) ) ;
493-
494516 let mut result = vec ! [ module] ;
495- let merged = merge ( seqs) . ok_or ( C3Error :: InconsistentLinearization {
496- module : self . modules [ module] . source . name ( ) . to_string ( ) ,
497- } ) ?;
517+ let merged = match merge ( seqs) {
518+ Ok ( m) => m,
519+ Err ( conflicts) => {
520+ // Map the failing usize sequences into readable module names
521+ let conflict_names: Vec < Vec < String > > = conflicts
522+ . into_iter ( )
523+ . map ( |seq| {
524+ seq. into_iter ( )
525+ . map ( |id| self . modules [ id] . source . name ( ) . to_string ( ) )
526+ . collect ( )
527+ } )
528+ . collect ( ) ;
529+
530+ return Err ( C3Error :: InconsistentLinearization {
531+ module : self . modules [ module] . source . name ( ) . to_string ( ) ,
532+ conflicts : conflict_names,
533+ } ) ;
534+ }
535+ } ;
498536
499537 result. extend ( merged) ;
500538
@@ -720,13 +758,18 @@ impl ProjectGraph {
720758 }
721759}
722760
723- fn merge ( mut seqs : Vec < Vec < usize > > ) -> Option < Vec < usize > > {
761+ /// C3 Merge Algorithm
762+ ///
763+ /// Merges a list of sequences (parent linearizations) into a single sequence.
764+ /// The algorithm ensures that the local precedence order of each sequence is preserved.
765+ // Change the return type to Result
766+ fn merge ( mut seqs : Vec < Vec < usize > > ) -> Result < Vec < usize > , Vec < Vec < usize > > > {
724767 let mut result = Vec :: new ( ) ;
725768
726769 loop {
727770 seqs. retain ( |s| !s. is_empty ( ) ) ;
728771 if seqs. is_empty ( ) {
729- return Some ( result) ;
772+ return Ok ( result) ;
730773 }
731774
732775 let mut candidate = None ;
@@ -740,7 +783,9 @@ fn merge(mut seqs: Vec<Vec<usize>>) -> Option<Vec<usize>> {
740783 }
741784 }
742785
743- let head = candidate?;
786+ let Some ( head) = candidate else {
787+ return Err ( seqs) ;
788+ } ;
744789
745790 result. push ( head) ;
746791
@@ -881,7 +926,6 @@ pub(crate) mod tests {
881926 use std:: path:: Path ;
882927 use tempfile:: TempDir ;
883928
884- // ProjectGraph::new tests
885929 // Creates a file with specific content in the temp directory
886930 pub ( crate ) fn create_simf_file ( dir : & Path , rel_path : & str , content : & str ) -> PathBuf {
887931 let full_path = dir. join ( rel_path) ;
@@ -898,7 +942,6 @@ pub(crate) mod tests {
898942 }
899943
900944 // Helper to mock the initial root program parsing
901- // (Assuming your parser works via a helper function)
902945 fn parse_root ( path : & Path ) -> ( parse:: Program , SourceFile ) {
903946 // 1. Read file
904947 let content = std:: fs:: read_to_string ( path) . expect ( "Failed to read root file for parsing" ) ;
0 commit comments