]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/tests/heavy_tests/main.rs
Merge #4710
[rust.git] / crates / rust-analyzer / tests / heavy_tests / main.rs
1 mod support;
2
3 use std::{collections::HashMap, path::PathBuf, time::Instant};
4
5 use lsp_types::{
6     notification::DidOpenTextDocument,
7     request::{
8         CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest,
9     },
10     CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
11     DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
12     PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
13     WorkDoneProgressParams,
14 };
15 use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
16 use serde_json::json;
17 use tempfile::TempDir;
18 use test_utils::skip_slow_tests;
19
20 use crate::support::{project, Project};
21
22 const PROFILE: &str = "";
23 // const PROFILE: &'static str = "*@3>100";
24
25 #[test]
26 fn completes_items_from_standard_library() {
27     if skip_slow_tests() {
28         return;
29     }
30
31     let project_start = Instant::now();
32     let server = Project::with_fixture(
33         r#"
34 //- Cargo.toml
35 [package]
36 name = "foo"
37 version = "0.0.0"
38
39 //- src/lib.rs
40 use std::collections::Spam;
41 "#,
42     )
43     .with_sysroot(true)
44     .server();
45     server.wait_until_workspace_is_loaded();
46     eprintln!("loading took    {:?}", project_start.elapsed());
47     let completion_start = Instant::now();
48     let res = server.send_request::<Completion>(CompletionParams {
49         text_document_position: TextDocumentPositionParams::new(
50             server.doc_id("src/lib.rs"),
51             Position::new(0, 23),
52         ),
53         context: None,
54         partial_result_params: PartialResultParams::default(),
55         work_done_progress_params: WorkDoneProgressParams::default(),
56     });
57     assert!(format!("{}", res).contains("HashMap"));
58     eprintln!("completion took {:?}", completion_start.elapsed());
59 }
60
61 #[test]
62 fn test_runnables_no_project() {
63     if skip_slow_tests() {
64         return;
65     }
66
67     let server = project(
68         r"
69 //- lib.rs
70 #[test]
71 fn foo() {
72 }
73 ",
74     );
75     server.wait_until_workspace_is_loaded();
76     server.request::<Runnables>(
77         RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
78         json!([
79             {
80               "args": {
81                 "cargoArgs": ["test"],
82                 "executableArgs": ["foo", "--nocapture"],
83               },
84               "kind": "cargo",
85               "label": "test foo",
86               "location": {
87                 "targetRange": {
88                   "end": { "character": 1, "line": 2 },
89                   "start": { "character": 0, "line": 0 }
90                 },
91                 "targetSelectionRange": {
92                   "end": { "character": 6, "line": 1 },
93                   "start": { "character": 3, "line": 1 }
94                 },
95                 "targetUri": "file:///[..]/lib.rs"
96               }
97             },
98             {
99               "args": {
100                 "cargoArgs": ["check", "--workspace"],
101                 "executableArgs": [],
102               },
103               "kind": "cargo",
104               "label": "cargo check --workspace"
105             }
106         ]),
107     );
108 }
109
110 #[test]
111 fn test_runnables_project() {
112     if skip_slow_tests() {
113         return;
114     }
115
116     let code = r#"
117 //- foo/Cargo.toml
118 [package]
119 name = "foo"
120 version = "0.0.0"
121
122 //- foo/src/lib.rs
123 pub fn foo() {}
124
125 //- foo/tests/spam.rs
126 #[test]
127 fn test_eggs() {}
128
129 //- bar/Cargo.toml
130 [package]
131 name = "bar"
132 version = "0.0.0"
133
134 //- bar/src/main.rs
135 fn main() {}
136 "#;
137
138     let server = Project::with_fixture(code).root("foo").root("bar").server();
139
140     server.wait_until_workspace_is_loaded();
141     server.request::<Runnables>(
142         RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
143         json!([
144           {
145             "args": {
146               "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
147               "executableArgs": ["test_eggs", "--exact", "--nocapture"],
148               "workspaceRoot": server.path().join("foo")
149             },
150             "kind": "cargo",
151             "label": "test test_eggs",
152             "location": {
153               "targetRange": {
154                 "end": { "character": 17, "line": 1 },
155                 "start": { "character": 0, "line": 0 }
156               },
157               "targetSelectionRange": {
158                 "end": { "character": 12, "line": 1 },
159                 "start": { "character": 3, "line": 1 }
160               },
161               "targetUri": "file:///[..]/tests/spam.rs"
162             }
163           },
164           {
165             "args": {
166               "cargoArgs": ["check", "--package", "foo"],
167               "executableArgs": [],
168               "workspaceRoot": server.path().join("foo")
169             },
170             "kind": "cargo",
171             "label": "cargo check -p foo"
172           },
173           {
174             "args": {
175               "cargoArgs": ["test", "--package", "foo"],
176               "executableArgs": [],
177               "workspaceRoot": server.path().join("foo")
178             },
179             "kind": "cargo",
180             "label": "cargo test -p foo"
181           }
182         ]),
183     );
184 }
185
186 #[test]
187 fn test_format_document() {
188     if skip_slow_tests() {
189         return;
190     }
191
192     let server = project(
193         r#"
194 //- Cargo.toml
195 [package]
196 name = "foo"
197 version = "0.0.0"
198
199 //- src/lib.rs
200 mod bar;
201
202 fn main() {
203 }
204
205 pub use std::collections::HashMap;
206 "#,
207     );
208     server.wait_until_workspace_is_loaded();
209
210     server.request::<Formatting>(
211         DocumentFormattingParams {
212             text_document: server.doc_id("src/lib.rs"),
213             options: FormattingOptions {
214                 tab_size: 4,
215                 insert_spaces: false,
216                 insert_final_newline: None,
217                 trim_final_newlines: None,
218                 trim_trailing_whitespace: None,
219                 properties: HashMap::new(),
220             },
221             work_done_progress_params: WorkDoneProgressParams::default(),
222         },
223         json!([
224             {
225                 "newText": r#"mod bar;
226
227 fn main() {}
228
229 pub use std::collections::HashMap;
230 "#,
231                 "range": {
232                     "end": {
233                         "character": 0,
234                         "line": 7
235                     },
236                     "start": {
237                         "character": 0,
238                         "line": 0
239                     }
240                 }
241             }
242         ]),
243     );
244 }
245
246 #[test]
247 fn test_format_document_2018() {
248     if skip_slow_tests() {
249         return;
250     }
251
252     let server = project(
253         r#"
254 //- Cargo.toml
255 [package]
256 name = "foo"
257 version = "0.0.0"
258 edition = "2018"
259
260 //- src/lib.rs
261 mod bar;
262
263 async fn test() {
264 }
265
266 fn main() {
267 }
268
269 pub use std::collections::HashMap;
270 "#,
271     );
272     server.wait_until_workspace_is_loaded();
273
274     server.request::<Formatting>(
275         DocumentFormattingParams {
276             text_document: server.doc_id("src/lib.rs"),
277             options: FormattingOptions {
278                 tab_size: 4,
279                 insert_spaces: false,
280                 properties: HashMap::new(),
281                 insert_final_newline: None,
282                 trim_final_newlines: None,
283                 trim_trailing_whitespace: None,
284             },
285             work_done_progress_params: WorkDoneProgressParams::default(),
286         },
287         json!([
288             {
289                 "newText": r#"mod bar;
290
291 async fn test() {}
292
293 fn main() {}
294
295 pub use std::collections::HashMap;
296 "#,
297                 "range": {
298                     "end": {
299                         "character": 0,
300                         "line": 10
301                     },
302                     "start": {
303                         "character": 0,
304                         "line": 0
305                     }
306                 }
307             }
308         ]),
309     );
310 }
311
312 #[test]
313 fn test_missing_module_code_action() {
314     if skip_slow_tests() {
315         return;
316     }
317
318     let server = project(
319         r#"
320 //- Cargo.toml
321 [package]
322 name = "foo"
323 version = "0.0.0"
324
325 //- src/lib.rs
326 mod bar;
327
328 fn main() {}
329 "#,
330     );
331     server.wait_until_workspace_is_loaded();
332     let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
333     server.request::<CodeActionRequest>(
334         CodeActionParams {
335             text_document: server.doc_id("src/lib.rs"),
336             range: Range::new(Position::new(0, 4), Position::new(0, 7)),
337             context: empty_context(),
338             partial_result_params: PartialResultParams::default(),
339             work_done_progress_params: WorkDoneProgressParams::default(),
340         },
341         json!([{
342             "edit": {
343               "documentChanges": [
344                 {
345                   "kind": "create",
346                   "uri": "file:///[..]/src/bar.rs"
347                 }
348               ]
349             },
350             "title": "Create module"
351         }]),
352     );
353
354     server.request::<CodeActionRequest>(
355         CodeActionParams {
356             text_document: server.doc_id("src/lib.rs"),
357             range: Range::new(Position::new(2, 4), Position::new(2, 7)),
358             context: empty_context(),
359             partial_result_params: PartialResultParams::default(),
360             work_done_progress_params: WorkDoneProgressParams::default(),
361         },
362         json!([]),
363     );
364 }
365
366 #[test]
367 fn test_missing_module_code_action_in_json_project() {
368     if skip_slow_tests() {
369         return;
370     }
371
372     let tmp_dir = TempDir::new().unwrap();
373
374     let path = tmp_dir.path();
375
376     let project = json!({
377         "roots": [path],
378         "crates": [ {
379             "root_module": path.join("src/lib.rs"),
380             "deps": [],
381             "edition": "2015",
382             "atom_cfgs": [],
383             "key_value_cfgs": {}
384         } ]
385     });
386
387     let code = format!(
388         r#"
389 //- rust-project.json
390 {PROJECT}
391
392 //- src/lib.rs
393 mod bar;
394
395 fn main() {{}}
396 "#,
397         PROJECT = project.to_string(),
398     );
399
400     let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
401
402     server.wait_until_workspace_is_loaded();
403     let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
404     server.request::<CodeActionRequest>(
405         CodeActionParams {
406             text_document: server.doc_id("src/lib.rs"),
407             range: Range::new(Position::new(0, 4), Position::new(0, 7)),
408             context: empty_context(),
409             partial_result_params: PartialResultParams::default(),
410             work_done_progress_params: WorkDoneProgressParams::default(),
411         },
412         json!([{
413             "edit": {
414               "documentChanges": [
415                 {
416                   "kind": "create",
417                   "uri": "file://[..]/src/bar.rs"
418                 }
419               ]
420             },
421             "title": "Create module"
422         }]),
423     );
424
425     server.request::<CodeActionRequest>(
426         CodeActionParams {
427             text_document: server.doc_id("src/lib.rs"),
428             range: Range::new(Position::new(2, 4), Position::new(2, 7)),
429             context: empty_context(),
430             partial_result_params: PartialResultParams::default(),
431             work_done_progress_params: WorkDoneProgressParams::default(),
432         },
433         json!([]),
434     );
435 }
436
437 #[test]
438 fn diagnostics_dont_block_typing() {
439     if skip_slow_tests() {
440         return;
441     }
442
443     let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
444     let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
445     let server = Project::with_fixture(&format!(
446         r#"
447 //- Cargo.toml
448 [package]
449 name = "foo"
450 version = "0.0.0"
451
452 //- src/lib.rs
453 {}
454
455 {}
456
457 fn main() {{}}
458 "#,
459         librs, libs
460     ))
461     .with_sysroot(true)
462     .server();
463
464     server.wait_until_workspace_is_loaded();
465     for i in 0..10 {
466         server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
467             text_document: TextDocumentItem {
468                 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
469                 language_id: "rust".to_string(),
470                 version: 0,
471                 text: "/// Docs\nfn foo() {}".to_string(),
472             },
473         });
474     }
475     let start = std::time::Instant::now();
476     server.request::<OnEnter>(
477         TextDocumentPositionParams {
478             text_document: server.doc_id("src/m0.rs"),
479             position: Position { line: 0, character: 5 },
480         },
481         json!([{
482             "insertTextFormat": 2,
483             "newText": "\n/// $0",
484             "range": {
485             "end": { "character": 5, "line": 0 },
486             "start": { "character": 5, "line": 0 }
487             }
488         }]),
489     );
490     let elapsed = start.elapsed();
491     assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
492 }
493
494 #[test]
495 fn preserves_dos_line_endings() {
496     if skip_slow_tests() {
497         return;
498     }
499
500     let server = Project::with_fixture(
501         &"
502 //- Cargo.toml
503 [package]
504 name = \"foo\"
505 version = \"0.0.0\"
506
507 //- src/main.rs
508 /// Some Docs\r\nfn main() {}
509 ",
510     )
511     .server();
512
513     server.request::<OnEnter>(
514         TextDocumentPositionParams {
515             text_document: server.doc_id("src/main.rs"),
516             position: Position { line: 0, character: 8 },
517         },
518         json!([{
519             "insertTextFormat": 2,
520             "newText": "\r\n/// $0",
521             "range": {
522             "end": { "line": 0, "character": 8 },
523             "start": { "line": 0, "character": 8 }
524             }
525         }]),
526     );
527 }
528
529 #[test]
530 fn out_dirs_check() {
531     if skip_slow_tests() {
532         return;
533     }
534
535     let server = Project::with_fixture(
536         r###"
537 //- Cargo.toml
538 [package]
539 name = "foo"
540 version = "0.0.0"
541
542 //- build.rs
543 use std::{env, fs, path::Path};
544
545 fn main() {
546     let out_dir = env::var_os("OUT_DIR").unwrap();
547     let dest_path = Path::new(&out_dir).join("hello.rs");
548     fs::write(
549         &dest_path,
550         r#"pub fn message() -> &'static str { "Hello, World!" }"#,
551     )
552     .unwrap();
553     println!("cargo:rustc-cfg=atom_cfg");
554     println!("cargo:rustc-cfg=featlike=\"set\"");
555     println!("cargo:rerun-if-changed=build.rs");
556 }
557 //- src/main.rs
558 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
559
560 #[cfg(atom_cfg)]
561 struct A;
562 #[cfg(bad_atom_cfg)]
563 struct A;
564 #[cfg(featlike = "set")]
565 struct B;
566 #[cfg(featlike = "not_set")]
567 struct B;
568
569 fn main() {
570     let va = A;
571     let vb = B;
572     message();
573 }
574
575 fn main() { message(); }
576 "###,
577     )
578     .with_config(|config| {
579         config.cargo.load_out_dirs_from_check = true;
580     })
581     .server();
582     server.wait_until_workspace_is_loaded();
583     let res = server.send_request::<GotoDefinition>(GotoDefinitionParams {
584         text_document_position_params: TextDocumentPositionParams::new(
585             server.doc_id("src/main.rs"),
586             Position::new(14, 8),
587         ),
588         work_done_progress_params: Default::default(),
589         partial_result_params: Default::default(),
590     });
591     assert!(format!("{}", res).contains("hello.rs"));
592     server.request::<GotoTypeDefinition>(
593         GotoDefinitionParams {
594             text_document_position_params: TextDocumentPositionParams::new(
595                 server.doc_id("src/main.rs"),
596                 Position::new(12, 9),
597             ),
598             work_done_progress_params: Default::default(),
599             partial_result_params: Default::default(),
600         },
601         json!([{
602             "originSelectionRange": {
603                 "end": {
604                     "character": 10,
605                     "line": 12
606                 },
607                 "start": {
608                     "character": 8,
609                     "line": 12
610                 }
611             },
612             "targetRange": {
613                 "end": {
614                     "character": 9,
615                     "line": 3
616                 },
617                 "start": {
618                     "character": 0,
619                     "line": 2
620                 }
621             },
622             "targetSelectionRange": {
623                 "end": {
624                     "character": 8,
625                     "line": 3
626                 },
627                 "start": {
628                     "character": 7,
629                     "line": 3
630                 }
631             },
632             "targetUri": "file:///[..]src/main.rs"
633         }]),
634     );
635     server.request::<GotoTypeDefinition>(
636         GotoDefinitionParams {
637             text_document_position_params: TextDocumentPositionParams::new(
638                 server.doc_id("src/main.rs"),
639                 Position::new(13, 9),
640             ),
641             work_done_progress_params: Default::default(),
642             partial_result_params: Default::default(),
643         },
644         json!([{
645             "originSelectionRange": {
646                 "end": {
647                     "character": 10,
648                     "line": 13
649                 },
650                 "start": {
651                     "character": 8,
652                     "line":13
653                 }
654             },
655             "targetRange": {
656                 "end": {
657                     "character": 9,
658                     "line": 7
659                 },
660                 "start": {
661                     "character": 0,
662                     "line":6
663                 }
664             },
665             "targetSelectionRange": {
666                 "end": {
667                     "character": 8,
668                     "line": 7
669                 },
670                 "start": {
671                     "character": 7,
672                     "line": 7
673                 }
674             },
675             "targetUri": "file:///[..]src/main.rs"
676         }]),
677     );
678 }
679
680 #[test]
681 fn resolve_proc_macro() {
682     if skip_slow_tests() {
683         return;
684     }
685     let server = Project::with_fixture(
686         r###"
687 //- foo/Cargo.toml
688 [package]
689 name = "foo"
690 version = "0.0.0"
691 edition = "2018"
692 [dependencies]
693 bar = {path = "../bar"}
694
695 //- foo/src/main.rs
696 use bar::Bar;
697 trait Bar {
698   fn bar();
699 }
700 #[derive(Bar)]
701 struct Foo {}
702 fn main() {
703   Foo::bar();
704 }
705
706 //- bar/Cargo.toml
707 [package]
708 name = "bar"
709 version = "0.0.0"
710 edition = "2018"
711
712 [lib]
713 proc-macro = true
714
715 //- bar/src/lib.rs
716 extern crate proc_macro;
717 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
718 macro_rules! t {
719     ($n:literal) => {
720         TokenTree::from(Ident::new($n, Span::call_site()))
721     };
722     ({}) => {
723         TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
724     };
725     (()) => {
726         TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
727     };
728 }
729 #[proc_macro_derive(Bar)]
730 pub fn foo(_input: TokenStream) -> TokenStream {
731     // We hard code the output here for preventing to use any deps
732     let mut res = TokenStream::new();
733
734     // impl Bar for Foo { fn bar() {} }
735     let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
736     let mut fn_stream = TokenStream::new();
737     fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
738     tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
739     res.extend(tokens);
740     res
741 }
742
743 "###,
744     )
745     .with_config(|config| {
746         let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
747
748         config.cargo.load_out_dirs_from_check = true;
749         config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
750     })
751     .root("foo")
752     .root("bar")
753     .server();
754     server.wait_until_workspace_is_loaded();
755     let res = server.send_request::<HoverRequest>(HoverParams {
756         text_document_position_params: TextDocumentPositionParams::new(
757             server.doc_id("foo/src/main.rs"),
758             Position::new(7, 9),
759         ),
760         work_done_progress_params: Default::default(),
761     });
762
763     let value = res.get("contents").unwrap().get("value").unwrap().to_string();
764     assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
765 }