aboutsummaryrefslogtreecommitdiff
path: root/crates/test_utils
diff options
context:
space:
mode:
Diffstat (limited to 'crates/test_utils')
-rw-r--r--crates/test_utils/src/fixture.rs168
-rw-r--r--crates/test_utils/src/lib.rs5
-rw-r--r--crates/test_utils/src/minicore.rs204
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
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,133 @@ 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 #[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]
177fn parse_fixture_checks_further_indented_metadata() { 325fn parse_fixture_checks_further_indented_metadata() {
@@ -189,12 +337,14 @@ fn parse_fixture_checks_further_indented_metadata() {
189 337
190#[test] 338#[test]
191fn parse_fixture_gets_full_meta() { 339fn 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 ", 344mod 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};
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..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
22pub 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
36pub 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
110pub 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
121pub 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
132pub 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
143pub mod pin {
144 #[lang = "pin"]
145 #[fundamental]
146 pub struct Pin<P> {
147 pointer: P,
148 }
149}
150// endregion:pin
151
152// region:future
153pub 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}
166pub 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
180pub 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)]
204use prelude::v1::*;