diff options
Diffstat (limited to 'crates/test_utils')
-rw-r--r-- | crates/test_utils/src/fixture.rs | 168 | ||||
-rw-r--r-- | crates/test_utils/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/test_utils/src/minicore.rs | 204 |
3 files changed, 367 insertions, 10 deletions
diff --git a/crates/test_utils/src/fixture.rs b/crates/test_utils/src/fixture.rs index d0bddf7d8..6ba112de8 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,133 @@ 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 | #[track_caller] | ||
202 | fn assert_valid_flag(&self, flag: &str) { | ||
203 | if !self.valid_flags.iter().any(|it| it == flag) { | ||
204 | panic!("invalid flag: {:?}, valid flags: {:?}", flag, self.valid_flags); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | fn parse(line: &str) -> MiniCore { | ||
209 | let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() }; | ||
210 | |||
211 | let line = line.strip_prefix("//- minicore:").unwrap().trim(); | ||
212 | for entry in line.split(", ") { | ||
213 | if res.has_flag(entry) { | ||
214 | panic!("duplicate minicore flag: {:?}", entry) | ||
215 | } | ||
216 | res.activated_flags.push(entry.to_string()) | ||
217 | } | ||
218 | |||
219 | res | ||
220 | } | ||
221 | |||
222 | /// Strips parts of minicore.rs which are flagged by inactive flags. | ||
223 | /// | ||
224 | /// This is probably over-engineered to support flags dependencies. | ||
225 | pub fn source_code(mut self) -> String { | ||
226 | let mut buf = String::new(); | ||
227 | let raw_mini_core = include_str!("./minicore.rs"); | ||
228 | let mut lines = raw_mini_core.split_inclusive('\n'); | ||
229 | |||
230 | let mut parsing_flags = false; | ||
231 | let mut implications = Vec::new(); | ||
232 | |||
233 | // Parse `//!` preamble and extract flags and dependencies. | ||
234 | for line in lines.by_ref() { | ||
235 | let line = match line.strip_prefix("//!") { | ||
236 | Some(it) => it, | ||
237 | None => { | ||
238 | assert!(line.trim().is_empty()); | ||
239 | break; | ||
240 | } | ||
241 | }; | ||
242 | |||
243 | if parsing_flags { | ||
244 | let (flag, deps) = line.split_once(':').unwrap(); | ||
245 | let flag = flag.trim(); | ||
246 | self.valid_flags.push(flag.to_string()); | ||
247 | for dep in deps.split(", ") { | ||
248 | let dep = dep.trim(); | ||
249 | if !dep.is_empty() { | ||
250 | self.assert_valid_flag(dep); | ||
251 | implications.push((flag, dep)); | ||
252 | } | ||
253 | } | ||
254 | } | ||
255 | |||
256 | if line.contains("Available flags:") { | ||
257 | parsing_flags = true; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | for flag in &self.activated_flags { | ||
262 | self.assert_valid_flag(flag); | ||
263 | } | ||
264 | |||
265 | // Fixed point loop to compute transitive closure of flags. | ||
266 | loop { | ||
267 | let mut changed = false; | ||
268 | for &(u, v) in implications.iter() { | ||
269 | if self.has_flag(u) && !self.has_flag(v) { | ||
270 | self.activated_flags.push(v.to_string()); | ||
271 | changed = true; | ||
272 | } | ||
273 | } | ||
274 | if !changed { | ||
275 | break; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | let mut curr_region = ""; | ||
280 | let mut seen_regions = Vec::new(); | ||
281 | for line in lines { | ||
282 | let trimmed = line.trim(); | ||
283 | if let Some(region) = trimmed.strip_prefix("// region:") { | ||
284 | assert_eq!(curr_region, ""); | ||
285 | curr_region = region; | ||
286 | continue; | ||
287 | } | ||
288 | if let Some(region) = trimmed.strip_prefix("// endregion:") { | ||
289 | assert_eq!(curr_region, region); | ||
290 | curr_region = ""; | ||
291 | continue; | ||
292 | } | ||
293 | seen_regions.push(curr_region); | ||
294 | |||
295 | let mut flag = curr_region; | ||
296 | if let Some(idx) = trimmed.find("// :") { | ||
297 | flag = &trimmed[idx + "// :".len()..]; | ||
298 | } | ||
299 | |||
300 | let skip = if flag == "" { | ||
301 | false | ||
302 | } else { | ||
303 | assert!(!flag.starts_with(' '), "region marker starts with a space: {:?}", flag); | ||
304 | self.assert_valid_flag(flag); | ||
305 | !self.has_flag(flag) | ||
306 | }; | ||
307 | |||
308 | if !skip { | ||
309 | buf.push_str(line) | ||
310 | } | ||
311 | } | ||
312 | |||
313 | for flag in &self.valid_flags { | ||
314 | if !seen_regions.iter().any(|it| it == flag) { | ||
315 | panic!("unused minicore flag: {:?}", flag); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | buf | ||
320 | } | ||
321 | } | ||
322 | |||
175 | #[test] | 323 | #[test] |
176 | #[should_panic] | 324 | #[should_panic] |
177 | fn parse_fixture_checks_further_indented_metadata() { | 325 | fn parse_fixture_checks_further_indented_metadata() { |
@@ -189,12 +337,14 @@ fn parse_fixture_checks_further_indented_metadata() { | |||
189 | 337 | ||
190 | #[test] | 338 | #[test] |
191 | fn parse_fixture_gets_full_meta() { | 339 | fn parse_fixture_gets_full_meta() { |
192 | let parsed = Fixture::parse( | 340 | let (mini_core, parsed) = Fixture::parse( |
193 | r" | 341 | r#" |
194 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo | 342 | //- minicore: coerce_unsized |
195 | mod m; | 343 | //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo |
196 | ", | 344 | mod m; |
345 | "#, | ||
197 | ); | 346 | ); |
347 | assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]); | ||
198 | assert_eq!(1, parsed.len()); | 348 | assert_eq!(1, parsed.len()); |
199 | 349 | ||
200 | let meta = &parsed[0]; | 350 | 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..5ff60178c --- /dev/null +++ b/crates/test_utils/src/minicore.rs | |||
@@ -0,0 +1,204 @@ | |||
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 | //! slice: | ||
13 | //! range: | ||
14 | //! unsize: sized | ||
15 | //! deref: sized | ||
16 | //! coerce_unsized: unsize | ||
17 | //! pin: | ||
18 | //! future: pin | ||
19 | //! option: | ||
20 | //! result: | ||
21 | |||
22 | pub mod marker { | ||
23 | // region:sized | ||
24 | #[lang = "sized"] | ||
25 | #[fundamental] | ||
26 | #[rustc_specialization_trait] | ||
27 | pub trait Sized {} | ||
28 | // endregion:sized | ||
29 | |||
30 | // region:unsize | ||
31 | #[lang = "unsize"] | ||
32 | pub trait Unsize<T: ?Sized> {} | ||
33 | // endregion:unsize | ||
34 | } | ||
35 | |||
36 | pub mod ops { | ||
37 | // region:coerce_unsized | ||
38 | mod unsize { | ||
39 | use crate::marker::Unsize; | ||
40 | |||
41 | #[lang = "coerce_unsized"] | ||
42 | pub trait CoerceUnsized<T: ?Sized> {} | ||
43 | |||
44 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} | ||
45 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {} | ||
46 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {} | ||
47 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {} | ||
48 | |||
49 | impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} | ||
50 | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {} | ||
51 | |||
52 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} | ||
53 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {} | ||
54 | impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {} | ||
55 | } | ||
56 | pub use self::unsize::CoerceUnsized; | ||
57 | // endregion:coerce_unsized | ||
58 | |||
59 | // region:deref | ||
60 | mod deref { | ||
61 | #[lang = "deref"] | ||
62 | pub trait Deref { | ||
63 | #[lang = "deref_target"] | ||
64 | type Target: ?Sized; | ||
65 | fn deref(&self) -> &Self::Target; | ||
66 | } | ||
67 | } | ||
68 | pub use self::deref::Deref; | ||
69 | // endregion:deref | ||
70 | |||
71 | // region:range | ||
72 | mod range { | ||
73 | #[lang = "RangeFull"] | ||
74 | pub struct RangeFull; | ||
75 | |||
76 | #[lang = "Range"] | ||
77 | pub struct Range<Idx> { | ||
78 | pub start: Idx, | ||
79 | pub end: Idx, | ||
80 | } | ||
81 | |||
82 | #[lang = "RangeFrom"] | ||
83 | pub struct RangeFrom<Idx> { | ||
84 | pub start: Idx, | ||
85 | } | ||
86 | |||
87 | #[lang = "RangeTo"] | ||
88 | pub struct RangeTo<Idx> { | ||
89 | pub end: Idx, | ||
90 | } | ||
91 | |||
92 | #[lang = "RangeInclusive"] | ||
93 | pub struct RangeInclusive<Idx> { | ||
94 | pub(crate) start: Idx, | ||
95 | pub(crate) end: Idx, | ||
96 | pub(crate) exhausted: bool, | ||
97 | } | ||
98 | |||
99 | #[lang = "RangeToInclusive"] | ||
100 | pub struct RangeToInclusive<Idx> { | ||
101 | pub end: Idx, | ||
102 | } | ||
103 | } | ||
104 | pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; | ||
105 | pub use self::range::{RangeInclusive, RangeToInclusive}; | ||
106 | // endregion:range | ||
107 | } | ||
108 | |||
109 | // region:slice | ||
110 | pub mod slice { | ||
111 | #[lang = "slice"] | ||
112 | impl<T> [T] { | ||
113 | pub fn len(&self) -> usize { | ||
114 | loop {} | ||
115 | } | ||
116 | } | ||
117 | } | ||
118 | // endregion:slice | ||
119 | |||
120 | // region:option | ||
121 | pub mod option { | ||
122 | pub enum Option<T> { | ||
123 | #[lang = "None"] | ||
124 | None, | ||
125 | #[lang = "Some"] | ||
126 | Some(T), | ||
127 | } | ||
128 | } | ||
129 | // endregion:option | ||
130 | |||
131 | // region:result | ||
132 | pub mod result { | ||
133 | pub enum Result<T, E> { | ||
134 | #[lang = "Ok"] | ||
135 | Ok(T), | ||
136 | #[lang = "Err"] | ||
137 | Err(E), | ||
138 | } | ||
139 | } | ||
140 | // endregion:result | ||
141 | |||
142 | // region:pin | ||
143 | pub mod pin { | ||
144 | #[lang = "pin"] | ||
145 | #[fundamental] | ||
146 | pub struct Pin<P> { | ||
147 | pointer: P, | ||
148 | } | ||
149 | } | ||
150 | // endregion:pin | ||
151 | |||
152 | // region:future | ||
153 | pub mod future { | ||
154 | use crate::{ | ||
155 | pin::Pin, | ||
156 | task::{Context, Poll}, | ||
157 | }; | ||
158 | |||
159 | #[lang = "future_trait"] | ||
160 | pub trait Future { | ||
161 | type Output; | ||
162 | #[lang = "poll"] | ||
163 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; | ||
164 | } | ||
165 | } | ||
166 | pub mod task { | ||
167 | pub enum Poll<T> { | ||
168 | #[lang = "Ready"] | ||
169 | Ready(T), | ||
170 | #[lang = "Pending"] | ||
171 | Pending, | ||
172 | } | ||
173 | |||
174 | pub struct Context<'a> { | ||
175 | waker: &'a (), | ||
176 | } | ||
177 | } | ||
178 | // endregion:future | ||
179 | |||
180 | pub mod prelude { | ||
181 | pub mod v1 { | ||
182 | pub use crate::{ | ||
183 | marker::Sized, // :sized | ||
184 | option::Option::{self, None, Some}, // :option | ||
185 | result::Result::{self, Err, Ok}, // :result | ||
186 | }; | ||
187 | } | ||
188 | |||
189 | pub mod rust_2015 { | ||
190 | pub use super::v1::*; | ||
191 | } | ||
192 | |||
193 | pub mod rust_2018 { | ||
194 | pub use super::v1::*; | ||
195 | } | ||
196 | |||
197 | pub mod rust_2021 { | ||
198 | pub use super::v1::*; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | #[prelude_import] | ||
203 | #[allow(unused)] | ||
204 | use prelude::v1::*; | ||