From 444daec7a6966021b605628b5d352efbe45514ac Mon Sep 17 00:00:00 2001 From: akrm al-hakimi Date: Mon, 19 Oct 2026 20:34:28 -0400 Subject: [PATCH 1/5] style(test): import `std::fs` in `cli_exact` --- tests/suite/cli_exact.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/suite/cli_exact.rs b/tests/suite/cli_exact.rs index 20ebaf71bd..24eb1bc0a4 100644 --- a/tests/suite/cli_exact.rs +++ b/tests/suite/cli_exact.rs @@ -1,6 +1,8 @@ //! Yet more cli test cases. These are testing that the output //! is exactly as expected. +use std::fs; + use rustup::{ test::{CROSS_ARCH1, CROSS_ARCH2, CliTestContext, MULTI_ARCH1, Scenario, this_host_tuple}, utils::raw, @@ -445,7 +447,7 @@ async fn remove_override_with_path_deleted() { .prefix("rustup-test") .tempdir() .unwrap(); - let path = std::fs::canonicalize(dir.path()).unwrap(); + let path = fs::canonicalize(dir.path()).unwrap(); let cx = cx.change_dir(&path); cx.config .expect(["rustup", "override", "add", "nightly"]) @@ -482,7 +484,7 @@ async fn remove_override_nonexistent() { .prefix("rustup-test") .tempdir() .unwrap(); - let path = std::fs::canonicalize(dir.path()).unwrap(); + let path = fs::canonicalize(dir.path()).unwrap(); let cx = cx.change_dir(&path); cx.config .expect(["rustup", "override", "add", "nightly"]) @@ -509,7 +511,7 @@ info: override toolchain for '[PATH]' removed #[tokio::test] async fn list_overrides() { let cx = CliTestContext::new(Scenario::SimpleV2).await; - let cwd = std::fs::canonicalize(cx.config.current_dir()).unwrap(); + let cwd = fs::canonicalize(cx.config.current_dir()).unwrap(); let mut cwd_formatted = format!("{}", cwd.display()); if cfg!(windows) { @@ -546,7 +548,7 @@ async fn list_overrides_with_nonexistent() { .expect(["rustup", "override", "add", "nightly"]) .await .is_ok(); - std::fs::canonicalize(dir.path()).unwrap() + fs::canonicalize(dir.path()).unwrap() }; // FIXME TempDir seems to succumb to difficulties removing dirs on windows let _ = raw::remove_dir(&nonexistent_path); From b1a0591bdd4b7ef9a85fbb2318abbaf175df62aa Mon Sep 17 00:00:00 2001 From: akrm al-hakimi Date: Mon, 19 Oct 2026 20:36:21 -0400 Subject: [PATCH 2/5] test(cli): show override handling of symlinked and relative paths Overrides are stored and looked up by canonical path, which breaks down once a directory is reached through a different path (#1138): - an override whose directory later moved behind a symlink no longer applies, yet is listed as a normal directory and cannot be removed with `--path`, which resolves to the target instead - `--path ` stores the target's path rather than the given one - a relative `--path` that does not exist yet is stored verbatim and can never match a directory --- tests/suite/cli_exact.rs | 256 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/tests/suite/cli_exact.rs b/tests/suite/cli_exact.rs index 24eb1bc0a4..fd756a5fa3 100644 --- a/tests/suite/cli_exact.rs +++ b/tests/suite/cli_exact.rs @@ -2,6 +2,8 @@ //! is exactly as expected. use std::fs; +#[cfg(unix)] +use std::path::PathBuf; use rustup::{ test::{CROSS_ARCH1, CROSS_ARCH2, CliTestContext, MULTI_ARCH1, Scenario, this_host_tuple}, @@ -576,6 +578,260 @@ info: you may remove overrides for non-existent directories with "#]]); } +#[tokio::test] +#[cfg(unix)] +async fn override_ignored_for_dir_moved_behind_symlink() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect(["rustup", "default", "stable"]) + .await + .is_ok(); + let (old, _new) = override_moved_behind_symlink(&cx).await; + + // The override is stored under the directory's old path, which the + // directory itself no longer resolves to. + let cx = cx.change_dir(&old); + cx.config + .expect(["rustc", "--version"]) + .await + .is_ok() + .with_stdout(snapbox::str![[r#" +1.1.0 (hash-stable-1.1.0) + +"#]]); +} + +#[tokio::test] +#[cfg(unix)] +async fn list_overrides_with_dir_moved_behind_symlink() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let (old, _new) = override_moved_behind_symlink(&cx).await; + + // The symlink leads to a directory, so the override is not nonexistent. + cx.config + .expect(["rustup", "override", "list"]) + .await + .extend_redactions([("[OLD]", old)]) + .is_ok() + .with_stdout(snapbox::str![[r#" +[OLD] nightly + +"#]]) + .with_stderr(snapbox::str![[""]]); + cx.config + .expect(["rustup", "override", "unset", "--nonexistent"]) + .await + .is_ok() + .with_stdout(snapbox::str![[""]]) + .with_stderr(snapbox::str![[r#" +info: no nonexistent paths detected + +"#]]); +} + +#[tokio::test] +#[cfg(unix)] +async fn remove_override_with_path_moved_behind_symlink() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let (old, _new) = override_moved_behind_symlink(&cx).await; + + // `--path` resolves the symlink, so the override stored under `old` is + // not found. + cx.config + .expect([ + "rustup", + "override", + "unset", + "--path", + old.to_str().unwrap(), + ]) + .await + .extend_redactions([("[OLD]", old.clone())]) + .is_ok() + .with_stdout(snapbox::str![[""]]) + .with_stderr(snapbox::str![[r#" +info: no override toolchain for '[OLD]' + +"#]]); + cx.config + .expect(["rustup", "override", "list"]) + .await + .extend_redactions([("[OLD]", old)]) + .is_ok() + .with_stdout(snapbox::str![[r#" +[OLD] nightly + +"#]]) + .with_stderr(snapbox::str![[""]]); +} + +#[tokio::test] +#[cfg(unix)] +async fn remove_override_with_path_symlink_removes_target() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let (old, new) = override_moved_behind_symlink(&cx).await; + cx.config + .expect([ + "rustup", + "override", + "set", + "nightly", + "--path", + new.to_str().unwrap(), + ]) + .await + .is_ok(); + cx.config + .expect(["rustup", "override", "list"]) + .await + .extend_redactions([("[OLD]", old.clone()), ("[NEW]", new.clone())]) + .is_ok() + .with_stdout(snapbox::str![[r#" +[NEW] nightly +[OLD] nightly + +"#]]) + .with_stderr(snapbox::str![[""]]); + + // `--path` resolves the symlink, so this removes the override of `new` + // and leaves the one stored under `old` behind. + cx.config + .expect([ + "rustup", + "override", + "unset", + "--path", + old.to_str().unwrap(), + ]) + .await + .extend_redactions([("[OLD]", old.clone())]) + .is_ok() + .with_stdout(snapbox::str![[""]]) + .with_stderr(snapbox::str![[r#" +info: override toolchain for '[OLD]' removed + +"#]]); + cx.config + .expect(["rustup", "override", "list"]) + .await + .extend_redactions([("[OLD]", old), ("[NEW]", new)]) + .is_ok() + .with_stdout(snapbox::str![[r#" +[OLD] nightly + +"#]]) + .with_stderr(snapbox::str![[""]]); +} + +/// Sets an override on `old`, then moves that directory to `new` and leaves a +/// symlink to it behind at `old`. +#[cfg(unix)] +async fn override_moved_behind_symlink(cx: &CliTestContext) -> (PathBuf, PathBuf) { + let cwd = fs::canonicalize(cx.config.current_dir()).unwrap(); + let old = cwd.join("old"); + let new = cwd.join("new"); + fs::create_dir(&old).unwrap(); + cx.config + .expect([ + "rustup", + "override", + "set", + "nightly", + "--path", + old.to_str().unwrap(), + ]) + .await + .is_ok(); + fs::rename(&old, &new).unwrap(); + raw::symlink_dir(&new, &old).unwrap(); + (old, new) +} + +#[tokio::test] +#[cfg(unix)] +async fn override_set_with_path_symlink() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + let cwd = fs::canonicalize(cx.config.current_dir()).unwrap(); + let target = cwd.join("target"); + let link = cwd.join("link"); + fs::create_dir(&target).unwrap(); + raw::symlink_dir(&target, &link).unwrap(); + cx.config + .expect([ + "rustup", + "override", + "set", + "nightly", + "--path", + link.to_str().unwrap(), + ]) + .await + .is_ok(); + + // The symlink is resolved before the override is stored. + cx.config + .expect(["rustup", "override", "list"]) + .await + .extend_redactions([("[LINK]", link.clone()), ("[TARGET]", target.clone())]) + .is_ok() + .with_stdout(snapbox::str![[r#" +[TARGET] nightly + +"#]]) + .with_stderr(snapbox::str![[""]]); + for dir in [&target, &link] { + let cx = cx.change_dir(dir); + cx.config + .expect(["rustc", "--version"]) + .await + .is_ok() + .with_stdout(snapbox::str![[r#" +1.3.0 (hash-nightly-2) + +"#]]); + } +} + +#[tokio::test] +async fn override_set_with_path_relative_nonexistent() { + let mut cx = CliTestContext::new(Scenario::SimpleV2).await; + cx.config + .expect(["rustup", "default", "stable"]) + .await + .is_ok(); + cx.config + .expect(["rustup", "override", "set", "nightly", "--path", "later"]) + .await + .is_ok(); + + // The relative path is stored as given, so it can never match a directory. + cx.config + .expect(["rustup", "override", "list"]) + .await + .is_ok() + .with_stdout(snapbox::str![[r#" +later (not a directory) nightly + + +"#]]) + .with_stderr(snapbox::str![[r#" +info: you may remove overrides for non-existent directories with +`rustup override unset --nonexistent` + +"#]]); + let later = cx.config.current_dir().join("later"); + fs::create_dir(&later).unwrap(); + let cx = cx.change_dir(&later); + cx.config + .expect(["rustc", "--version"]) + .await + .is_ok() + .with_stdout(snapbox::str![[r#" +1.1.0 (hash-stable-1.1.0) + +"#]]); +} + #[tokio::test] async fn update_no_manifest() { let cx = CliTestContext::new(Scenario::SimpleV2).await; From 9f6642c9cedf76787528bf73e99f2a4ba6ba87b0 Mon Sep 17 00:00:00 2001 From: akrm al-hakimi Date: Mon, 19 Oct 2026 20:38:00 -0400 Subject: [PATCH 3/5] fix(config): look up directory overrides by directory identity Overrides were looked up by canonicalizing each ancestor of the current directory and comparing the result with the stored paths. An override therefore stopped applying as soon as its directory was reached through another path, such as after the directory moved behind a symlink of its old name (#1138). Each ancestor is now matched against the override directories through `same_file::Handle`, so an override applies however its directory is reached. The override directories are opened once per lookup rather than once per ancestor, and nothing is opened when there are none; before, every ancestor was canonicalized even with no overrides set. --- src/config.rs | 5 +++-- src/settings.rs | 32 ++++++++++++++++++++++++++++---- tests/suite/cli_exact.rs | 8 ++++---- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/config.rs b/src/config.rs index 042477f006..1c14176446 100644 --- a/src/config.rs +++ b/src/config.rs @@ -687,14 +687,15 @@ impl<'a> Cfg<'a> { dir: &Path, settings: &Settings, ) -> anyhow::Result> { + let overrides = settings.overrides_by_dir(); let mut dir = Some(dir); while let Some(d) = dir { // First check the override database - if let Some(name) = settings.dir_override(d) { + if let Some(name) = overrides.get(d) { let source = ActiveSource::OverrideDb(d.to_owned()); return Ok(Some(( - Override::::from_str(&name)? + Override::::from_str(name)? .resolve(self)? .into(), source, diff --git a/src/settings.rs b/src/settings.rs index c4f2e70cd8..1559dff35b 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,12 +1,13 @@ use std::{ cell::RefCell, - collections::BTreeMap, + collections::{BTreeMap, HashMap}, fmt, path::{Path, PathBuf}, str::FromStr, }; use anyhow::Context; +use same_file::Handle; use serde::{Deserialize, Serialize}; use tracing::info; @@ -126,9 +127,16 @@ impl Settings { self.overrides.insert(key, toolchain); } - pub(crate) fn dir_override(&self, dir: &Path) -> Option { - let key = Self::path_to_key(dir); - self.overrides.get(&key).cloned() + /// Indexes the overrides by the directories they refer to, for a series of lookups. + pub(crate) fn overrides_by_dir(&self) -> OverridesByDir<'_> { + let mut by_dir = HashMap::new(); + for (path, toolchain) in &self.overrides { + // A directory that cannot be opened cannot be the current one either. + if let Ok(dir) = Handle::from_path(path) { + by_dir.entry(dir).or_insert_with(|| toolchain.as_str()); + } + } + OverridesByDir { by_dir } } pub(crate) fn parse(data: &str) -> anyhow::Result { @@ -140,6 +148,22 @@ impl Settings { } } +/// Overrides keyed by the identity of their directories rather than by their +/// paths, so that an override applies however its directory is reached. +pub(crate) struct OverridesByDir<'a> { + by_dir: HashMap, +} + +impl OverridesByDir<'_> { + pub(crate) fn get(&self, dir: &Path) -> Option<&str> { + if self.by_dir.is_empty() { + return None; + } + let dir = Handle::from_path(dir).ok()?; + self.by_dir.get(&dir).copied() + } +} + #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] pub enum MetadataVersion { #[serde(rename = "2")] diff --git a/tests/suite/cli_exact.rs b/tests/suite/cli_exact.rs index fd756a5fa3..f13f2a8773 100644 --- a/tests/suite/cli_exact.rs +++ b/tests/suite/cli_exact.rs @@ -580,7 +580,7 @@ info: you may remove overrides for non-existent directories with #[tokio::test] #[cfg(unix)] -async fn override_ignored_for_dir_moved_behind_symlink() { +async fn override_applies_to_dir_moved_behind_symlink() { let mut cx = CliTestContext::new(Scenario::SimpleV2).await; cx.config .expect(["rustup", "default", "stable"]) @@ -588,15 +588,15 @@ async fn override_ignored_for_dir_moved_behind_symlink() { .is_ok(); let (old, _new) = override_moved_behind_symlink(&cx).await; - // The override is stored under the directory's old path, which the - // directory itself no longer resolves to. + // The override is stored under the directory's old path, which still + // leads to the directory itself. let cx = cx.change_dir(&old); cx.config .expect(["rustc", "--version"]) .await .is_ok() .with_stdout(snapbox::str![[r#" -1.1.0 (hash-stable-1.1.0) +1.3.0 (hash-nightly-2) "#]]); } From 9609742c634f3cb4a8dc335465ed97402793e9d5 Mon Sep 17 00:00:00 2001 From: akrm al-hakimi Date: Mon, 19 Oct 2026 20:39:32 -0400 Subject: [PATCH 4/5] fix(config): set and unset overrides by directory identity `rustup override unset --path` resolved the given path before looking it up, so it could not remove an override whose directory had since moved behind a symlink of its old name, and removed the override of the symlink's target instead (#1138). `rustup override set --path` on such a symlink likewise added a second entry for the same directory. Both now match the stored paths by the directory they lead to. `unset` removes every entry for that directory, trying the given path verbatim first so that the override of a directory that no longer exists can still be removed. `set` replaces the entries for the directory, so a single override remains. --- src/settings.rs | 20 ++++++++++++++++++-- tests/suite/cli_exact.rs | 19 ++++++++----------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/settings.rs b/src/settings.rs index 1559dff35b..489bc911a1 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -114,12 +114,23 @@ impl Settings { } pub(crate) fn remove_override(&mut self, path: &Path) -> bool { - let key = Self::path_to_key(path); - self.overrides.remove(&key).is_some() + let mut removed = self.overrides.remove(&Self::path_to_key(path)).is_some(); + // The directory may also be stored under other paths that lead to it. + if let Ok(dir) = Handle::from_path(path) { + let count = self.overrides.len(); + self.overrides.retain(|stored, _| !leads_to(stored, &dir)); + removed |= self.overrides.len() < count; + } + removed } pub(crate) fn add_override(&mut self, path: &Path, toolchain: String) { let key = Self::path_to_key(path); + // Drop every entry stored for this directory under any path, so that + // the one inserted below under `key` is its only override. + if let Ok(dir) = Handle::from_path(path) { + self.overrides.retain(|stored, _| !leads_to(stored, &dir)); + } info!( "override toolchain for {} set to {toolchain}", path.display(), @@ -148,6 +159,11 @@ impl Settings { } } +/// Whether `path` leads to the directory behind `dir`. +fn leads_to(path: &str, dir: &Handle) -> bool { + Handle::from_path(path).is_ok_and(|other| other == *dir) +} + /// Overrides keyed by the identity of their directories rather than by their /// paths, so that an override applies however its directory is reached. pub(crate) struct OverridesByDir<'a> { diff --git a/tests/suite/cli_exact.rs b/tests/suite/cli_exact.rs index f13f2a8773..23099b3be1 100644 --- a/tests/suite/cli_exact.rs +++ b/tests/suite/cli_exact.rs @@ -635,8 +635,7 @@ async fn remove_override_with_path_moved_behind_symlink() { let cx = CliTestContext::new(Scenario::SimpleV2).await; let (old, _new) = override_moved_behind_symlink(&cx).await; - // `--path` resolves the symlink, so the override stored under `old` is - // not found. + // The symlink leads to the directory the override was stored for. cx.config .expect([ "rustup", @@ -650,16 +649,15 @@ async fn remove_override_with_path_moved_behind_symlink() { .is_ok() .with_stdout(snapbox::str![[""]]) .with_stderr(snapbox::str![[r#" -info: no override toolchain for '[OLD]' +info: override toolchain for '[OLD]' removed "#]]); cx.config .expect(["rustup", "override", "list"]) .await - .extend_redactions([("[OLD]", old)]) .is_ok() .with_stdout(snapbox::str![[r#" -[OLD] nightly +no overrides "#]]) .with_stderr(snapbox::str![[""]]); @@ -667,9 +665,11 @@ info: no override toolchain for '[OLD]' #[tokio::test] #[cfg(unix)] -async fn remove_override_with_path_symlink_removes_target() { +async fn remove_override_with_path_symlink() { let cx = CliTestContext::new(Scenario::SimpleV2).await; let (old, new) = override_moved_behind_symlink(&cx).await; + + // `old` and `new` are the same directory, so this replaces its override. cx.config .expect([ "rustup", @@ -688,13 +688,11 @@ async fn remove_override_with_path_symlink_removes_target() { .is_ok() .with_stdout(snapbox::str![[r#" [NEW] nightly -[OLD] nightly "#]]) .with_stderr(snapbox::str![[""]]); - // `--path` resolves the symlink, so this removes the override of `new` - // and leaves the one stored under `old` behind. + // The symlink leads to the same directory, so this removes that override. cx.config .expect([ "rustup", @@ -714,10 +712,9 @@ info: override toolchain for '[OLD]' removed cx.config .expect(["rustup", "override", "list"]) .await - .extend_redactions([("[OLD]", old), ("[NEW]", new)]) .is_ok() .with_stdout(snapbox::str![[r#" -[OLD] nightly +no overrides "#]]) .with_stderr(snapbox::str![[""]]); From 4dac8a8739918a66cc75fec3a5e75c08f629826b Mon Sep 17 00:00:00 2001 From: akrm al-hakimi Date: Mon, 19 Oct 2026 20:43:04 -0400 Subject: [PATCH 5/5] fix(config)!: store override paths as given `rustup override set` canonicalized the directory before storing it, so `settings.toml` could end up with a path the user never typed: `--path` on a symlink recorded its target, and a relative `--path` to a directory that did not exist yet was recorded verbatim and could never match. The current directory is now stored as rustup sees it, and `--path` is only made absolute, relative to that directory. Since overrides are matched by the directory a stored path leads to, entries written by earlier versions keep working, and one set on a symlink applies to its target as well. --- src/cli/rustup_mode.rs | 12 +++++++++--- src/settings.rs | 12 ++---------- tests/suite/cli_exact.rs | 33 ++++++++++++--------------------- 3 files changed, 23 insertions(+), 34 deletions(-) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index 080e888631..d571853ce4 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -3,7 +3,7 @@ use std::{ env::consts::EXE_SUFFIX, fmt, io::{self, Write}, - path::{Path, PathBuf}, + path::{self, Path, PathBuf}, process::ExitStatus, str::FromStr, sync::Arc, @@ -1750,7 +1750,13 @@ async fn override_add( Err(e) => Err(e)?, } - cfg.make_override(path.unwrap_or(&cfg.current_dir), &toolchain)?; + // Relative to the directory rustup runs in, which `path::absolute` alone + // would take from the OS rather than from `cfg`. + let path = match path { + Some(path) => &path::absolute(cfg.current_dir.join(path))?, + None => &cfg.current_dir, + }; + cfg.make_override(path, &toolchain)?; Ok(ExitCode::SUCCESS) } @@ -1774,7 +1780,7 @@ fn override_remove( } list } else if let Some(path) = path { - vec![path.to_owned()] + vec![path::absolute(cfg.current_dir.join(path))?] } else { vec![cfg.current_dir.clone()] }; diff --git a/src/settings.rs b/src/settings.rs index 489bc911a1..ace3e7fdaa 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -105,16 +105,8 @@ pub struct Settings { } impl Settings { - fn path_to_key(path: &Path) -> String { - if path.exists() { - utils::canonicalize_path(path).display().to_string() - } else { - path.display().to_string() - } - } - pub(crate) fn remove_override(&mut self, path: &Path) -> bool { - let mut removed = self.overrides.remove(&Self::path_to_key(path)).is_some(); + let mut removed = self.overrides.remove(&path.display().to_string()).is_some(); // The directory may also be stored under other paths that lead to it. if let Ok(dir) = Handle::from_path(path) { let count = self.overrides.len(); @@ -125,7 +117,7 @@ impl Settings { } pub(crate) fn add_override(&mut self, path: &Path, toolchain: String) { - let key = Self::path_to_key(path); + let key = path.display().to_string(); // Drop every entry stored for this directory under any path, so that // the one inserted below under `key` is its only override. if let Ok(dir) = Handle::from_path(path) { diff --git a/tests/suite/cli_exact.rs b/tests/suite/cli_exact.rs index 23099b3be1..9ab6d7536e 100644 --- a/tests/suite/cli_exact.rs +++ b/tests/suite/cli_exact.rs @@ -513,13 +513,6 @@ info: override toolchain for '[PATH]' removed #[tokio::test] async fn list_overrides() { let cx = CliTestContext::new(Scenario::SimpleV2).await; - let cwd = fs::canonicalize(cx.config.current_dir()).unwrap(); - let mut cwd_formatted = format!("{}", cwd.display()); - - if cfg!(windows) { - cwd_formatted.drain(..4); - } - cx.config .expect(["rustup", "override", "add", "nightly"]) .await @@ -527,7 +520,7 @@ async fn list_overrides() { cx.config .expect(["rustup", "override", "list"]) .await - .extend_redactions([("[CWD]", cwd_formatted)]) + .extend_redactions([("[CWD]", cx.config.current_dir())]) .is_ok() .with_stdout(snapbox::str![[r#" [CWD] nightly @@ -550,24 +543,19 @@ async fn list_overrides_with_nonexistent() { .expect(["rustup", "override", "add", "nightly"]) .await .is_ok(); - fs::canonicalize(dir.path()).unwrap() + dir.path().to_owned() }; // FIXME TempDir seems to succumb to difficulties removing dirs on windows let _ = raw::remove_dir(&nonexistent_path); assert!(!nonexistent_path.exists()); - let mut path_formatted = format!("{}", nonexistent_path.display()); - - if cfg!(windows) { - path_formatted.drain(..4); - } cx.config .expect(["rustup", "override", "list"]) .await - .extend_redactions([("[PATH]", path_formatted + " (not a directory)")]) + .extend_redactions([("[PATH]", nonexistent_path)]) .is_ok() .with_stdout(snapbox::str![[r#" -[PATH] nightly +[PATH] (not a directory) nightly "#]]) @@ -765,14 +753,15 @@ async fn override_set_with_path_symlink() { .await .is_ok(); - // The symlink is resolved before the override is stored. + // The override is stored under the path as given, and applies to the + // directory however it is reached. cx.config .expect(["rustup", "override", "list"]) .await .extend_redactions([("[LINK]", link.clone()), ("[TARGET]", target.clone())]) .is_ok() .with_stdout(snapbox::str![[r#" -[TARGET] nightly +[LINK] nightly "#]]) .with_stderr(snapbox::str![[""]]); @@ -801,13 +790,15 @@ async fn override_set_with_path_relative_nonexistent() { .await .is_ok(); - // The relative path is stored as given, so it can never match a directory. + // The relative path is made absolute, so it matches the directory once + // that exists. cx.config .expect(["rustup", "override", "list"]) .await + .extend_redactions([("[CWD]", cx.config.current_dir())]) .is_ok() .with_stdout(snapbox::str![[r#" -later (not a directory) nightly +[CWD]/later (not a directory) nightly "#]]) @@ -824,7 +815,7 @@ info: you may remove overrides for non-existent directories with .await .is_ok() .with_stdout(snapbox::str![[r#" -1.1.0 (hash-stable-1.1.0) +1.3.0 (hash-nightly-2) "#]]); }