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