@@ -239,6 +239,8 @@ public async Task<RestoreResult> ExecuteAsync(CancellationToken token)
239239 packagesLockFile ,
240240 token ) ;
241241
242+ AnalyzePruningResults ( _request . Project , _logger ) ;
243+
242244 var graphs = await GenerateRestoreGraphsAsync ( telemetry , contextForProject , token ) ;
243245
244246 bool auditRan = false ;
@@ -758,6 +760,130 @@ private bool HasValidPlatformVersions()
758760 }
759761 }
760762
763+ internal static void AnalyzePruningResults ( PackageSpec project , ILogger logger )
764+ {
765+ bool enablePruningWarnings =
766+ SdkAnalysisLevelMinimums . IsEnabled (
767+ project . RestoreMetadata . SdkAnalysisLevel ,
768+ project . RestoreMetadata . UsingMicrosoftNETSdk ,
769+ SdkAnalysisLevelMinimums . PruningWarnings ) &&
770+ HasFrameworkNewerThanNET10 ( project ) ;
771+
772+ if ( ! enablePruningWarnings )
773+ {
774+ return ;
775+ }
776+
777+ Dictionary < string , List < string > > prunedDirectPackages = GetPrunableDirectPackages ( project ) ;
778+
779+ if ( prunedDirectPackages != null )
780+ {
781+ RaiseNU1510WarningsIfNeeded ( project , logger , prunedDirectPackages ) ;
782+ }
783+
784+ static Dictionary < string , List < string > > GetPrunableDirectPackages ( PackageSpec project )
785+ {
786+ Dictionary < string , List < string > > prunedDirectPackages = null ;
787+
788+ // Calculate direct packages that are in the pruning range.
789+ foreach ( TargetFrameworkInformation framework in project . TargetFrameworks )
790+ {
791+ if ( framework . PackagesToPrune != null && framework . PackagesToPrune . Count > 0 )
792+ {
793+ foreach ( var dependency in framework . Dependencies )
794+ {
795+ if ( framework . PackagesToPrune . TryGetValue ( dependency . Name , out PrunePackageReference packageToPrune )
796+ && dependency . LibraryRange . VersionRange . Satisfies ( packageToPrune . VersionRange . MaxVersion ! ) )
797+ {
798+ prunedDirectPackages ??= new ( StringComparer . OrdinalIgnoreCase ) ;
799+ if ( ! prunedDirectPackages . ContainsKey ( dependency . Name ) )
800+ {
801+ prunedDirectPackages . Add ( dependency . Name , [ framework . TargetAlias ] ) ;
802+ }
803+ else
804+ {
805+ prunedDirectPackages [ dependency . Name ] . Add ( framework . TargetAlias ) ;
806+ }
807+ }
808+ }
809+ }
810+ }
811+
812+ return prunedDirectPackages ;
813+ }
814+
815+ static void RaiseNU1510WarningsIfNeeded ( PackageSpec project , ILogger logger , Dictionary < string , List < string > > prunedDirectPackages )
816+ {
817+ Dictionary < string , string > aliasToTargetGraphName = null ;
818+ foreach ( var prunedPackage in prunedDirectPackages )
819+ {
820+ // Do not warn if the package exists in any framework.
821+ if ( prunedPackage . Value . Count != project . TargetFrameworks . Count )
822+ {
823+ bool doesPackageRemain = false ;
824+ foreach ( var framework in project . TargetFrameworks )
825+ {
826+ if ( ! prunedPackage . Value . Contains ( framework . TargetAlias ) )
827+ {
828+ if ( ContainsPackage ( prunedPackage , framework ) )
829+ {
830+ doesPackageRemain = true ;
831+ break ;
832+ }
833+ }
834+ }
835+ if ( doesPackageRemain )
836+ {
837+ continue ;
838+ }
839+ }
840+
841+ aliasToTargetGraphName ??= InitializeAliasToTargetGraphName ( project ) ;
842+ logger . Log ( RestoreLogMessage . CreateWarning (
843+ NuGetLogCode . NU1510 ,
844+ string . Format ( CultureInfo . CurrentCulture , Strings . Error_RestorePruningDirectPackageReference , prunedPackage . Key ) ,
845+ prunedPackage . Key ,
846+ prunedPackage . Value . Select ( e => aliasToTargetGraphName [ e ] ) . ToArray ( ) ) ) ;
847+ }
848+ }
849+
850+ static bool HasFrameworkNewerThanNET10 ( PackageSpec project )
851+ {
852+ foreach ( var framework in project . TargetFrameworks . NoAllocEnumerate ( ) )
853+ {
854+ if ( StringComparer . OrdinalIgnoreCase . Equals ( framework . FrameworkName . Framework , FrameworkConstants . FrameworkIdentifiers . NetCoreApp ) &&
855+ framework . FrameworkName . Version . Major >= 10 )
856+ {
857+ return true ;
858+ }
859+ }
860+ return false ;
861+ }
862+
863+ static Dictionary < string , string > InitializeAliasToTargetGraphName ( PackageSpec project )
864+ {
865+ var aliasToTargetGraphName = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
866+ foreach ( var framework in project . TargetFrameworks )
867+ {
868+ aliasToTargetGraphName . Add ( framework . TargetAlias , FrameworkRuntimePair . GetTargetGraphName ( framework . FrameworkName , runtimeIdentifier : null ) ) ;
869+ }
870+
871+ return aliasToTargetGraphName ;
872+ }
873+
874+ static bool ContainsPackage ( KeyValuePair < string , List < string > > prunedPackage , TargetFrameworkInformation framework )
875+ {
876+ foreach ( var dependency in framework . Dependencies . NoAllocEnumerate ( ) )
877+ {
878+ if ( dependency . Name . Equals ( prunedPackage . Key , StringComparison . OrdinalIgnoreCase ) )
879+ {
880+ return true ;
881+ }
882+ }
883+ return false ;
884+ }
885+ }
886+
761887 private async Task < bool > AreCentralVersionRequirementsSatisfiedAsync ( RestoreRequest restoreRequest , int httpSourcesCount )
762888 {
763889 if ( restoreRequest ? . Project ? . RestoreMetadata == null || ! restoreRequest . Project . RestoreMetadata . CentralPackageVersionsEnabled )
0 commit comments