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