From f521e4185323699cd5d063b2704367a319583982 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 15 Jun 2021 21:02:40 +0300 Subject: internal: introduce minicore -- a subset of libcore for testing --- crates/test_utils/src/fixture.rs | 157 +++++++++++++++++++++++++++++++++++--- crates/test_utils/src/lib.rs | 5 +- crates/test_utils/src/minicore.rs | 69 +++++++++++++++++ 3 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 crates/test_utils/src/minicore.rs (limited to 'crates/test_utils') diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs index d0bddf7d8..535892f3f 100644 --- a/crates/test_utils/src/fixture.rs +++ b/crates/test_utils/src/fixture.rs @@ -77,6 +77,11 @@ pub struct Fixture { pub introduce_new_source_root: bool, } +pub struct MiniCore { + activated_flags: Vec, + valid_flags: Vec, +} + impl Fixture { /// Parses text which looks like this: /// @@ -86,12 +91,28 @@ impl Fixture { /// line 2 /// //- other meta /// ``` - pub fn parse(ra_fixture: &str) -> Vec { + /// + /// Fixture can also start with a minicore declaration: + /// + /// ``` + /// //- minicore: sized + /// ``` + /// + /// That will include a subset of `libcore` into the fixture, see + /// `minicore.rs` for what's available. + pub fn parse(ra_fixture: &str) -> (Option, Vec) { let fixture = trim_indent(ra_fixture); - + let mut fixture = fixture.as_str(); + let mut mini_core = None; let mut res: Vec = Vec::new(); - let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") }; + if fixture.starts_with("//- minicore:") { + let first_line = fixture.split_inclusive('\n').next().unwrap(); + mini_core = Some(MiniCore::parse(first_line)); + fixture = &fixture[first_line.len()..]; + } + + let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") }; for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() { if line.contains("//-") { @@ -113,7 +134,7 @@ impl Fixture { } } - res + (mini_core, res) } //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo @@ -172,6 +193,122 @@ impl Fixture { } } +impl MiniCore { + fn has_flag(&self, flag: &str) -> bool { + self.activated_flags.iter().any(|it| it == flag) + } + + fn assert_valid_flag(&self, flag: &str) { + if !self.valid_flags.iter().any(|it| it == flag) { + panic!("invalid flag: {:?}, valid flags: {:?}", flag, self.valid_flags); + } + } + + fn parse(line: &str) -> MiniCore { + let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() }; + + let line = line.strip_prefix("//- minicore:").unwrap().trim(); + for entry in line.split(", ") { + if res.has_flag(entry) { + panic!("duplicate minicore flag: {:?}", entry) + } + res.activated_flags.push(entry.to_string()) + } + + res + } + + /// Strips parts of minicore.rs which are flagged by inactive flags. + /// + /// This is probably over-engineered to support flags dependencies. + pub fn source_code(mut self) -> String { + let mut buf = String::new(); + let raw_mini_core = include_str!("./minicore.rs"); + let mut lines = raw_mini_core.split_inclusive('\n'); + + let mut parsing_flags = false; + let mut implications = Vec::new(); + + // Parse `//!` preamble and extract flags and dependencies. + for line in lines.by_ref() { + let line = match line.strip_prefix("//!") { + Some(it) => it, + None => { + assert!(line.trim().is_empty()); + break; + } + }; + + if parsing_flags { + let (flag, deps) = line.split_once(':').unwrap(); + let flag = flag.trim(); + self.valid_flags.push(flag.to_string()); + for dep in deps.split(", ") { + let dep = dep.trim(); + if !dep.is_empty() { + self.assert_valid_flag(dep); + implications.push((flag, dep)); + } + } + } + + if line.contains("Available flags:") { + parsing_flags = true; + } + } + + for flag in &self.activated_flags { + self.assert_valid_flag(flag); + } + + // Fixed point loop to compute transitive closure of flags. + loop { + let mut changed = false; + for &(u, v) in implications.iter() { + if self.has_flag(u) && !self.has_flag(v) { + self.activated_flags.push(v.to_string()); + changed = true; + } + } + if !changed { + break; + } + } + + let mut curr_region = ""; + for line in lines { + let trimmed = line.trim(); + if let Some(region) = trimmed.strip_prefix("// region:") { + assert_eq!(curr_region, ""); + curr_region = region; + continue; + } + if let Some(region) = trimmed.strip_prefix("// endregion:") { + assert_eq!(curr_region, region); + curr_region = ""; + continue; + } + + let mut flag = curr_region; + if let Some(idx) = trimmed.find("// :") { + flag = &trimmed[idx + "// :".len()..]; + } + + let skip = if flag == "" { + false + } else { + self.assert_valid_flag(flag); + !self.has_flag(flag) + }; + + if !skip { + buf.push_str(line) + } + } + buf + } +} + #[test] #[should_panic] fn parse_fixture_checks_further_indented_metadata() { @@ -189,12 +326,14 @@ fn parse_fixture_checks_further_indented_metadata() { #[test] fn parse_fixture_gets_full_meta() { - let parsed = Fixture::parse( - r" - //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo - mod m; - ", + let (mini_core, parsed) = Fixture::parse( + r#" +//- minicore: coerce_unsized +//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo +mod m; +"#, ); + assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]); assert_eq!(1, parsed.len()); let meta = &parsed[0]; diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index b2fe25f82..d55bae62a 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -23,7 +23,10 @@ use text_size::{TextRange, TextSize}; pub use dissimilar::diff as __diff; pub use rustc_hash::FxHashMap; -pub use crate::{assert_linear::AssertLinear, fixture::Fixture}; +pub use crate::{ + assert_linear::AssertLinear, + fixture::{Fixture, MiniCore}, +}; pub const CURSOR_MARKER: &str = "$0"; pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; diff --git a/crates/test_utils/src/minicore.rs b/crates/test_utils/src/minicore.rs new file mode 100644 index 000000000..629c06ed0 --- /dev/null +++ b/crates/test_utils/src/minicore.rs @@ -0,0 +1,69 @@ +//! This is a fixture we use for tests that need lang items. +//! +//! We want to include the minimal subset of core for each test, so this file +//! supports "conditional compilation". Tests use the following syntax to include minicore: +//! +//! //- minicore: flag1, flag2 +//! +//! We then strip all the code marked with other flags. +//! +//! Available flags: +//! sized: +//! coerce_unsized: sized + +pub mod marker { + // region:sized + #[lang = "sized"] + #[fundamental] + #[rustc_specialization_trait] + pub trait Sized {} + + #[lang = "unsize"] + pub trait Unsize {} + // endregion:sized +} + +pub mod ops { + mod unsize { + // region:coerce_unsized + use crate::marker::Unsize; + + #[lang = "coerce_unsized"] + pub trait CoerceUnsized {} + + impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} + impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {} + impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {} + impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {} + + impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} + impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<*const U> for &'a T {} + + impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} + impl, U: ?Sized> CoerceUnsized<*const U> for *mut T {} + impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} + // endregion:coerce_unsized + } +} + +pub mod prelude { + pub mod v1 { + pub use crate::marker::Sized; // :sized + } + + pub mod rust_2015 { + pub use super::v1::*; + } + + pub mod rust_2018 { + pub use super::v1::*; + } + + pub mod rust_2021 { + pub use super::v1::*; + } +} + +#[prelude_import] +#[allow(unused)] +use prelude::v1::*; -- cgit v1.2.3