diff --git a/crates/ide-extension/Cargo.toml b/crates/ide-extension/Cargo.toml index 74d71b312..cc6839ef9 100644 --- a/crates/ide-extension/Cargo.toml +++ b/crates/ide-extension/Cargo.toml @@ -18,6 +18,7 @@ semver = "1" blake3.workspace = true zip.workspace = true base64.workspace = true +dunce.workspace = true ed25519-dalek = "3" [dev-dependencies] diff --git a/crates/ide-extension/src/ledger.rs b/crates/ide-extension/src/ledger.rs index 2cb772520..8bd2a8bda 100644 --- a/crates/ide-extension/src/ledger.rs +++ b/crates/ide-extension/src/ledger.rs @@ -441,11 +441,13 @@ pub fn resolve_program(name: &str, path_var: Option<&OsStr>) -> Result PathBuf { let Some(dir) = p.parent() else { return p.to_path_buf() }; let Some(name) = p.file_name() else { return p.to_path_buf() }; - std::fs::canonicalize(dir).map(|d| d.join(name)).unwrap_or_else(|_| p.to_path_buf()) + dunce::canonicalize(dir).map(|d| d.join(name)).unwrap_or_else(|_| p.to_path_buf()) } fn is_executable(p: &Path) -> bool { @@ -498,6 +500,20 @@ mod tests { list.iter().map(|s| s.parse().unwrap()).collect() } + #[test] + #[cfg(windows)] + fn pinned_program_path_has_no_verbatim_prefix() { + // std::fs::canonicalize returns \\?\-prefixed paths on Windows; a pin + // stored in that form would never match the plain path the program is + // started with. dunce keeps the usual form. + let dir = tempfile::tempdir().unwrap(); + std::fs::write(dir.path().join("cargo.exe"), b"").unwrap(); + let pinned = resolve_program("cargo.exe", Some(dir.path().as_os_str())).unwrap(); + let shown = pinned.to_string_lossy(); + assert!(!shown.starts_with(r"\\?\"), "verbatim prefix leaked into the pin: {shown}"); + assert!(pinned.is_absolute()); + } + fn req<'a>(version: &'a str, hash: &'a str, required: &[&str], optional: &[&str]) -> Request<'a> { Request { subject: Subject::Installed("p".into()),