mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 12:54:58 +00:00
internal: introduce minicore -- a subset of libcore for testing
This commit is contained in:
parent
3f5eead9e3
commit
f521e41853
6 changed files with 271 additions and 42 deletions
|
@ -9,8 +9,8 @@ use test_utils::{
|
|||
use vfs::{file_set::FileSet, VfsPath};
|
||||
|
||||
use crate::{
|
||||
input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, FileRange,
|
||||
SourceDatabaseExt, SourceRoot, SourceRootId,
|
||||
input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Edition, Env, FileId,
|
||||
FilePosition, FileRange, SourceDatabaseExt, SourceRoot, SourceRootId,
|
||||
};
|
||||
|
||||
pub const WORKSPACE: SourceRootId = SourceRootId(0);
|
||||
|
@ -81,7 +81,7 @@ pub struct ChangeFixture {
|
|||
|
||||
impl 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 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)));
|
||||
change.set_roots(roots);
|
||||
change.set_crate_graph(crate_graph);
|
||||
|
|
|
@ -23,6 +23,7 @@ fn infer_block_expr_type_mismatch() {
|
|||
fn coerce_places() {
|
||||
check_infer(
|
||||
r#"
|
||||
//- minicore: coerce_unsized
|
||||
struct S<T> { a: T }
|
||||
|
||||
fn f<T>(_: &[T]) -> T { loop {} }
|
||||
|
@ -44,16 +45,6 @@ fn coerce_places() {
|
|||
let f: [&[_]; 2] = [arr; 2];
|
||||
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#"
|
||||
30..31 '_': &[T]
|
||||
|
|
|
@ -75,7 +75,9 @@ impl<'a> Project<'a> {
|
|||
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()..]);
|
||||
fs::create_dir_all(path.parent().unwrap()).unwrap();
|
||||
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
|
||||
|
|
|
@ -77,6 +77,11 @@ pub struct Fixture {
|
|||
pub introduce_new_source_root: bool,
|
||||
}
|
||||
|
||||
pub struct MiniCore {
|
||||
activated_flags: Vec<String>,
|
||||
valid_flags: Vec<String>,
|
||||
}
|
||||
|
||||
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> {
|
||||
///
|
||||
/// 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 mut fixture = fixture.as_str();
|
||||
let mut mini_core = None;
|
||||
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() {
|
||||
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"
|
||||
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];
|
||||
|
|
|
@ -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";
|
||||
|
|
69
crates/test_utils/src/minicore.rs
Normal file
69
crates/test_utils/src/minicore.rs
Normal 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::*;
|
Loading…
Add table
Add a link
Reference in a new issue