diff options
| -rw-r--r-- | src/tools/compiletest/src/common.rs | 6 | ||||
| -rw-r--r-- | src/tools/compiletest/src/directives.rs | 97 | ||||
| -rw-r--r-- | src/tools/compiletest/src/directives/tests.rs | 142 | ||||
| -rw-r--r-- | src/tools/compiletest/src/edition.rs | 35 | ||||
| -rw-r--r-- | src/tools/compiletest/src/lib.rs | 4 |
5 files changed, 270 insertions, 14 deletions
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 6d959948918..65db816ad1a 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -7,6 +7,7 @@ use build_helper::git::GitConfig; use camino::{Utf8Path, Utf8PathBuf}; use semver::Version; +use crate::edition::Edition; use crate::executor::ColorConfig; use crate::fatal; use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum}; @@ -612,10 +613,7 @@ pub struct Config { pub git_hash: bool, /// The default Rust edition. - /// - /// FIXME: perform stronger validation for this. There are editions that *definitely* exists, - /// but there might also be "future" edition. - pub edition: Option<String>, + pub edition: Option<Edition>, // Configuration for various run-make tests frobbing things like C compilers or querying about // various LLVM component information. diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 0d85e76c2cc..a79978d036c 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -16,10 +16,11 @@ use crate::directives::directive_names::{ KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES, }; use crate::directives::needs::CachedNeedsConditions; +use crate::edition::{Edition, parse_edition}; use crate::errors::ErrorKind; use crate::executor::{CollectedTestDesc, ShouldPanic}; -use crate::help; use crate::util::static_regex; +use crate::{fatal, help}; pub(crate) mod auxiliary; mod cfg; @@ -437,10 +438,13 @@ impl TestProps { panic!("`compiler-flags` directive should be spelled `compile-flags`"); } - if let Some(edition) = config.parse_edition(ln, testfile) { + if let Some(range) = parse_edition_range(config, ln, testfile) { // The edition is added at the start, since flags from //@compile-flags must // be passed to rustc last. - self.compile_flags.insert(0, format!("--edition={}", edition.trim())); + self.compile_flags.insert( + 0, + format!("--edition={}", range.edition_to_test(config.edition)), + ); has_edition = true; } @@ -1124,10 +1128,6 @@ impl Config { } } - fn parse_edition(&self, line: &DirectiveLine<'_>, testfile: &Utf8Path) -> Option<String> { - self.parse_name_value_directive(line, "edition", testfile) - } - fn set_name_directive(&self, line: &DirectiveLine<'_>, directive: &str, value: &mut bool) { // If the flag is already true, don't bother looking at the directive. *value = *value || self.parse_name_directive(line, directive); @@ -1769,3 +1769,86 @@ enum IgnoreDecision { Continue, Error { message: String }, } + +fn parse_edition_range( + config: &Config, + line: &DirectiveLine<'_>, + testfile: &Utf8Path, +) -> Option<EditionRange> { + let raw = config.parse_name_value_directive(line, "edition", testfile)?; + let line_number = line.line_number; + + // Edition range is half-open: `[lower_bound, upper_bound)` + if let Some((lower_bound, upper_bound)) = raw.split_once("..") { + Some(match (maybe_parse_edition(lower_bound), maybe_parse_edition(upper_bound)) { + (Some(lower_bound), Some(upper_bound)) if upper_bound <= lower_bound => { + fatal!( + "{testfile}:{line_number}: the left side of `//@ edition` cannot be greater than or equal to the right side" + ); + } + (Some(lower_bound), Some(upper_bound)) => { + EditionRange::Range { lower_bound, upper_bound } + } + (Some(lower_bound), None) => EditionRange::RangeFrom(lower_bound), + (None, Some(_)) => { + fatal!( + "{testfile}:{line_number}: `..edition` is not a supported range in `//@ edition`" + ); + } + (None, None) => { + fatal!("{testfile}:{line_number}: `..` is not a supported range in `//@ edition`"); + } + }) + } else { + match maybe_parse_edition(&raw) { + Some(edition) => Some(EditionRange::Exact(edition)), + None => { + fatal!("{testfile}:{line_number}: empty value for `//@ edition`"); + } + } + } +} + +fn maybe_parse_edition(mut input: &str) -> Option<Edition> { + input = input.trim(); + if input.is_empty() { + return None; + } + Some(parse_edition(input)) +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum EditionRange { + Exact(Edition), + RangeFrom(Edition), + /// Half-open range: `[lower_bound, upper_bound)` + Range { + lower_bound: Edition, + upper_bound: Edition, + }, +} + +impl EditionRange { + fn edition_to_test(&self, requested: impl Into<Option<Edition>>) -> Edition { + let min_edition = Edition::Year(2015); + let requested = requested.into().unwrap_or(min_edition); + + match *self { + EditionRange::Exact(exact) => exact, + EditionRange::RangeFrom(lower_bound) => { + if requested >= lower_bound { + requested + } else { + lower_bound + } + } + EditionRange::Range { lower_bound, upper_bound } => { + if requested >= lower_bound && requested < upper_bound { + requested + } else { + lower_bound + } + } + } + } +} diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 16cf76be9a5..93621192d4b 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -4,10 +4,11 @@ use camino::Utf8Path; use semver::Version; use super::{ - DirectivesCache, EarlyProps, extract_llvm_version, extract_version_range, iter_directives, - parse_normalize_rule, + DirectivesCache, EarlyProps, Edition, EditionRange, extract_llvm_version, + extract_version_range, iter_directives, parse_normalize_rule, }; use crate::common::{Config, Debugger, TestMode}; +use crate::directives::parse_edition; use crate::executor::{CollectedTestDesc, ShouldPanic}; fn make_test_description<R: Read>( @@ -73,6 +74,7 @@ fn test_parse_normalize_rule() { struct ConfigBuilder { mode: Option<String>, channel: Option<String>, + edition: Option<Edition>, host: Option<String>, target: Option<String>, stage: Option<u32>, @@ -96,6 +98,11 @@ impl ConfigBuilder { self } + fn edition(&mut self, e: Edition) -> &mut Self { + self.edition = Some(e); + self + } + fn host(&mut self, s: &str) -> &mut Self { self.host = Some(s.to_owned()); self @@ -183,6 +190,10 @@ impl ConfigBuilder { ]; let mut args: Vec<String> = args.iter().map(ToString::to_string).collect(); + if let Some(edition) = &self.edition { + args.push(format!("--edition={edition}")); + } + if let Some(ref llvm_version) = self.llvm_version { args.push("--llvm-version".to_owned()); args.push(llvm_version.clone()); @@ -941,3 +952,130 @@ fn test_needs_target_std() { let config = cfg().target("x86_64-unknown-linux-gnu").build(); assert!(!check_ignore(&config, "//@ needs-target-std")); } + +fn parse_edition_range(line: &str) -> Option<EditionRange> { + let config = cfg().build(); + let line = super::DirectiveLine { line_number: 0, revision: None, raw_directive: line }; + + super::parse_edition_range(&config, &line, "tmp.rs".into()) +} + +#[test] +fn test_parse_edition_range() { + assert_eq!(None, parse_edition_range("hello-world")); + assert_eq!(None, parse_edition_range("edition")); + + assert_eq!(Some(EditionRange::Exact(2018.into())), parse_edition_range("edition: 2018")); + assert_eq!(Some(EditionRange::Exact(2021.into())), parse_edition_range("edition:2021")); + assert_eq!(Some(EditionRange::Exact(2024.into())), parse_edition_range("edition: 2024 ")); + assert_eq!(Some(EditionRange::Exact(Edition::Future)), parse_edition_range("edition: future")); + + assert_eq!(Some(EditionRange::RangeFrom(2018.into())), parse_edition_range("edition: 2018..")); + assert_eq!(Some(EditionRange::RangeFrom(2021.into())), parse_edition_range("edition:2021 ..")); + assert_eq!( + Some(EditionRange::RangeFrom(2024.into())), + parse_edition_range("edition: 2024 .. ") + ); + assert_eq!( + Some(EditionRange::RangeFrom(Edition::Future)), + parse_edition_range("edition: future.. ") + ); + + assert_eq!( + Some(EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() }), + parse_edition_range("edition: 2018..2024") + ); + assert_eq!( + Some(EditionRange::Range { lower_bound: 2015.into(), upper_bound: 2021.into() }), + parse_edition_range("edition:2015 .. 2021 ") + ); + assert_eq!( + Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: 2027.into() }), + parse_edition_range("edition: 2021 .. 2027 ") + ); + assert_eq!( + Some(EditionRange::Range { lower_bound: 2021.into(), upper_bound: Edition::Future }), + parse_edition_range("edition: 2021..future") + ); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_empty() { + parse_edition_range("edition:"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_invalid_edition() { + parse_edition_range("edition: hello"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_double_dots() { + parse_edition_range("edition: .."); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_inverted_range() { + parse_edition_range("edition: 2021..2015"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_inverted_range_future() { + parse_edition_range("edition: future..2015"); +} + +#[test] +#[should_panic] +fn test_parse_edition_range_empty_range() { + parse_edition_range("edition: 2021..2021"); +} + +#[track_caller] +fn assert_edition_to_test( + expected: impl Into<Edition>, + range: EditionRange, + default: Option<Edition>, +) { + let mut cfg = cfg(); + if let Some(default) = default { + cfg.edition(default); + } + assert_eq!(expected.into(), range.edition_to_test(cfg.build().edition)); +} + +#[test] +fn test_edition_range_edition_to_test() { + let e2015 = parse_edition("2015"); + let e2018 = parse_edition("2018"); + let e2021 = parse_edition("2021"); + let e2024 = parse_edition("2024"); + let efuture = parse_edition("future"); + + let exact = EditionRange::Exact(2021.into()); + assert_edition_to_test(2021, exact, None); + assert_edition_to_test(2021, exact, Some(e2018)); + assert_edition_to_test(2021, exact, Some(efuture)); + + assert_edition_to_test(Edition::Future, EditionRange::Exact(Edition::Future), None); + + let greater_equal_than = EditionRange::RangeFrom(2021.into()); + assert_edition_to_test(2021, greater_equal_than, None); + assert_edition_to_test(2021, greater_equal_than, Some(e2015)); + assert_edition_to_test(2021, greater_equal_than, Some(e2018)); + assert_edition_to_test(2021, greater_equal_than, Some(e2021)); + assert_edition_to_test(2024, greater_equal_than, Some(e2024)); + assert_edition_to_test(Edition::Future, greater_equal_than, Some(efuture)); + + let range = EditionRange::Range { lower_bound: 2018.into(), upper_bound: 2024.into() }; + assert_edition_to_test(2018, range, None); + assert_edition_to_test(2018, range, Some(e2015)); + assert_edition_to_test(2018, range, Some(e2018)); + assert_edition_to_test(2021, range, Some(e2021)); + assert_edition_to_test(2018, range, Some(e2024)); + assert_edition_to_test(2018, range, Some(efuture)); +} diff --git a/src/tools/compiletest/src/edition.rs b/src/tools/compiletest/src/edition.rs new file mode 100644 index 00000000000..36550cf5b2b --- /dev/null +++ b/src/tools/compiletest/src/edition.rs @@ -0,0 +1,35 @@ +use crate::fatal; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Edition { + // Note that the ordering here is load-bearing, as we want the future edition to be greater than + // any year-based edition. + Year(u32), + Future, +} + +impl std::fmt::Display for Edition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Edition::Year(year) => write!(f, "{year}"), + Edition::Future => f.write_str("future"), + } + } +} + +impl From<u32> for Edition { + fn from(value: u32) -> Self { + Edition::Year(value) + } +} + +pub fn parse_edition(mut input: &str) -> Edition { + input = input.trim(); + if input == "future" { + Edition::Future + } else { + Edition::Year(input.parse().unwrap_or_else(|_| { + fatal!("`{input}` doesn't look like an edition"); + })) + } +} diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 15e31dadf97..2d759279f34 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -7,6 +7,7 @@ pub mod common; mod debuggers; pub mod diagnostics; pub mod directives; +pub mod edition; pub mod errors; mod executor; mod json; @@ -39,6 +40,7 @@ use crate::common::{ expected_output_path, output_base_dir, output_relative_path, }; use crate::directives::DirectivesCache; +use crate::edition::parse_edition; use crate::executor::{CollectedTest, ColorConfig}; /// Creates the `Config` instance for this invocation of compiletest. @@ -449,7 +451,7 @@ pub fn parse_config(args: Vec<String>) -> Config { has_enzyme, channel: matches.opt_str("channel").unwrap(), git_hash: matches.opt_present("git-hash"), - edition: matches.opt_str("edition"), + edition: matches.opt_str("edition").as_deref().map(parse_edition), cc: matches.opt_str("cc").unwrap(), cxx: matches.opt_str("cxx").unwrap(), |
