-
-
Notifications
You must be signed in to change notification settings - Fork 3k
fix(resolve): detect unused patches when multiple patches share a PackageId #16987
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -511,8 +511,7 @@ pub fn resolve_with_previous<'gctx>( | |
| Some(ws.gctx()), | ||
| )?; | ||
|
|
||
| let patches = registry.patches().values().flat_map(|v| v.iter()); | ||
| resolved.register_used_patches(patches); | ||
| resolved.register_used_patches(registry.patches()); | ||
|
|
||
| if register_patches && !resolved.unused_patches().is_empty() { | ||
| emit_warnings_of_unused_patches(ws, &resolved, registry)?; | ||
|
|
@@ -818,14 +817,12 @@ fn emit_warnings_of_unused_patches( | |
| ) -> CargoResult<()> { | ||
| const MESSAGE: &str = "was not used in the crate graph"; | ||
|
|
||
| // Patch package with the source URLs being patch | ||
| let mut patch_pkgid_to_urls = HashMap::new(); | ||
| for (url, summaries) in registry.patches().iter() { | ||
| for summary in summaries.iter() { | ||
| patch_pkgid_to_urls | ||
| .entry(summary.package_id()) | ||
| .or_insert_with(HashSet::new) | ||
| .insert(url); | ||
| let mut used_patches = HashSet::new(); | ||
| for pkg in resolve.iter() { | ||
| for (dep_pkg_id, deps) in resolve.deps_not_replaced(pkg) { | ||
| for dep in deps { | ||
| used_patches.insert((dep.source_id().canonical_url().clone(), dep_pkg_id)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -839,34 +836,38 @@ fn emit_warnings_of_unused_patches( | |
| } | ||
|
|
||
| let mut unemitted_unused_patches = Vec::new(); | ||
| for unused in resolve.unused_patches().iter() { | ||
| // Show alternative source URLs if the source URLs being patched | ||
| // cannot be found in the crate graph. | ||
| match ( | ||
| source_ids_grouped_by_pkg_name.get(&unused.name()), | ||
| patch_pkgid_to_urls.get(unused), | ||
| ) { | ||
| (Some(ids), Some(patched_urls)) | ||
| if ids | ||
| .iter() | ||
| .all(|id| !patched_urls.contains(id.canonical_url())) => | ||
| { | ||
| let mut help = "perhaps you meant one of the following:".to_owned(); | ||
| for id in ids { | ||
| help.push_str("\n\t"); | ||
| help.push_str(&id.display_registry_name()); | ||
| for (url, summaries) in registry.patches().iter() { | ||
| for summary in summaries.iter() { | ||
| let unused = summary.package_id(); | ||
| if !used_patches.contains(&(url.clone(), unused)) { | ||
| // Show alternative source URLs if the source URLs being patched | ||
| // cannot be found in the crate graph. | ||
| match source_ids_grouped_by_pkg_name.get(&unused.name()) { | ||
| Some(ids) if ids.iter().all(|id| id.canonical_url() != url) => { | ||
| let mut ids: Vec<_> = ids.into_iter().collect(); | ||
| ids.sort_by_key(|id| id.url()); // SourceId implements Ord but url() is safe | ||
| let mut help = "perhaps you meant one of the following:".to_owned(); | ||
| for id in ids { | ||
| help.push_str("\n\t"); | ||
| help.push_str(&id.display_registry_name()); | ||
| } | ||
| ws.gctx().shell().print_report( | ||
| &[Level::WARNING | ||
| .secondary_title(format!("patch `{unused}` {MESSAGE}")) | ||
| .element(Level::HELP.message(help))], | ||
| false, | ||
| )?; | ||
| } | ||
| _ => unemitted_unused_patches.push(unused), | ||
|
Comment on lines
+839
to
+861
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This change means X is in Will this cause any issues for consumers of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my exploration, the main consumers of With this change, if a To fully fix this ambiguity without confusion, we might actually need to make
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think once we decide to include it in the cargo.lock, this may require bumping the lockfile. Because if one user upgrades their toolchain, we will have lockfile changes, and they will need to submit that change. So this should be a lockfile version bump required change. And we need to use this mechanism to maintain compatibility. This issue is far more complex than I thought. It is not technically challenging, but it requires a lot of design work. That is also why we marked it as needing design. So I personally prefer to rework the design to make sure we are on the same page with everyone else about how to deliver this improvement. We should decide whether we need to update the lockfile first, because the implementation would be very different based on these decisions. But I also saw @weihanglo's comment from #12464 (comment). I'm not sure that simply adding some warnings would be enough to move some parts forward first. I would love to hear your thoughts on this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regarding @weihanglo comment
Based on this, we have two ways to move :
I can go with |
||
| } | ||
| ws.gctx().shell().print_report( | ||
| &[Level::WARNING | ||
| .secondary_title(format!("patch `{unused}` {MESSAGE}")) | ||
| .element(Level::HELP.message(help))], | ||
| false, | ||
| )?; | ||
| } | ||
| _ => unemitted_unused_patches.push(unused), | ||
| } | ||
| } | ||
|
|
||
| // Deduplicate general warnings by PackageId | ||
| unemitted_unused_patches.sort(); | ||
| unemitted_unused_patches.dedup(); | ||
|
|
||
| // Show general help message. | ||
| if !unemitted_unused_patches.is_empty() { | ||
| let mut warnings: Vec<_> = unemitted_unused_patches | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems we literally build the same set twice: once in
register_used_patchesand once here.View changes since the review
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One solution could be to maintain an
unused_patches_with_urlthat also includes the URL. But we need to consider this issue first to evaluate the real impact of this change.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I intentionally built the set twice locally to strictly avoid altering the
Resolvestruct, because modifying or replacingunused_patchescould affect how[patch.unused]is encoded incargo.lockviaencode.rs.If we add
unused_patches_with_urltoResolve, should we keep it as a purely in-memory field (and skip serializing it), or are you open to modifying the lockfile structure to store the URLs as well?