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