diff --git a/src/config.rs b/src/config.rs index cc720b75b0..49b5f86294 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1003,11 +1003,13 @@ impl Cfg { } pub(crate) fn resolve_toolchain(&self, name: &str) -> Result { - if let Ok(desc) = dist::PartialToolchainDesc::from_str(name) { + // remove trailing slashes in toolchain name + let normalized_name = name.trim_end_matches('/'); + if let Ok(desc) = dist::PartialToolchainDesc::from_str(normalized_name) { let host = self.get_default_host_triple()?; Ok(desc.resolve(&host)?.to_string()) } else { - Ok(name.to_owned()) + Ok(normalized_name.to_owned()) } } } diff --git a/tests/cli-v2.rs b/tests/cli-v2.rs index d0b2ebf92a..b2717493ef 100644 --- a/tests/cli-v2.rs +++ b/tests/cli-v2.rs @@ -212,6 +212,32 @@ fn remove_toolchain() { }); } +// Issue #2873 +#[test] +fn remove_toolchain_ignore_trailing_slash() { + setup(&|config| { + // custom toolchain name with trailing slash + let path = config.customdir.join("custom-1"); + let path_str = path.to_string_lossy(); + expect_ok(config, &["rustup", "toolchain", "link", "dev", &path_str]); + expect_stderr_ok( + config, + &["rustup", "toolchain", "remove", "dev/"], + "toolchain 'dev' uninstalled", + ); + // check if custom toolchain directory contents are not removed + let toolchain_dir_is_non_empty = fs::read_dir(&path).unwrap().next().is_some(); + assert!(toolchain_dir_is_non_empty); + // distributable toolchain name with trailing slash + expect_ok(config, &["rustup", "update", "nightly"]); + expect_stderr_ok( + config, + &["rustup", "toolchain", "remove", for_host!("nightly-{}/")], + for_host!("toolchain 'nightly-{}' uninstalled"), + ); + }); +} + #[test] fn add_remove_multiple_toolchains() { fn go(add: &str, rm: &str) {