diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index dc613b8f8f..80d6074cb6 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -19,7 +19,6 @@ use itertools::Itertools; use tracing::{info, trace, warn}; use tracing_subscriber::{EnvFilter, Registry, reload::Handle}; -use crate::dist::AutoInstallMode; use crate::{ cli::{ common::{self, PackageUpdate, update_console_filter}, @@ -28,10 +27,10 @@ use crate::{ self_update::{self, SelfUpdateMode, check_rustup_update}, topical_doc, }, - command, + command, component_for_bin, config::{ActiveReason, Cfg}, dist::{ - PartialToolchainDesc, Profile, TargetTriple, + AutoInstallMode, PartialToolchainDesc, Profile, TargetTriple, manifest::{Component, ComponentStatus}, }, errors::RustupError, @@ -1026,12 +1025,28 @@ async fn which( binary: &str, toolchain: Option, ) -> Result { - let binary_path = cfg.resolve_toolchain(toolchain).await?.binary_file(binary); + let toolchain = cfg.resolve_toolchain(toolchain).await?; + let binary_path = toolchain.binary_file(binary); + if utils::is_file(&binary_path) { + writeln!(cfg.process.stdout().lock(), "{}", binary_path.display())?; + return Ok(utils::ExitCode(0)); + } - utils::assert_is_file(&binary_path)?; + let toolchain_name = toolchain.name(); + let Some(component_name) = component_for_bin(binary) else { + return Err(anyhow!( + "unknown binary '{binary}' in toolchain '{toolchain_name}'", + )); + }; - writeln!(cfg.process.stdout().lock(), "{}", binary_path.display())?; - Ok(utils::ExitCode(0)) + let selector = match cfg.active_toolchain() { + Ok(Some((t, _))) if &t == toolchain_name => String::new(), + _ => format!("--toolchain {} ", toolchain.name()), + }; + + Err(anyhow!( + "'{binary}' is not installed for the toolchain '{toolchain_name}'.\nhelp: run `rustup component add {selector}{component_name}` to install it" + )) } #[tracing::instrument(level = "trace", skip_all)] @@ -1699,11 +1714,8 @@ async fn doc( .as_slice() { info!( - "`rust-docs` not installed in toolchain `{}`", - distributable.desc() - ); - info!( - "To install, try `rustup component add --toolchain {} rust-docs`", + "`rust-docs` not installed in toolchain `{}`\nhelp: run `rustup component add --toolchain {} rust-docs` to install it", + distributable.desc(), distributable.desc() ); return Err(anyhow!( diff --git a/src/toolchain/distributable.rs b/src/toolchain/distributable.rs index 6441b5272f..94a84e0c62 100644 --- a/src/toolchain/distributable.rs +++ b/src/toolchain/distributable.rs @@ -453,7 +453,7 @@ impl<'a> DistributableToolchain<'a> { _ => format!("--toolchain {} ", self.toolchain.name()), }; Err(anyhow!( - "'{binary_lossy}' is not installed for the toolchain '{desc}'.\nTo install, run `rustup component add {selector}{component_name}`" + "'{binary_lossy}' is not installed for the toolchain '{desc}'.\nhelp: run `rustup component add {selector}{component_name}` to install it" )) } } else { diff --git a/tests/suite/cli_misc.rs b/tests/suite/cli_misc.rs index ac8f2ceccc..ba4c13e821 100644 --- a/tests/suite/cli_misc.rs +++ b/tests/suite/cli_misc.rs @@ -705,7 +705,7 @@ async fn run_rls_when_not_installed() { .await .with_stderr(snapbox::str![[r#" error: 'rls[EXE]' is not installed for the toolchain 'stable-[HOST_TRIPLE]'. -To install, run `rustup component add rls` +help: run `rustup component add rls` to install it "#]]) .is_err(); @@ -727,7 +727,7 @@ async fn run_rls_when_not_installed_for_nightly() { .await .with_stderr(snapbox::str![[r#" error: 'rls[EXE]' is not installed for the toolchain 'nightly-[HOST_TRIPLE]'. -To install, run `rustup component add --toolchain nightly-[HOST_TRIPLE] rls` +help: run `rustup component add --toolchain nightly-[HOST_TRIPLE] rls` to install it "#]]) .is_err(); @@ -1384,6 +1384,70 @@ async fn which_asking_uninstalled_toolchain() { .is_ok(); } +#[tokio::test] +async fn which_asking_uninstalled_components() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + + let path_1 = cx.config.customdir.join("custom-1"); + let path_1 = path_1.to_string_lossy(); + cx.config + .expect(["rustup", "toolchain", "link", "custom-1", &path_1]) + .await + .is_ok(); + cx.config + .expect(["rustup", "default", "custom-1"]) + .await + .is_ok(); + cx.config + .expect(["rustup", "which", "rustfmt"]) + .await + .with_stderr(snapbox::str![[r#" +error: 'rustfmt' is not installed for the toolchain 'custom-1'. +[..]`rustup component add rustfmt`[..] + +"#]]) + .is_err(); + + let path_2 = cx.config.customdir.join("custom-2"); + let path_2 = path_2.to_string_lossy(); + cx.config + .expect(["rustup", "toolchain", "link", "custom-2", &path_2]) + .await + .is_ok(); + cx.config + .expect(["rustup", "which", "--toolchain=custom-2", "rustfmt"]) + .await + .with_stderr(snapbox::str![[r#" +[..]'rustfmt' is not installed for the toolchain 'custom-2'. +[..]`rustup component add --toolchain custom-2 rustfmt`[..] + +"#]]) + .is_err(); +} + +#[tokio::test] +async fn which_unknown_binary() { + let cx = CliTestContext::new(Scenario::SimpleV2).await; + let path_1 = cx.config.customdir.join("custom-1"); + let path_1 = path_1.to_string_lossy(); + cx.config + .expect(["rustup", "toolchain", "link", "custom-1", &path_1]) + .await + .is_ok(); + cx.config + .expect(["rustup", "default", "custom-1"]) + .await + .is_ok(); + cx.config + .expect(["rustup", "which", "ls"]) + .await + .with_stderr(snapbox::str![[r#" +[..]unknown binary 'ls' in toolchain 'custom-1' + +"#]]) + .is_err(); +} + #[tokio::test] async fn override_by_toolchain_on_the_command_line() { let cx = CliTestContext::new(Scenario::SimpleV2).await; diff --git a/tests/suite/cli_rustup.rs b/tests/suite/cli_rustup.rs index 78ae856244..6a23550875 100644 --- a/tests/suite/cli_rustup.rs +++ b/tests/suite/cli_rustup.rs @@ -3342,7 +3342,7 @@ async fn docs_missing() { .await .with_stderr(snapbox::str![[r#" info: `rust-docs` not installed in toolchain `nightly-[HOST_TRIPLE]` -info: To install, try `rustup component add --toolchain nightly-[HOST_TRIPLE] rust-docs` +help: run `rustup component add --toolchain nightly-[HOST_TRIPLE] rust-docs` to install it error: unable to view documentation which is not installed "#]]) diff --git a/tests/suite/cli_self_upd.rs b/tests/suite/cli_self_upd.rs index 2ab18ba908..6ca1ad1d5f 100644 --- a/tests/suite/cli_self_upd.rs +++ b/tests/suite/cli_self_upd.rs @@ -1020,7 +1020,7 @@ async fn rls_proxy_set_up_after_install() { .await .with_stderr(snapbox::str![[r#" error: 'rls[EXE]' is not installed for the toolchain 'stable-[HOST_TRIPLE]'. -To install, run `rustup component add rls` +help: run `rustup component add rls` to install it "#]]) .is_err();