Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ rustls-platform-verifier = { version = "0.7", optional = true }
same-file = "1"
semver = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_ignored = "0.1"
sha2 = "0.11"
sharded-slab = "0.1.1"
strsim = "0.11"
Expand Down
5 changes: 5 additions & 0 deletions doc/user-guide/src/overrides.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ issues with dependencies.

### Toolchain file settings

Rustup reports unrecognized settings as warnings. It ignores them when other
valid settings are present, so these warnings can indicate a misspelling that
would otherwise leave part of the requested configuration unapplied. A future
rustup release will reject unrecognized settings.

#### channel

The `channel` setting specifies which [toolchain] to use. The value is a
Expand Down
64 changes: 54 additions & 10 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::collections::HashSet;
use std::fmt::{self, Debug, Display};
use std::io;
use std::io::Write;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};

use anyhow::{Context, Result, anyhow, bail};
Expand Down Expand Up @@ -290,6 +292,7 @@ pub(crate) struct Cfg<'a> {
pub quiet: bool,
pub current_dir: PathBuf,
pub process: &'a Process,
warned_unknown_toolchain_keys: Mutex<HashSet<(PathBuf, String)>>,

/// Allows the auto-installation of the active toolchain when it is missing.
///
Expand Down Expand Up @@ -370,6 +373,7 @@ impl<'a> Cfg<'a> {
quiet,
current_dir,
process,
warned_unknown_toolchain_keys: Mutex::new(HashSet::new()),
allow_auto_install,
};

Expand Down Expand Up @@ -688,13 +692,37 @@ impl<'a> Cfg<'a> {
if let Ok(contents) = contents {
// XXX Should not return the unvalidated contents; but a new
// internal only safe struct
let override_file =
Cfg::parse_override_file(contents, parse_mode).with_context(|| {
let (override_file, unknown_keys) = Cfg::parse_override_file_with_unknown_keys(
contents, parse_mode,
)
.with_context(|| RustupError::ParsingFile {
name: "override",
path: toolchain_file.clone(),
})?;
{
let mut warned_unknown_toolchain_keys = self
.warned_unknown_toolchain_keys
.lock()
.unwrap_or_else(|e| e.into_inner());
for key in unknown_keys {
if warned_unknown_toolchain_keys
.insert((toolchain_file.clone(), key.clone()))
{
warn!(
"unknown key `{key}` in '{}'; it is currently ignored, but will become an error in a future release",
toolchain_file.display()
);
}
}
}
if override_file.is_empty() {
return Err(anyhow!(OverrideFileConfigError::Invalid)).with_context(|| {
RustupError::ParsingFile {
name: "override",
path: toolchain_file.clone(),
}
})?;
});
}
if let Some(toolchain_name_str) = &override_file.toolchain.channel {
let toolchain_name = ResolvableToolchainName::try_from(
toolchain_name_str.as_str(),
Expand Down Expand Up @@ -744,10 +772,23 @@ impl<'a> Cfg<'a> {
Ok(None)
}

#[cfg(test)]
fn parse_override_file<S: AsRef<str>>(
contents: S,
parse_mode: ParseMode,
) -> Result<OverrideFile> {
let (override_file, _) = Self::parse_override_file_with_unknown_keys(contents, parse_mode)?;
if override_file.is_empty() {
Err(anyhow!(OverrideFileConfigError::Invalid))
} else {
Ok(override_file)
}
}

fn parse_override_file_with_unknown_keys<S: AsRef<str>>(
contents: S,
parse_mode: ParseMode,
) -> Result<(OverrideFile, Vec<String>)> {
let contents = contents.as_ref();

match (contents.lines().count(), parse_mode) {
Expand All @@ -758,18 +799,20 @@ impl<'a> Cfg<'a> {
if channel.is_empty() {
Err(anyhow!(OverrideFileConfigError::Empty))
} else {
Ok(channel.into())
Ok((channel.into(), Vec::new()))
}
}
_ => {
let override_file = toml::from_str::<OverrideFile>(contents)
let deserializer = toml::Deserializer::parse(contents)
.context(OverrideFileConfigError::Parsing)?;
let mut unknown_keys = Vec::new();
let override_file: OverrideFile =
serde_ignored::deserialize(deserializer, |path| {
unknown_keys.push(path.to_string());
})
.context(OverrideFileConfigError::Parsing)?;

if override_file.is_empty() {
Err(anyhow!(OverrideFileConfigError::Invalid))
} else {
Ok(override_file)
}
Ok((override_file, unknown_keys))
}
}
}
Expand Down Expand Up @@ -1095,6 +1138,7 @@ impl Debug for Cfg<'_> {
quiet,
current_dir,
allow_auto_install,
warned_unknown_toolchain_keys: _,
process: _,
} = self;

Expand Down
62 changes: 62 additions & 0 deletions tests/suite/cli_rustup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3968,6 +3968,68 @@ error: rustup could not choose a version of rustc to run, because one wasn't spe
.is_ok();
}

#[tokio::test]
async fn rust_toolchain_toml_warns_on_unknown_keys() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "install", "nightly"])
.await
.is_ok();
let cwd = cx.config.current_dir();
let toolchain_file = cwd.join("rust-toolchain.toml");

raw::write_file(
&toolchain_file,
"[toolchain]\nchannel = \"nightly\"\nchannnel = \"stable\"",
)
.unwrap();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.extend_redactions([("[CWD]", &cwd)])
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (overridden by '[CWD]/rust-toolchain.toml')

"#]])
.with_stderr(snapbox::str![[r#"
warn: unknown key `toolchain.channnel` in '[CWD]/rust-toolchain.toml'; it is currently ignored, but will become an error in a future release

"#]])
.is_ok();

raw::write_file(
&toolchain_file,
"[toolchain]\nchannel = \"nightly\"\n\n[future]\nkey = \"value\"",
)
.unwrap();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.extend_redactions([("[CWD]", &cwd)])
.with_stdout(snapbox::str![[r#"
nightly-[HOST_TUPLE] (overridden by '[CWD]/rust-toolchain.toml')

"#]])
.with_stderr(snapbox::str![[r#"
warn: unknown key `future` in '[CWD]/rust-toolchain.toml'; it is currently ignored, but will become an error in a future release

"#]])
.is_ok();

raw::write_file(&toolchain_file, "[toolchain]\nchannnel = \"nightly\"").unwrap();
cx.config
.expect(["rustup", "show", "active-toolchain"])
.await
.extend_redactions([("[CWD]", &cwd)])
.with_stdout(snapbox::str![[""]])
.with_stderr(snapbox::str![[r#"
warn: unknown key `toolchain.channnel` in '[CWD]/rust-toolchain.toml'; it is currently ignored, but will become an error in a future release
error: could not parse override file: '[CWD]/rust-toolchain.toml': missing toolchain properties in toolchain override file

"#]])
.is_err();
}

/// Ensures that `rust-toolchain.toml` files (with `.toml` extension) only allow TOML contents
#[tokio::test]
async fn only_toml_in_rust_toolchain_toml() {
Expand Down
Loading