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