]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / rust-analyzer / tests / slow-tests / main.rs
1 //! The most high-level integrated tests for rust-analyzer.
2 //!
3 //! This tests run a full LSP event loop, spawn cargo and process stdlib from
4 //! sysroot. For this reason, the tests here are very slow, and should be
5 //! avoided unless absolutely necessary.
6 //!
7 //! In particular, it's fine *not* to test that client & server agree on
8 //! specific JSON shapes here -- there's little value in such tests, as we can't
9 //! be sure without a real client anyway.
10
11 #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
12
13 #[cfg(not(feature = "in-rust-tree"))]
14 mod sourcegen;
15 mod support;
16 mod testdir;
17 mod tidy;
18
19 use std::{collections::HashMap, path::PathBuf, time::Instant};
20
21 use lsp_types::{
22 notification::DidOpenTextDocument,
23 request::{
24 CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
25 WillRenameFiles, WorkspaceSymbol,
26 },
27 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
28 DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
29 PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
30 TextDocumentPositionParams, WorkDoneProgressParams,
31 };
32 use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
33 use serde_json::json;
34 use test_utils::skip_slow_tests;
35
36 use crate::{
37 support::{project, Project},
38 testdir::TestDir,
39 };
40
41 const PROFILE: &str = "";
42 // const PROFILE: &'static str = "*@3>100";
43
44 #[test]
45 fn completes_items_from_standard_library() {
46 if skip_slow_tests() {
47 return;
48 }
49
50 let server = Project::with_fixture(
51 r#"
52 //- /Cargo.toml
53 [package]
54 name = "foo"
55 version = "0.0.0"
56
57 //- /src/lib.rs
58 use std::collections::Spam;
59 "#,
60 )
61 .with_config(serde_json::json!({
62 "cargo": { "sysroot": "discover" }
63 }))
64 .server()
65 .wait_until_workspace_is_loaded();
66
67 let res = server.send_request::<Completion>(CompletionParams {
68 text_document_position: TextDocumentPositionParams::new(
69 server.doc_id("src/lib.rs"),
70 Position::new(0, 23),
71 ),
72 context: None,
73 partial_result_params: PartialResultParams::default(),
74 work_done_progress_params: WorkDoneProgressParams::default(),
75 });
76 assert!(res.to_string().contains("HashMap"));
77 }
78
79 #[test]
80 fn test_runnables_project() {
81 if skip_slow_tests() {
82 return;
83 }
84
85 let server = Project::with_fixture(
86 r#"
87 //- /foo/Cargo.toml
88 [package]
89 name = "foo"
90 version = "0.0.0"
91
92 //- /foo/src/lib.rs
93 pub fn foo() {}
94
95 //- /foo/tests/spam.rs
96 #[test]
97 fn test_eggs() {}
98
99 //- /bar/Cargo.toml
100 [package]
101 name = "bar"
102 version = "0.0.0"
103
104 //- /bar/src/main.rs
105 fn main() {}
106 "#,
107 )
108 .root("foo")
109 .root("bar")
110 .server()
111 .wait_until_workspace_is_loaded();
112
113 server.request::<Runnables>(
114 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
115 json!([
116 {
117 "args": {
118 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
119 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
120 "cargoExtraArgs": [],
121 "overrideCargo": null,
122 "workspaceRoot": server.path().join("foo")
123 },
124 "kind": "cargo",
125 "label": "test test_eggs",
126 "location": {
127 "targetRange": {
128 "end": { "character": 17, "line": 1 },
129 "start": { "character": 0, "line": 0 }
130 },
131 "targetSelectionRange": {
132 "end": { "character": 12, "line": 1 },
133 "start": { "character": 3, "line": 1 }
134 },
135 "targetUri": "file:///[..]/tests/spam.rs"
136 }
137 },
138 {
139 "args": {
140 "overrideCargo": null,
141 "workspaceRoot": server.path().join("foo"),
142 "cargoArgs": [
143 "test",
144 "--package",
145 "foo",
146 "--test",
147 "spam"
148 ],
149 "cargoExtraArgs": [],
150 "executableArgs": [
151 "",
152 "--nocapture"
153 ]
154 },
155 "kind": "cargo",
156 "label": "test-mod ",
157 "location": {
158 "targetUri": "file:///[..]/tests/spam.rs",
159 "targetRange": {
160 "start": {
161 "line": 0,
162 "character": 0
163 },
164 "end": {
165 "line": 3,
166 "character": 0
167 }
168 },
169 "targetSelectionRange": {
170 "start": {
171 "line": 0,
172 "character": 0
173 },
174 "end": {
175 "line": 3,
176 "character": 0
177 }
178 }
179 },
180 },
181 {
182 "args": {
183 "cargoArgs": ["check", "--package", "foo", "--all-targets"],
184 "executableArgs": [],
185 "cargoExtraArgs": [],
186 "overrideCargo": null,
187 "workspaceRoot": server.path().join("foo")
188 },
189 "kind": "cargo",
190 "label": "cargo check -p foo --all-targets"
191 },
192 {
193 "args": {
194 "cargoArgs": ["test", "--package", "foo", "--all-targets"],
195 "executableArgs": [],
196 "cargoExtraArgs": [],
197 "overrideCargo": null,
198 "workspaceRoot": server.path().join("foo")
199 },
200 "kind": "cargo",
201 "label": "cargo test -p foo --all-targets"
202 }
203 ]),
204 );
205 }
206
207 // Each package in these workspaces should be run from its own root
208 #[test]
209 fn test_path_dependency_runnables() {
210 if skip_slow_tests() {
211 return;
212 }
213
214 let server = Project::with_fixture(
215 r#"
216 //- /consumer/Cargo.toml
217 [package]
218 name = "consumer"
219 version = "0.1.0"
220 [dependencies]
221 dependency = { path = "../dependency" }
222
223 //- /consumer/src/lib.rs
224 #[cfg(test)]
225 mod tests {
226 #[test]
227 fn consumer() {}
228 }
229
230 //- /dependency/Cargo.toml
231 [package]
232 name = "dependency"
233 version = "0.1.0"
234 [dev-dependencies]
235 devdependency = { path = "../devdependency" }
236
237 //- /dependency/src/lib.rs
238 #[cfg(test)]
239 mod tests {
240 #[test]
241 fn dependency() {}
242 }
243
244 //- /devdependency/Cargo.toml
245 [package]
246 name = "devdependency"
247 version = "0.1.0"
248
249 //- /devdependency/src/lib.rs
250 #[cfg(test)]
251 mod tests {
252 #[test]
253 fn devdependency() {}
254 }
255 "#,
256 )
257 .root("consumer")
258 .root("dependency")
259 .root("devdependency")
260 .server()
261 .wait_until_workspace_is_loaded();
262
263 for runnable in ["consumer", "dependency", "devdependency"] {
264 server.request::<Runnables>(
265 RunnablesParams {
266 text_document: server.doc_id(&format!("{runnable}/src/lib.rs")),
267 position: None,
268 },
269 json!([
270 "{...}",
271 {
272 "label": "cargo test -p [..] --all-targets",
273 "kind": "cargo",
274 "args": {
275 "overrideCargo": null,
276 "workspaceRoot": server.path().join(runnable),
277 "cargoArgs": [
278 "test",
279 "--package",
280 runnable,
281 "--all-targets"
282 ],
283 "cargoExtraArgs": [],
284 "executableArgs": []
285 },
286 },
287 "{...}",
288 "{...}"
289 ]),
290 );
291 }
292 }
293
294 #[test]
295 fn test_format_document() {
296 if skip_slow_tests() {
297 return;
298 }
299
300 let server = project(
301 r#"
302 //- /Cargo.toml
303 [package]
304 name = "foo"
305 version = "0.0.0"
306
307 //- /src/lib.rs
308 mod bar;
309
310 fn main() {
311 }
312
313 pub use std::collections::HashMap;
314 "#,
315 )
316 .wait_until_workspace_is_loaded();
317
318 server.request::<Formatting>(
319 DocumentFormattingParams {
320 text_document: server.doc_id("src/lib.rs"),
321 options: FormattingOptions {
322 tab_size: 4,
323 insert_spaces: false,
324 insert_final_newline: None,
325 trim_final_newlines: None,
326 trim_trailing_whitespace: None,
327 properties: HashMap::new(),
328 },
329 work_done_progress_params: WorkDoneProgressParams::default(),
330 },
331 json!([
332 {
333 "newText": "",
334 "range": {
335 "end": { "character": 0, "line": 3 },
336 "start": { "character": 11, "line": 2 }
337 }
338 }
339 ]),
340 );
341 }
342
343 #[test]
344 fn test_format_document_2018() {
345 if skip_slow_tests() {
346 return;
347 }
348
349 let server = project(
350 r#"
351 //- /Cargo.toml
352 [package]
353 name = "foo"
354 version = "0.0.0"
355 edition = "2018"
356
357 //- /src/lib.rs
358 mod bar;
359
360 async fn test() {
361 }
362
363 fn main() {
364 }
365
366 pub use std::collections::HashMap;
367 "#,
368 )
369 .wait_until_workspace_is_loaded();
370
371 server.request::<Formatting>(
372 DocumentFormattingParams {
373 text_document: server.doc_id("src/lib.rs"),
374 options: FormattingOptions {
375 tab_size: 4,
376 insert_spaces: false,
377 properties: HashMap::new(),
378 insert_final_newline: None,
379 trim_final_newlines: None,
380 trim_trailing_whitespace: None,
381 },
382 work_done_progress_params: WorkDoneProgressParams::default(),
383 },
384 json!([
385 {
386 "newText": "",
387 "range": {
388 "end": { "character": 0, "line": 3 },
389 "start": { "character": 17, "line": 2 }
390 }
391 },
392 {
393 "newText": "",
394 "range": {
395 "end": { "character": 0, "line": 6 },
396 "start": { "character": 11, "line": 5 }
397 }
398 }
399 ]),
400 );
401 }
402
403 #[test]
404 fn test_format_document_unchanged() {
405 if skip_slow_tests() {
406 return;
407 }
408
409 let server = project(
410 r#"
411 //- /Cargo.toml
412 [package]
413 name = "foo"
414 version = "0.0.0"
415
416 //- /src/lib.rs
417 fn main() {}
418 "#,
419 )
420 .wait_until_workspace_is_loaded();
421
422 server.request::<Formatting>(
423 DocumentFormattingParams {
424 text_document: server.doc_id("src/lib.rs"),
425 options: FormattingOptions {
426 tab_size: 4,
427 insert_spaces: false,
428 insert_final_newline: None,
429 trim_final_newlines: None,
430 trim_trailing_whitespace: None,
431 properties: HashMap::new(),
432 },
433 work_done_progress_params: WorkDoneProgressParams::default(),
434 },
435 json!(null),
436 );
437 }
438
439 #[test]
440 fn test_missing_module_code_action() {
441 if skip_slow_tests() {
442 return;
443 }
444
445 let server = project(
446 r#"
447 //- /Cargo.toml
448 [package]
449 name = "foo"
450 version = "0.0.0"
451
452 //- /src/lib.rs
453 mod bar;
454
455 fn main() {}
456 "#,
457 )
458 .wait_until_workspace_is_loaded();
459
460 server.request::<CodeActionRequest>(
461 CodeActionParams {
462 text_document: server.doc_id("src/lib.rs"),
463 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
464 context: CodeActionContext::default(),
465 partial_result_params: PartialResultParams::default(),
466 work_done_progress_params: WorkDoneProgressParams::default(),
467 },
468 json!([
469 {
470 "title": "Create module at `bar.rs`",
471 "kind": "quickfix",
472 "edit": {
473 "documentChanges": [
474 {
475 "kind": "create",
476 "uri": "file://[..]/src/bar.rs"
477 }
478 ]
479 }
480 },
481 {
482 "title": "Create module at `bar/mod.rs`",
483 "kind": "quickfix",
484 "edit": {
485 "documentChanges": [
486 {
487 "kind": "create",
488 "uri": "file://[..]src/bar/mod.rs"
489 }
490 ]
491 }
492 }
493 ]),
494 );
495
496 server.request::<CodeActionRequest>(
497 CodeActionParams {
498 text_document: server.doc_id("src/lib.rs"),
499 range: Range::new(Position::new(2, 8), Position::new(2, 8)),
500 context: CodeActionContext::default(),
501 partial_result_params: PartialResultParams::default(),
502 work_done_progress_params: WorkDoneProgressParams::default(),
503 },
504 json!([]),
505 );
506 }
507
508 #[test]
509 fn test_missing_module_code_action_in_json_project() {
510 if skip_slow_tests() {
511 // return;
512 }
513
514 let tmp_dir = TestDir::new();
515
516 let path = tmp_dir.path();
517
518 let project = json!({
519 "roots": [path],
520 "crates": [ {
521 "root_module": path.join("src/lib.rs"),
522 "deps": [],
523 "edition": "2015",
524 "cfg": [ "cfg_atom_1", "feature=\"cfg_1\""],
525 } ]
526 });
527
528 let code = format!(
529 r#"
530 //- /rust-project.json
531 {project}
532
533 //- /src/lib.rs
534 mod bar;
535
536 fn main() {{}}
537 "#,
538 );
539
540 let server =
541 Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
542
543 server.request::<CodeActionRequest>(
544 CodeActionParams {
545 text_document: server.doc_id("src/lib.rs"),
546 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
547 context: CodeActionContext::default(),
548 partial_result_params: PartialResultParams::default(),
549 work_done_progress_params: WorkDoneProgressParams::default(),
550 },
551 json!([
552 {
553 "title": "Create module at `bar.rs`",
554 "kind": "quickfix",
555 "edit": {
556 "documentChanges": [
557 {
558 "kind": "create",
559 "uri": "file://[..]/src/bar.rs"
560 }
561 ]
562 }
563 },
564 {
565 "title": "Create module at `bar/mod.rs`",
566 "kind": "quickfix",
567 "edit": {
568 "documentChanges": [
569 {
570 "kind": "create",
571 "uri": "file://[..]src/bar/mod.rs"
572 }
573 ]
574 }
575 }
576 ]),
577 );
578
579 server.request::<CodeActionRequest>(
580 CodeActionParams {
581 text_document: server.doc_id("src/lib.rs"),
582 range: Range::new(Position::new(2, 8), Position::new(2, 8)),
583 context: CodeActionContext::default(),
584 partial_result_params: PartialResultParams::default(),
585 work_done_progress_params: WorkDoneProgressParams::default(),
586 },
587 json!([]),
588 );
589 }
590
591 #[test]
592 fn diagnostics_dont_block_typing() {
593 if skip_slow_tests() {
594 return;
595 }
596
597 let librs: String = (0..10).map(|i| format!("mod m{i};")).collect();
598 let libs: String = (0..10).map(|i| format!("//- /src/m{i}.rs\nfn foo() {{}}\n\n")).collect();
599 let server = Project::with_fixture(&format!(
600 r#"
601 //- /Cargo.toml
602 [package]
603 name = "foo"
604 version = "0.0.0"
605
606 //- /src/lib.rs
607 {librs}
608
609 {libs}
610
611 fn main() {{}}
612 "#
613 ))
614 .with_config(serde_json::json!({
615 "cargo": { "sysroot": "discover" }
616 }))
617 .server()
618 .wait_until_workspace_is_loaded();
619
620 for i in 0..10 {
621 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
622 text_document: TextDocumentItem {
623 uri: server.doc_id(&format!("src/m{i}.rs")).uri,
624 language_id: "rust".to_string(),
625 version: 0,
626 text: "/// Docs\nfn foo() {}".to_string(),
627 },
628 });
629 }
630 let start = Instant::now();
631 server.request::<OnEnter>(
632 TextDocumentPositionParams {
633 text_document: server.doc_id("src/m0.rs"),
634 position: Position { line: 0, character: 5 },
635 },
636 json!([{
637 "insertTextFormat": 2,
638 "newText": "\n/// $0",
639 "range": {
640 "end": { "character": 5, "line": 0 },
641 "start": { "character": 5, "line": 0 }
642 }
643 }]),
644 );
645 let elapsed = start.elapsed();
646 assert!(elapsed.as_millis() < 2000, "typing enter took {elapsed:?}");
647 }
648
649 #[test]
650 fn preserves_dos_line_endings() {
651 if skip_slow_tests() {
652 return;
653 }
654
655 let server = Project::with_fixture(
656 "
657 //- /Cargo.toml
658 [package]
659 name = \"foo\"
660 version = \"0.0.0\"
661
662 //- /src/main.rs
663 /// Some Docs\r\nfn main() {}
664 ",
665 )
666 .server()
667 .wait_until_workspace_is_loaded();
668
669 server.request::<OnEnter>(
670 TextDocumentPositionParams {
671 text_document: server.doc_id("src/main.rs"),
672 position: Position { line: 0, character: 8 },
673 },
674 json!([{
675 "insertTextFormat": 2,
676 "newText": "\r\n/// $0",
677 "range": {
678 "end": { "line": 0, "character": 8 },
679 "start": { "line": 0, "character": 8 }
680 }
681 }]),
682 );
683 }
684
685 #[test]
686 fn out_dirs_check() {
687 if skip_slow_tests() {
688 // return;
689 }
690
691 let server = Project::with_fixture(
692 r###"
693 //- /Cargo.toml
694 [package]
695 name = "foo"
696 version = "0.0.0"
697
698 //- /build.rs
699 use std::{env, fs, path::Path};
700
701 fn main() {
702 let out_dir = env::var_os("OUT_DIR").unwrap();
703 let dest_path = Path::new(&out_dir).join("hello.rs");
704 fs::write(
705 &dest_path,
706 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
707 )
708 .unwrap();
709 println!("cargo:rustc-cfg=atom_cfg");
710 println!("cargo:rustc-cfg=featlike=\"set\"");
711 println!("cargo:rerun-if-changed=build.rs");
712 }
713 //- /src/main.rs
714 #[rustc_builtin_macro] macro_rules! include {}
715 #[rustc_builtin_macro] macro_rules! include_str {}
716 #[rustc_builtin_macro] macro_rules! concat {}
717 #[rustc_builtin_macro] macro_rules! env {}
718
719 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
720
721 #[cfg(atom_cfg)]
722 struct A;
723 #[cfg(bad_atom_cfg)]
724 struct A;
725 #[cfg(featlike = "set")]
726 struct B;
727 #[cfg(featlike = "not_set")]
728 struct B;
729
730 fn main() {
731 let va = A;
732 let vb = B;
733 let should_be_str = message();
734 let another_str = include_str!("main.rs");
735 }
736 "###,
737 )
738 .with_config(serde_json::json!({
739 "cargo": {
740 "buildScripts": {
741 "enable": true
742 },
743 "sysroot": null,
744 }
745 }))
746 .server()
747 .wait_until_workspace_is_loaded();
748
749 let res = server.send_request::<HoverRequest>(HoverParams {
750 text_document_position_params: TextDocumentPositionParams::new(
751 server.doc_id("src/main.rs"),
752 Position::new(19, 10),
753 ),
754 work_done_progress_params: Default::default(),
755 });
756 assert!(res.to_string().contains("&str"));
757
758 let res = server.send_request::<HoverRequest>(HoverParams {
759 text_document_position_params: TextDocumentPositionParams::new(
760 server.doc_id("src/main.rs"),
761 Position::new(20, 10),
762 ),
763 work_done_progress_params: Default::default(),
764 });
765 assert!(res.to_string().contains("&str"));
766
767 server.request::<GotoTypeDefinition>(
768 GotoDefinitionParams {
769 text_document_position_params: TextDocumentPositionParams::new(
770 server.doc_id("src/main.rs"),
771 Position::new(17, 9),
772 ),
773 work_done_progress_params: Default::default(),
774 partial_result_params: Default::default(),
775 },
776 json!([{
777 "originSelectionRange": {
778 "end": { "character": 10, "line": 17 },
779 "start": { "character": 8, "line": 17 }
780 },
781 "targetRange": {
782 "end": { "character": 9, "line": 8 },
783 "start": { "character": 0, "line": 7 }
784 },
785 "targetSelectionRange": {
786 "end": { "character": 8, "line": 8 },
787 "start": { "character": 7, "line": 8 }
788 },
789 "targetUri": "file:///[..]src/main.rs"
790 }]),
791 );
792
793 server.request::<GotoTypeDefinition>(
794 GotoDefinitionParams {
795 text_document_position_params: TextDocumentPositionParams::new(
796 server.doc_id("src/main.rs"),
797 Position::new(18, 9),
798 ),
799 work_done_progress_params: Default::default(),
800 partial_result_params: Default::default(),
801 },
802 json!([{
803 "originSelectionRange": {
804 "end": { "character": 10, "line": 18 },
805 "start": { "character": 8, "line": 18 }
806 },
807 "targetRange": {
808 "end": { "character": 9, "line": 12 },
809 "start": { "character": 0, "line":11 }
810 },
811 "targetSelectionRange": {
812 "end": { "character": 8, "line": 12 },
813 "start": { "character": 7, "line": 12 }
814 },
815 "targetUri": "file:///[..]src/main.rs"
816 }]),
817 );
818 }
819
820 #[test]
821 // FIXME: Re-enable once we can run proc-macro tests on rust-lang/rust-analyzer again
822 #[cfg(any())]
823 fn resolve_proc_macro() {
824 use expect_test::expect;
825 if skip_slow_tests() {
826 return;
827 }
828
829 let server = Project::with_fixture(
830 r###"
831 //- /foo/Cargo.toml
832 [package]
833 name = "foo"
834 version = "0.0.0"
835 edition = "2021"
836 [dependencies]
837 bar = {path = "../bar"}
838
839 //- /foo/src/main.rs
840 use bar::Bar;
841
842 #[rustc_builtin_macro]
843 macro derive($item:item) {}
844 trait Bar {
845 fn bar();
846 }
847 #[derive(Bar)]
848 struct Foo {}
849 fn main() {
850 Foo::bar();
851 }
852
853 //- /bar/Cargo.toml
854 [package]
855 name = "bar"
856 version = "0.0.0"
857 edition = "2021"
858
859 [lib]
860 proc-macro = true
861
862 //- /bar/src/lib.rs
863 extern crate proc_macro;
864 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
865 macro_rules! t {
866 ($n:literal) => {
867 TokenTree::from(Ident::new($n, Span::call_site()))
868 };
869 ({}) => {
870 TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
871 };
872 (()) => {
873 TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
874 };
875 }
876 #[proc_macro_derive(Bar)]
877 pub fn foo(_input: TokenStream) -> TokenStream {
878 // We hard code the output here for preventing to use any deps
879 let mut res = TokenStream::new();
880
881 // ill behaved proc-macro will use the stdout
882 // we should ignore it
883 println!("I am bad guy");
884
885 // impl Bar for Foo { fn bar() {} }
886 let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
887 let mut fn_stream = TokenStream::new();
888 fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
889 tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
890 res.extend(tokens);
891 res
892 }
893
894 "###,
895 )
896 .with_config(serde_json::json!({
897 "cargo": {
898 "buildScripts": {
899 "enable": true
900 },
901 "sysroot": null,
902 },
903 "procMacro": {
904 "enable": true,
905 "server": PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")),
906 }
907 }))
908 .root("foo")
909 .root("bar")
910 .server()
911 .wait_until_workspace_is_loaded();
912
913 let res = server.send_request::<HoverRequest>(HoverParams {
914 text_document_position_params: TextDocumentPositionParams::new(
915 server.doc_id("foo/src/main.rs"),
916 Position::new(10, 9),
917 ),
918 work_done_progress_params: Default::default(),
919 });
920 let value = res.get("contents").unwrap().get("value").unwrap().as_str().unwrap();
921
922 expect![[r#"
923
924 ```rust
925 foo::Foo
926 ```
927
928 ```rust
929 fn bar()
930 ```"#]]
931 .assert_eq(value);
932 }
933
934 #[test]
935 fn test_will_rename_files_same_level() {
936 if skip_slow_tests() {
937 return;
938 }
939
940 let tmp_dir = TestDir::new();
941 let tmp_dir_path = tmp_dir.path().to_owned();
942 let tmp_dir_str = tmp_dir_path.to_str().unwrap();
943 let base_path = PathBuf::from(format!("file://{tmp_dir_str}"));
944
945 let code = r#"
946 //- /Cargo.toml
947 [package]
948 name = "foo"
949 version = "0.0.0"
950
951 //- /src/lib.rs
952 mod old_file;
953 mod from_mod;
954 mod to_mod;
955 mod old_folder;
956 fn main() {}
957
958 //- /src/old_file.rs
959
960 //- /src/old_folder/mod.rs
961
962 //- /src/from_mod/mod.rs
963
964 //- /src/to_mod/foo.rs
965
966 "#;
967 let server =
968 Project::with_fixture(code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded();
969
970 //rename same level file
971 server.request::<WillRenameFiles>(
972 RenameFilesParams {
973 files: vec![FileRename {
974 old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_string(),
975 new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_string(),
976 }],
977 },
978 json!({
979 "documentChanges": [
980 {
981 "textDocument": {
982 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace('\\', "/")),
983 "version": null
984 },
985 "edits": [
986 {
987 "range": {
988 "start": {
989 "line": 0,
990 "character": 4
991 },
992 "end": {
993 "line": 0,
994 "character": 12
995 }
996 },
997 "newText": "new_file"
998 }
999 ]
1000 }
1001 ]
1002 }),
1003 );
1004
1005 //rename file from mod.rs to foo.rs
1006 server.request::<WillRenameFiles>(
1007 RenameFilesParams {
1008 files: vec![FileRename {
1009 old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_string(),
1010 new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_string(),
1011 }],
1012 },
1013 json!(null),
1014 );
1015
1016 //rename file from foo.rs to mod.rs
1017 server.request::<WillRenameFiles>(
1018 RenameFilesParams {
1019 files: vec![FileRename {
1020 old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_string(),
1021 new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_string(),
1022 }],
1023 },
1024 json!(null),
1025 );
1026
1027 //rename same level file
1028 server.request::<WillRenameFiles>(
1029 RenameFilesParams {
1030 files: vec![FileRename {
1031 old_uri: base_path.join("src/old_folder").to_str().unwrap().to_string(),
1032 new_uri: base_path.join("src/new_folder").to_str().unwrap().to_string(),
1033 }],
1034 },
1035 json!({
1036 "documentChanges": [
1037 {
1038 "textDocument": {
1039 "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").to_str().unwrap().to_string().replace("C:\\", "/c:/").replace('\\', "/")),
1040 "version": null
1041 },
1042 "edits": [
1043 {
1044 "range": {
1045 "start": {
1046 "line": 3,
1047 "character": 4
1048 },
1049 "end": {
1050 "line": 3,
1051 "character": 14
1052 }
1053 },
1054 "newText": "new_folder"
1055 }
1056 ]
1057 }
1058 ]
1059 }),
1060 );
1061 }
1062
1063 #[test]
1064 fn test_exclude_config_works() {
1065 if skip_slow_tests() {
1066 return;
1067 }
1068
1069 let server = Project::with_fixture(
1070 r#"
1071 //- /foo/Cargo.toml
1072 [package]
1073 name = "foo"
1074 version = "0.0.0"
1075
1076 //- /foo/src/lib.rs
1077 pub fn foo() {}
1078
1079 //- /bar/Cargo.toml
1080 [package]
1081 name = "bar"
1082 version = "0.0.0"
1083
1084 //- /bar/src/lib.rs
1085 pub fn bar() {}
1086 "#,
1087 )
1088 .root("foo")
1089 .root("bar")
1090 .with_config(json!({
1091 "files": {
1092 "excludeDirs": ["foo", "bar"]
1093 }
1094 }))
1095 .server()
1096 .wait_until_workspace_is_loaded();
1097
1098 server.request::<WorkspaceSymbol>(Default::default(), json!([]));
1099 }