internal: introduce minicore -- a subset of libcore for testing

This commit is contained in:
Aleksey Kladov 2021-06-15 21:02:40 +03:00
parent 3f5eead9e3
commit f521e41853
6 changed files with 271 additions and 42 deletions

View file

@ -9,8 +9,8 @@ use test_utils::{
use vfs::{file_set::FileSet, VfsPath}; use vfs::{file_set::FileSet, VfsPath};
use crate::{ use crate::{
input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, FileRange, input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Edition, Env, FileId,
SourceDatabaseExt, SourceRoot, SourceRootId, FilePosition, FileRange, SourceDatabaseExt, SourceRoot, SourceRootId,
}; };
pub const WORKSPACE: SourceRootId = SourceRootId(0); pub const WORKSPACE: SourceRootId = SourceRootId(0);
@ -81,7 +81,7 @@ pub struct ChangeFixture {
impl ChangeFixture { impl ChangeFixture {
pub fn parse(ra_fixture: &str) -> ChangeFixture { pub fn parse(ra_fixture: &str) -> ChangeFixture {
let fixture = Fixture::parse(ra_fixture); let (mini_core, fixture) = Fixture::parse(ra_fixture);
let mut change = Change::new(); let mut change = Change::new();
let mut files = Vec::new(); let mut files = Vec::new();
@ -166,6 +166,31 @@ impl ChangeFixture {
} }
} }
if let Some(mini_core) = mini_core {
let core_file = file_id;
file_id.0 += 1;
let mut fs = FileSet::default();
fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
roots.push(SourceRoot::new_library(fs));
change.change_file(core_file, Some(Arc::new(mini_core.source_code())));
let all_crates = crate_graph.crates_in_topological_order();
let core_crate = crate_graph.add_crate_root(
core_file,
Edition::Edition2021,
Some(CrateDisplayName::from_canonical_name("core".to_string())),
CfgOptions::default(),
Env::default(),
Vec::new(),
);
for krate in all_crates {
crate_graph.add_dep(krate, CrateName::new("core").unwrap(), core_crate).unwrap();
}
}
roots.push(SourceRoot::new_local(mem::take(&mut file_set))); roots.push(SourceRoot::new_local(mem::take(&mut file_set)));
change.set_roots(roots); change.set_roots(roots);
change.set_crate_graph(crate_graph); change.set_crate_graph(crate_graph);

View file

@ -23,6 +23,7 @@ fn infer_block_expr_type_mismatch() {
fn coerce_places() { fn coerce_places() {
check_infer( check_infer(
r#" r#"
//- minicore: coerce_unsized
struct S<T> { a: T } struct S<T> { a: T }
fn f<T>(_: &[T]) -> T { loop {} } fn f<T>(_: &[T]) -> T { loop {} }
@ -44,16 +45,6 @@ fn coerce_places() {
let f: [&[_]; 2] = [arr; 2]; let f: [&[_]; 2] = [arr; 2];
let g: (&[_], &[_]) = (arr, arr); let g: (&[_], &[_]) = (arr, arr);
} }
#[lang = "sized"]
pub trait Sized {}
#[lang = "unsize"]
pub trait Unsize<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
"#, "#,
expect![[r#" expect![[r#"
30..31 '_': &[T] 30..31 '_': &[T]

View file

@ -75,7 +75,9 @@ impl<'a> Project<'a> {
profile::init_from(crate::PROFILE); profile::init_from(crate::PROFILE);
}); });
for entry in Fixture::parse(self.fixture) { let (mini_core, fixtures) = Fixture::parse(self.fixture);
assert!(mini_core.is_none());
for entry in fixtures {
let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]); let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
fs::create_dir_all(path.parent().unwrap()).unwrap(); fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); fs::write(path.as_path(), entry.text.as_bytes()).unwrap();

View file

@ -77,6 +77,11 @@ pub struct Fixture {
pub introduce_new_source_root: bool, pub introduce_new_source_root: bool,
} }
pub struct MiniCore {
activated_flags: Vec<String>,
valid_flags: Vec<String>,
}
impl Fixture { impl Fixture {
/// Parses text which looks like this: /// Parses text which looks like this:
/// ///
@ -86,12 +91,28 @@ impl Fixture {
/// line 2 /// line 2
/// //- other meta /// //- other meta
/// ``` /// ```
pub fn parse(ra_fixture: &str) -> Vec<Fixture> { ///
/// 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<MiniCore>, Vec<Fixture>) {
let fixture = trim_indent(ra_fixture); let fixture = trim_indent(ra_fixture);
let mut fixture = fixture.as_str();
let mut mini_core = None;
let mut res: Vec<Fixture> = Vec::new(); let mut res: Vec<Fixture> = 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() { for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() {
if line.contains("//-") { 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 //- /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] #[test]
#[should_panic] #[should_panic]
fn parse_fixture_checks_further_indented_metadata() { fn parse_fixture_checks_further_indented_metadata() {
@ -189,12 +326,14 @@ fn parse_fixture_checks_further_indented_metadata() {
#[test] #[test]
fn parse_fixture_gets_full_meta() { fn parse_fixture_gets_full_meta() {
let parsed = Fixture::parse( let (mini_core, parsed) = Fixture::parse(
r" r#"
//- minicore: coerce_unsized
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
mod m; mod m;
", "#,
); );
assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
assert_eq!(1, parsed.len()); assert_eq!(1, parsed.len());
let meta = &parsed[0]; let meta = &parsed[0];

View file

@ -23,7 +23,10 @@ use text_size::{TextRange, TextSize};
pub use dissimilar::diff as __diff; pub use dissimilar::diff as __diff;
pub use rustc_hash::FxHashMap; 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 CURSOR_MARKER: &str = "$0";
pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; pub const ESCAPED_CURSOR_MARKER: &str = "\\$0";

View file

@ -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<T: ?Sized> {}
// endregion:sized
}
pub mod ops {
mod unsize {
// region:coerce_unsized
use crate::marker::Unsize;
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T: ?Sized> {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {}
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
impl<T: ?Sized + Unsize<U>, 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::*;