]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/syntax_highlighting/tests.rs
Code blocks with tilde also works like code block
[rust.git] / crates / ide / src / syntax_highlighting / tests.rs
1 use std::time::Instant;
2
3 use expect_test::{expect_file, ExpectFile};
4 use ide_db::SymbolKind;
5 use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
6
7 use crate::{fixture, FileRange, HlTag, TextRange};
8
9 #[test]
10 fn attributes() {
11     check_highlighting(
12         r#"
13 //- proc_macros: identity
14 //- minicore: derive, copy
15 #[allow(dead_code)]
16 #[rustfmt::skip]
17 #[proc_macros::identity]
18 #[derive(Copy)]
19 /// This is a doc comment
20 // This is a normal comment
21 /// This is a doc comment
22 #[derive(Copy)]
23 // This is another normal comment
24 /// This is another doc comment
25 // This is another normal comment
26 #[derive(Copy)]
27 // The reason for these being here is to test AttrIds
28 struct Foo;
29 "#,
30         expect_file!["./test_data/highlight_attributes.html"],
31         false,
32     );
33 }
34 #[test]
35 fn macros() {
36     check_highlighting(
37         r#"
38 //- proc_macros: mirror
39 proc_macros::mirror! {
40     {
41         ,i32 :x pub
42         ,i32 :y pub
43     } Foo struct
44 }
45 macro_rules! def_fn {
46     ($($tt:tt)*) => {$($tt)*}
47 }
48
49 def_fn! {
50     fn bar() -> u32 {
51         100
52     }
53 }
54
55 macro_rules! dont_color_me_braces {
56     () => {0}
57 }
58
59 macro_rules! noop {
60     ($expr:expr) => {
61         $expr
62     }
63 }
64
65 macro_rules! keyword_frag {
66     ($type:ty) => ($type)
67 }
68
69 macro with_args($i:ident) {
70     $i
71 }
72
73 macro without_args {
74     ($i:ident) => {
75         $i
76     }
77 }
78
79 fn main() {
80     println!("Hello, {}!", 92);
81     dont_color_me_braces!();
82     noop!(noop!(1));
83 }
84 "#,
85         expect_file!["./test_data/highlight_macros.html"],
86         false,
87     );
88 }
89
90 /// If what you want to test feels like a specific entity consider making a new test instead,
91 /// this test fixture here in fact should shrink instead of grow ideally.
92 #[test]
93 fn test_highlighting() {
94     check_highlighting(
95         r#"
96 //- minicore: derive, copy
97 //- /main.rs crate:main deps:foo
98 use inner::{self as inner_mod};
99 mod inner {}
100
101 pub mod ops {
102     #[lang = "fn_once"]
103     pub trait FnOnce<Args> {}
104
105     #[lang = "fn_mut"]
106     pub trait FnMut<Args>: FnOnce<Args> {}
107
108     #[lang = "fn"]
109     pub trait Fn<Args>: FnMut<Args> {}
110 }
111
112 struct Foo {
113     x: u32,
114 }
115
116 trait Bar {
117     fn bar(&self) -> i32;
118 }
119
120 impl Bar for Foo {
121     fn bar(&self) -> i32 {
122         self.x
123     }
124 }
125
126 impl Foo {
127     fn baz(mut self, f: Foo) -> i32 {
128         f.baz(self)
129     }
130
131     fn qux(&mut self) {
132         self.x = 0;
133     }
134
135     fn quop(&self) -> i32 {
136         self.x
137     }
138 }
139
140 use self::FooCopy::{self as BarCopy};
141
142 #[derive(Copy)]
143 struct FooCopy {
144     x: u32,
145 }
146
147 impl FooCopy {
148     fn baz(self, f: FooCopy) -> u32 {
149         f.baz(self)
150     }
151
152     fn qux(&mut self) {
153         self.x = 0;
154     }
155
156     fn quop(&self) -> u32 {
157         self.x
158     }
159 }
160
161 fn str() {
162     str();
163 }
164
165 fn foo<'a, T>() -> T {
166     foo::<'a, i32>()
167 }
168
169 fn never() -> ! {
170     loop {}
171 }
172
173 fn const_param<const FOO: usize>() -> usize {
174     FOO
175 }
176
177 use ops::Fn;
178 fn baz<F: Fn() -> ()>(f: F) {
179     f()
180 }
181
182 fn foobar() -> impl Copy {}
183
184 fn foo() {
185     let bar = foobar();
186 }
187
188 // comment
189 fn main() {
190     let mut x = 42;
191     x += 1;
192     let y = &mut x;
193     let z = &y;
194
195     let Foo { x: z, y } = Foo { x: z, y };
196
197     y;
198
199     let mut foo = Foo { x, y: x };
200     let foo2 = Foo { x, y: x };
201     foo.quop();
202     foo.qux();
203     foo.baz(foo2);
204
205     let mut copy = FooCopy { x };
206     copy.quop();
207     copy.qux();
208     copy.baz(copy);
209
210     let a = |x| x;
211     let bar = Foo::baz;
212
213     let baz = (-42,);
214     let baz = -baz.0;
215
216     let _ = !true;
217
218     'foo: loop {
219         break 'foo;
220         continue 'foo;
221     }
222 }
223
224 enum Option<T> {
225     Some(T),
226     None,
227 }
228 use Option::*;
229
230 impl<T> Option<T> {
231     fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
232         match other {
233             None => unimplemented!(),
234             Nope => Nope,
235         }
236     }
237 }
238
239 async fn learn_and_sing() {
240     let song = learn_song().await;
241     sing_song(song).await;
242 }
243
244 async fn async_main() {
245     let f1 = learn_and_sing();
246     let f2 = dance();
247     futures::join!(f1, f2);
248 }
249
250 fn use_foo_items() {
251     let bob = foo::Person {
252         name: "Bob",
253         age: foo::consts::NUMBER,
254     };
255
256     let control_flow = foo::identity(foo::ControlFlow::Continue);
257
258     if control_flow.should_die() {
259         foo::die!();
260     }
261 }
262
263 pub enum Bool { True, False }
264
265 impl Bool {
266     pub const fn to_primitive(self) -> bool {
267         true
268     }
269 }
270 const USAGE_OF_BOOL:bool = Bool::True.to_primitive();
271
272 trait Baz {
273     type Qux;
274 }
275
276 fn baz<T>(t: T)
277 where
278     T: Baz,
279     <T as Baz>::Qux: Bar {}
280
281 //- /foo.rs crate:foo
282 pub struct Person {
283     pub name: &'static str,
284     pub age: u8,
285 }
286
287 pub enum ControlFlow {
288     Continue,
289     Die,
290 }
291
292 impl ControlFlow {
293     pub fn should_die(self) -> bool {
294         matches!(self, ControlFlow::Die)
295     }
296 }
297
298 pub fn identity<T>(x: T) -> T { x }
299
300 pub mod consts {
301     pub const NUMBER: i64 = 92;
302 }
303
304 macro_rules! die {
305     () => {
306         panic!();
307     };
308 }
309 "#,
310         expect_file!["./test_data/highlight_general.html"],
311         false,
312     );
313 }
314
315 #[test]
316 fn test_lifetime_highlighting() {
317     check_highlighting(
318         r#"
319 //- minicore: derive
320
321 #[derive()]
322 struct Foo<'a, 'b, 'c> where 'a: 'a, 'static: 'static {
323     field: &'a (),
324     field2: &'static (),
325 }
326 impl<'a> Foo<'_, 'a, 'static>
327 where
328     'a: 'a,
329     'static: 'static
330 {}
331 "#,
332         expect_file!["./test_data/highlight_lifetimes.html"],
333         false,
334     );
335 }
336
337 #[test]
338 fn test_keyword_highlighting() {
339     check_highlighting(
340         r#"
341 extern crate self;
342
343 use crate;
344 use self;
345 mod __ {
346     use super::*;
347 }
348
349 macro_rules! void {
350     ($($tt:tt)*) => {}
351 }
352 void!(Self);
353 struct __ where Self:;
354 fn __(_: Self) {}
355 "#,
356         expect_file!["./test_data/highlight_keywords.html"],
357         false,
358     );
359 }
360
361 #[test]
362 fn test_string_highlighting() {
363     // The format string detection is based on macro-expansion,
364     // thus, we have to copy the macro definition from `std`
365     check_highlighting(
366         r#"
367 macro_rules! println {
368     ($($arg:tt)*) => ({
369         $crate::io::_print($crate::format_args_nl!($($arg)*));
370     })
371 }
372 #[rustc_builtin_macro]
373 #[macro_export]
374 macro_rules! format_args {}
375 #[rustc_builtin_macro]
376 #[macro_export]
377 macro_rules! const_format_args {}
378 #[rustc_builtin_macro]
379 #[macro_export]
380 macro_rules! format_args_nl {}
381
382 mod panic {
383     pub macro panic_2015 {
384         () => (
385             $crate::panicking::panic("explicit panic")
386         ),
387         ($msg:literal $(,)?) => (
388             $crate::panicking::panic($msg)
389         ),
390         // Use `panic_str` instead of `panic_display::<&str>` for non_fmt_panic lint.
391         ($msg:expr $(,)?) => (
392             $crate::panicking::panic_str($msg)
393         ),
394         // Special-case the single-argument case for const_panic.
395         ("{}", $arg:expr $(,)?) => (
396             $crate::panicking::panic_display(&$arg)
397         ),
398         ($fmt:expr, $($arg:tt)+) => (
399             $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+))
400         ),
401     }
402 }
403
404 #[rustc_builtin_macro(std_panic)]
405 #[macro_export]
406 macro_rules! panic {}
407 #[rustc_builtin_macro]
408 macro_rules! assert {}
409 #[rustc_builtin_macro]
410 macro_rules! asm {}
411
412 macro_rules! toho {
413     () => ($crate::panic!("not yet implemented"));
414     ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+)));
415 }
416
417 fn main() {
418     // from https://doc.rust-lang.org/std/fmt/index.html
419     println!("Hello");                 // => "Hello"
420     println!("Hello, {}!", "world");   // => "Hello, world!"
421     println!("The number is {}", 1);   // => "The number is 1"
422     println!("{:?}", (3, 4));          // => "(3, 4)"
423     println!("{value}", value=4);      // => "4"
424     println!("{} {}", 1, 2);           // => "1 2"
425     println!("{:04}", 42);             // => "0042" with leading zerosV
426     println!("{1} {} {0} {}", 1, 2);   // => "2 1 1 2"
427     println!("{argument}", argument = "test");   // => "test"
428     println!("{name} {}", 1, name = 2);          // => "2 1"
429     println!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"
430     println!("{{{}}}", 2);                       // => "{2}"
431     println!("Hello {:5}!", "x");
432     println!("Hello {:1$}!", "x", 5);
433     println!("Hello {1:0$}!", 5, "x");
434     println!("Hello {:width$}!", "x", width = 5);
435     println!("Hello {:<5}!", "x");
436     println!("Hello {:-<5}!", "x");
437     println!("Hello {:^5}!", "x");
438     println!("Hello {:>5}!", "x");
439     println!("Hello {:+}!", 5);
440     println!("{:#x}!", 27);
441     println!("Hello {:05}!", 5);
442     println!("Hello {:05}!", -5);
443     println!("{:#010x}!", 27);
444     println!("Hello {0} is {1:.5}", "x", 0.01);
445     println!("Hello {1} is {2:.0$}", 5, "x", 0.01);
446     println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
447     println!("Hello {} is {:.*}",    "x", 5, 0.01);
448     println!("Hello {} is {2:.*}",   "x", 5, 0.01);
449     println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
450     println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56);
451     println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56");
452     println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56");
453     println!("Hello {{}}");
454     println!("{{ Hello");
455
456     println!(r"Hello, {}!", "world");
457
458     // escape sequences
459     println!("Hello\nWorld");
460     println!("\u{48}\x65\x6C\x6C\x6F World");
461
462     let _ = "\x28\x28\x00\x63\n";
463     let _ = b"\x28\x28\x00\x63\n";
464
465     println!("{\x41}", A = 92);
466     println!("{ничоси}", ничоси = 92);
467
468     println!("{:x?} {} ", thingy, n2);
469     panic!("{}", 0);
470     panic!("more {}", 1);
471     assert!(true, "{}", 1);
472     assert!(true, "{} asdasd", 1);
473     toho!("{}fmt", 0);
474     asm!("mov eax, {0}");
475     format_args!(concat!("{}"), "{}");
476 }"#,
477         expect_file!["./test_data/highlight_strings.html"],
478         false,
479     );
480 }
481
482 #[test]
483 fn test_unsafe_highlighting() {
484     check_highlighting(
485         r#"
486 macro_rules! id {
487     ($($tt:tt)*) => {
488         $($tt)*
489     };
490 }
491 macro_rules! unsafe_deref {
492     () => {
493         *(&() as *const ())
494     };
495 }
496 static mut MUT_GLOBAL: Struct = Struct { field: 0 };
497 static GLOBAL: Struct = Struct { field: 0 };
498 unsafe fn unsafe_fn() {}
499
500 union Union {
501     a: u32,
502     b: f32,
503 }
504
505 struct Struct { field: i32 }
506 impl Struct {
507     unsafe fn unsafe_method(&self) {}
508 }
509
510 #[repr(packed)]
511 struct Packed {
512     a: u16,
513 }
514
515 unsafe trait UnsafeTrait {}
516 unsafe impl UnsafeTrait for Packed {}
517 impl !UnsafeTrait for () {}
518
519 fn unsafe_trait_bound<T: UnsafeTrait>(_: T) {}
520
521 trait DoTheAutoref {
522     fn calls_autoref(&self);
523 }
524
525 impl DoTheAutoref for u16 {
526     fn calls_autoref(&self) {}
527 }
528
529 fn main() {
530     let x = &5 as *const _ as *const usize;
531     let u = Union { b: 0 };
532
533     id! {
534         unsafe { unsafe_deref!() }
535     };
536
537     unsafe {
538         unsafe_deref!();
539         id! { unsafe_deref!() };
540
541         // unsafe fn and method calls
542         unsafe_fn();
543         let b = u.b;
544         match u {
545             Union { b: 0 } => (),
546             Union { a } => (),
547         }
548         Struct { field: 0 }.unsafe_method();
549
550         // unsafe deref
551         *x;
552
553         // unsafe access to a static mut
554         MUT_GLOBAL.field;
555         GLOBAL.field;
556
557         // unsafe ref of packed fields
558         let packed = Packed { a: 0 };
559         let a = &packed.a;
560         let ref a = packed.a;
561         let Packed { ref a } = packed;
562         let Packed { a: ref _a } = packed;
563
564         // unsafe auto ref of packed field
565         packed.a.calls_autoref();
566     }
567 }
568 "#,
569         expect_file!["./test_data/highlight_unsafe.html"],
570         false,
571     );
572 }
573
574 #[test]
575 fn test_highlight_doc_comment() {
576     check_highlighting(
577         r#"
578 //- /main.rs
579 //! This is a module to test doc injection.
580 //! ```
581 //! fn test() {}
582 //! ```
583
584 mod outline_module;
585
586 /// ```
587 /// let _ = "early doctests should not go boom";
588 /// ```
589 struct Foo {
590     bar: bool,
591 }
592
593 /// This is an impl with a code block.
594 ///
595 /// ```
596 /// fn foo() {
597 ///
598 /// }
599 /// ```
600 impl Foo {
601     /// ```
602     /// let _ = "Call me
603     //    KILLER WHALE
604     ///     Ishmael.";
605     /// ```
606     pub const bar: bool = true;
607
608     /// Constructs a new `Foo`.
609     ///
610     /// # Examples
611     ///
612     /// ```
613     /// # #![allow(unused_mut)]
614     /// let mut foo: Foo = Foo::new();
615     /// ```
616     pub const fn new() -> Foo {
617         Foo { bar: true }
618     }
619
620     /// `bar` method on `Foo`.
621     ///
622     /// # Examples
623     ///
624     /// ```
625     /// use x::y;
626     ///
627     /// let foo = Foo::new();
628     ///
629     /// // calls bar on foo
630     /// assert!(foo.bar());
631     ///
632     /// let bar = foo.bar || Foo::bar;
633     ///
634     /// /* multi-line
635     ///        comment */
636     ///
637     /// let multi_line_string = "Foo
638     ///   bar\n
639     ///          ";
640     ///
641     /// ```
642     ///
643     /// ```rust,no_run
644     /// let foobar = Foo::new().bar();
645     /// ```
646     ///
647     /// ~~~rust,no_run
648     /// // code block with tilde.
649     /// let foobar = Foo::new().bar();
650     /// ~~~
651     ///
652     /// ```
653     /// // functions
654     /// fn foo<T, const X: usize>(arg: i32) {
655     ///     let x: T = X;
656     /// }
657     /// ```
658     ///
659     /// ```sh
660     /// echo 1
661     /// ```
662     pub fn foo(&self) -> bool {
663         true
664     }
665 }
666
667 /// [`Foo`](Foo) is a struct
668 /// This function is > [`all_the_links`](all_the_links) <
669 /// [`noop`](noop) is a macro below
670 /// [`Item`] is a struct in the module [`module`]
671 ///
672 /// [`Item`]: module::Item
673 /// [mix_and_match]: ThisShouldntResolve
674 pub fn all_the_links() {}
675
676 pub mod module {
677     pub struct Item;
678 }
679
680 /// ```
681 /// macro_rules! noop { ($expr:expr) => { $expr }}
682 /// noop!(1);
683 /// ```
684 macro_rules! noop {
685     ($expr:expr) => {
686         $expr
687     }
688 }
689
690 /// ```rust
691 /// let _ = example(&[1, 2, 3]);
692 /// ```
693 ///
694 /// ```
695 /// loop {}
696 #[cfg_attr(not(feature = "false"), doc = "loop {}")]
697 #[doc = "loop {}"]
698 /// ```
699 ///
700 #[cfg_attr(feature = "alloc", doc = "```rust")]
701 #[cfg_attr(not(feature = "alloc"), doc = "```ignore")]
702 /// let _ = example(&alloc::vec![1, 2, 3]);
703 /// ```
704 pub fn mix_and_match() {}
705
706 /**
707 It is beyond me why you'd use these when you got ///
708 ```rust
709 let _ = example(&[1, 2, 3]);
710 ```
711 [`block_comments2`] tests these with indentation
712  */
713 pub fn block_comments() {}
714
715 /**
716     Really, I don't get it
717     ```rust
718     let _ = example(&[1, 2, 3]);
719     ```
720     [`block_comments`] tests these without indentation
721 */
722 pub fn block_comments2() {}
723
724 //- /outline_module.rs
725 //! This is an outline module whose purpose is to test that its inline attribute injection does not
726 //! spill into its parent.
727 //! ```
728 //! fn test() {}
729 //! ```
730 "#,
731         expect_file!["./test_data/highlight_doctest.html"],
732         false,
733     );
734 }
735
736 #[test]
737 fn test_extern_crate() {
738     check_highlighting(
739         r#"
740 //- /main.rs crate:main deps:std,alloc
741 extern crate std;
742 extern crate alloc as abc;
743 //- /std/lib.rs crate:std
744 pub struct S;
745 //- /alloc/lib.rs crate:alloc
746 pub struct A
747 "#,
748         expect_file!["./test_data/highlight_extern_crate.html"],
749         false,
750     );
751 }
752
753 #[test]
754 fn test_crate_root() {
755     check_highlighting(
756         r#"
757 //- minicore: iterators
758 //- /main.rs crate:main deps:foo
759 extern crate foo;
760 use core::iter;
761
762 pub const NINETY_TWO: u8 = 92;
763
764 use foo as foooo;
765
766 pub(crate) fn main() {
767     let baz = iter::repeat(92);
768 }
769
770 mod bar {
771     pub(in super) const FORTY_TWO: u8 = 42;
772
773     mod baz {
774         use super::super::NINETY_TWO;
775         use crate::foooo::Point;
776
777         pub(in super::super) const TWENTY_NINE: u8 = 29;
778     }
779 }
780 //- /foo.rs crate:foo
781 struct Point {
782     x: u8,
783     y: u8,
784 }
785
786 mod inner {
787     pub(super) fn swap(p: crate::Point) -> crate::Point {
788         crate::Point { x: p.y, y: p.x }
789     }
790 }
791 "#,
792         expect_file!["./test_data/highlight_crate_root.html"],
793         false,
794     );
795 }
796
797 #[test]
798 fn test_default_library() {
799     check_highlighting(
800         r#"
801 //- minicore: option, iterators
802 use core::iter;
803
804 fn main() {
805     let foo = Some(92);
806     let nums = iter::repeat(foo.unwrap());
807 }
808 "#,
809         expect_file!["./test_data/highlight_default_library.html"],
810         false,
811     );
812 }
813
814 #[test]
815 fn test_associated_function() {
816     check_highlighting(
817         r#"
818 fn not_static() {}
819
820 struct foo {}
821
822 impl foo {
823     pub fn is_static() {}
824     pub fn is_not_static(&self) {}
825 }
826
827 trait t {
828     fn t_is_static() {}
829     fn t_is_not_static(&self) {}
830 }
831
832 impl t for foo {
833     pub fn is_static() {}
834     pub fn is_not_static(&self) {}
835 }
836 "#,
837         expect_file!["./test_data/highlight_assoc_functions.html"],
838         false,
839     )
840 }
841
842 #[test]
843 fn test_injection() {
844     check_highlighting(
845         r##"
846 fn fixture(ra_fixture: &str) {}
847
848 fn main() {
849     fixture(r#"
850 trait Foo {
851     fn foo() {
852         println!("2 + 2 = {}", 4);
853     }
854 }"#
855     );
856     fixture(r"
857 fn foo() {
858     foo(\$0{
859         92
860     }\$0)
861 }"
862     );
863 }
864 "##,
865         expect_file!["./test_data/highlight_injection.html"],
866         false,
867     );
868 }
869
870 #[test]
871 fn test_operators() {
872     check_highlighting(
873         r##"
874 fn main() {
875     1 + 1 - 1 * 1 / 1 % 1 | 1 & 1 ! 1 ^ 1 >> 1 << 1;
876     let mut a = 0;
877     a += 1;
878     a -= 1;
879     a *= 1;
880     a /= 1;
881     a %= 1;
882     a |= 1;
883     a &= 1;
884     a ^= 1;
885     a >>= 1;
886     a <<= 1;
887 }
888 "##,
889         expect_file!["./test_data/highlight_operators.html"],
890         false,
891     );
892 }
893
894 #[test]
895 fn test_rainbow_highlighting() {
896     check_highlighting(
897         r#"
898 fn main() {
899     let hello = "hello";
900     let x = hello.to_string();
901     let y = hello.to_string();
902
903     let x = "other color please!";
904     let y = x.to_string();
905 }
906
907 fn bar() {
908     let mut hello = "hello";
909 }
910 "#,
911         expect_file!["./test_data/highlight_rainbow.html"],
912         true,
913     );
914 }
915
916 #[test]
917 fn test_ranges() {
918     let (analysis, file_id) = fixture::file(
919         r#"
920 #[derive(Clone, Debug)]
921 struct Foo {
922     pub x: i32,
923     pub y: i32,
924 }
925 "#,
926     );
927
928     // The "x"
929     let highlights = &analysis
930         .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
931         .unwrap();
932
933     assert_eq!(&highlights[0].highlight.to_string(), "field.declaration.public");
934 }
935
936 #[test]
937 fn ranges_sorted() {
938     let (analysis, file_id) = fixture::file(
939         r#"
940 #[foo(bar = "bar")]
941 macro_rules! test {}
942 }"#
943         .trim(),
944     );
945     let _ = analysis.highlight(file_id).unwrap();
946 }
947
948 /// Highlights the code given by the `ra_fixture` argument, renders the
949 /// result as HTML, and compares it with the HTML file given as `snapshot`.
950 /// Note that the `snapshot` file is overwritten by the rendered HTML.
951 fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
952     let (analysis, file_id) = fixture::file(ra_fixture.trim());
953     let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
954     expect.assert_eq(actual_html)
955 }
956
957 #[test]
958 fn benchmark_syntax_highlighting_long_struct() {
959     if skip_slow_tests() {
960         return;
961     }
962
963     let fixture = bench_fixture::big_struct();
964     let (analysis, file_id) = fixture::file(&fixture);
965
966     let hash = {
967         let _pt = bench("syntax highlighting long struct");
968         analysis
969             .highlight(file_id)
970             .unwrap()
971             .iter()
972             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
973             .count()
974     };
975     assert_eq!(hash, 2001);
976 }
977
978 #[test]
979 fn syntax_highlighting_not_quadratic() {
980     if skip_slow_tests() {
981         return;
982     }
983
984     let mut al = AssertLinear::default();
985     while al.next_round() {
986         for i in 6..=10 {
987             let n = 1 << i;
988
989             let fixture = bench_fixture::big_struct_n(n);
990             let (analysis, file_id) = fixture::file(&fixture);
991
992             let time = Instant::now();
993
994             let hash = analysis
995                 .highlight(file_id)
996                 .unwrap()
997                 .iter()
998                 .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
999                 .count();
1000             assert!(hash > n as usize);
1001
1002             let elapsed = time.elapsed();
1003             al.sample(n as f64, elapsed.as_millis() as f64);
1004         }
1005     }
1006 }
1007
1008 #[test]
1009 fn benchmark_syntax_highlighting_parser() {
1010     if skip_slow_tests() {
1011         return;
1012     }
1013
1014     let fixture = bench_fixture::glorious_old_parser();
1015     let (analysis, file_id) = fixture::file(&fixture);
1016
1017     let hash = {
1018         let _pt = bench("syntax highlighting parser");
1019         analysis
1020             .highlight(file_id)
1021             .unwrap()
1022             .iter()
1023             .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
1024             .count()
1025     };
1026     assert_eq!(hash, 1609);
1027 }