Skip to content

Commit 846c9a5

Browse files
committed
Avoid parsing TOML manually
When trying to detect the format of the toolchain override file, let serde do all the work. If the file does not parse as TOML, fallback to the legacy format, but keep the TOML error. If the first line does not happen to be a valid toolchain, show also the TOML error so that TOML syntax errors are reported.
1 parent 7b88517 commit 846c9a5

File tree

3 files changed

+84
-33
lines changed

3 files changed

+84
-33
lines changed

src/config.rs

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -555,19 +555,26 @@ impl Cfg {
555555
// Then look for 'rust-toolchain'
556556
let toolchain_file = d.join("rust-toolchain");
557557
if let Ok(contents) = utils::read_file("toolchain file", &toolchain_file) {
558-
if let Some(override_file) = Cfg::parse_override_file(contents)? {
558+
if let (Some(override_file), toml_err) = Cfg::parse_override_file(contents) {
559559
if let Some(toolchain_name) = &override_file.toolchain.channel {
560560
let all_toolchains = self.list_toolchains()?;
561561
if !all_toolchains.iter().any(|s| s == toolchain_name) {
562562
// The given name is not resolvable as a toolchain, so
563563
// instead check it's plausible for installation later
564-
dist::validate_channel_name(&toolchain_name).chain_err(|| {
565-
format!(
566-
"invalid channel name '{}' in '{}'",
567-
toolchain_name,
568-
toolchain_file.display()
569-
)
570-
})?;
564+
let mut validation = dist::validate_channel_name(&toolchain_name)
565+
.chain_err(|| {
566+
format!(
567+
"error parsing override file as legacy format: invalid channel name '{}' in '{}'",
568+
toolchain_name,
569+
toolchain_file.display()
570+
)
571+
});
572+
// If there was an error parsing the toolchain file as TOML, report it now.
573+
// This can help if the toolchain file is actually TOML but there was a syntax error.
574+
if let Some(err) = toml_err {
575+
validation = validation.chain_err(|| err);
576+
}
577+
validation?;
571578
}
572579
}
573580

@@ -582,15 +589,27 @@ impl Cfg {
582589
Ok(None)
583590
}
584591

585-
fn parse_override_file<S: AsRef<str>>(contents: S) -> Result<Option<OverrideFile>> {
592+
fn parse_override_file<S: AsRef<str>>(
593+
contents: S,
594+
) -> (Option<OverrideFile>, Option<ErrorKind>) {
586595
let contents = contents.as_ref();
587596

588-
if contents.lines().any(|line| line.starts_with("[toolchain]")) {
589-
toml::from_str::<OverrideFile>(contents)
590-
.map(|file| if file.is_empty() { None } else { Some(file) })
591-
.map_err(|e| ErrorKind::ParsingOverride(e).into())
592-
} else {
593-
Ok(contents.lines().next().map(|line| line.trim().into()))
597+
// Avoid TOML parsing error for backwards compatibility with the legacy format
598+
if contents.is_empty() {
599+
return (None, None);
600+
}
601+
602+
// Try to parse as TOML ...
603+
match toml::from_str::<OverrideFile>(contents)
604+
.map(|file| if file.is_empty() { None } else { Some(file) })
605+
.map_err(ErrorKind::ParsingOverride)
606+
{
607+
Ok(override_file) => (override_file, None),
608+
// ... in case of error, try to parse as the legacy format
609+
Err(toml_err) => (
610+
contents.lines().next().map(|line| line.trim().into()),
611+
Some(toml_err),
612+
),
594613
}
595614
}
596615

@@ -862,16 +881,17 @@ mod tests {
862881
fn parse_legacy_toolchain_file() {
863882
let contents = "nightly-2020-07-10";
864883

865-
let result = Cfg::parse_override_file(contents);
884+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
866885
assert_eq!(
867-
result.unwrap(),
886+
override_file,
868887
Some(OverrideFile {
869888
toolchain: ToolchainSection {
870889
channel: Some(contents.into()),
871890
..Default::default()
872891
}
873892
})
874893
);
894+
assert!(toml_err.is_some());
875895
}
876896

877897
#[test]
@@ -883,9 +903,9 @@ components = [ "rustfmt", "rustc-dev" ]
883903
targets = [ "wasm32-unknown-unknown", "thumbv2-none-eabi" ]
884904
"#;
885905

886-
let result = Cfg::parse_override_file(contents);
906+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
887907
assert_eq!(
888-
result.unwrap(),
908+
override_file,
889909
Some(OverrideFile {
890910
toolchain: ToolchainSection {
891911
channel: Some("nightly-2020-07-10".into()),
@@ -897,6 +917,7 @@ targets = [ "wasm32-unknown-unknown", "thumbv2-none-eabi" ]
897917
}
898918
})
899919
);
920+
assert!(toml_err.is_none());
900921
}
901922

902923
#[test]
@@ -906,9 +927,9 @@ targets = [ "wasm32-unknown-unknown", "thumbv2-none-eabi" ]
906927
channel = "nightly-2020-07-10"
907928
"#;
908929

909-
let result = Cfg::parse_override_file(contents);
930+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
910931
assert_eq!(
911-
result.unwrap(),
932+
override_file,
912933
Some(OverrideFile {
913934
toolchain: ToolchainSection {
914935
channel: Some("nightly-2020-07-10".into()),
@@ -917,6 +938,7 @@ channel = "nightly-2020-07-10"
917938
}
918939
})
919940
);
941+
assert!(toml_err.is_none());
920942
}
921943

922944
#[test]
@@ -927,9 +949,9 @@ channel = "nightly-2020-07-10"
927949
components = []
928950
"#;
929951

930-
let result = Cfg::parse_override_file(contents);
952+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
931953
assert_eq!(
932-
result.unwrap(),
954+
override_file,
933955
Some(OverrideFile {
934956
toolchain: ToolchainSection {
935957
channel: Some("nightly-2020-07-10".into()),
@@ -938,6 +960,7 @@ components = []
938960
}
939961
})
940962
);
963+
assert!(toml_err.is_none());
941964
}
942965

943966
#[test]
@@ -948,9 +971,9 @@ channel = "nightly-2020-07-10"
948971
targets = []
949972
"#;
950973

951-
let result = Cfg::parse_override_file(contents);
974+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
952975
assert_eq!(
953-
result.unwrap(),
976+
override_file,
954977
Some(OverrideFile {
955978
toolchain: ToolchainSection {
956979
channel: Some("nightly-2020-07-10".into()),
@@ -959,6 +982,7 @@ targets = []
959982
}
960983
})
961984
);
985+
assert!(toml_err.is_none());
962986
}
963987

964988
#[test]
@@ -968,9 +992,9 @@ targets = []
968992
components = [ "rustfmt" ]
969993
"#;
970994

971-
let result = Cfg::parse_override_file(contents);
995+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
972996
assert_eq!(
973-
result.unwrap(),
997+
override_file,
974998
Some(OverrideFile {
975999
toolchain: ToolchainSection {
9761000
channel: None,
@@ -979,6 +1003,7 @@ components = [ "rustfmt" ]
9791003
}
9801004
})
9811005
);
1006+
assert!(toml_err.is_none());
9821007
}
9831008

9841009
#[test]
@@ -987,15 +1012,36 @@ components = [ "rustfmt" ]
9871012
[toolchain]
9881013
"#;
9891014

990-
let result = Cfg::parse_override_file(contents);
991-
assert_eq!(result.unwrap(), None);
1015+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
1016+
assert!(override_file.is_none());
1017+
assert!(toml_err.is_none());
9921018
}
9931019

9941020
#[test]
9951021
fn parse_empty_toolchain_file() {
9961022
let contents = "";
9971023

998-
let result = Cfg::parse_override_file(contents);
999-
assert_eq!(result.unwrap(), None);
1024+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
1025+
assert!(override_file.is_none());
1026+
assert!(toml_err.is_none());
1027+
}
1028+
1029+
#[test]
1030+
fn parse_toml_syntax_error() {
1031+
let contents = r#"[toolchain]
1032+
channel = nightly
1033+
"#;
1034+
1035+
let (override_file, toml_err) = Cfg::parse_override_file(contents);
1036+
assert_eq!(
1037+
override_file,
1038+
Some(OverrideFile {
1039+
toolchain: ToolchainSection {
1040+
channel: Some("[toolchain]".into()),
1041+
..Default::default()
1042+
}
1043+
})
1044+
);
1045+
assert!(toml_err.is_some());
10001046
}
10011047
}

src/errors.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ error_chain! {
357357
description("error parsing settings")
358358
}
359359
ParsingOverride(e: toml::de::Error) {
360-
description("error parsing override file")
360+
description("error parsing override file as TOML")
361361
}
362362
NoExeName {
363363
description("couldn't determine self executable name")

tests/cli-rustup.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1706,7 +1706,12 @@ fn bad_file_override() {
17061706
expect_err(
17071707
config,
17081708
&["rustc", "--version"],
1709-
"invalid channel name 'gumbo' in",
1709+
"error parsing override file as legacy format: invalid channel name 'gumbo' in",
1710+
);
1711+
expect_err(
1712+
config,
1713+
&["rustc", "--version"],
1714+
"error parsing override file as TOML",
17101715
);
17111716
});
17121717
}

0 commit comments

Comments
 (0)