@@ -337,7 +337,7 @@ pub fn resolve_with_previous<'gctx>(
337337 register_patches : bool ,
338338) -> CargoResult < Resolve > {
339339 let mut patches = if register_patches {
340- Some ( PatchEntries :: from_workspace ( ws) ?)
340+ Some ( PatchEntries :: from_workspace ( ws, previous ) ?)
341341 } else {
342342 None
343343 } ;
@@ -364,7 +364,7 @@ pub fn resolve_with_previous<'gctx>(
364364
365365 if let Some ( patches) = patches. as_ref ( ) {
366366 emit_warnings_of_unused_patches ( ws, & resolved, registry) ?;
367- emit_warnings_of_unresolved_patches ( ws, patches) ?;
367+ emit_warnings_of_unresolved_patches ( ws, & resolved , patches) ?;
368368 }
369369
370370 return Ok ( resolved) ;
@@ -843,16 +843,45 @@ fn emit_warnings_of_unused_patches(
843843/// [`emit_warnings_of_unused_patches`] which requires summaries.
844844fn emit_warnings_of_unresolved_patches (
845845 ws : & Workspace < ' _ > ,
846+ resolve : & Resolve ,
846847 patches : & PatchEntries ,
847848) -> CargoResult < ( ) > {
849+ let resolved_patched_pkg_ids: Vec < _ > = resolve
850+ . iter ( )
851+ . filter ( |pkg_id| pkg_id. source_id ( ) . is_patched ( ) )
852+ . collect ( ) ;
848853 for ( url, patches) in & patches. unresolved {
854+ let canonical_url = CanonicalUrl :: new ( url) ?;
849855 for patch in patches {
856+ let maybe_already_used = resolved_patched_pkg_ids
857+ . iter ( )
858+ . filter ( |id| {
859+ let source_id = id. source_id ( ) ;
860+ let url = source_id. canonical_url ( ) . raw_canonicalized_url ( ) ;
861+ // To check if the patch is for the same source,
862+ // we need strip `patched` prefix here,
863+ // as CanonicalUrl preserves that.
864+ let url = url. as_str ( ) . strip_prefix ( "patched+" ) . unwrap ( ) ;
865+ url == canonical_url. raw_canonicalized_url ( ) . as_str ( )
866+ } )
867+ . any ( |id| patch. matches_ignoring_source ( * id) ) ;
868+
869+ if maybe_already_used {
870+ continue ;
871+ }
872+
850873 let url = if url. as_str ( ) == CRATES_IO_INDEX {
851874 "crates-io"
852875 } else {
853876 url. as_str ( )
854877 } ;
855- let msg = format ! ( "unused patch for `{}` in `{url}`" , patch. name_in_toml( ) ) ;
878+
879+ let msg = format ! (
880+ "unused patch `{}@{}` {} in `{url}`" ,
881+ patch. name_in_toml( ) ,
882+ patch. version_req( ) ,
883+ patch. source_id( ) ,
884+ ) ;
856885 ws. gctx ( ) . shell ( ) . warn ( msg) ?;
857886 }
858887 }
@@ -880,8 +909,8 @@ fn register_patch_entries(
880909
881910 registry. clear_patch ( ) ;
882911
883- for ( url, patches) in patches {
884- for patch in patches {
912+ for ( url, patches) in patches. iter ( ) {
913+ for patch in patches. iter ( ) {
885914 version_prefs. prefer_dependency ( patch. clone ( ) ) ;
886915 }
887916 let Some ( previous) = previous else {
@@ -900,7 +929,7 @@ fn register_patch_entries(
900929 // previous resolve graph, which is primarily what's done here to
901930 // build the `registrations` list.
902931 let mut registrations = Vec :: new ( ) ;
903- for dep in patches {
932+ for dep in patches. iter ( ) {
904933 let candidates = || {
905934 previous
906935 . iter ( )
@@ -1029,14 +1058,16 @@ struct PatchEntries {
10291058 /// resolved dependency graph.
10301059 unresolved : Vec < ( Url , Vec < Dependency > ) > ,
10311060
1061+ from_previous : HashMap < Url , Vec < LockedPatchDependency > > ,
1062+
10321063 /// How many times [`PatchEntries::resolves`] has been called.
10331064 resolve_times : u32 ,
10341065}
10351066
10361067impl PatchEntries {
10371068 const RESOLVE_TIMES_LIMIT : u32 = 20 ;
10381069
1039- fn from_workspace ( ws : & Workspace < ' _ > ) -> CargoResult < PatchEntries > {
1070+ fn from_workspace ( ws : & Workspace < ' _ > , previous : Option < & Resolve > ) -> CargoResult < PatchEntries > {
10401071 let mut ready_patches = HashMap :: new ( ) ;
10411072 let mut unresolved = Vec :: new ( ) ;
10421073 for ( url, patches) in ws. root_patch ( ) ?. into_iter ( ) {
@@ -1050,9 +1081,60 @@ impl PatchEntries {
10501081 unresolved. push ( ( url, file_patches) ) ;
10511082 }
10521083 }
1084+
1085+ let mut from_previous = HashMap :: < Url , Vec < _ > > :: new ( ) ;
1086+ let resolved_patched_pkg_ids: Vec < _ > = previous
1087+ . iter ( )
1088+ . flat_map ( |previous| {
1089+ previous
1090+ . iter ( )
1091+ . chain ( previous. unused_patches ( ) . iter ( ) . cloned ( ) )
1092+ } )
1093+ . filter ( |pkg_id| {
1094+ pkg_id
1095+ . source_id ( )
1096+ . patch_info ( )
1097+ . map ( |info| info. is_resolved ( ) )
1098+ . unwrap_or_default ( )
1099+ } )
1100+ . collect ( ) ;
1101+ for ( url, patches) in & unresolved {
1102+ let mut ready_patches = Vec :: new ( ) ;
1103+ let canonical_url = CanonicalUrl :: new ( url) ?;
1104+ for patch in patches {
1105+ for pkg_id in resolved_patched_pkg_ids. iter ( ) . filter ( |id| {
1106+ let source_id = id. source_id ( ) ;
1107+ let url = source_id. canonical_url ( ) . raw_canonicalized_url ( ) ;
1108+ // To check if the patch is for the same source,
1109+ // we need strip `patched` prefix here,
1110+ // as CanonicalUrl preserves that.
1111+ let url = url. as_str ( ) . strip_prefix ( "patched+" ) . unwrap ( ) ;
1112+ url == canonical_url. raw_canonicalized_url ( ) . as_str ( )
1113+ } ) {
1114+ if patch. matches_ignoring_source ( * pkg_id) {
1115+ let mut patch = patch. clone ( ) ;
1116+ patch. set_source_id ( pkg_id. source_id ( ) ) ;
1117+ patch. lock_to ( * pkg_id) ;
1118+ ready_patches. push ( LockedPatchDependency {
1119+ dependency : patch,
1120+ package_id : * pkg_id,
1121+ alt_package_id : None ,
1122+ } ) ;
1123+ }
1124+ }
1125+ }
1126+ if !ready_patches. is_empty ( ) {
1127+ from_previous
1128+ . entry ( url. clone ( ) )
1129+ . or_default ( )
1130+ . extend ( ready_patches) ;
1131+ }
1132+ }
1133+
10531134 Ok ( PatchEntries {
10541135 ready_patches,
10551136 unresolved,
1137+ from_previous,
10561138 resolve_times : 0 ,
10571139 } )
10581140 }
@@ -1102,6 +1184,16 @@ impl PatchEntries {
11021184 * unresolved = pending_patches;
11031185
11041186 if !ready_patches. is_empty ( ) {
1187+ if let Some ( from_previous) = self . from_previous . get_mut ( url) {
1188+ for patch in & ready_patches {
1189+ if let Some ( index) = from_previous
1190+ . iter ( )
1191+ . position ( |locked| patch. matches_ignoring_source ( locked. package_id ) )
1192+ {
1193+ from_previous. swap_remove ( index) ;
1194+ }
1195+ }
1196+ }
11051197 self . ready_patches
11061198 . entry ( url. clone ( ) )
11071199 . or_default ( )
@@ -1112,13 +1204,39 @@ impl PatchEntries {
11121204 self . resolve_times += 1 ;
11131205 Ok ( has_new_patches)
11141206 }
1207+
1208+ fn iter ( & self ) -> Box < dyn Iterator < Item = ( & Url , Deps < ' _ > ) > + ' _ > {
1209+ if self . ready_patches . is_empty ( ) {
1210+ Box :: new ( self . from_previous . iter ( ) . map ( |( url, deps) | {
1211+ let deps = Deps {
1212+ deps : None ,
1213+ locked_deps : Some ( deps) ,
1214+ } ;
1215+ ( url, deps)
1216+ } ) )
1217+ } else {
1218+ Box :: new ( self . ready_patches . iter ( ) . map ( |( url, deps) | {
1219+ let deps = Deps {
1220+ deps : Some ( deps) ,
1221+ locked_deps : self . from_previous . get ( url) ,
1222+ } ;
1223+ ( url, deps)
1224+ } ) )
1225+ }
1226+ }
11151227}
11161228
1117- impl < ' a > IntoIterator for & ' a PatchEntries {
1118- type Item = ( & ' a Url , & ' a Vec < Dependency > ) ;
1119- type IntoIter = std:: collections:: hash_map:: Iter < ' a , Url , Vec < Dependency > > ;
1229+ struct Deps < ' a > {
1230+ deps : Option < & ' a Vec < Dependency > > ,
1231+ locked_deps : Option < & ' a Vec < LockedPatchDependency > > ,
1232+ }
11201233
1121- fn into_iter ( self ) -> Self :: IntoIter {
1122- self . ready_patches . iter ( )
1234+ impl < ' a > Deps < ' a > {
1235+ fn iter ( & self ) -> impl Iterator < Item = & ' a Dependency > {
1236+ let locked_deps = self
1237+ . locked_deps
1238+ . into_iter ( )
1239+ . flat_map ( |deps| deps. into_iter ( ) . map ( |locked| & locked. dependency ) ) ;
1240+ self . deps . into_iter ( ) . flatten ( ) . chain ( locked_deps)
11231241 }
11241242}
0 commit comments