Skip to content

Commit 827aa2a

Browse files
mikolalysenkoclaude
andcommitted
fix(redirect): never pin a manifest under target/
Member discovery skipped cargo's build directory only while expanding a glob: a literal `members = ["target/gen"]` entry, or a path dependency pointing into target/, was returned and pinned — which the key predicate's own doc comment said could not happen. The next `cargo clean` deletes that manifest, and a recorded hosted edit whose file no longer exists refuses the rollback of every other cargo edit. A `target` segment is now excluded wherever a member path is accepted, in discovery and in the key predicate both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 722cf8e commit 827aa2a

2 files changed

Lines changed: 38 additions & 4 deletions

File tree

‎crates/socket-patch-core/src/patch/redirect/mod.rs‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,9 +1094,9 @@ fn is_cargo_member_manifest_key(key: &str) -> bool {
10941094
&& !key.starts_with('/')
10951095
&& !key.contains('\\')
10961096
&& !key.contains(':')
1097-
&& dir
1098-
.split('/')
1099-
.all(|seg| !seg.is_empty() && seg != "." && seg != ".." && seg != ".socket")
1097+
&& dir.split('/').all(|seg| {
1098+
!seg.is_empty() && seg != "." && seg != ".." && seg != ".socket" && seg != "target"
1099+
})
11001100
}
11011101

11021102
/// The not-declared warning for a crate no manifest names at the patched
@@ -9502,6 +9502,8 @@ mod tests {
95029502
"a/../b/Cargo.toml",
95039503
"./a/Cargo.toml",
95049504
".socket/vendor/cargo/x/Cargo.toml",
9505+
"target/generated/Cargo.toml",
9506+
"crates/a/target/gen/Cargo.toml",
95059507
"a//Cargo.toml",
95069508
"a/Cargo.toml.orig",
95079509
] {

‎crates/socket-patch-core/src/utils/cargo_workspace.rs‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,15 @@ fn enqueue(
9898
dirs: &mut BTreeSet<String>,
9999
queue: &mut Vec<(String, DocumentMut)>,
100100
) {
101+
// `target` is cargo's build directory at every level: the glob walk
102+
// already skips it, and a literal `members = ["target/gen"]` or a path
103+
// dependency into it names a manifest the next `cargo clean` deletes —
104+
// pinning it would record a hosted edit whose file can vanish, blocking
105+
// the rollback of every other cargo edit.
101106
if dir.is_empty()
102107
|| dirs.len() >= MAX_MANIFESTS
103108
|| dirs.contains(&dir)
109+
|| dir.split('/').any(|seg| seg == "target")
104110
|| !is_real_dir_path(root, &dir)
105111
{
106112
return;
@@ -160,7 +166,7 @@ fn path_dependencies(doc: &DocumentMut) -> Vec<String> {
160166

161167
/// `base/rel` lexically normalized to a repo-relative slash path; `None`
162168
/// when it is absolute or climbs out of the root.
163-
fn normalize_rel(base: &str, rel: &str) -> Option<String> {
169+
pub(crate) fn normalize_rel(base: &str, rel: &str) -> Option<String> {
164170
let rel = rel.replace('\\', "/");
165171
if rel.starts_with('/') || Path::new(&rel).is_absolute() {
166172
return None;
@@ -389,6 +395,32 @@ mod tests {
389395
assert!(member_manifests(&root).is_empty());
390396
}
391397

398+
/// Cargo's build directory is never a member, whichever way it is
399+
/// named: the glob walk skips it, and so do a literal `members` entry
400+
/// and a path dependency pointing into it. `cargo clean` deletes those
401+
/// manifests, and a recorded hosted edit for a file that no longer
402+
/// exists blocks the rollback of every other cargo edit.
403+
#[test]
404+
fn build_directory_manifests_are_never_members() {
405+
let tmp = tempfile::tempdir().unwrap();
406+
let root = tmp.path();
407+
write(
408+
root,
409+
"Cargo.toml",
410+
&format!(
411+
"[workspace]\nmembers = [\"crates/*\", \"target/generated\"]\n\n\
412+
{}[dependencies]\ngen2 = {{ path = \"target/gen2\" }}\n\
413+
nested = {{ path = \"crates/a/target/gen3\" }}\n",
414+
pkg("root")
415+
),
416+
);
417+
write(root, "crates/a/Cargo.toml", &pkg("a"));
418+
write(root, "crates/a/target/gen3/Cargo.toml", &pkg("gen3"));
419+
write(root, "target/generated/Cargo.toml", &pkg("generated"));
420+
write(root, "target/gen2/Cargo.toml", &pkg("gen2"));
421+
assert_eq!(member_manifests(root), vec!["crates/a/Cargo.toml"]);
422+
}
423+
392424
#[test]
393425
fn wildcard_matching() {
394426
assert!(wildcard_match(b"a*c", b"abbc"));

0 commit comments

Comments
 (0)