diff options
Diffstat (limited to 'crates/ide/src/syntax_highlighting/tests.rs')
-rw-r--r-- | crates/ide/src/syntax_highlighting/tests.rs | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs new file mode 100644 index 000000000..1c3fea058 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -0,0 +1,484 @@ | |||
1 | use std::fs; | ||
2 | |||
3 | use expect_test::{expect_file, ExpectFile}; | ||
4 | use test_utils::project_dir; | ||
5 | |||
6 | use crate::{mock_analysis::single_file, FileRange, TextRange}; | ||
7 | |||
8 | #[test] | ||
9 | fn test_highlighting() { | ||
10 | check_highlighting( | ||
11 | r#" | ||
12 | use inner::{self as inner_mod}; | ||
13 | mod inner {} | ||
14 | |||
15 | // Needed for function consuming vs normal | ||
16 | pub mod marker { | ||
17 | #[lang = "copy"] | ||
18 | pub trait Copy {} | ||
19 | } | ||
20 | |||
21 | #[derive(Clone, Debug)] | ||
22 | struct Foo { | ||
23 | pub x: i32, | ||
24 | pub y: i32, | ||
25 | } | ||
26 | |||
27 | trait Bar { | ||
28 | fn bar(&self) -> i32; | ||
29 | } | ||
30 | |||
31 | impl Bar for Foo { | ||
32 | fn bar(&self) -> i32 { | ||
33 | self.x | ||
34 | } | ||
35 | } | ||
36 | |||
37 | impl Foo { | ||
38 | fn baz(mut self) -> i32 { | ||
39 | self.x | ||
40 | } | ||
41 | |||
42 | fn qux(&mut self) { | ||
43 | self.x = 0; | ||
44 | } | ||
45 | |||
46 | fn quop(&self) -> i32 { | ||
47 | self.x | ||
48 | } | ||
49 | } | ||
50 | |||
51 | #[derive(Copy, Clone)] | ||
52 | struct FooCopy { | ||
53 | x: u32, | ||
54 | } | ||
55 | |||
56 | impl FooCopy { | ||
57 | fn baz(self) -> u32 { | ||
58 | self.x | ||
59 | } | ||
60 | |||
61 | fn qux(&mut self) { | ||
62 | self.x = 0; | ||
63 | } | ||
64 | |||
65 | fn quop(&self) -> u32 { | ||
66 | self.x | ||
67 | } | ||
68 | } | ||
69 | |||
70 | static mut STATIC_MUT: i32 = 0; | ||
71 | |||
72 | fn foo<'a, T>() -> T { | ||
73 | foo::<'a, i32>() | ||
74 | } | ||
75 | |||
76 | macro_rules! def_fn { | ||
77 | ($($tt:tt)*) => {$($tt)*} | ||
78 | } | ||
79 | |||
80 | def_fn! { | ||
81 | fn bar() -> u32 { | ||
82 | 100 | ||
83 | } | ||
84 | } | ||
85 | |||
86 | macro_rules! noop { | ||
87 | ($expr:expr) => { | ||
88 | $expr | ||
89 | } | ||
90 | } | ||
91 | |||
92 | // comment | ||
93 | fn main() { | ||
94 | println!("Hello, {}!", 92); | ||
95 | |||
96 | let mut vec = Vec::new(); | ||
97 | if true { | ||
98 | let x = 92; | ||
99 | vec.push(Foo { x, y: 1 }); | ||
100 | } | ||
101 | unsafe { | ||
102 | vec.set_len(0); | ||
103 | STATIC_MUT = 1; | ||
104 | } | ||
105 | |||
106 | for e in vec { | ||
107 | // Do nothing | ||
108 | } | ||
109 | |||
110 | noop!(noop!(1)); | ||
111 | |||
112 | let mut x = 42; | ||
113 | let y = &mut x; | ||
114 | let z = &y; | ||
115 | |||
116 | let Foo { x: z, y } = Foo { x: z, y }; | ||
117 | |||
118 | y; | ||
119 | |||
120 | let mut foo = Foo { x, y: x }; | ||
121 | foo.quop(); | ||
122 | foo.qux(); | ||
123 | foo.baz(); | ||
124 | |||
125 | let mut copy = FooCopy { x }; | ||
126 | copy.quop(); | ||
127 | copy.qux(); | ||
128 | copy.baz(); | ||
129 | } | ||
130 | |||
131 | enum Option<T> { | ||
132 | Some(T), | ||
133 | None, | ||
134 | } | ||
135 | use Option::*; | ||
136 | |||
137 | impl<T> Option<T> { | ||
138 | fn and<U>(self, other: Option<U>) -> Option<(T, U)> { | ||
139 | match other { | ||
140 | None => unimplemented!(), | ||
141 | Nope => Nope, | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | "# | ||
146 | .trim(), | ||
147 | expect_file!["crates/ide/test_data/highlighting.html"], | ||
148 | false, | ||
149 | ); | ||
150 | } | ||
151 | |||
152 | #[test] | ||
153 | fn test_rainbow_highlighting() { | ||
154 | check_highlighting( | ||
155 | r#" | ||
156 | fn main() { | ||
157 | let hello = "hello"; | ||
158 | let x = hello.to_string(); | ||
159 | let y = hello.to_string(); | ||
160 | |||
161 | let x = "other color please!"; | ||
162 | let y = x.to_string(); | ||
163 | } | ||
164 | |||
165 | fn bar() { | ||
166 | let mut hello = "hello"; | ||
167 | } | ||
168 | "# | ||
169 | .trim(), | ||
170 | expect_file!["crates/ide/test_data/rainbow_highlighting.html"], | ||
171 | true, | ||
172 | ); | ||
173 | } | ||
174 | |||
175 | #[test] | ||
176 | fn accidentally_quadratic() { | ||
177 | let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic"); | ||
178 | let src = fs::read_to_string(file).unwrap(); | ||
179 | |||
180 | let (analysis, file_id) = single_file(&src); | ||
181 | |||
182 | // let t = std::time::Instant::now(); | ||
183 | let _ = analysis.highlight(file_id).unwrap(); | ||
184 | // eprintln!("elapsed: {:?}", t.elapsed()); | ||
185 | } | ||
186 | |||
187 | #[test] | ||
188 | fn test_ranges() { | ||
189 | let (analysis, file_id) = single_file( | ||
190 | r#" | ||
191 | #[derive(Clone, Debug)] | ||
192 | struct Foo { | ||
193 | pub x: i32, | ||
194 | pub y: i32, | ||
195 | } | ||
196 | "#, | ||
197 | ); | ||
198 | |||
199 | // The "x" | ||
200 | let highlights = &analysis | ||
201 | .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) }) | ||
202 | .unwrap(); | ||
203 | |||
204 | assert_eq!(&highlights[0].highlight.to_string(), "field.declaration"); | ||
205 | } | ||
206 | |||
207 | #[test] | ||
208 | fn test_flattening() { | ||
209 | check_highlighting( | ||
210 | r##" | ||
211 | fn fixture(ra_fixture: &str) {} | ||
212 | |||
213 | fn main() { | ||
214 | fixture(r#" | ||
215 | trait Foo { | ||
216 | fn foo() { | ||
217 | println!("2 + 2 = {}", 4); | ||
218 | } | ||
219 | }"# | ||
220 | ); | ||
221 | }"## | ||
222 | .trim(), | ||
223 | expect_file!["crates/ide/test_data/highlight_injection.html"], | ||
224 | false, | ||
225 | ); | ||
226 | } | ||
227 | |||
228 | #[test] | ||
229 | fn ranges_sorted() { | ||
230 | let (analysis, file_id) = single_file( | ||
231 | r#" | ||
232 | #[foo(bar = "bar")] | ||
233 | macro_rules! test {} | ||
234 | }"# | ||
235 | .trim(), | ||
236 | ); | ||
237 | let _ = analysis.highlight(file_id).unwrap(); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
241 | fn test_string_highlighting() { | ||
242 | // The format string detection is based on macro-expansion, | ||
243 | // thus, we have to copy the macro definition from `std` | ||
244 | check_highlighting( | ||
245 | r#" | ||
246 | macro_rules! println { | ||
247 | ($($arg:tt)*) => ({ | ||
248 | $crate::io::_print($crate::format_args_nl!($($arg)*)); | ||
249 | }) | ||
250 | } | ||
251 | #[rustc_builtin_macro] | ||
252 | macro_rules! format_args_nl { | ||
253 | ($fmt:expr) => {{ /* compiler built-in */ }}; | ||
254 | ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }}; | ||
255 | } | ||
256 | |||
257 | fn main() { | ||
258 | // from https://doc.rust-lang.org/std/fmt/index.html | ||
259 | println!("Hello"); // => "Hello" | ||
260 | println!("Hello, {}!", "world"); // => "Hello, world!" | ||
261 | println!("The number is {}", 1); // => "The number is 1" | ||
262 | println!("{:?}", (3, 4)); // => "(3, 4)" | ||
263 | println!("{value}", value=4); // => "4" | ||
264 | println!("{} {}", 1, 2); // => "1 2" | ||
265 | println!("{:04}", 42); // => "0042" with leading zerosV | ||
266 | println!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" | ||
267 | println!("{argument}", argument = "test"); // => "test" | ||
268 | println!("{name} {}", 1, name = 2); // => "2 1" | ||
269 | println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" | ||
270 | println!("{{{}}}", 2); // => "{2}" | ||
271 | println!("Hello {:5}!", "x"); | ||
272 | println!("Hello {:1$}!", "x", 5); | ||
273 | println!("Hello {1:0$}!", 5, "x"); | ||
274 | println!("Hello {:width$}!", "x", width = 5); | ||
275 | println!("Hello {:<5}!", "x"); | ||
276 | println!("Hello {:-<5}!", "x"); | ||
277 | println!("Hello {:^5}!", "x"); | ||
278 | println!("Hello {:>5}!", "x"); | ||
279 | println!("Hello {:+}!", 5); | ||
280 | println!("{:#x}!", 27); | ||
281 | println!("Hello {:05}!", 5); | ||
282 | println!("Hello {:05}!", -5); | ||
283 | println!("{:#010x}!", 27); | ||
284 | println!("Hello {0} is {1:.5}", "x", 0.01); | ||
285 | println!("Hello {1} is {2:.0$}", 5, "x", 0.01); | ||
286 | println!("Hello {0} is {2:.1$}", "x", 5, 0.01); | ||
287 | println!("Hello {} is {:.*}", "x", 5, 0.01); | ||
288 | println!("Hello {} is {2:.*}", "x", 5, 0.01); | ||
289 | println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); | ||
290 | println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); | ||
291 | println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); | ||
292 | println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); | ||
293 | println!("Hello {{}}"); | ||
294 | println!("{{ Hello"); | ||
295 | |||
296 | println!(r"Hello, {}!", "world"); | ||
297 | |||
298 | // escape sequences | ||
299 | println!("Hello\nWorld"); | ||
300 | println!("\u{48}\x65\x6C\x6C\x6F World"); | ||
301 | |||
302 | println!("{\x41}", A = 92); | ||
303 | println!("{ничоси}", ничоси = 92); | ||
304 | }"# | ||
305 | .trim(), | ||
306 | expect_file!["crates/ide/test_data/highlight_strings.html"], | ||
307 | false, | ||
308 | ); | ||
309 | } | ||
310 | |||
311 | #[test] | ||
312 | fn test_unsafe_highlighting() { | ||
313 | check_highlighting( | ||
314 | r#" | ||
315 | unsafe fn unsafe_fn() {} | ||
316 | |||
317 | union Union { | ||
318 | a: u32, | ||
319 | b: f32, | ||
320 | } | ||
321 | |||
322 | struct HasUnsafeFn; | ||
323 | |||
324 | impl HasUnsafeFn { | ||
325 | unsafe fn unsafe_method(&self) {} | ||
326 | } | ||
327 | |||
328 | struct TypeForStaticMut { | ||
329 | a: u8 | ||
330 | } | ||
331 | |||
332 | static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; | ||
333 | |||
334 | #[repr(packed)] | ||
335 | struct Packed { | ||
336 | a: u16, | ||
337 | } | ||
338 | |||
339 | trait DoTheAutoref { | ||
340 | fn calls_autoref(&self); | ||
341 | } | ||
342 | |||
343 | impl DoTheAutoref for u16 { | ||
344 | fn calls_autoref(&self) {} | ||
345 | } | ||
346 | |||
347 | fn main() { | ||
348 | let x = &5 as *const _ as *const usize; | ||
349 | let u = Union { b: 0 }; | ||
350 | unsafe { | ||
351 | // unsafe fn and method calls | ||
352 | unsafe_fn(); | ||
353 | let b = u.b; | ||
354 | match u { | ||
355 | Union { b: 0 } => (), | ||
356 | Union { a } => (), | ||
357 | } | ||
358 | HasUnsafeFn.unsafe_method(); | ||
359 | |||
360 | // unsafe deref | ||
361 | let y = *x; | ||
362 | |||
363 | // unsafe access to a static mut | ||
364 | let a = global_mut.a; | ||
365 | |||
366 | // unsafe ref of packed fields | ||
367 | let packed = Packed { a: 0 }; | ||
368 | let a = &packed.a; | ||
369 | let ref a = packed.a; | ||
370 | let Packed { ref a } = packed; | ||
371 | let Packed { a: ref _a } = packed; | ||
372 | |||
373 | // unsafe auto ref of packed field | ||
374 | packed.a.calls_autoref(); | ||
375 | } | ||
376 | } | ||
377 | "# | ||
378 | .trim(), | ||
379 | expect_file!["crates/ide/test_data/highlight_unsafe.html"], | ||
380 | false, | ||
381 | ); | ||
382 | } | ||
383 | |||
384 | #[test] | ||
385 | fn test_highlight_doctest() { | ||
386 | check_highlighting( | ||
387 | r#" | ||
388 | /// ``` | ||
389 | /// let _ = "early doctests should not go boom"; | ||
390 | /// ``` | ||
391 | struct Foo { | ||
392 | bar: bool, | ||
393 | } | ||
394 | |||
395 | impl Foo { | ||
396 | pub const bar: bool = true; | ||
397 | |||
398 | /// Constructs a new `Foo`. | ||
399 | /// | ||
400 | /// # Examples | ||
401 | /// | ||
402 | /// ``` | ||
403 | /// # #![allow(unused_mut)] | ||
404 | /// let mut foo: Foo = Foo::new(); | ||
405 | /// ``` | ||
406 | pub const fn new() -> Foo { | ||
407 | Foo { bar: true } | ||
408 | } | ||
409 | |||
410 | /// `bar` method on `Foo`. | ||
411 | /// | ||
412 | /// # Examples | ||
413 | /// | ||
414 | /// ``` | ||
415 | /// use x::y; | ||
416 | /// | ||
417 | /// let foo = Foo::new(); | ||
418 | /// | ||
419 | /// // calls bar on foo | ||
420 | /// assert!(foo.bar()); | ||
421 | /// | ||
422 | /// let bar = foo.bar || Foo::bar; | ||
423 | /// | ||
424 | /// /* multi-line | ||
425 | /// comment */ | ||
426 | /// | ||
427 | /// let multi_line_string = "Foo | ||
428 | /// bar | ||
429 | /// "; | ||
430 | /// | ||
431 | /// ``` | ||
432 | /// | ||
433 | /// ```rust,no_run | ||
434 | /// let foobar = Foo::new().bar(); | ||
435 | /// ``` | ||
436 | /// | ||
437 | /// ```sh | ||
438 | /// echo 1 | ||
439 | /// ``` | ||
440 | pub fn foo(&self) -> bool { | ||
441 | true | ||
442 | } | ||
443 | } | ||
444 | |||
445 | /// ``` | ||
446 | /// noop!(1); | ||
447 | /// ``` | ||
448 | macro_rules! noop { | ||
449 | ($expr:expr) => { | ||
450 | $expr | ||
451 | } | ||
452 | } | ||
453 | "# | ||
454 | .trim(), | ||
455 | expect_file!["crates/ide/test_data/highlight_doctest.html"], | ||
456 | false, | ||
457 | ); | ||
458 | } | ||
459 | |||
460 | #[test] | ||
461 | fn test_extern_crate() { | ||
462 | check_highlighting( | ||
463 | r#" | ||
464 | //- /main.rs | ||
465 | extern crate std; | ||
466 | extern crate alloc as abc; | ||
467 | //- /std/lib.rs | ||
468 | pub struct S; | ||
469 | //- /alloc/lib.rs | ||
470 | pub struct A | ||
471 | "#, | ||
472 | expect_file!["crates/ide/test_data/highlight_extern_crate.html"], | ||
473 | false, | ||
474 | ); | ||
475 | } | ||
476 | |||
477 | /// Highlights the code given by the `ra_fixture` argument, renders the | ||
478 | /// result as HTML, and compares it with the HTML file given as `snapshot`. | ||
479 | /// Note that the `snapshot` file is overwritten by the rendered HTML. | ||
480 | fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { | ||
481 | let (analysis, file_id) = single_file(ra_fixture); | ||
482 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); | ||
483 | expect.assert_eq(actual_html) | ||
484 | } | ||