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