diff --git a/crates/cargo-util/src/paths.rs b/crates/cargo-util/src/paths.rs index 59e812f3aea..fc10c5912d9 100644 --- a/crates/cargo-util/src/paths.rs +++ b/crates/cargo-util/src/paths.rs @@ -94,11 +94,18 @@ pub fn normalize_path(path: &Path) -> PathBuf { match component { Component::Prefix(..) => unreachable!(), Component::RootDir => { - ret.push(component.as_os_str()); + ret.push(Component::RootDir); } Component::CurDir => {} Component::ParentDir => { - ret.pop(); + if ret.ends_with(Component::ParentDir) { + ret.push(Component::ParentDir); + } else { + let popped = ret.pop(); + if !popped && !ret.has_root() { + ret.push(Component::ParentDir); + } + } } Component::Normal(c) => { ret.push(c); @@ -856,9 +863,43 @@ fn exclude_from_time_machine(path: &Path) { #[cfg(test)] mod tests { use super::join_paths; + use super::normalize_path; use super::write; use super::write_atomic; + #[test] + fn test_normalize_path() { + let cases = &[ + ("", ""), + (".", ""), + (".////./.", ""), + ("/", "/"), + ("/..", "/"), + ("/foo/bar", "/foo/bar"), + ("/foo/bar/", "/foo/bar"), + ("/foo/bar/./././///", "/foo/bar"), + ("/foo/bar/..", "/foo"), + ("/foo/bar/../..", "/"), + ("/foo/bar/../../..", "/"), + ("foo/bar", "foo/bar"), + ("foo/bar/", "foo/bar"), + ("foo/bar/./././///", "foo/bar"), + ("foo/bar/..", "foo"), + ("foo/bar/../..", ""), + ("foo/bar/../../..", ".."), + ("../../foo/bar", "../../foo/bar"), + ("../../foo/bar/", "../../foo/bar"), + ("../../foo/bar/./././///", "../../foo/bar"), + ("../../foo/bar/..", "../../foo"), + ("../../foo/bar/../..", "../.."), + ("../../foo/bar/../../..", "../../.."), + ]; + for (input, expected) in cases { + let actual = normalize_path(std::path::Path::new(input)); + assert_eq!(actual, std::path::Path::new(expected), "input: {input}"); + } + } + #[test] fn write_works() { let original_contents = "[dependencies]\nfoo = 0.1.0";