aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-06-15 19:02:40 +0100
committerAleksey Kladov <[email protected]>2021-06-15 19:03:08 +0100
commitf521e4185323699cd5d063b2704367a319583982 (patch)
tree249c358780bebe049e01f0003caf8e15eb3c4a26 /crates
parent3f5eead9e3b32f7e88562337c95f76ea75c82486 (diff)
internal: introduce minicore -- a subset of libcore for testing
Diffstat (limited to 'crates')
-rw-r--r--crates/base_db/src/fixture.rs31
-rw-r--r--crates/hir_ty/src/tests/coercion.rs47
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs4
-rw-r--r--crates/test_utils/src/fixture.rs157
-rw-r--r--crates/test_utils/src/lib.rs5
-rw-r--r--crates/test_utils/src/minicore.rs69
6 files changed, 271 insertions, 42 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 1b17db102..d56b20b83 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -9,8 +9,8 @@ use test_utils::{
9use vfs::{file_set::FileSet, VfsPath}; 9use vfs::{file_set::FileSet, VfsPath};
10 10
11use crate::{ 11use crate::{
12 input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, FileRange, 12 input::CrateName, Change, CrateDisplayName, CrateGraph, CrateId, Edition, Env, FileId,
13 SourceDatabaseExt, SourceRoot, SourceRootId, 13 FilePosition, FileRange, SourceDatabaseExt, SourceRoot, SourceRootId,
14}; 14};
15 15
16pub const WORKSPACE: SourceRootId = SourceRootId(0); 16pub const WORKSPACE: SourceRootId = SourceRootId(0);
@@ -81,7 +81,7 @@ pub struct ChangeFixture {
81 81
82impl ChangeFixture { 82impl ChangeFixture {
83 pub fn parse(ra_fixture: &str) -> ChangeFixture { 83 pub fn parse(ra_fixture: &str) -> ChangeFixture {
84 let fixture = Fixture::parse(ra_fixture); 84 let (mini_core, fixture) = Fixture::parse(ra_fixture);
85 let mut change = Change::new(); 85 let mut change = Change::new();
86 86
87 let mut files = Vec::new(); 87 let mut files = Vec::new();
@@ -166,6 +166,31 @@ impl ChangeFixture {
166 } 166 }
167 } 167 }
168 168
169 if let Some(mini_core) = mini_core {
170 let core_file = file_id;
171 file_id.0 += 1;
172
173 let mut fs = FileSet::default();
174 fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
175 roots.push(SourceRoot::new_library(fs));
176
177 change.change_file(core_file, Some(Arc::new(mini_core.source_code())));
178
179 let all_crates = crate_graph.crates_in_topological_order();
180
181 let core_crate = crate_graph.add_crate_root(
182 core_file,
183 Edition::Edition2021,
184 Some(CrateDisplayName::from_canonical_name("core".to_string())),
185 CfgOptions::default(),
186 Env::default(),
187 Vec::new(),
188 );
189
190 for krate in all_crates {
191 crate_graph.add_dep(krate, CrateName::new("core").unwrap(), core_crate).unwrap();
192 }
193 }
169 roots.push(SourceRoot::new_local(mem::take(&mut file_set))); 194 roots.push(SourceRoot::new_local(mem::take(&mut file_set)));
170 change.set_roots(roots); 195 change.set_roots(roots);
171 change.set_crate_graph(crate_graph); 196 change.set_crate_graph(crate_graph);
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index 4f859fc85..91236e974 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -23,38 +23,29 @@ fn infer_block_expr_type_mismatch() {
23fn coerce_places() { 23fn coerce_places() {
24 check_infer( 24 check_infer(
25 r#" 25 r#"
26 struct S<T> { a: T } 26//- minicore: coerce_unsized
27struct S<T> { a: T }
27 28
28 fn f<T>(_: &[T]) -> T { loop {} } 29fn f<T>(_: &[T]) -> T { loop {} }
29 fn g<T>(_: S<&[T]>) -> T { loop {} } 30fn g<T>(_: S<&[T]>) -> T { loop {} }
30 31
31 fn gen<T>() -> *mut [T; 2] { loop {} } 32fn gen<T>() -> *mut [T; 2] { loop {} }
32 fn test1<U>() -> *mut [U] { 33fn test1<U>() -> *mut [U] {
33 gen() 34 gen()
34 } 35}
35
36 fn test2() {
37 let arr: &[u8; 1] = &[1];
38 36
39 let a: &[_] = arr; 37fn test2() {
40 let b = f(arr); 38 let arr: &[u8; 1] = &[1];
41 let c: &[_] = { arr };
42 let d = g(S { a: arr });
43 let e: [&[_]; 1] = [arr];
44 let f: [&[_]; 2] = [arr; 2];
45 let g: (&[_], &[_]) = (arr, arr);
46 }
47 39
48 #[lang = "sized"] 40 let a: &[_] = arr;
49 pub trait Sized {} 41 let b = f(arr);
50 #[lang = "unsize"] 42 let c: &[_] = { arr };
51 pub trait Unsize<T: ?Sized> {} 43 let d = g(S { a: arr });
52 #[lang = "coerce_unsized"] 44 let e: [&[_]; 1] = [arr];
53 pub trait CoerceUnsized<T> {} 45 let f: [&[_]; 2] = [arr; 2];
54 46 let g: (&[_], &[_]) = (arr, arr);
55 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} 47}
56 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} 48"#,
57 "#,
58 expect![[r#" 49 expect![[r#"
59 30..31 '_': &[T] 50 30..31 '_': &[T]
60 44..55 '{ loop {} }': T 51 44..55 '{ loop {} }': T
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index e22c295f9..260a504e7 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -75,7 +75,9 @@ impl<'a> Project<'a> {
75 profile::init_from(crate::PROFILE); 75 profile::init_from(crate::PROFILE);
76 }); 76 });
77 77
78 for entry in Fixture::parse(self.fixture) { 78 let (mini_core, fixtures) = Fixture::parse(self.fixture);
79 assert!(mini_core.is_none());
80 for entry in fixtures {
79 let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]); 81 let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
80 fs::create_dir_all(path.parent().unwrap()).unwrap(); 82 fs::create_dir_all(path.parent().unwrap()).unwrap();
81 fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); 83 fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
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 {
77 pub introduce_new_source_root: bool, 77 pub introduce_new_source_root: bool,
78} 78}
79 79
80pub struct MiniCore {
81 activated_flags: Vec<String>,
82 valid_flags: Vec<String>,
83}
84
80impl Fixture { 85impl Fixture {
81 /// Parses text which looks like this: 86 /// Parses text which looks like this:
82 /// 87 ///
@@ -86,12 +91,28 @@ impl Fixture {
86 /// line 2 91 /// line 2
87 /// //- other meta 92 /// //- other meta
88 /// ``` 93 /// ```
89 pub fn parse(ra_fixture: &str) -> Vec<Fixture> { 94 ///
95 /// Fixture can also start with a minicore declaration:
96 ///
97 /// ```
98 /// //- minicore: sized
99 /// ```
100 ///
101 /// That will include a subset of `libcore` into the fixture, see
102 /// `minicore.rs` for what's available.
103 pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<Fixture>) {
90 let fixture = trim_indent(ra_fixture); 104 let fixture = trim_indent(ra_fixture);
91 105 let mut fixture = fixture.as_str();
106 let mut mini_core = None;
92 let mut res: Vec<Fixture> = Vec::new(); 107 let mut res: Vec<Fixture> = Vec::new();
93 108
94 let default = if ra_fixture.contains("//-") { None } else { Some("//- /main.rs") }; 109 if fixture.starts_with("//- minicore:") {
110 let first_line = fixture.split_inclusive('\n').next().unwrap();
111 mini_core = Some(MiniCore::parse(first_line));
112 fixture = &fixture[first_line.len()..];
113 }
114
115 let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") };
95 116
96 for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() { 117 for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() {
97 if line.contains("//-") { 118 if line.contains("//-") {
@@ -113,7 +134,7 @@ impl Fixture {
113 } 134 }
114 } 135 }
115 136
116 res 137 (mini_core, res)
117 } 138 }
118 139
119 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo 140 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -172,6 +193,122 @@ impl Fixture {
172 } 193 }
173} 194}
174 195
196impl MiniCore {
197 fn has_flag(&self, flag: &str) -> bool {
198 self.activated_flags.iter().any(|it| it == flag)
199 }
200
201 fn assert_valid_flag(&self, flag: &str) {
202 if !self.valid_flags.iter().any(|it| it == flag) {
203 panic!("invalid flag: {:?}, valid flags: {:?}", flag, self.valid_flags);
204 }
205 }
206
207 fn parse(line: &str) -> MiniCore {
208 let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() };
209
210 let line = line.strip_prefix("//- minicore:").unwrap().trim();
211 for entry in line.split(", ") {
212 if res.has_flag(entry) {
213 panic!("duplicate minicore flag: {:?}", entry)
214 }
215 res.activated_flags.push(entry.to_string())
216 }
217
218 res
219 }
220
221 /// Strips parts of minicore.rs which are flagged by inactive flags.
222 ///
223 /// This is probably over-engineered to support flags dependencies.
224 pub fn source_code(mut self) -> String {
225 let mut buf = String::new();
226 let raw_mini_core = include_str!("./minicore.rs");
227 let mut lines = raw_mini_core.split_inclusive('\n');
228
229 let mut parsing_flags = false;
230 let mut implications = Vec::new();
231
232 // Parse `//!` preamble and extract flags and dependencies.
233 for line in lines.by_ref() {
234 let line = match line.strip_prefix("//!") {
235 Some(it) => it,
236 None => {
237 assert!(line.trim().is_empty());
238 break;
239 }
240 };
241
242 if parsing_flags {
243 let (flag, deps) = line.split_once(':').unwrap();
244 let flag = flag.trim();
245 self.valid_flags.push(flag.to_string());
246 for dep in deps.split(", ") {
247 let dep = dep.trim();
248 if !dep.is_empty() {
249 self.assert_valid_flag(dep);
250 implications.push((flag, dep));
251 }
252 }
253 }
254
255 if line.contains("Available flags:") {
256 parsing_flags = true;
257 }
258 }
259
260 for flag in &self.activated_flags {
261 self.assert_valid_flag(flag);
262 }
263
264 // Fixed point loop to compute transitive closure of flags.
265 loop {
266 let mut changed = false;
267 for &(u, v) in implications.iter() {
268 if self.has_flag(u) && !self.has_flag(v) {
269 self.activated_flags.push(v.to_string());
270 changed = true;
271 }
272 }
273 if !changed {
274 break;
275 }
276 }
277
278 let mut curr_region = "";
279 for line in lines {
280 let trimmed = line.trim();
281 if let Some(region) = trimmed.strip_prefix("// region:") {
282 assert_eq!(curr_region, "");
283 curr_region = region;
284 continue;
285 }
286 if let Some(region) = trimmed.strip_prefix("// endregion:") {
287 assert_eq!(curr_region, region);
288 curr_region = "";
289 continue;
290 }
291
292 let mut flag = curr_region;
293 if let Some(idx) = trimmed.find("// :") {
294 flag = &trimmed[idx + "// :".len()..];
295 }
296
297 let skip = if flag == "" {
298 false
299 } else {
300 self.assert_valid_flag(flag);
301 !self.has_flag(flag)
302 };
303
304 if !skip {
305 buf.push_str(line)
306 }
307 }
308 buf
309 }
310}
311
175#[test] 312#[test]
176#[should_panic] 313#[should_panic]
177fn parse_fixture_checks_further_indented_metadata() { 314fn parse_fixture_checks_further_indented_metadata() {
@@ -189,12 +326,14 @@ fn parse_fixture_checks_further_indented_metadata() {
189 326
190#[test] 327#[test]
191fn parse_fixture_gets_full_meta() { 328fn parse_fixture_gets_full_meta() {
192 let parsed = Fixture::parse( 329 let (mini_core, parsed) = Fixture::parse(
193 r" 330 r#"
194 //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo 331//- minicore: coerce_unsized
195 mod m; 332//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
196 ", 333mod m;
334"#,
197 ); 335 );
336 assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
198 assert_eq!(1, parsed.len()); 337 assert_eq!(1, parsed.len());
199 338
200 let meta = &parsed[0]; 339 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};
23pub use dissimilar::diff as __diff; 23pub use dissimilar::diff as __diff;
24pub use rustc_hash::FxHashMap; 24pub use rustc_hash::FxHashMap;
25 25
26pub use crate::{assert_linear::AssertLinear, fixture::Fixture}; 26pub use crate::{
27 assert_linear::AssertLinear,
28 fixture::{Fixture, MiniCore},
29};
27 30
28pub const CURSOR_MARKER: &str = "$0"; 31pub const CURSOR_MARKER: &str = "$0";
29pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; 32pub 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 @@
1//! This is a fixture we use for tests that need lang items.
2//!
3//! We want to include the minimal subset of core for each test, so this file
4//! supports "conditional compilation". Tests use the following syntax to include minicore:
5//!
6//! //- minicore: flag1, flag2
7//!
8//! We then strip all the code marked with other flags.
9//!
10//! Available flags:
11//! sized:
12//! coerce_unsized: sized
13
14pub mod marker {
15 // region:sized
16 #[lang = "sized"]
17 #[fundamental]
18 #[rustc_specialization_trait]
19 pub trait Sized {}
20
21 #[lang = "unsize"]
22 pub trait Unsize<T: ?Sized> {}
23 // endregion:sized
24}
25
26pub mod ops {
27 mod unsize {
28 // region:coerce_unsized
29 use crate::marker::Unsize;
30
31 #[lang = "coerce_unsized"]
32 pub trait CoerceUnsized<T: ?Sized> {}
33
34 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
35 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {}
36 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {}
37 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {}
38
39 impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
40 impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {}
41
42 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
43 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
44 impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
45 // endregion:coerce_unsized
46 }
47}
48
49pub mod prelude {
50 pub mod v1 {
51 pub use crate::marker::Sized; // :sized
52 }
53
54 pub mod rust_2015 {
55 pub use super::v1::*;
56 }
57
58 pub mod rust_2018 {
59 pub use super::v1::*;
60 }
61
62 pub mod rust_2021 {
63 pub use super::v1::*;
64 }
65}
66
67#[prelude_import]
68#[allow(unused)]
69use prelude::v1::*;