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