]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/tests/heavy_tests/main.rs
Cleanup test
[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             "kind": "quickfix",
302             "title": "Create module"
303         }]),
304     );
305
306     server.request::<CodeActionRequest>(
307         CodeActionParams {
308             text_document: server.doc_id("src/lib.rs"),
309             range: Range::new(Position::new(2, 4), Position::new(2, 7)),
310             context: empty_context(),
311             partial_result_params: PartialResultParams::default(),
312             work_done_progress_params: WorkDoneProgressParams::default(),
313         },
314         json!([]),
315     );
316 }
317
318 #[test]
319 fn test_missing_module_code_action_in_json_project() {
320     if skip_slow_tests() {
321         return;
322     }
323
324     let tmp_dir = TempDir::new().unwrap();
325
326     let path = tmp_dir.path();
327
328     let project = json!({
329         "roots": [path],
330         "crates": [ {
331             "root_module": path.join("src/lib.rs"),
332             "deps": [],
333             "edition": "2015",
334             "cfg": [ "cfg_atom_1", "feature=cfg_1"],
335         } ]
336     });
337
338     let code = format!(
339         r#"
340 //- rust-project.json
341 {PROJECT}
342
343 //- src/lib.rs
344 mod bar;
345
346 fn main() {{}}
347 "#,
348         PROJECT = project.to_string(),
349     );
350
351     let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
352
353     server.wait_until_workspace_is_loaded();
354     let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
355     server.request::<CodeActionRequest>(
356         CodeActionParams {
357             text_document: server.doc_id("src/lib.rs"),
358             range: Range::new(Position::new(0, 4), Position::new(0, 7)),
359             context: empty_context(),
360             partial_result_params: PartialResultParams::default(),
361             work_done_progress_params: WorkDoneProgressParams::default(),
362         },
363         json!([{
364             "edit": {
365               "documentChanges": [
366                 {
367                   "kind": "create",
368                   "uri": "file://[..]/src/bar.rs"
369                 }
370               ]
371             },
372             "kind": "quickfix",
373             "title": "Create module"
374         }]),
375     );
376
377     server.request::<CodeActionRequest>(
378         CodeActionParams {
379             text_document: server.doc_id("src/lib.rs"),
380             range: Range::new(Position::new(2, 4), Position::new(2, 7)),
381             context: empty_context(),
382             partial_result_params: PartialResultParams::default(),
383             work_done_progress_params: WorkDoneProgressParams::default(),
384         },
385         json!([]),
386     );
387 }
388
389 #[test]
390 fn diagnostics_dont_block_typing() {
391     if skip_slow_tests() {
392         return;
393     }
394
395     let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
396     let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
397     let server = Project::with_fixture(&format!(
398         r#"
399 //- Cargo.toml
400 [package]
401 name = "foo"
402 version = "0.0.0"
403
404 //- src/lib.rs
405 {}
406
407 {}
408
409 fn main() {{}}
410 "#,
411         librs, libs
412     ))
413     .with_sysroot(true)
414     .server();
415
416     server.wait_until_workspace_is_loaded();
417     for i in 0..10 {
418         server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
419             text_document: TextDocumentItem {
420                 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
421                 language_id: "rust".to_string(),
422                 version: 0,
423                 text: "/// Docs\nfn foo() {}".to_string(),
424             },
425         });
426     }
427     let start = std::time::Instant::now();
428     server.request::<OnEnter>(
429         TextDocumentPositionParams {
430             text_document: server.doc_id("src/m0.rs"),
431             position: Position { line: 0, character: 5 },
432         },
433         json!([{
434             "insertTextFormat": 2,
435             "newText": "\n/// $0",
436             "range": {
437             "end": { "character": 5, "line": 0 },
438             "start": { "character": 5, "line": 0 }
439             }
440         }]),
441     );
442     let elapsed = start.elapsed();
443     assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
444 }
445
446 #[test]
447 fn preserves_dos_line_endings() {
448     if skip_slow_tests() {
449         return;
450     }
451
452     let server = Project::with_fixture(
453         &"
454 //- Cargo.toml
455 [package]
456 name = \"foo\"
457 version = \"0.0.0\"
458
459 //- src/main.rs
460 /// Some Docs\r\nfn main() {}
461 ",
462     )
463     .server();
464
465     server.request::<OnEnter>(
466         TextDocumentPositionParams {
467             text_document: server.doc_id("src/main.rs"),
468             position: Position { line: 0, character: 8 },
469         },
470         json!([{
471             "insertTextFormat": 2,
472             "newText": "\r\n/// $0",
473             "range": {
474             "end": { "line": 0, "character": 8 },
475             "start": { "line": 0, "character": 8 }
476             }
477         }]),
478     );
479 }
480
481 #[test]
482 fn out_dirs_check() {
483     if skip_slow_tests() {
484         return;
485     }
486
487     let server = Project::with_fixture(
488         r###"
489 //- Cargo.toml
490 [package]
491 name = "foo"
492 version = "0.0.0"
493
494 //- build.rs
495 use std::{env, fs, path::Path};
496
497 fn main() {
498     let out_dir = env::var_os("OUT_DIR").unwrap();
499     let dest_path = Path::new(&out_dir).join("hello.rs");
500     fs::write(
501         &dest_path,
502         r#"pub fn message() -> &'static str { "Hello, World!" }"#,
503     )
504     .unwrap();
505     println!("cargo:rustc-cfg=atom_cfg");
506     println!("cargo:rustc-cfg=featlike=\"set\"");
507     println!("cargo:rerun-if-changed=build.rs");
508 }
509 //- src/main.rs
510 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
511
512 #[cfg(atom_cfg)]
513 struct A;
514 #[cfg(bad_atom_cfg)]
515 struct A;
516 #[cfg(featlike = "set")]
517 struct B;
518 #[cfg(featlike = "not_set")]
519 struct B;
520
521 fn main() {
522     let va = A;
523     let vb = B;
524     message();
525 }
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": { "character": 10, "line": 12 },
554                 "start": { "character": 8, "line": 12 }
555             },
556             "targetRange": {
557                 "end": { "character": 9, "line": 3 },
558                 "start": { "character": 0, "line": 2 }
559             },
560             "targetSelectionRange": {
561                 "end": { "character": 8, "line": 3 },
562                 "start": { "character": 7, "line": 3 }
563             },
564             "targetUri": "file:///[..]src/main.rs"
565         }]),
566     );
567     server.request::<GotoTypeDefinition>(
568         GotoDefinitionParams {
569             text_document_position_params: TextDocumentPositionParams::new(
570                 server.doc_id("src/main.rs"),
571                 Position::new(13, 9),
572             ),
573             work_done_progress_params: Default::default(),
574             partial_result_params: Default::default(),
575         },
576         json!([{
577             "originSelectionRange": {
578                 "end": { "character": 10, "line": 13 },
579                 "start": { "character": 8, "line":13 }
580             },
581             "targetRange": {
582                 "end": { "character": 9, "line": 7 },
583                 "start": { "character": 0, "line":6 }
584             },
585             "targetSelectionRange": {
586                 "end": { "character": 8, "line": 7 },
587                 "start": { "character": 7, "line": 7 }
588             },
589             "targetUri": "file:///[..]src/main.rs"
590         }]),
591     );
592 }
593
594 #[test]
595 fn resolve_proc_macro() {
596     if skip_slow_tests() {
597         return;
598     }
599     let server = Project::with_fixture(
600         r###"
601 //- foo/Cargo.toml
602 [package]
603 name = "foo"
604 version = "0.0.0"
605 edition = "2018"
606 [dependencies]
607 bar = {path = "../bar"}
608
609 //- foo/src/main.rs
610 use bar::Bar;
611 trait Bar {
612   fn bar();
613 }
614 #[derive(Bar)]
615 struct Foo {}
616 fn main() {
617   Foo::bar();
618 }
619
620 //- bar/Cargo.toml
621 [package]
622 name = "bar"
623 version = "0.0.0"
624 edition = "2018"
625
626 [lib]
627 proc-macro = true
628
629 //- bar/src/lib.rs
630 extern crate proc_macro;
631 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
632 macro_rules! t {
633     ($n:literal) => {
634         TokenTree::from(Ident::new($n, Span::call_site()))
635     };
636     ({}) => {
637         TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
638     };
639     (()) => {
640         TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
641     };
642 }
643 #[proc_macro_derive(Bar)]
644 pub fn foo(_input: TokenStream) -> TokenStream {
645     // We hard code the output here for preventing to use any deps
646     let mut res = TokenStream::new();
647
648     // impl Bar for Foo { fn bar() {} }
649     let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
650     let mut fn_stream = TokenStream::new();
651     fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
652     tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
653     res.extend(tokens);
654     res
655 }
656
657 "###,
658     )
659     .with_config(|config| {
660         let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
661
662         config.cargo.load_out_dirs_from_check = true;
663         config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
664     })
665     .root("foo")
666     .root("bar")
667     .server();
668     server.wait_until_workspace_is_loaded();
669     let res = server.send_request::<HoverRequest>(HoverParams {
670         text_document_position_params: TextDocumentPositionParams::new(
671             server.doc_id("foo/src/main.rs"),
672             Position::new(7, 9),
673         ),
674         work_done_progress_params: Default::default(),
675     });
676
677     let value = res.get("contents").unwrap().get("value").unwrap().to_string();
678     assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
679 }