@@ -396,6 +396,7 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
396396 }
397397
398398 LibraryDependencyIndex childLibraryDependencyIndex = resolvedDependencyGraphItem . GetDependencyIndexForDependencyAt ( i ) ;
399+ LibraryRangeIndex currentRangeIndex = resolvedDependencyGraphItem . GetRangeIndexForDependencyAt ( i ) ;
399400
400401 if ( ! resolvedDependencyGraphItems . TryGetValue ( childLibraryDependencyIndex , out ResolvedDependencyGraphItem ? childResolvedDependencyGraphItem ) )
401402 {
@@ -409,8 +410,6 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
409410 // Determine if this dependency has already been visited
410411 if ( ! visitedItems . Add ( childLibraryDependencyIndex ) )
411412 {
412- LibraryRangeIndex currentRangeIndex = resolvedDependencyGraphItem . GetRangeIndexForDependencyAt ( i ) ;
413-
414413 if ( resolvedDependencyGraphItem . Path . Contains ( currentRangeIndex ) )
415414 {
416415 // If the dependency exists in the its own path, then a cycle exists
@@ -513,10 +512,27 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
513512 continue ;
514513 }
515514
515+ if ( TryDetectVersionConflict (
516+ currentGraphNode ,
517+ childResolvedDependencyGraphItem ,
518+ versionConflicts ,
519+ downgrades ,
520+ childLibraryDependency ,
521+ currentRangeIndex ,
522+ childResolvedLibraryRangeIndex ,
523+ out GraphNode < RemoteResolveResult > ? nodeWithConflict ) )
524+ {
525+ currentGraphNode . InnerNodes . Add ( nodeWithConflict ) ;
526+
527+ nodesById . Add ( currentRangeIndex , nodeWithConflict ) ;
528+
529+ continue ;
530+ }
531+
516532 // If it wasn't a downgrade, then it was a version conflict like A -> B [1.0.0] but B 1.0.0 was not in the resolved graph
517533 if ( versionConflicts . ContainsKey ( childResolvedLibraryRangeIndex ) && ! nodesById . ContainsKey ( currentRangeIndex ) )
518534 {
519- GraphNode < RemoteResolveResult > nodeWithConflict = new ( childResolvedLibraryDependency . LibraryRange )
535+ nodeWithConflict = new ( childResolvedLibraryDependency . LibraryRange )
520536 {
521537 Item = childResolvedDependencyGraphItem . Item ,
522538 Disposition = Disposition . Acceptable ,
@@ -581,43 +597,26 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
581597 ) ) ;
582598 }
583599
584- // This is a version conflict if:
585- // 1. The node is not a project and isn't unresolved
586- // 2. The conflict has not already been detected
587- // 3. The dependency is transitive and doesn't have PrivateAssets=All
588- // 4. The dependency has a version specified
589- // 5. The version range is not satisfied by the resolved version
590- // 6. A corresponding downgrade was not detected
591- if ( newGraphNode . Item . Key . Type != LibraryType . Project
592- && newGraphNode . Item . Key . Type != LibraryType . ExternalProject
593- && newGraphNode . Item . Key . Type != LibraryType . Unresolved
594- && ! versionConflicts . ContainsKey ( childResolvedLibraryRangeIndex )
595- && childLibraryDependency . SuppressParent != LibraryIncludeFlags . All
596- && childLibraryDependency . LibraryRange . VersionRange != null
597- && ! childLibraryDependency . LibraryRange . VersionRange ! . Satisfies ( newGraphNode . Item . Key . Version )
598- && ! downgrades . ContainsKey ( childResolvedLibraryRangeIndex ) )
600+ if ( TryDetectVersionConflict (
601+ currentGraphNode ,
602+ childResolvedDependencyGraphItem ,
603+ versionConflicts , downgrades ,
604+ childLibraryDependency ,
605+ currentRangeIndex ,
606+ childResolvedLibraryRangeIndex ,
607+ out GraphNode < RemoteResolveResult > ? conflictingNode ) )
599608 {
600609 // Remove the existing node so it can be replaced with a node representing the conflict
601610 currentGraphNode . InnerNodes . Remove ( newGraphNode ) ;
602611
603- GraphNode < RemoteResolveResult > conflictingNode = new ( childLibraryDependency . LibraryRange )
604- {
605- Disposition = Disposition . Acceptable ,
606- Item = new GraphItem < RemoteResolveResult > (
607- new LibraryIdentity (
608- childLibraryDependency . Name ,
609- childLibraryDependency . LibraryRange . VersionRange . MinVersion ! ,
610- LibraryType . Package ) ) ,
611- OuterNode = currentGraphNode ,
612- } ;
613-
614612 // Add the conflict node to the parent
615613 currentGraphNode . InnerNodes . Add ( conflictingNode ) ;
616614
617- // Track the version conflict for later
618- versionConflicts . Add ( childResolvedLibraryRangeIndex , conflictingNode ) ;
615+ nodesById . Add ( currentRangeIndex , conflictingNode ) ;
616+
617+ // This node conflicts and won't be included in the final graph
618+ visitedItems . Remove ( childLibraryDependencyIndex ) ;
619619
620- // Process the next child
621620 continue ;
622621 }
623622
@@ -752,6 +751,59 @@ public async Task<ValueTuple<bool, List<RestoreTargetGraph>, RuntimeGraph>> Reso
752751 resolvedDependencies : resolvedPackages ) ;
753752
754753 return ( success , restoreTargetGraph ) ;
754+
755+ static bool TryDetectVersionConflict (
756+ GraphNode < RemoteResolveResult > currentGraphNode ,
757+ ResolvedDependencyGraphItem resolvedDependencyGraphItem ,
758+ Dictionary < LibraryRangeIndex , GraphNode < RemoteResolveResult > > versionConflicts ,
759+ Dictionary < LibraryRangeIndex , ( LibraryRangeIndex FromParentLibraryRangeIndex , LibraryDependency FromLibraryDependency , LibraryRangeIndex ToParentLibraryRangeIndex , LibraryDependencyIndex ToLibraryDependencyIndex , bool IsCentralTransitive ) > downgrades ,
760+ LibraryDependency childLibraryDependency ,
761+ LibraryRangeIndex childLibraryDependencyIndex ,
762+ LibraryRangeIndex childResolvedLibraryRangeIndex ,
763+ [ NotNullWhen ( true ) ] out GraphNode < RemoteResolveResult > ? conflictingNode )
764+ {
765+ conflictingNode = null ;
766+
767+ if ( resolvedDependencyGraphItem . IsRootPackageReference )
768+ {
769+ return false ;
770+ }
771+
772+ // This is a version conflict if:
773+ // 1. The node is not a project and isn't unresolved
774+ // 2. The conflict has not already been detected
775+ // 3. The dependency is transitive and doesn't have PrivateAssets=All
776+ // 4. The dependency has a version specified
777+ // 5. The version range is not satisfied by the resolved version
778+ // 6. A corresponding downgrade was not detected
779+ if ( resolvedDependencyGraphItem . Item . Key . Type != LibraryType . Project
780+ && resolvedDependencyGraphItem . Item . Key . Type != LibraryType . ExternalProject
781+ && resolvedDependencyGraphItem . Item . Key . Type != LibraryType . Unresolved
782+ && ! versionConflicts . ContainsKey ( childResolvedLibraryRangeIndex )
783+ && childLibraryDependency . SuppressParent != LibraryIncludeFlags . All
784+ && childLibraryDependency . LibraryRange . VersionRange != null
785+ && ! childLibraryDependency . LibraryRange . VersionRange ! . Satisfies ( resolvedDependencyGraphItem . Item . Key . Version )
786+ && ! downgrades . ContainsKey ( childResolvedLibraryRangeIndex ) )
787+ {
788+ conflictingNode = new ( childLibraryDependency . LibraryRange )
789+ {
790+ Disposition = Disposition . Rejected ,
791+ Item = new GraphItem < RemoteResolveResult > (
792+ new LibraryIdentity (
793+ childLibraryDependency . Name ,
794+ childLibraryDependency . LibraryRange . VersionRange . MinVersion ! ,
795+ LibraryType . Package ) ) ,
796+ OuterNode = currentGraphNode ,
797+ } ;
798+
799+ // Track the version conflict for later
800+ versionConflicts . Add ( childResolvedLibraryRangeIndex , conflictingNode ) ;
801+
802+ return true ;
803+ }
804+
805+ return false ;
806+ }
755807 }
756808
757809 private static bool EvaluateRuntimeDependencies ( ref LibraryDependency libraryDependency , RuntimeGraph ? runtimeGraph , string ? runtimeIdentifier , ref HashSet < LibraryDependency > ? runtimeDependencies )
0 commit comments