@@ -334,7 +334,7 @@ pub fn resolve_with_previous<'gctx>(
334334 register_patches : bool ,
335335) -> CargoResult < Resolve > {
336336 let mut patches = if register_patches {
337- Some ( PatchEntries :: from_workspace ( ws) ?)
337+ Some ( PatchEntries :: from_workspace ( ws, previous ) ?)
338338 } else {
339339 None
340340 } ;
@@ -361,7 +361,7 @@ pub fn resolve_with_previous<'gctx>(
361361
362362 if let Some ( patches) = patches. as_ref ( ) {
363363 emit_warnings_of_unused_patches ( ws, & resolved, registry) ?;
364- emit_warnings_of_unresolved_patches ( ws, patches) ?;
364+ emit_warnings_of_unresolved_patches ( ws, & resolved , patches) ?;
365365 }
366366
367367 return Ok ( resolved) ;
@@ -850,16 +850,45 @@ fn emit_warnings_of_unused_patches(
850850/// [`emit_warnings_of_unused_patches`] which requires summaries.
851851fn emit_warnings_of_unresolved_patches (
852852 ws : & Workspace < ' _ > ,
853+ resolve : & Resolve ,
853854 patches : & PatchEntries ,
854855) -> CargoResult < ( ) > {
856+ let resolved_patched_pkg_ids: Vec < _ > = resolve
857+ . iter ( )
858+ . filter ( |pkg_id| pkg_id. source_id ( ) . is_patched ( ) )
859+ . collect ( ) ;
855860 for ( url, patches) in & patches. unresolved {
861+ let canonical_url = CanonicalUrl :: new ( url) ?;
856862 for patch in patches {
863+ let maybe_already_used = resolved_patched_pkg_ids
864+ . iter ( )
865+ . filter ( |id| {
866+ let source_id = id. source_id ( ) ;
867+ let url = source_id. canonical_url ( ) . raw_canonicalized_url ( ) ;
868+ // To check if the patch is for the same source,
869+ // we need strip `patched` prefix here,
870+ // as CanonicalUrl preserves that.
871+ let url = url. as_str ( ) . strip_prefix ( "patched+" ) . unwrap ( ) ;
872+ url == canonical_url. raw_canonicalized_url ( ) . as_str ( )
873+ } )
874+ . any ( |id| patch. matches_ignoring_source ( * id) ) ;
875+
876+ if maybe_already_used {
877+ continue ;
878+ }
879+
857880 let url = if url. as_str ( ) == CRATES_IO_INDEX {
858881 "crates-io"
859882 } else {
860883 url. as_str ( )
861884 } ;
862- let msg = format ! ( "unused patch for `{}` in `{url}`" , patch. name_in_toml( ) ) ;
885+
886+ let msg = format ! (
887+ "unused patch `{}@{}` {} in `{url}`" ,
888+ patch. name_in_toml( ) ,
889+ patch. version_req( ) ,
890+ patch. source_id( ) ,
891+ ) ;
863892 ws. gctx ( ) . shell ( ) . warn ( msg) ?;
864893 }
865894 }
@@ -887,8 +916,8 @@ fn register_patch_entries(
887916
888917 registry. clear_patch ( ) ;
889918
890- for ( url, patches) in patches {
891- for patch in patches {
919+ for ( url, patches) in patches. iter ( ) {
920+ for patch in patches. iter ( ) {
892921 version_prefs. prefer_dependency ( patch. clone ( ) ) ;
893922 }
894923 let Some ( previous) = previous else {
@@ -907,7 +936,7 @@ fn register_patch_entries(
907936 // previous resolve graph, which is primarily what's done here to
908937 // build the `registrations` list.
909938 let mut registrations = Vec :: new ( ) ;
910- for dep in patches {
939+ for dep in patches. iter ( ) {
911940 let candidates = || {
912941 previous
913942 . iter ( )
@@ -1036,14 +1065,16 @@ struct PatchEntries {
10361065 /// resolved dependency graph.
10371066 unresolved : Vec < ( Url , Vec < Dependency > ) > ,
10381067
1068+ from_previous : HashMap < Url , Vec < LockedPatchDependency > > ,
1069+
10391070 /// How many times [`PatchEntries::requires_reresolve`] has been called.
10401071 resolve_times : u32 ,
10411072}
10421073
10431074impl PatchEntries {
10441075 const RESOLVE_TIMES_LIMIT : u32 = 20 ;
10451076
1046- fn from_workspace ( ws : & Workspace < ' _ > ) -> CargoResult < PatchEntries > {
1077+ fn from_workspace ( ws : & Workspace < ' _ > , previous : Option < & Resolve > ) -> CargoResult < PatchEntries > {
10471078 let mut ready_patches = HashMap :: new ( ) ;
10481079 let mut unresolved = Vec :: new ( ) ;
10491080 for ( url, patches) in ws. root_patch ( ) ?. into_iter ( ) {
@@ -1057,9 +1088,60 @@ impl PatchEntries {
10571088 unresolved. push ( ( url, file_patches) ) ;
10581089 }
10591090 }
1091+
1092+ let mut from_previous = HashMap :: < Url , Vec < _ > > :: new ( ) ;
1093+ let resolved_patched_pkg_ids: Vec < _ > = previous
1094+ . iter ( )
1095+ . flat_map ( |previous| {
1096+ previous
1097+ . iter ( )
1098+ . chain ( previous. unused_patches ( ) . iter ( ) . cloned ( ) )
1099+ } )
1100+ . filter ( |pkg_id| {
1101+ pkg_id
1102+ . source_id ( )
1103+ . patch_info ( )
1104+ . map ( |info| info. is_resolved ( ) )
1105+ . unwrap_or_default ( )
1106+ } )
1107+ . collect ( ) ;
1108+ for ( url, patches) in & unresolved {
1109+ let mut ready_patches = Vec :: new ( ) ;
1110+ let canonical_url = CanonicalUrl :: new ( url) ?;
1111+ for patch in patches {
1112+ for pkg_id in resolved_patched_pkg_ids. iter ( ) . filter ( |id| {
1113+ let source_id = id. source_id ( ) ;
1114+ let url = source_id. canonical_url ( ) . raw_canonicalized_url ( ) ;
1115+ // To check if the patch is for the same source,
1116+ // we need strip `patched` prefix here,
1117+ // as CanonicalUrl preserves that.
1118+ let url = url. as_str ( ) . strip_prefix ( "patched+" ) . unwrap ( ) ;
1119+ url == canonical_url. raw_canonicalized_url ( ) . as_str ( )
1120+ } ) {
1121+ if patch. matches_ignoring_source ( * pkg_id) {
1122+ let mut patch = patch. clone ( ) ;
1123+ patch. set_source_id ( pkg_id. source_id ( ) ) ;
1124+ patch. lock_to ( * pkg_id) ;
1125+ ready_patches. push ( LockedPatchDependency {
1126+ dependency : patch,
1127+ package_id : * pkg_id,
1128+ alt_package_id : None ,
1129+ } ) ;
1130+ }
1131+ }
1132+ }
1133+ if !ready_patches. is_empty ( ) {
1134+ from_previous
1135+ . entry ( url. clone ( ) )
1136+ . or_default ( )
1137+ . extend ( ready_patches) ;
1138+ }
1139+ }
1140+
10601141 Ok ( PatchEntries {
10611142 ready_patches,
10621143 unresolved,
1144+ from_previous,
10631145 resolve_times : 0 ,
10641146 } )
10651147 }
@@ -1109,6 +1191,16 @@ impl PatchEntries {
11091191 * unresolved = pending_patches;
11101192
11111193 if !ready_patches. is_empty ( ) {
1194+ if let Some ( from_previous) = self . from_previous . get_mut ( url) {
1195+ for patch in & ready_patches {
1196+ if let Some ( index) = from_previous
1197+ . iter ( )
1198+ . position ( |locked| patch. matches_ignoring_source ( locked. package_id ) )
1199+ {
1200+ from_previous. swap_remove ( index) ;
1201+ }
1202+ }
1203+ }
11121204 self . ready_patches
11131205 . entry ( url. clone ( ) )
11141206 . or_default ( )
@@ -1119,13 +1211,39 @@ impl PatchEntries {
11191211 self . resolve_times += 1 ;
11201212 Ok ( has_new_patches)
11211213 }
1214+
1215+ fn iter ( & self ) -> Box < dyn Iterator < Item = ( & Url , Deps < ' _ > ) > + ' _ > {
1216+ if self . ready_patches . is_empty ( ) {
1217+ Box :: new ( self . from_previous . iter ( ) . map ( |( url, deps) | {
1218+ let deps = Deps {
1219+ deps : None ,
1220+ locked_deps : Some ( deps) ,
1221+ } ;
1222+ ( url, deps)
1223+ } ) )
1224+ } else {
1225+ Box :: new ( self . ready_patches . iter ( ) . map ( |( url, deps) | {
1226+ let deps = Deps {
1227+ deps : Some ( deps) ,
1228+ locked_deps : self . from_previous . get ( url) ,
1229+ } ;
1230+ ( url, deps)
1231+ } ) )
1232+ }
1233+ }
11221234}
11231235
1124- impl < ' a > IntoIterator for & ' a PatchEntries {
1125- type Item = ( & ' a Url , & ' a Vec < Dependency > ) ;
1126- type IntoIter = std:: collections:: hash_map:: Iter < ' a , Url , Vec < Dependency > > ;
1236+ struct Deps < ' a > {
1237+ deps : Option < & ' a Vec < Dependency > > ,
1238+ locked_deps : Option < & ' a Vec < LockedPatchDependency > > ,
1239+ }
11271240
1128- fn into_iter ( self ) -> Self :: IntoIter {
1129- self . ready_patches . iter ( )
1241+ impl < ' a > Deps < ' a > {
1242+ fn iter ( & self ) -> impl Iterator < Item = & ' a Dependency > {
1243+ let locked_deps = self
1244+ . locked_deps
1245+ . into_iter ( )
1246+ . flat_map ( |deps| deps. into_iter ( ) . map ( |locked| & locked. dependency ) ) ;
1247+ self . deps . into_iter ( ) . flatten ( ) . chain ( locked_deps)
11301248 }
11311249}
0 commit comments