diff options
-rw-r--r-- | crates/base_db/src/fixture.rs | 31 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/coercion.rs | 47 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/slow-tests/support.rs | 4 | ||||
-rw-r--r-- | crates/test_utils/src/fixture.rs | 157 | ||||
-rw-r--r-- | crates/test_utils/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/test_utils/src/minicore.rs | 69 |
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::{ | |||
9 | use vfs::{file_set::FileSet, VfsPath}; | 9 | use vfs::{file_set::FileSet, VfsPath}; |
10 | 10 | ||
11 | use crate::{ | 11 | use 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 | ||
16 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 16 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
@@ -81,7 +81,7 @@ pub struct ChangeFixture { | |||
81 | 81 | ||
82 | impl ChangeFixture { | 82 | impl 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() { | |||
23 | fn coerce_places() { | 23 | fn coerce_places() { |
24 | check_infer( | 24 | check_infer( |
25 | r#" | 25 | r#" |
26 | struct S<T> { a: T } | 26 | //- minicore: coerce_unsized |
27 | struct S<T> { a: T } | ||
27 | 28 | ||
28 | fn f<T>(_: &[T]) -> T { loop {} } | 29 | fn f<T>(_: &[T]) -> T { loop {} } |
29 | fn g<T>(_: S<&[T]>) -> T { loop {} } | 30 | fn g<T>(_: S<&[T]>) -> T { loop {} } |
30 | 31 | ||
31 | fn gen<T>() -> *mut [T; 2] { loop {} } | 32 | fn gen<T>() -> *mut [T; 2] { loop {} } |
32 | fn test1<U>() -> *mut [U] { | 33 | fn 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; | 37 | fn 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 | ||
80 | pub struct MiniCore { | ||
81 | activated_flags: Vec<String>, | ||
82 | valid_flags: Vec<String>, | ||
83 | } | ||
84 | |||
80 | impl Fixture { | 85 | impl 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 | ||
196 | impl 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] |
177 | fn parse_fixture_checks_further_indented_metadata() { | 314 | fn parse_fixture_checks_further_indented_metadata() { |
@@ -189,12 +326,14 @@ fn parse_fixture_checks_further_indented_metadata() { | |||
189 | 326 | ||
190 | #[test] | 327 | #[test] |
191 | fn parse_fixture_gets_full_meta() { | 328 | fn 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 | ", | 333 | mod 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}; | |||
23 | pub use dissimilar::diff as __diff; | 23 | pub use dissimilar::diff as __diff; |
24 | pub use rustc_hash::FxHashMap; | 24 | pub use rustc_hash::FxHashMap; |
25 | 25 | ||
26 | pub use crate::{assert_linear::AssertLinear, fixture::Fixture}; | 26 | pub use crate::{ |
27 | assert_linear::AssertLinear, | ||
28 | fixture::{Fixture, MiniCore}, | ||
29 | }; | ||
27 | 30 | ||
28 | pub const CURSOR_MARKER: &str = "$0"; | 31 | pub const CURSOR_MARKER: &str = "$0"; |
29 | pub const ESCAPED_CURSOR_MARKER: &str = "\\$0"; | 32 | 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 @@ | |||
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 | |||
14 | pub 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 | |||
26 | pub 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 | |||
49 | pub 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)] | ||
69 | use prelude::v1::*; | ||