]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/tests/heavy_tests/main.rs
Remove RelativePathBuf from fixture
[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::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest},
8     CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
9     DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
10     PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
11     WorkDoneProgressParams,
12 };
13 use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
14 use serde_json::json;
15 use tempfile::TempDir;
16 use test_utils::skip_slow_tests;
17
18 use crate::support::{project, Project};
19
20 const PROFILE: &str = "";
21 // const PROFILE: &'static str = "*@3>100";
22
23 #[test]
24 fn completes_items_from_standard_library() {
25     if skip_slow_tests() {
26         return;
27     }
28
29     let project_start = Instant::now();
30     let server = Project::with_fixture(
31         r#"
32 //- /Cargo.toml
33 [package]
34 name = "foo"
35 version = "0.0.0"
36
37 //- /src/lib.rs
38 use std::collections::Spam;
39 "#,
40     )
41     .with_sysroot(true)
42     .server();
43     server.wait_until_workspace_is_loaded();
44     eprintln!("loading took    {:?}", project_start.elapsed());
45     let completion_start = Instant::now();
46     let res = server.send_request::<Completion>(CompletionParams {
47         text_document_position: TextDocumentPositionParams::new(
48             server.doc_id("src/lib.rs"),
49             Position::new(0, 23),
50         ),
51         context: None,
52         partial_result_params: PartialResultParams::default(),
53         work_done_progress_params: WorkDoneProgressParams::default(),
54     });
55     assert!(format!("{}", res).contains("HashMap"));
56     eprintln!("completion took {:?}", completion_start.elapsed());
57 }
58
59 #[test]
60 fn test_runnables_project() {
61     if skip_slow_tests() {
62         return;
63     }
64
65     let code = r#"
66 //- /foo/Cargo.toml
67 [package]
68 name = "foo"
69 version = "0.0.0"
70
71 //- /foo/src/lib.rs
72 pub fn foo() {}
73
74 //- /foo/tests/spam.rs
75 #[test]
76 fn test_eggs() {}
77
78 //- /bar/Cargo.toml
79 [package]
80 name = "bar"
81 version = "0.0.0"
82
83 //- /bar/src/main.rs
84 fn main() {}
85 "#;
86
87     let server = Project::with_fixture(code).root("foo").root("bar").server();
88
89     server.wait_until_workspace_is_loaded();
90     server.request::<Runnables>(
91         RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
92         json!([
93           {
94             "args": {
95               "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
96               "executableArgs": ["test_eggs", "--exact", "--nocapture"],
97               "workspaceRoot": server.path().join("foo")
98             },
99             "kind": "cargo",
100             "label": "test test_eggs",
101             "location": {
102               "targetRange": {
103                 "end": { "character": 17, "line": 1 },
104                 "start": { "character": 0, "line": 0 }
105               },
106               "targetSelectionRange": {
107                 "end": { "character": 12, "line": 1 },
108                 "start": { "character": 3, "line": 1 }
109               },
110               "targetUri": "file:///[..]/tests/spam.rs"
111             }
112           },
113           {
114             "args": {
115               "cargoArgs": ["check", "--package", "foo"],
116               "executableArgs": [],
117               "workspaceRoot": server.path().join("foo")
118             },
119             "kind": "cargo",
120             "label": "cargo check -p foo"
121           },
122           {
123             "args": {
124               "cargoArgs": ["test", "--package", "foo"],
125               "executableArgs": [],
126               "workspaceRoot": server.path().join("foo")
127             },
128             "kind": "cargo",
129             "label": "cargo test -p foo"
130           }
131         ]),
132     );
133 }
134
135 #[test]
136 fn test_format_document() {
137     if skip_slow_tests() {
138         return;
139     }
140
141     let server = project(
142         r#"
143 //- /Cargo.toml
144 [package]
145 name = "foo"
146 version = "0.0.0"
147
148 //- /src/lib.rs
149 mod bar;
150
151 fn main() {
152 }
153
154 pub use std::collections::HashMap;
155 "#,
156     );
157     server.wait_until_workspace_is_loaded();
158
159     server.request::<Formatting>(
160         DocumentFormattingParams {
161             text_document: server.doc_id("src/lib.rs"),
162             options: FormattingOptions {
163                 tab_size: 4,
164                 insert_spaces: false,
165                 insert_final_newline: None,
166                 trim_final_newlines: None,
167                 trim_trailing_whitespace: None,
168                 properties: HashMap::new(),
169             },
170             work_done_progress_params: WorkDoneProgressParams::default(),
171         },
172         json!([
173             {
174                 "newText": r#"mod bar;
175
176 fn main() {}
177
178 pub use std::collections::HashMap;
179 "#,
180                 "range": {
181                     "end": {
182                         "character": 0,
183                         "line": 7
184                     },
185                     "start": {
186                         "character": 0,
187                         "line": 0
188                     }
189                 }
190             }
191         ]),
192     );
193 }
194
195 #[test]
196 fn test_format_document_2018() {
197     if skip_slow_tests() {
198         return;
199     }
200
201     let server = project(
202         r#"
203 //- /Cargo.toml
204 [package]
205 name = "foo"
206 version = "0.0.0"
207 edition = "2018"
208
209 //- /src/lib.rs
210 mod bar;
211
212 async fn test() {
213 }
214
215 fn main() {
216 }
217
218 pub use std::collections::HashMap;
219 "#,
220     );
221     server.wait_until_workspace_is_loaded();
222
223     server.request::<Formatting>(
224         DocumentFormattingParams {
225             text_document: server.doc_id("src/lib.rs"),
226             options: FormattingOptions {
227                 tab_size: 4,
228                 insert_spaces: false,
229                 properties: HashMap::new(),
230                 insert_final_newline: None,
231                 trim_final_newlines: None,
232                 trim_trailing_whitespace: None,
233             },
234             work_done_progress_params: WorkDoneProgressParams::default(),
235         },
236         json!([
237             {
238                 "newText": r#"mod bar;
239
240 async fn test() {}
241
242 fn main() {}
243
244 pub use std::collections::HashMap;
245 "#,
246                 "range": {
247                     "end": {
248                         "character": 0,
249                         "line": 10
250                     },
251                     "start": {
252                         "character": 0,
253                         "line": 0
254                     }
255                 }
256             }
257         ]),
258     );
259 }
260
261 #[test]
262 fn test_missing_module_code_action() {
263     if skip_slow_tests() {
264         return;
265     }
266
267     let server = project(
268         r#"
269 //- /Cargo.toml
270 [package]
271 name = "foo"
272 version = "0.0.0"
273
274 //- /src/lib.rs
275 mod bar;
276
277 fn main() {}
278 "#,
279     );
280     server.wait_until_workspace_is_loaded();
281     let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
282     server.request::<CodeActionRequest>(
283         CodeActionParams {
284             text_document: server.doc_id("src/lib.rs"),
285             range: Range::new(Position::new(0, 4), Position::new(0, 7)),
286             context: empty_context(),
287             partial_result_params: PartialResultParams::default(),
288             work_done_progress_params: WorkDoneProgressParams::default(),
289         },
290         json!([{
291             "edit": {
292               "documentChanges": [
293                 {
294                   "kind": "create",
295                   "uri": "file:///[..]/src/bar.rs"
296                 }
297               ]
298             },
299             "kind": "quickfix",
300             "title": "Create module"
301         }]),
302     );
303
304     server.request::<CodeActionRequest>(
305         CodeActionParams {
306             text_document: server.doc_id("src/lib.rs"),
307             range: Range::new(Position::new(2, 4), Position::new(2, 7)),
308             context: empty_context(),
309             partial_result_params: PartialResultParams::default(),
310             work_done_progress_params: WorkDoneProgressParams::default(),
311         },
312         json!([]),
313     );
314 }
315
316 #[test]
317 fn test_missing_module_code_action_in_json_project() {
318     if skip_slow_tests() {
319         return;
320     }
321
322     let tmp_dir = TempDir::new().unwrap();
323
324     let path = tmp_dir.path();
325
326     let project = json!({
327         "roots": [path],
328         "crates": [ {
329             "root_module": path.join("src/lib.rs"),
330             "deps": [],
331             "edition": "2015",
332             "cfg": [ "cfg_atom_1", "feature=cfg_1"],
333         } ]
334     });
335
336     let code = format!(
337         r#"
338 //- /rust-project.json
339 {PROJECT}
340
341 //- /src/lib.rs
342 mod bar;
343
344 fn main() {{}}
345 "#,
346         PROJECT = project.to_string(),
347     );
348
349     let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
350
351     server.wait_until_workspace_is_loaded();
352     let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
353     server.request::<CodeActionRequest>(
354         CodeActionParams {
355             text_document: server.doc_id("src/lib.rs"),
356             range: Range::new(Position::new(0, 4), Position::new(0, 7)),
357             context: empty_context(),
358             partial_result_params: PartialResultParams::default(),
359             work_done_progress_params: WorkDoneProgressParams::default(),
360         },
361         json!([{
362             "edit": {
363               "documentChanges": [
364                 {
365                   "kind": "create",
366                   "uri": "file://[..]/src/bar.rs"
367                 }
368               ]
369             },
370             "kind": "quickfix",
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 #[rustc_builtin_macro] macro_rules! include {}
509 #[rustc_builtin_macro] macro_rules! concat {}
510 #[rustc_builtin_macro] macro_rules! env {}
511
512 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
513
514 #[cfg(atom_cfg)]
515 struct A;
516 #[cfg(bad_atom_cfg)]
517 struct A;
518 #[cfg(featlike = "set")]
519 struct B;
520 #[cfg(featlike = "not_set")]
521 struct B;
522
523 fn main() {
524     let va = A;
525     let vb = B;
526     let should_be_str = message();
527 }
528 "###,
529     )
530     .with_config(|config| {
531         config.cargo.load_out_dirs_from_check = true;
532     })
533     .server();
534     server.wait_until_workspace_is_loaded();
535     let res = server.send_request::<HoverRequest>(HoverParams {
536         text_document_position_params: TextDocumentPositionParams::new(
537             server.doc_id("src/main.rs"),
538             Position::new(18, 10),
539         ),
540         work_done_progress_params: Default::default(),
541     });
542     assert!(res.to_string().contains("&str"));
543     server.request::<GotoTypeDefinition>(
544         GotoDefinitionParams {
545             text_document_position_params: TextDocumentPositionParams::new(
546                 server.doc_id("src/main.rs"),
547                 Position::new(16, 9),
548             ),
549             work_done_progress_params: Default::default(),
550             partial_result_params: Default::default(),
551         },
552         json!([{
553             "originSelectionRange": {
554                 "end": { "character": 10, "line": 16 },
555                 "start": { "character": 8, "line": 16 }
556             },
557             "targetRange": {
558                 "end": { "character": 9, "line": 7 },
559                 "start": { "character": 0, "line": 6 }
560             },
561             "targetSelectionRange": {
562                 "end": { "character": 8, "line": 7 },
563                 "start": { "character": 7, "line": 7 }
564             },
565             "targetUri": "file:///[..]src/main.rs"
566         }]),
567     );
568     server.request::<GotoTypeDefinition>(
569         GotoDefinitionParams {
570             text_document_position_params: TextDocumentPositionParams::new(
571                 server.doc_id("src/main.rs"),
572                 Position::new(17, 9),
573             ),
574             work_done_progress_params: Default::default(),
575             partial_result_params: Default::default(),
576         },
577         json!([{
578             "originSelectionRange": {
579                 "end": { "character": 10, "line": 17 },
580                 "start": { "character": 8, "line": 17 }
581             },
582             "targetRange": {
583                 "end": { "character": 9, "line": 11 },
584                 "start": { "character": 0, "line":10 }
585             },
586             "targetSelectionRange": {
587                 "end": { "character": 8, "line": 11 },
588                 "start": { "character": 7, "line": 11 }
589             },
590             "targetUri": "file:///[..]src/main.rs"
591         }]),
592     );
593 }
594
595 #[test]
596 fn resolve_proc_macro() {
597     if skip_slow_tests() {
598         return;
599     }
600     let server = Project::with_fixture(
601         r###"
602 //- /foo/Cargo.toml
603 [package]
604 name = "foo"
605 version = "0.0.0"
606 edition = "2018"
607 [dependencies]
608 bar = {path = "../bar"}
609
610 //- /foo/src/main.rs
611 use bar::Bar;
612 trait Bar {
613   fn bar();
614 }
615 #[derive(Bar)]
616 struct Foo {}
617 fn main() {
618   Foo::bar();
619 }
620
621 //- /bar/Cargo.toml
622 [package]
623 name = "bar"
624 version = "0.0.0"
625 edition = "2018"
626
627 [lib]
628 proc-macro = true
629
630 //- /bar/src/lib.rs
631 extern crate proc_macro;
632 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
633 macro_rules! t {
634     ($n:literal) => {
635         TokenTree::from(Ident::new($n, Span::call_site()))
636     };
637     ({}) => {
638         TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
639     };
640     (()) => {
641         TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
642     };
643 }
644 #[proc_macro_derive(Bar)]
645 pub fn foo(_input: TokenStream) -> TokenStream {
646     // We hard code the output here for preventing to use any deps
647     let mut res = TokenStream::new();
648
649     // impl Bar for Foo { fn bar() {} }
650     let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
651     let mut fn_stream = TokenStream::new();
652     fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
653     tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
654     res.extend(tokens);
655     res
656 }
657
658 "###,
659     )
660     .with_config(|config| {
661         let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
662
663         config.cargo.load_out_dirs_from_check = true;
664         config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
665     })
666     .root("foo")
667     .root("bar")
668     .server();
669     server.wait_until_workspace_is_loaded();
670     let res = server.send_request::<HoverRequest>(HoverParams {
671         text_document_position_params: TextDocumentPositionParams::new(
672             server.doc_id("foo/src/main.rs"),
673             Position::new(7, 9),
674         ),
675         work_done_progress_params: Default::default(),
676     });
677
678     let value = res.get("contents").unwrap().get("value").unwrap().to_string();
679     assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
680 }