@@ -547,6 +547,7 @@ impl<L: Language, D: ParsedSource<Language = L>> Graph<D> {
547547 fn format_imports_list < W : std:: fmt:: Write > (
548548 & self ,
549549 idx : usize ,
550+ imports : impl IntoIterator < Item = usize > ,
550551 f : & mut W ,
551552 ) -> std:: result:: Result < ( ) , std:: fmt:: Error > {
552553 let node = self . node ( idx) ;
@@ -555,7 +556,7 @@ impl<L: Language, D: ParsedSource<Language = L>> Graph<D> {
555556 write ! ( f, "{req}" ) ?;
556557 }
557558 write ! ( f, " imports:" ) ?;
558- for dep in self . node_ids ( idx ) . skip ( 1 ) {
559+ for dep in imports {
559560 let dep = self . node ( dep) ;
560561 write ! ( f, "\n {} " , utils:: source_name( & dep. path, & self . root) . display( ) ) ?;
561562 if let Some ( req) = dep. data . version_req ( ) {
@@ -566,18 +567,74 @@ impl<L: Language, D: ParsedSource<Language = L>> Graph<D> {
566567 Ok ( ( ) )
567568 }
568569
569- /// Filters incompatible versions from the `candidates`.
570- fn retain_compatible_versions ( & self , idx : usize , candidates : & mut Vec < & CompilerVersion > ) {
571- let nodes: HashSet < _ > = self . node_ids ( idx) . collect ( ) ;
572- for node in nodes {
573- if let Some ( req) = & self . node ( node) . data . version_req ( ) {
570+ /// Filters incompatible versions from the `candidates`. It iterates over node imports and in
571+ /// case if there is no compatible version it returns the latest seen node id.
572+ fn retain_compatible_versions (
573+ & self ,
574+ idx : usize ,
575+ candidates : & mut Vec < & CompilerVersion > ,
576+ offline : bool ,
577+ ) -> Result < ( ) , String > {
578+ let mut all_versions = candidates. clone ( ) ;
579+
580+ let nodes: Vec < _ > = self . node_ids ( idx) . collect ( ) ;
581+ let mut failed_node = None ;
582+ for node in nodes. iter ( ) {
583+ if let Some ( req) = self . node ( * node) . data . version_req ( ) {
574584 candidates. retain ( |v| req. matches ( v. as_ref ( ) ) ) ;
585+
586+ if candidates. is_empty ( ) {
587+ failed_node = Some ( * node) ;
588+ break ;
589+ }
590+ }
591+ }
592+
593+ let Some ( failed_node_idx) = failed_node else {
594+ // everything is fine
595+ return Ok ( ( ) ) ;
596+ } ;
597+
598+ // This now keeps data for the node which were the last one before we had no candidates
599+ // left. It means that there
600+ let failed_node = self . node ( failed_node_idx) ;
601+
602+ if let Err ( version_err) = failed_node. check_available_version ( & all_versions, offline) {
603+ // check if the version is even valid
604+ let f = utils:: source_name ( & failed_node. path , & self . root ) . display ( ) ;
605+ return Err (
606+ format ! ( "Encountered invalid solc version in {f}: {version_err}" ) . to_string ( )
607+ ) ;
608+ } else {
609+ // if the node requirement makes sense, it means that there is at least one node
610+ // which requirement conflicts with it
611+
612+ // retain only versions compatible with the `failed_node`
613+ if let Some ( req) = failed_node. data . version_req ( ) {
614+ all_versions. retain ( |v| req. matches ( v. as_ref ( ) ) ) ;
575615 }
576- if candidates. is_empty ( ) {
577- // nothing to filter anymore
578- return ;
616+
617+ // iterate over all the nodes once again and find the one incompatible
618+ for node in & nodes {
619+ if self . node ( * node) . check_available_version ( & all_versions, offline) . is_err ( ) {
620+ let mut msg = String :: new ( ) ;
621+
622+ // avoid formatting root node as import
623+ let imports = if * node == idx {
624+ vec ! [ failed_node_idx]
625+ } else {
626+ vec ! [ * node, failed_node_idx]
627+ } ;
628+
629+ self . format_imports_list ( idx, imports, & mut msg) . unwrap ( ) ;
630+ return Err ( format ! ( "Found incompatible versions:\n {msg}" ) ) ;
631+ }
579632 }
580633 }
634+
635+ let mut msg = String :: new ( ) ;
636+ self . format_imports_list ( idx, nodes[ 1 ..] . to_vec ( ) , & mut msg) . unwrap ( ) ;
637+ Err ( format ! ( "Found incompatible versions:\n {msg}" ) ) ;
581638 }
582639
583640 fn input_nodes_by_language ( & self ) -> HashMap < D :: Language , Vec < usize > > {
@@ -619,8 +676,6 @@ impl<L: Language, D: ParsedSource<Language = L>> Graph<D> {
619676 // exit on first error, instead gather all the errors and return a bundled
620677 // error message instead
621678 let mut errors = Vec :: new ( ) ;
622- // we also don't want duplicate error diagnostic
623- let mut erroneous_nodes = HashSet :: with_capacity ( self . edges . num_input_files ) ;
624679
625680 // the sorted list of all versions
626681 let all_versions = if offline {
@@ -649,23 +704,8 @@ impl<L: Language, D: ParsedSource<Language = L>> Graph<D> {
649704 let mut candidates = all_versions. iter ( ) . collect :: < Vec < _ > > ( ) ;
650705 // remove all incompatible versions from the candidates list by checking the node
651706 // and all its imports
652- self . retain_compatible_versions ( idx, & mut candidates) ;
653-
654- if candidates. is_empty ( ) && !erroneous_nodes. contains ( & idx) {
655- // check if the version is even valid
656- let node = self . node ( idx) ;
657- if let Err ( version_err) = node. check_available_version ( & all_versions, offline) {
658- let f = utils:: source_name ( & node. path , & self . root ) . display ( ) ;
659- errors. push ( format ! (
660- "Encountered invalid solc version in {f}: {version_err}"
661- ) ) ;
662- } else {
663- let mut msg = String :: new ( ) ;
664- self . format_imports_list ( idx, & mut msg) . unwrap ( ) ;
665- errors. push ( format ! ( "Found incompatible versions:\n {msg}" ) ) ;
666- }
667-
668- erroneous_nodes. insert ( idx) ;
707+ if let Err ( err) = self . retain_compatible_versions ( idx, & mut candidates, offline) {
708+ errors. push ( err) ;
669709 } else {
670710 // found viable candidates, pick the most recent version that's already
671711 // installed
@@ -878,7 +918,7 @@ impl<D: ParsedSource> Node<D> {
878918 /// 0.8.20, if the highest available version is `0.8.19`
879919 fn check_available_version (
880920 & self ,
881- all_versions : & [ CompilerVersion ] ,
921+ all_versions : & [ & CompilerVersion ] ,
882922 offline : bool ,
883923 ) -> std:: result:: Result < ( ) , SourceVersionError > {
884924 let Some ( req) = self . data . version_req ( ) else { return Ok ( ( ) ) } ;
0 commit comments