Skip to content

Commit 714f2ca

Browse files
pietroalbinipvdrz
authored andcommitted
implement range support in //@ edition
1 parent 51ff895 commit 714f2ca

File tree

2 files changed

+250
-8
lines changed

2 files changed

+250
-8
lines changed

src/tools/compiletest/src/directives.rs

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -432,10 +432,11 @@ impl TestProps {
432432
panic!("`compiler-flags` directive should be spelled `compile-flags`");
433433
}
434434

435-
if let Some(edition) = config.parse_edition(ln, testfile, line_number) {
435+
if let Some(range) = parse_edition_range(config, ln, testfile, line_number) {
436436
// The edition is added at the start, since flags from //@compile-flags must
437437
// be passed to rustc last.
438-
self.compile_flags.insert(0, format!("--edition={}", edition.trim()));
438+
self.compile_flags
439+
.insert(0, format!("--edition={}", range.edition_to_test(config)));
439440
has_edition = true;
440441
}
441442

@@ -1125,10 +1126,6 @@ impl Config {
11251126
}
11261127
}
11271128

1128-
fn parse_edition(&self, line: &str, testfile: &Utf8Path, line_number: usize) -> Option<String> {
1129-
self.parse_name_value_directive(line, "edition", testfile, line_number)
1130-
}
1131-
11321129
fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) {
11331130
match value {
11341131
true => {
@@ -1784,3 +1781,103 @@ enum IgnoreDecision {
17841781
Continue,
17851782
Error { message: String },
17861783
}
1784+
1785+
fn parse_edition_range(
1786+
config: &Config,
1787+
line: &str,
1788+
testfile: &Utf8Path,
1789+
line_number: usize,
1790+
) -> Option<EditionRange> {
1791+
let raw = config.parse_name_value_directive(line, "edition", testfile, line_number)?;
1792+
1793+
if let Some((greter_equal_than, lower_than)) = raw.split_once("..") {
1794+
Some(match (maybe_parse_edition(greter_equal_than), maybe_parse_edition(lower_than)) {
1795+
(Some(greater_equal_than), Some(lower_than)) if lower_than < greater_equal_than => {
1796+
panic!("the left side of `//@ edition` cannot be higher than the right side");
1797+
}
1798+
(Some(greater_equal_than), Some(lower_than)) if lower_than == greater_equal_than => {
1799+
panic!("the left side of `//@ edition` cannot be equal to the right side");
1800+
}
1801+
(Some(greater_equal_than), Some(lower_than)) => {
1802+
EditionRange::Range { greater_equal_than, lower_than }
1803+
}
1804+
(Some(greater_equal_than), None) => EditionRange::GreaterEqualThan(greater_equal_than),
1805+
(None, Some(_)) => panic!("..edition is not a supported range in //@ edition"),
1806+
(None, None) => panic!("'..' is not a supported range in //@ edition"),
1807+
})
1808+
} else {
1809+
Some(EditionRange::Exact(maybe_parse_edition(&raw).expect("empty value for //@ edition")))
1810+
}
1811+
}
1812+
1813+
fn maybe_parse_edition(mut input: &str) -> Option<Edition> {
1814+
input = input.trim();
1815+
if input.is_empty() {
1816+
return None;
1817+
}
1818+
Some(parse_edition(input))
1819+
}
1820+
1821+
fn parse_edition(mut input: &str) -> Edition {
1822+
input = input.trim();
1823+
if input == "future" {
1824+
Edition::Future
1825+
} else {
1826+
Edition::Year(input.parse().expect(&format!("'{input}' doesn't look like an edition")))
1827+
}
1828+
}
1829+
1830+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
1831+
enum Edition {
1832+
// Note that the ordering here is load-bearing, as we want the future edition to be last.
1833+
Year(u32),
1834+
Future,
1835+
}
1836+
1837+
impl std::fmt::Display for Edition {
1838+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1839+
match self {
1840+
Edition::Year(year) => write!(f, "{year}"),
1841+
Edition::Future => f.write_str("future"),
1842+
}
1843+
}
1844+
}
1845+
1846+
impl From<u32> for Edition {
1847+
fn from(value: u32) -> Self {
1848+
Edition::Year(value)
1849+
}
1850+
}
1851+
1852+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
1853+
enum EditionRange {
1854+
Exact(Edition),
1855+
GreaterEqualThan(Edition),
1856+
Range { greater_equal_than: Edition, lower_than: Edition },
1857+
}
1858+
1859+
impl EditionRange {
1860+
fn edition_to_test(&self, config: &Config) -> Edition {
1861+
let min_edition = Edition::Year(2015);
1862+
let requested: Edition =
1863+
config.edition.as_deref().map(parse_edition).unwrap_or(min_edition);
1864+
1865+
match *self {
1866+
EditionRange::Exact(exact) => exact,
1867+
EditionRange::GreaterEqualThan(greater_equal_than) => {
1868+
if requested >= greater_equal_than {
1869+
requested
1870+
} else {
1871+
greater_equal_than // Lower bound
1872+
}
1873+
}
1874+
EditionRange::Range { greater_equal_than, lower_than } => {
1875+
if requested >= greater_equal_than && requested < lower_than {
1876+
requested
1877+
} else {
1878+
greater_equal_than // Lower bound
1879+
}
1880+
}
1881+
}
1882+
}
1883+
}

src/tools/compiletest/src/directives/tests.rs

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use camino::Utf8Path;
44
use semver::Version;
55

66
use super::{
7-
DirectivesCache, EarlyProps, extract_llvm_version, extract_version_range, iter_directives,
8-
parse_normalize_rule,
7+
DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version,
8+
extract_version_range, iter_directives, parse_normalize_rule,
99
};
1010
use crate::common::{Config, Debugger, TestMode};
1111
use crate::executor::{CollectedTestDesc, ShouldPanic};
@@ -71,6 +71,7 @@ fn test_parse_normalize_rule() {
7171
struct ConfigBuilder {
7272
mode: Option<String>,
7373
channel: Option<String>,
74+
edition: Option<String>,
7475
host: Option<String>,
7576
target: Option<String>,
7677
stage: Option<u32>,
@@ -94,6 +95,11 @@ impl ConfigBuilder {
9495
self
9596
}
9697

98+
fn edition(&mut self, s: &str) -> &mut Self {
99+
self.edition = Some(s.to_owned());
100+
self
101+
}
102+
97103
fn host(&mut self, s: &str) -> &mut Self {
98104
self.host = Some(s.to_owned());
99105
self
@@ -181,6 +187,10 @@ impl ConfigBuilder {
181187
];
182188
let mut args: Vec<String> = args.iter().map(ToString::to_string).collect();
183189

190+
if let Some(edition) = &self.edition {
191+
args.push(format!("--edition={edition}"));
192+
}
193+
184194
if let Some(ref llvm_version) = self.llvm_version {
185195
args.push("--llvm-version".to_owned());
186196
args.push(llvm_version.clone());
@@ -939,3 +949,138 @@ fn test_needs_target_std() {
939949
let config = cfg().target("x86_64-unknown-linux-gnu").build();
940950
assert!(!check_ignore(&config, "//@ needs-target-std"));
941951
}
952+
953+
fn y(year: u32) -> Edition {
954+
Edition::Year(year)
955+
}
956+
957+
fn parse_edition_range(line: &str) -> Option<EditionRange> {
958+
let config = cfg().build();
959+
super::parse_edition_range(&config, line, "tmp.rs".into(), 0)
960+
}
961+
962+
#[test]
963+
fn test_parse_edition_range() {
964+
assert_eq!(None, parse_edition_range("hello-world"));
965+
assert_eq!(None, parse_edition_range("edition"));
966+
967+
assert_eq!(Some(EditionRange::Exact(y(2018))), parse_edition_range("edition: 2018"));
968+
assert_eq!(Some(EditionRange::Exact(y(2021))), parse_edition_range("edition:2021"));
969+
assert_eq!(Some(EditionRange::Exact(y(2024))), parse_edition_range("edition: 2024 "));
970+
assert_eq!(Some(EditionRange::Exact(Edition::Future)), parse_edition_range("edition: future"));
971+
972+
assert_eq!(
973+
Some(EditionRange::GreaterEqualThan(y(2018))),
974+
parse_edition_range("edition: 2018..")
975+
);
976+
assert_eq!(
977+
Some(EditionRange::GreaterEqualThan(y(2021))),
978+
parse_edition_range("edition:2021 ..")
979+
);
980+
assert_eq!(
981+
Some(EditionRange::GreaterEqualThan(y(2024))),
982+
parse_edition_range("edition: 2024 .. ")
983+
);
984+
assert_eq!(
985+
Some(EditionRange::GreaterEqualThan(Edition::Future)),
986+
parse_edition_range("edition: future.. ")
987+
);
988+
989+
assert_eq!(
990+
Some(EditionRange::Range { greater_equal_than: y(2018), lower_than: y(2024) }),
991+
parse_edition_range("edition: 2018..2024")
992+
);
993+
assert_eq!(
994+
Some(EditionRange::Range { greater_equal_than: y(2015), lower_than: y(2021) }),
995+
parse_edition_range("edition:2015 .. 2021 ")
996+
);
997+
assert_eq!(
998+
Some(EditionRange::Range { greater_equal_than: y(2021), lower_than: y(2027) }),
999+
parse_edition_range("edition: 2021 .. 2027 ")
1000+
);
1001+
assert_eq!(
1002+
Some(EditionRange::Range { greater_equal_than: y(2021), lower_than: Edition::Future }),
1003+
parse_edition_range("edition: 2021..future")
1004+
);
1005+
}
1006+
1007+
#[test]
1008+
#[should_panic = "empty directive value detected"]
1009+
fn test_parse_edition_range_empty() {
1010+
parse_edition_range("edition:");
1011+
}
1012+
1013+
#[test]
1014+
#[should_panic = "'hello' doesn't look like an edition"]
1015+
fn test_parse_edition_range_invalid_edition() {
1016+
parse_edition_range("edition: hello");
1017+
}
1018+
1019+
#[test]
1020+
#[should_panic = "'..' is not a supported range in //@ edition"]
1021+
fn test_parse_edition_range_double_dots() {
1022+
parse_edition_range("edition: ..");
1023+
}
1024+
1025+
#[test]
1026+
#[should_panic = "the left side of `//@ edition` cannot be higher than the right side"]
1027+
fn test_parse_edition_range_inverted_range() {
1028+
parse_edition_range("edition: 2021..2015");
1029+
}
1030+
1031+
#[test]
1032+
#[should_panic = "the left side of `//@ edition` cannot be higher than the right side"]
1033+
fn test_parse_edition_range_inverted_range_future() {
1034+
parse_edition_range("edition: future..2015");
1035+
}
1036+
1037+
#[test]
1038+
#[should_panic = "the left side of `//@ edition` cannot be equal to the right side"]
1039+
fn test_parse_edition_range_empty_range() {
1040+
parse_edition_range("edition: 2021..2021");
1041+
}
1042+
1043+
#[track_caller]
1044+
fn assert_edition_to_test(
1045+
expected: impl Into<Edition>,
1046+
range: EditionRange,
1047+
default: Option<&str>,
1048+
) {
1049+
let mut cfg = cfg();
1050+
if let Some(default) = default {
1051+
cfg.edition(default);
1052+
}
1053+
assert_eq!(expected.into(), range.edition_to_test(&cfg.build()));
1054+
}
1055+
1056+
#[test]
1057+
fn test_edition_range_edition_to_test() {
1058+
let exact = EditionRange::Exact(y(2021));
1059+
assert_edition_to_test(2021, exact, None);
1060+
assert_edition_to_test(2021, exact, Some("2018"));
1061+
assert_edition_to_test(2021, exact, Some("future"));
1062+
1063+
assert_edition_to_test(Edition::Future, EditionRange::Exact(Edition::Future), None);
1064+
1065+
let greater_equal_than = EditionRange::GreaterEqualThan(y(2021));
1066+
assert_edition_to_test(2021, greater_equal_than, None);
1067+
assert_edition_to_test(2021, greater_equal_than, Some("2015"));
1068+
assert_edition_to_test(2021, greater_equal_than, Some("2018"));
1069+
assert_edition_to_test(2021, greater_equal_than, Some("2021"));
1070+
assert_edition_to_test(2024, greater_equal_than, Some("2024"));
1071+
assert_edition_to_test(Edition::Future, greater_equal_than, Some("future"));
1072+
1073+
let range = EditionRange::Range { greater_equal_than: y(2018), lower_than: y(2024) };
1074+
assert_edition_to_test(2018, range, None);
1075+
assert_edition_to_test(2018, range, Some("2015"));
1076+
assert_edition_to_test(2018, range, Some("2018"));
1077+
assert_edition_to_test(2021, range, Some("2021"));
1078+
assert_edition_to_test(2018, range, Some("2024"));
1079+
assert_edition_to_test(2018, range, Some("future"));
1080+
}
1081+
1082+
#[test]
1083+
#[should_panic = "'not an edition' doesn't look like an edition"]
1084+
fn test_edition_range_edition_to_test_bad_cli() {
1085+
assert_edition_to_test(2021, EditionRange::Exact(y(2021)), Some("not an edition"));
1086+
}

0 commit comments

Comments
 (0)