Skip to content

Commit af431e1

Browse files
committed
Auto merge of #12454 - alcolmenar:alcolmenar/fix-invalid-rm, r=epage
Fix cargo remove incorrectly removing used patches ### What does this PR try to resolve? Fixes an issue where patches are being removed when member dependencies don't explicitly contain the patched crate. Fixes #12419 ### How should we test and review this PR? - Created a test for the failing use case - Verify passing test <!-- ### Additional information Other information you want to mention in this PR, such as prior arts, future extensions, an unresolved problem, or a TODO list. -->
2 parents 7ed917f + 72e66fb commit af431e1

File tree

36 files changed

+155
-71
lines changed

36 files changed

+155
-71
lines changed

src/bin/cargo/commands/remove.rs

+52-50
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use cargo::core::dependency::DepKind;
22
use cargo::core::PackageIdSpec;
3+
use cargo::core::Resolve;
34
use cargo::core::Workspace;
45
use cargo::ops::cargo_remove::remove;
56
use cargo::ops::cargo_remove::RemoveOptions;
@@ -109,9 +110,24 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
109110

110111
// Reload the workspace since we've changed dependencies
111112
let ws = args.workspace(config)?;
112-
resolve_ws(&ws)?;
113-
}
113+
let resolve = {
114+
// HACK: Avoid unused patch warnings by temporarily changing the verbosity.
115+
// In rare cases, this might cause index update messages to not show up
116+
let verbosity = ws.config().shell().verbosity();
117+
ws.config()
118+
.shell()
119+
.set_verbosity(cargo::core::Verbosity::Quiet);
120+
let resolve = resolve_ws(&ws);
121+
ws.config().shell().set_verbosity(verbosity);
122+
resolve?.1
123+
};
114124

125+
// Attempt to gc unused patches and re-resolve if anything is removed
126+
if gc_unused_patches(&workspace, &resolve)? {
127+
let ws = args.workspace(config)?;
128+
resolve_ws(&ws)?;
129+
}
130+
}
115131
Ok(())
116132
}
117133

@@ -229,31 +245,6 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> {
229245
}
230246
}
231247

232-
// Clean up the patch section
233-
if let Some(toml_edit::Item::Table(patch_section_table)) = manifest.get_mut("patch") {
234-
patch_section_table.set_implicit(true);
235-
236-
// The key in each of the subtables is a source (either a registry or a URL)
237-
for (source, item) in patch_section_table.iter_mut() {
238-
if let toml_edit::Item::Table(patch_table) = item {
239-
patch_table.set_implicit(true);
240-
241-
for (key, item) in patch_table.iter_mut() {
242-
let package_name =
243-
Dependency::from_toml(&workspace.root_manifest(), key.get(), item)?.name;
244-
if !source_has_match(
245-
&package_name,
246-
source.get(),
247-
&dependencies,
248-
workspace.config(),
249-
)? {
250-
*item = toml_edit::Item::None;
251-
}
252-
}
253-
}
254-
}
255-
}
256-
257248
// Clean up the replace section
258249
if let Some(toml_edit::Item::Table(table)) = manifest.get_mut("replace") {
259250
table.set_implicit(true);
@@ -310,35 +301,46 @@ fn spec_has_match(
310301
Ok(false)
311302
}
312303

313-
/// Check whether or not a source (URL or registry name) matches any non-workspace dependencies.
314-
fn source_has_match(
315-
name: &str,
316-
source: &str,
317-
dependencies: &[Dependency],
318-
config: &Config,
319-
) -> CargoResult<bool> {
320-
for dep in dependencies {
321-
if &dep.name != name {
322-
continue;
323-
}
304+
/// Removes unused patches from the manifest
305+
fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResult<bool> {
306+
let mut manifest: toml_edit::Document =
307+
cargo_util::paths::read(workspace.root_manifest())?.parse()?;
308+
let mut modified = false;
324309

325-
match dep.source_id(config)? {
326-
MaybeWorkspace::Other(source_id) => {
327-
if source_id.is_registry() {
328-
if source_id.display_registry_name() == source
329-
|| source_id.url().as_str() == source
310+
// Clean up the patch section
311+
if let Some(toml_edit::Item::Table(patch_section_table)) = manifest.get_mut("patch") {
312+
patch_section_table.set_implicit(true);
313+
314+
for (_, item) in patch_section_table.iter_mut() {
315+
if let toml_edit::Item::Table(patch_table) = item {
316+
patch_table.set_implicit(true);
317+
318+
for (key, item) in patch_table.iter_mut() {
319+
let dep = Dependency::from_toml(&workspace.root_manifest(), key.get(), item)?;
320+
321+
// Generate a PackageIdSpec url for querying
322+
let url = if let MaybeWorkspace::Other(source_id) =
323+
dep.source_id(workspace.config())?
330324
{
331-
return Ok(true);
332-
}
333-
} else if source_id.is_git() {
334-
if source_id.url().as_str() == source {
335-
return Ok(true);
325+
format!("{}#{}", source_id.url(), dep.name)
326+
} else {
327+
continue;
328+
};
329+
330+
if PackageIdSpec::query_str(&url, resolve.unused_patches().iter().cloned())
331+
.is_ok()
332+
{
333+
*item = toml_edit::Item::None;
334+
modified = true;
336335
}
337336
}
338337
}
339-
MaybeWorkspace::Workspace(_) => {}
340338
}
341339
}
342340

343-
Ok(false)
341+
if modified {
342+
cargo_util::paths::write(workspace.root_manifest(), manifest.to_string().as_bytes())?;
343+
}
344+
345+
Ok(modified)
344346
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing clippy from dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing semver from build-dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing regex from dev-dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Cargo.toml
2+
3+
[workspace]
4+
members = ["serde", "serde_derive"]
5+
6+
[patch.crates-io]
7+
serde = { path = "serde" }
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# serde/Cargo.toml
2+
3+
[package]
4+
name = "serde"
5+
version = "1.0.0"
6+
7+
[dependencies]
8+
serde_derive = { path = "../serde_derive" }
9+

tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/src/lib.rs

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# serde_derive/Cargo.toml
2+
3+
[package]
4+
name = "serde_derive"
5+
version = "1.0.0"
6+
7+
[dev-dependencies]
8+
serde_json = "1.0.0"

tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/src/lib.rs

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use cargo_test_support::compare::assert_ui;
2+
use cargo_test_support::curr_dir;
3+
use cargo_test_support::CargoCommand;
4+
use cargo_test_support::Project;
5+
6+
#[cargo_test]
7+
fn case() {
8+
cargo_test_support::registry::init();
9+
cargo_test_support::registry::Package::new("serde", "1.0.0").publish();
10+
cargo_test_support::registry::Package::new("serde_json", "1.0.0")
11+
.dep("serde", "1.0.0")
12+
.publish();
13+
14+
let project = Project::from_template(curr_dir!().join("in"));
15+
let project_root = project.root();
16+
17+
snapbox::cmd::Command::cargo_ui()
18+
.current_dir(&project_root)
19+
.arg("remove")
20+
.args(["--package", "serde", "serde_derive"])
21+
.assert()
22+
.code(0)
23+
.stdout_matches_path(curr_dir!().join("stdout.log"))
24+
.stderr_matches_path(curr_dir!().join("stderr.log"));
25+
26+
assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Cargo.toml
2+
3+
[workspace]
4+
members = ["serde", "serde_derive"]
5+
6+
[patch.crates-io]
7+
serde = { path = "serde" }
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# serde/Cargo.toml
2+
3+
[package]
4+
name = "serde"
5+
version = "1.0.0"
6+

tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/src/lib.rs

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# serde_derive/Cargo.toml
2+
3+
[package]
4+
name = "serde_derive"
5+
version = "1.0.0"
6+
7+
[dev-dependencies]
8+
serde_json = "1.0.0"

tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/src/lib.rs

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Removing serde_derive from dependencies

tests/testsuite/cargo_remove/gc_keep_used_patch/stdout.log

Whitespace-only changes.

tests/testsuite/cargo_remove/gc_patch/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ fn case() {
2323
})
2424
.url();
2525

26+
let git_project3 = git::new("bar3", |project| {
27+
project
28+
.file("Cargo.toml", &basic_manifest("bar", "0.1.0"))
29+
.file("src/lib.rs", "")
30+
})
31+
.url();
32+
2633
let in_project = project()
2734
.file(
2835
"Cargo.toml",
@@ -38,7 +45,7 @@ fn case() {
3845
bar = {{ git = \"{git_project1}\" }}\n\
3946
\n\
4047
[patch.\"{git_project1}\"]\n\
41-
bar = {{ git = \"{git_project2}\" }}\n\
48+
bar = {{ git = \"{git_project3}\" }}\n\
4249
\n\
4350
[patch.crates-io]\n\
4451
bar = {{ git = \"{git_project2}\" }}\n",

tests/testsuite/cargo_remove/gc_patch/out/Cargo.lock

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
11
Removing bar from dependencies
2-
Updating git repository `[ROOTURL]/bar2`
3-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing toml from dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing toml from dependencies
2-
Updating `dummy-registry` index

tests/testsuite/cargo_remove/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ mod avoid_empty_tables;
22
mod build;
33
mod dev;
44
mod dry_run;
5+
mod gc_keep_used_patch;
56
mod gc_patch;
67
mod gc_profile;
78
mod gc_replace;
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
Removing docopt from dependencies
22
Removing semver from dependencies
3-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
Removing regex from dev-dependencies
22
Removing serde from dev-dependencies
3-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing serde from dev-dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing semver from dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing docopt from dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing docopt from dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing dbus from dependencies for target `x86_64-unknown-linux-gnu`
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing semver from build-dependencies for target `x86_64-unknown-linux-gnu`
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing ncurses from dev-dependencies for target `x86_64-unknown-linux-gnu`
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing rustc-serialize from dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing semver from build-dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing semver from build-dependencies
2-
Updating `dummy-registry` index
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
Removing semver from build-dependencies
2-
Updating `dummy-registry` index

0 commit comments

Comments
 (0)