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