aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-07-09 15:19:14 +0100
committerGitHub <[email protected]>2020-07-09 15:19:14 +0100
commitf1d084fbd9504497aed24b907b03247bfcd31675 (patch)
tree2e4d75008c01531075bced1596ec65047d74ca91
parent6132111f87a6891db28ce37909607034c228f7a9 (diff)
parent65d9966a4f6c35b63f97c16f5f62f83a04574f3e (diff)
Merge #5282
5282: Move diagnostics tests to expect r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--Cargo.lock2
-rw-r--r--crates/expect/src/lib.rs4
-rw-r--r--crates/flycheck/src/lib.rs3
-rw-r--r--crates/ra_ide/src/completion/presentation.rs4
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs22
-rw-r--r--crates/ra_ide/src/inlay_hints.rs94
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs294
-rw-r--r--crates/rust-analyzer/src/main_loop.rs2
-rw-r--r--crates/rust-analyzer/test_data/clippy_pass_by_ref.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap)18
-rw-r--r--crates/rust-analyzer/test_data/handles_macro_location.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap)18
-rw-r--r--crates/rust-analyzer/test_data/macro_compiler_error.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_mismatched_type.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_unused_variable.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap)18
-rw-r--r--crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap)18
-rw-r--r--crates/rust-analyzer/test_data/snap_multi_line_fix.txt (renamed from crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap)18
20 files changed, 201 insertions, 408 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 84ca3344c..b429aae01 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1435,9 +1435,9 @@ dependencies = [
1435 "anyhow", 1435 "anyhow",
1436 "crossbeam-channel", 1436 "crossbeam-channel",
1437 "env_logger", 1437 "env_logger",
1438 "expect",
1438 "flycheck", 1439 "flycheck",
1439 "globset", 1440 "globset",
1440 "insta",
1441 "itertools", 1441 "itertools",
1442 "jod-thread", 1442 "jod-thread",
1443 "log", 1443 "log",
diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs
index c54e99203..21a458d47 100644
--- a/crates/expect/src/lib.rs
+++ b/crates/expect/src/lib.rs
@@ -121,6 +121,10 @@ impl ExpectFile {
121 } 121 }
122 Runtime::fail_file(self, &expected, actual); 122 Runtime::fail_file(self, &expected, actual);
123 } 123 }
124 pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
125 let actual = format!("{:#?}\n", actual);
126 self.assert_eq(&actual)
127 }
124 fn read(&self) -> String { 128 fn read(&self) -> String {
125 fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n") 129 fs::read_to_string(self.abs_path()).unwrap_or_default().replace("\r\n", "\n")
126 } 130 }
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 844b093d4..6804d9bda 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -14,7 +14,8 @@ use std::{
14use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; 14use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
15 15
16pub use cargo_metadata::diagnostic::{ 16pub use cargo_metadata::diagnostic::{
17 Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion, 17 Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
18 DiagnosticSpanMacroExpansion,
18}; 19};
19 20
20#[derive(Clone, Debug, PartialEq, Eq)] 21#[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index e4c57e41a..48afee5fb 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -491,7 +491,7 @@ mod tests {
491 } 491 }
492 } 492 }
493 493
494 let mut completions = get_all_completion_items(ra_fixture, &CompletionConfig::default()); 494 let mut completions = get_all_completion_items(CompletionConfig::default(), ra_fixture);
495 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 495 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
496 let actual = completions 496 let actual = completions
497 .into_iter() 497 .into_iter()
@@ -835,6 +835,7 @@ fn bar(s: &S) {
835 fn suppress_arg_snippets() { 835 fn suppress_arg_snippets() {
836 mark::check!(suppress_arg_snippets); 836 mark::check!(suppress_arg_snippets);
837 check_edit_with_config( 837 check_edit_with_config(
838 CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
838 "with_args", 839 "with_args",
839 r#" 840 r#"
840fn with_args(x: i32, y: String) {} 841fn with_args(x: i32, y: String) {}
@@ -844,7 +845,6 @@ fn main() { with_<|> }
844fn with_args(x: i32, y: String) {} 845fn with_args(x: i32, y: String) {}
845fn main() { with_args($0) } 846fn main() { with_args($0) }
846"#, 847"#,
847 &CompletionConfig { add_call_argument_snippets: false, ..CompletionConfig::default() },
848 ); 848 );
849 } 849 }
850 850
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index c2be23697..919177745 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -13,15 +13,15 @@ use crate::{
13}; 13};
14 14
15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 15pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
16 do_completion_with_config(code, kind, &CompletionConfig::default()) 16 do_completion_with_config(CompletionConfig::default(), code, kind)
17} 17}
18 18
19pub(crate) fn do_completion_with_config( 19pub(crate) fn do_completion_with_config(
20 config: CompletionConfig,
20 code: &str, 21 code: &str,
21 kind: CompletionKind, 22 kind: CompletionKind,
22 config: &CompletionConfig,
23) -> Vec<CompletionItem> { 23) -> Vec<CompletionItem> {
24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, config) 24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
25 .into_iter() 25 .into_iter()
26 .filter(|c| c.completion_kind == kind) 26 .filter(|c| c.completion_kind == kind)
27 .collect(); 27 .collect();
@@ -30,15 +30,15 @@ pub(crate) fn do_completion_with_config(
30} 30}
31 31
32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 32pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
33 completion_list_with_config(code, kind, &CompletionConfig::default()) 33 completion_list_with_config(CompletionConfig::default(), code, kind)
34} 34}
35 35
36pub(crate) fn completion_list_with_config( 36pub(crate) fn completion_list_with_config(
37 config: CompletionConfig,
37 code: &str, 38 code: &str,
38 kind: CompletionKind, 39 kind: CompletionKind,
39 config: &CompletionConfig,
40) -> String { 40) -> String {
41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, config) 41 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(config, code)
42 .into_iter() 42 .into_iter()
43 .filter(|c| c.completion_kind == kind) 43 .filter(|c| c.completion_kind == kind)
44 .collect(); 44 .collect();
@@ -70,19 +70,19 @@ fn monospace_width(s: &str) -> usize {
70} 70}
71 71
72pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 72pub(crate) fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
73 check_edit_with_config(what, ra_fixture_before, ra_fixture_after, &CompletionConfig::default()) 73 check_edit_with_config(CompletionConfig::default(), what, ra_fixture_before, ra_fixture_after)
74} 74}
75 75
76pub(crate) fn check_edit_with_config( 76pub(crate) fn check_edit_with_config(
77 config: CompletionConfig,
77 what: &str, 78 what: &str,
78 ra_fixture_before: &str, 79 ra_fixture_before: &str,
79 ra_fixture_after: &str, 80 ra_fixture_after: &str,
80 config: &CompletionConfig,
81) { 81) {
82 let ra_fixture_after = trim_indent(ra_fixture_after); 82 let ra_fixture_after = trim_indent(ra_fixture_after);
83 let (analysis, position) = analysis_and_position(ra_fixture_before); 83 let (analysis, position) = analysis_and_position(ra_fixture_before);
84 let completions: Vec<CompletionItem> = 84 let completions: Vec<CompletionItem> =
85 analysis.completions(config, position).unwrap().unwrap().into(); 85 analysis.completions(&config, position).unwrap().unwrap().into();
86 let (completion,) = completions 86 let (completion,) = completions
87 .iter() 87 .iter()
88 .filter(|it| it.lookup() == what) 88 .filter(|it| it.lookup() == what)
@@ -106,9 +106,9 @@ pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -
106} 106}
107 107
108pub(crate) fn get_all_completion_items( 108pub(crate) fn get_all_completion_items(
109 config: CompletionConfig,
109 code: &str, 110 code: &str,
110 options: &CompletionConfig,
111) -> Vec<CompletionItem> { 111) -> Vec<CompletionItem> {
112 let (analysis, position) = analysis_and_position(code); 112 let (analysis, position) = analysis_and_position(code);
113 analysis.completions(options, position).unwrap().unwrap().into() 113 analysis.completions(&config, position).unwrap().unwrap().into()
114} 114}
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 62d364bfa..35ab741d8 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -351,10 +351,10 @@ mod tests {
351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file}; 351 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
352 352
353 fn check(ra_fixture: &str) { 353 fn check(ra_fixture: &str) {
354 check_with_config(ra_fixture, InlayHintsConfig::default()); 354 check_with_config(InlayHintsConfig::default(), ra_fixture);
355 } 355 }
356 356
357 fn check_with_config(ra_fixture: &str, config: InlayHintsConfig) { 357 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
358 let (analysis, file_id) = single_file(ra_fixture); 358 let (analysis, file_id) = single_file(ra_fixture);
359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); 359 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
@@ -363,7 +363,7 @@ mod tests {
363 assert_eq!(expected, actual); 363 assert_eq!(expected, actual);
364 } 364 }
365 365
366 fn check_expect(ra_fixture: &str, config: InlayHintsConfig, expect: Expect) { 366 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
367 let (analysis, file_id) = single_file(ra_fixture); 367 let (analysis, file_id) = single_file(ra_fixture);
368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 368 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
369 expect.assert_debug_eq(&inlay_hints) 369 expect.assert_debug_eq(&inlay_hints)
@@ -372,6 +372,12 @@ mod tests {
372 #[test] 372 #[test]
373 fn param_hints_only() { 373 fn param_hints_only() {
374 check_with_config( 374 check_with_config(
375 InlayHintsConfig {
376 parameter_hints: true,
377 type_hints: false,
378 chaining_hints: false,
379 max_length: None,
380 },
375 r#" 381 r#"
376fn foo(a: i32, b: i32) -> i32 { a + b } 382fn foo(a: i32, b: i32) -> i32 { a + b }
377fn main() { 383fn main() {
@@ -382,47 +388,41 @@ fn main() {
382 //^ b 388 //^ b
383 ); 389 );
384}"#, 390}"#,
385 InlayHintsConfig {
386 parameter_hints: true,
387 type_hints: false,
388 chaining_hints: false,
389 max_length: None,
390 },
391 ); 391 );
392 } 392 }
393 393
394 #[test] 394 #[test]
395 fn hints_disabled() { 395 fn hints_disabled() {
396 check_with_config( 396 check_with_config(
397 r#"
398fn foo(a: i32, b: i32) -> i32 { a + b }
399fn main() {
400 let _x = foo(4, 4);
401}"#,
402 InlayHintsConfig { 397 InlayHintsConfig {
403 type_hints: false, 398 type_hints: false,
404 parameter_hints: false, 399 parameter_hints: false,
405 chaining_hints: false, 400 chaining_hints: false,
406 max_length: None, 401 max_length: None,
407 }, 402 },
403 r#"
404fn foo(a: i32, b: i32) -> i32 { a + b }
405fn main() {
406 let _x = foo(4, 4);
407}"#,
408 ); 408 );
409 } 409 }
410 410
411 #[test] 411 #[test]
412 fn type_hints_only() { 412 fn type_hints_only() {
413 check_with_config( 413 check_with_config(
414 r#"
415fn foo(a: i32, b: i32) -> i32 { a + b }
416fn main() {
417 let _x = foo(4, 4);
418 //^^ i32
419}"#,
420 InlayHintsConfig { 414 InlayHintsConfig {
421 type_hints: true, 415 type_hints: true,
422 parameter_hints: false, 416 parameter_hints: false,
423 chaining_hints: false, 417 chaining_hints: false,
424 max_length: None, 418 max_length: None,
425 }, 419 },
420 r#"
421fn foo(a: i32, b: i32) -> i32 { a + b }
422fn main() {
423 let _x = foo(4, 4);
424 //^^ i32
425}"#,
426 ); 426 );
427 } 427 }
428 428
@@ -590,6 +590,7 @@ fn main() {
590 #[test] 590 #[test]
591 fn hint_truncation() { 591 fn hint_truncation() {
592 check_with_config( 592 check_with_config(
593 InlayHintsConfig { max_length: Some(8), ..Default::default() },
593 r#" 594 r#"
594struct Smol<T>(T); 595struct Smol<T>(T);
595 596
@@ -603,7 +604,6 @@ fn main() {
603 let c = Smol(Smol(0u32)) 604 let c = Smol(Smol(0u32))
604 //^ Smol<Smol<…>> 605 //^ Smol<Smol<…>>
605}"#, 606}"#,
606 InlayHintsConfig { max_length: Some(8), ..Default::default() },
607 ); 607 );
608 } 608 }
609 609
@@ -673,6 +673,7 @@ fn main() {
673 #[test] 673 #[test]
674 fn omitted_parameters_hints_heuristics() { 674 fn omitted_parameters_hints_heuristics() {
675 check_with_config( 675 check_with_config(
676 InlayHintsConfig { max_length: Some(8), ..Default::default() },
676 r#" 677 r#"
677fn map(f: i32) {} 678fn map(f: i32) {}
678fn filter(predicate: i32) {} 679fn filter(predicate: i32) {}
@@ -753,13 +754,13 @@ fn main() {
753 let _: f64 = a.div_euclid(b); 754 let _: f64 = a.div_euclid(b);
754 let _: f64 = a.abs_sub(b); 755 let _: f64 = a.abs_sub(b);
755}"#, 756}"#,
756 InlayHintsConfig { max_length: Some(8), ..Default::default() },
757 ); 757 );
758 } 758 }
759 759
760 #[test] 760 #[test]
761 fn unit_structs_have_no_type_hints() { 761 fn unit_structs_have_no_type_hints() {
762 check_with_config( 762 check_with_config(
763 InlayHintsConfig { max_length: Some(8), ..Default::default() },
763 r#" 764 r#"
764enum Result<T, E> { Ok(T), Err(E) } 765enum Result<T, E> { Ok(T), Err(E) }
765use Result::*; 766use Result::*;
@@ -772,13 +773,18 @@ fn main() {
772 Err(SyntheticSyntax) => (), 773 Err(SyntheticSyntax) => (),
773 } 774 }
774}"#, 775}"#,
775 InlayHintsConfig { max_length: Some(8), ..Default::default() },
776 ); 776 );
777 } 777 }
778 778
779 #[test] 779 #[test]
780 fn chaining_hints_ignore_comments() { 780 fn chaining_hints_ignore_comments() {
781 check_expect( 781 check_expect(
782 InlayHintsConfig {
783 parameter_hints: false,
784 type_hints: false,
785 chaining_hints: true,
786 max_length: None,
787 },
782 r#" 788 r#"
783struct A(B); 789struct A(B);
784impl A { fn into_b(self) -> B { self.0 } } 790impl A { fn into_b(self) -> B { self.0 } }
@@ -792,12 +798,6 @@ fn main() {
792 .into_c(); 798 .into_c();
793} 799}
794"#, 800"#,
795 InlayHintsConfig {
796 parameter_hints: false,
797 type_hints: false,
798 chaining_hints: true,
799 max_length: None,
800 },
801 expect![[r#" 801 expect![[r#"
802 [ 802 [
803 InlayHint { 803 InlayHint {
@@ -818,6 +818,12 @@ fn main() {
818 #[test] 818 #[test]
819 fn chaining_hints_without_newlines() { 819 fn chaining_hints_without_newlines() {
820 check_with_config( 820 check_with_config(
821 InlayHintsConfig {
822 parameter_hints: false,
823 type_hints: false,
824 chaining_hints: true,
825 max_length: None,
826 },
821 r#" 827 r#"
822struct A(B); 828struct A(B);
823impl A { fn into_b(self) -> B { self.0 } } 829impl A { fn into_b(self) -> B { self.0 } }
@@ -828,18 +834,18 @@ struct C;
828fn main() { 834fn main() {
829 let c = A(B(C)).into_b().into_c(); 835 let c = A(B(C)).into_b().into_c();
830}"#, 836}"#,
831 InlayHintsConfig {
832 parameter_hints: false,
833 type_hints: false,
834 chaining_hints: true,
835 max_length: None,
836 },
837 ); 837 );
838 } 838 }
839 839
840 #[test] 840 #[test]
841 fn struct_access_chaining_hints() { 841 fn struct_access_chaining_hints() {
842 check_expect( 842 check_expect(
843 InlayHintsConfig {
844 parameter_hints: false,
845 type_hints: false,
846 chaining_hints: true,
847 max_length: None,
848 },
843 r#" 849 r#"
844struct A { pub b: B } 850struct A { pub b: B }
845struct B { pub c: C } 851struct B { pub c: C }
@@ -858,12 +864,6 @@ fn main() {
858 let x = D 864 let x = D
859 .foo(); 865 .foo();
860}"#, 866}"#,
861 InlayHintsConfig {
862 parameter_hints: false,
863 type_hints: false,
864 chaining_hints: true,
865 max_length: None,
866 },
867 expect![[r#" 867 expect![[r#"
868 [ 868 [
869 InlayHint { 869 InlayHint {
@@ -884,6 +884,12 @@ fn main() {
884 #[test] 884 #[test]
885 fn generic_chaining_hints() { 885 fn generic_chaining_hints() {
886 check_expect( 886 check_expect(
887 InlayHintsConfig {
888 parameter_hints: false,
889 type_hints: false,
890 chaining_hints: true,
891 max_length: None,
892 },
887 r#" 893 r#"
888struct A<T>(T); 894struct A<T>(T);
889struct B<T>(T); 895struct B<T>(T);
@@ -903,12 +909,6 @@ fn main() {
903 .into_c(); 909 .into_c();
904} 910}
905"#, 911"#,
906 InlayHintsConfig {
907 parameter_hints: false,
908 type_hints: false,
909 chaining_hints: true,
910 max_length: None,
911 },
912 expect![[r#" 912 expect![[r#"
913 [ 913 [
914 InlayHint { 914 InlayHint {
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 837b6714d..dc8dbbe77 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -58,7 +58,7 @@ winapi = "0.3.8"
58 58
59[dev-dependencies] 59[dev-dependencies]
60tempfile = "3.1.0" 60tempfile = "3.1.0"
61insta = "0.16.0" 61expect = { path = "../expect" }
62test_utils = { path = "../test_utils" } 62test_utils = { path = "../test_utils" }
63mbe = { path = "../ra_mbe", package = "ra_mbe" } 63mbe = { path = "../ra_mbe", package = "ra_mbe" }
64tt = { path = "../ra_tt", package = "ra_tt" } 64tt = { path = "../ra_tt", package = "ra_tt" }
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index b46281c98..d24c55cee 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -18,7 +18,9 @@ pub struct DiagnosticsConfig {
18 18
19#[derive(Debug, Default, Clone)] 19#[derive(Debug, Default, Clone)]
20pub(crate) struct DiagnosticCollection { 20pub(crate) struct DiagnosticCollection {
21 // FIXME: should be FxHashMap<FileId, Vec<ra_id::Diagnostic>>
21 pub(crate) native: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>, 22 pub(crate) native: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
23 // FIXME: should be Vec<flycheck::Diagnostic>
22 pub(crate) check: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>, 24 pub(crate) check: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
23 pub(crate) check_fixes: CheckFixes, 25 pub(crate) check_fixes: CheckFixes,
24 changes: FxHashSet<FileId>, 26 changes: FxHashSet<FileId>,
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 3eed118a9..b1b222deb 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -2,11 +2,7 @@
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{collections::HashMap, path::Path}; 3use std::{collections::HashMap, path::Path};
4 4
5use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; 5use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
6use lsp_types::{
7 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
8 NumberOrString, Position, Range, TextEdit, Url,
9};
10use stdx::format_to; 6use stdx::format_to;
11 7
12use crate::{lsp_ext, to_proto::url_from_abs_path}; 8use crate::{lsp_ext, to_proto::url_from_abs_path};
@@ -14,22 +10,25 @@ use crate::{lsp_ext, to_proto::url_from_abs_path};
14use super::DiagnosticsConfig; 10use super::DiagnosticsConfig;
15 11
16/// Determines the LSP severity from a diagnostic 12/// Determines the LSP severity from a diagnostic
17fn map_diagnostic_to_severity( 13fn diagnostic_severity(
18 config: &DiagnosticsConfig, 14 config: &DiagnosticsConfig,
19 val: &flycheck::Diagnostic, 15 level: flycheck::DiagnosticLevel,
20) -> Option<DiagnosticSeverity> { 16 code: Option<flycheck::DiagnosticCode>,
21 let res = match val.level { 17) -> Option<lsp_types::DiagnosticSeverity> {
22 DiagnosticLevel::Ice => DiagnosticSeverity::Error, 18 let res = match level {
23 DiagnosticLevel::Error => DiagnosticSeverity::Error, 19 DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::Error,
24 DiagnosticLevel::Warning => match &val.code { 20 DiagnosticLevel::Error => lsp_types::DiagnosticSeverity::Error,
25 Some(code) if config.warnings_as_hint.contains(&code.code) => DiagnosticSeverity::Hint, 21 DiagnosticLevel::Warning => match &code {
22 Some(code) if config.warnings_as_hint.contains(&code.code) => {
23 lsp_types::DiagnosticSeverity::Hint
24 }
26 Some(code) if config.warnings_as_info.contains(&code.code) => { 25 Some(code) if config.warnings_as_info.contains(&code.code) => {
27 DiagnosticSeverity::Information 26 lsp_types::DiagnosticSeverity::Information
28 } 27 }
29 _ => DiagnosticSeverity::Warning, 28 _ => lsp_types::DiagnosticSeverity::Warning,
30 }, 29 },
31 DiagnosticLevel::Note => DiagnosticSeverity::Information, 30 DiagnosticLevel::Note => lsp_types::DiagnosticSeverity::Information,
32 DiagnosticLevel::Help => DiagnosticSeverity::Hint, 31 DiagnosticLevel::Help => lsp_types::DiagnosticSeverity::Hint,
33 DiagnosticLevel::Unknown => return None, 32 DiagnosticLevel::Unknown => return None,
34 }; 33 };
35 Some(res) 34 Some(res)
@@ -40,90 +39,50 @@ fn is_from_macro(file_name: &str) -> bool {
40 file_name.starts_with('<') && file_name.ends_with('>') 39 file_name.starts_with('<') && file_name.ends_with('>')
41} 40}
42 41
43/// Converts a Rust macro span to a LSP location recursively
44fn map_macro_span_to_location(
45 span_macro: &DiagnosticSpanMacroExpansion,
46 workspace_root: &Path,
47) -> Option<Location> {
48 if !is_from_macro(&span_macro.span.file_name) {
49 return Some(map_span_to_location(&span_macro.span, workspace_root));
50 }
51
52 if let Some(expansion) = &span_macro.span.expansion {
53 return map_macro_span_to_location(&expansion, workspace_root);
54 }
55
56 None
57}
58
59/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary 42/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
60fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &Path) -> Location { 43fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
61 if span.expansion.is_some() { 44 let mut span = span.clone();
62 let expansion = span.expansion.as_ref().unwrap(); 45 while let Some(expansion) = span.expansion {
63 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) { 46 span = expansion.span;
64 return macro_range;
65 }
66 } 47 }
67 48 return location_naive(workspace_root, &span);
68 map_span_to_location_naive(span, workspace_root)
69} 49}
70 50
71/// Converts a Rust span to a LSP location 51/// Converts a Rust span to a LSP location
72fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &Path) -> Location { 52fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
73 let mut file_name = workspace_root.to_path_buf(); 53 let file_name = workspace_root.join(&span.file_name);
74 file_name.push(&span.file_name);
75 let uri = url_from_abs_path(&file_name); 54 let uri = url_from_abs_path(&file_name);
76 55
77 // FIXME: this doesn't handle UTF16 offsets correctly 56 // FIXME: this doesn't handle UTF16 offsets correctly
78 let range = Range::new( 57 let range = lsp_types::Range::new(
79 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1), 58 lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
80 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1), 59 lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
81 ); 60 );
82 61
83 Location { uri, range } 62 lsp_types::Location { uri, range }
84} 63}
85 64
86/// Converts a secondary Rust span to a LSP related information 65/// Converts a secondary Rust span to a LSP related inflocation(ormation
87/// 66///
88/// If the span is unlabelled this will return `None`. 67/// If the span is unlabelled this will return `None`.
89fn map_secondary_span_to_related( 68fn diagnostic_related_information(
90 span: &DiagnosticSpan,
91 workspace_root: &Path, 69 workspace_root: &Path,
92) -> Option<DiagnosticRelatedInformation> { 70 span: &DiagnosticSpan,
71) -> Option<lsp_types::DiagnosticRelatedInformation> {
93 let message = span.label.clone()?; 72 let message = span.label.clone()?;
94 let location = map_span_to_location(span, workspace_root); 73 let location = location(workspace_root, span);
95 Some(DiagnosticRelatedInformation { location, message }) 74 Some(lsp_types::DiagnosticRelatedInformation { location, message })
96}
97
98/// Determines if diagnostic is related to unused code
99fn is_unused_or_unnecessary(rd: &flycheck::Diagnostic) -> bool {
100 match &rd.code {
101 Some(code) => match code.code.as_str() {
102 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
103 | "unused_imports" | "unused_macros" | "unused_variables" => true,
104 _ => false,
105 },
106 None => false,
107 }
108}
109
110/// Determines if diagnostic is related to deprecated code
111fn is_deprecated(rd: &flycheck::Diagnostic) -> bool {
112 match &rd.code {
113 Some(code) => code.code.as_str() == "deprecated",
114 None => false,
115 }
116} 75}
117 76
118enum MappedRustChildDiagnostic { 77enum MappedRustChildDiagnostic {
119 Related(DiagnosticRelatedInformation), 78 Related(lsp_types::DiagnosticRelatedInformation),
120 SuggestedFix(lsp_ext::CodeAction), 79 SuggestedFix(lsp_ext::CodeAction),
121 MessageLine(String), 80 MessageLine(String),
122} 81}
123 82
124fn map_rust_child_diagnostic( 83fn map_rust_child_diagnostic(
125 rd: &flycheck::Diagnostic,
126 workspace_root: &Path, 84 workspace_root: &Path,
85 rd: &flycheck::Diagnostic,
127) -> MappedRustChildDiagnostic { 86) -> MappedRustChildDiagnostic {
128 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); 87 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
129 if spans.is_empty() { 88 if spans.is_empty() {
@@ -132,21 +91,20 @@ fn map_rust_child_diagnostic(
132 return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); 91 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
133 } 92 }
134 93
135 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new(); 94 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
136 for &span in &spans { 95 for &span in &spans {
137 match (&span.suggestion_applicability, &span.suggested_replacement) { 96 if let (Some(Applicability::MachineApplicable), Some(suggested_replacement)) =
138 (Some(Applicability::MachineApplicable), Some(suggested_replacement)) => { 97 (&span.suggestion_applicability, &span.suggested_replacement)
139 let location = map_span_to_location(span, workspace_root); 98 {
140 let edit = TextEdit::new(location.range, suggested_replacement.clone()); 99 let location = location(workspace_root, span);
141 edit_map.entry(location.uri).or_default().push(edit); 100 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
142 } 101 edit_map.entry(location.uri).or_default().push(edit);
143 _ => {}
144 } 102 }
145 } 103 }
146 104
147 if edit_map.is_empty() { 105 if edit_map.is_empty() {
148 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { 106 MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
149 location: map_span_to_location(spans[0], workspace_root), 107 location: location(workspace_root, spans[0]),
150 message: rd.message.clone(), 108 message: rd.message.clone(),
151 }) 109 })
152 } else { 110 } else {
@@ -167,8 +125,8 @@ fn map_rust_child_diagnostic(
167 125
168#[derive(Debug)] 126#[derive(Debug)]
169pub(crate) struct MappedRustDiagnostic { 127pub(crate) struct MappedRustDiagnostic {
170 pub(crate) location: Location, 128 pub(crate) url: lsp_types::Url,
171 pub(crate) diagnostic: Diagnostic, 129 pub(crate) diagnostic: lsp_types::Diagnostic,
172 pub(crate) fixes: Vec<lsp_ext::CodeAction>, 130 pub(crate) fixes: Vec<lsp_ext::CodeAction>,
173} 131}
174 132
@@ -192,7 +150,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
192 return Vec::new(); 150 return Vec::new();
193 } 151 }
194 152
195 let severity = map_diagnostic_to_severity(config, rd); 153 let severity = diagnostic_severity(config, rd.level.clone(), rd.code.clone());
196 154
197 let mut source = String::from("rustc"); 155 let mut source = String::from("rustc");
198 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 156 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -210,7 +168,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
210 let mut tags = Vec::new(); 168 let mut tags = Vec::new();
211 169
212 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) { 170 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
213 let related = map_secondary_span_to_related(secondary_span, workspace_root); 171 let related = diagnostic_related_information(workspace_root, secondary_span);
214 if let Some(related) = related { 172 if let Some(related) = related {
215 related_information.push(related); 173 related_information.push(related);
216 } 174 }
@@ -219,7 +177,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
219 let mut fixes = Vec::new(); 177 let mut fixes = Vec::new();
220 let mut message = rd.message.clone(); 178 let mut message = rd.message.clone();
221 for child in &rd.children { 179 for child in &rd.children {
222 let child = map_rust_child_diagnostic(&child, workspace_root); 180 let child = map_rust_child_diagnostic(workspace_root, &child);
223 match child { 181 match child {
224 MappedRustChildDiagnostic::Related(related) => related_information.push(related), 182 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
225 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), 183 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
@@ -233,18 +191,30 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
233 } 191 }
234 } 192 }
235 193
236 if is_unused_or_unnecessary(rd) { 194 if let Some(code) = &rd.code {
237 tags.push(DiagnosticTag::Unnecessary); 195 let code = code.code.as_str();
238 } 196 if matches!(
197 code,
198 "dead_code"
199 | "unknown_lints"
200 | "unreachable_code"
201 | "unused_attributes"
202 | "unused_imports"
203 | "unused_macros"
204 | "unused_variables"
205 ) {
206 tags.push(lsp_types::DiagnosticTag::Unnecessary);
207 }
239 208
240 if is_deprecated(rd) { 209 if matches!(code, "deprecated") {
241 tags.push(DiagnosticTag::Deprecated); 210 tags.push(lsp_types::DiagnosticTag::Deprecated);
211 }
242 } 212 }
243 213
244 primary_spans 214 primary_spans
245 .iter() 215 .iter()
246 .map(|primary_span| { 216 .map(|primary_span| {
247 let location = map_span_to_location(&primary_span, workspace_root); 217 let location = location(workspace_root, &primary_span);
248 218
249 let mut message = message.clone(); 219 let mut message = message.clone();
250 if needs_primary_span_label { 220 if needs_primary_span_label {
@@ -256,17 +226,16 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
256 // If error occurs from macro expansion, add related info pointing to 226 // If error occurs from macro expansion, add related info pointing to
257 // where the error originated 227 // where the error originated
258 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { 228 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
259 let def_loc = map_span_to_location_naive(&primary_span, workspace_root); 229 related_information.push(lsp_types::DiagnosticRelatedInformation {
260 related_information.push(DiagnosticRelatedInformation { 230 location: location_naive(workspace_root, &primary_span),
261 location: def_loc,
262 message: "Error originated from macro here".to_string(), 231 message: "Error originated from macro here".to_string(),
263 }); 232 });
264 } 233 }
265 234
266 let diagnostic = Diagnostic { 235 let diagnostic = lsp_types::Diagnostic {
267 range: location.range, 236 range: location.range,
268 severity, 237 severity,
269 code: code.clone().map(NumberOrString::String), 238 code: code.clone().map(lsp_types::NumberOrString::String),
270 source: Some(source.clone()), 239 source: Some(source.clone()),
271 message, 240 message,
272 related_information: if related_information.is_empty() { 241 related_information: if related_information.is_empty() {
@@ -277,7 +246,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
277 tags: if tags.is_empty() { None } else { Some(tags.clone()) }, 246 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
278 }; 247 };
279 248
280 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() } 249 MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }
281 }) 250 })
282 .collect() 251 .collect()
283} 252}
@@ -287,13 +256,22 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
287mod tests { 256mod tests {
288 use super::*; 257 use super::*;
289 258
290 fn parse_diagnostic(val: &str) -> flycheck::Diagnostic { 259 use expect::{expect_file, ExpectFile};
291 serde_json::from_str::<flycheck::Diagnostic>(val).unwrap() 260
261 fn check(diagnostics_json: &str, expect: ExpectFile) {
262 check_with_config(DiagnosticsConfig::default(), diagnostics_json, expect)
263 }
264
265 fn check_with_config(config: DiagnosticsConfig, diagnostics_json: &str, expect: ExpectFile) {
266 let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap();
267 let workspace_root = Path::new("/test/");
268 let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root);
269 expect.assert_debug_eq(&actual)
292 } 270 }
293 271
294 #[test] 272 #[test]
295 fn snap_rustc_incompatible_type_for_trait() { 273 fn rustc_incompatible_type_for_trait() {
296 let diag = parse_diagnostic( 274 check(
297 r##"{ 275 r##"{
298 "message": "method `next` has an incompatible type for trait", 276 "message": "method `next` has an incompatible type for trait",
299 "code": { 277 "code": {
@@ -337,16 +315,13 @@ mod tests {
337 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n" 315 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
338 } 316 }
339 "##, 317 "##,
318 expect_file!["crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt"],
340 ); 319 );
341
342 let workspace_root = Path::new("/test/");
343 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
344 insta::assert_debug_snapshot!(diag);
345 } 320 }
346 321
347 #[test] 322 #[test]
348 fn snap_rustc_unused_variable() { 323 fn rustc_unused_variable() {
349 let diag = parse_diagnostic( 324 check(
350 r##"{ 325 r##"{
351 "message": "unused variable: `foo`", 326 "message": "unused variable: `foo`",
352 "code": { 327 "code": {
@@ -419,17 +394,18 @@ mod tests {
419 ], 394 ],
420 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n" 395 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
421 }"##, 396 }"##,
397 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable.txt"],
422 ); 398 );
423
424 let workspace_root = Path::new("/test/");
425 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
426 insta::assert_debug_snapshot!(diag);
427 } 399 }
428 400
429 #[test] 401 #[test]
430 #[cfg(not(windows))] 402 #[cfg(not(windows))]
431 fn snap_rustc_unused_variable_as_info() { 403 fn rustc_unused_variable_as_info() {
432 let diag = parse_diagnostic( 404 check_with_config(
405 DiagnosticsConfig {
406 warnings_as_info: vec!["unused_variables".to_string()],
407 ..DiagnosticsConfig::default()
408 },
433 r##"{ 409 r##"{
434 "message": "unused variable: `foo`", 410 "message": "unused variable: `foo`",
435 "code": { 411 "code": {
@@ -502,22 +478,18 @@ mod tests {
502 ], 478 ],
503 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n" 479 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
504 }"##, 480 }"##,
481 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt"],
505 ); 482 );
506
507 let config = DiagnosticsConfig {
508 warnings_as_info: vec!["unused_variables".to_string()],
509 ..DiagnosticsConfig::default()
510 };
511
512 let workspace_root = Path::new("/test/");
513 let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
514 insta::assert_debug_snapshot!(diag);
515 } 483 }
516 484
517 #[test] 485 #[test]
518 #[cfg(not(windows))] 486 #[cfg(not(windows))]
519 fn snap_rustc_unused_variable_as_hint() { 487 fn rustc_unused_variable_as_hint() {
520 let diag = parse_diagnostic( 488 check_with_config(
489 DiagnosticsConfig {
490 warnings_as_hint: vec!["unused_variables".to_string()],
491 ..DiagnosticsConfig::default()
492 },
521 r##"{ 493 r##"{
522 "message": "unused variable: `foo`", 494 "message": "unused variable: `foo`",
523 "code": { 495 "code": {
@@ -590,21 +562,13 @@ mod tests {
590 ], 562 ],
591 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n" 563 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
592 }"##, 564 }"##,
565 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt"],
593 ); 566 );
594
595 let config = DiagnosticsConfig {
596 warnings_as_hint: vec!["unused_variables".to_string()],
597 ..DiagnosticsConfig::default()
598 };
599
600 let workspace_root = Path::new("/test/");
601 let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
602 insta::assert_debug_snapshot!(diag);
603 } 567 }
604 568
605 #[test] 569 #[test]
606 fn snap_rustc_wrong_number_of_parameters() { 570 fn rustc_wrong_number_of_parameters() {
607 let diag = parse_diagnostic( 571 check(
608 r##"{ 572 r##"{
609 "message": "this function takes 2 parameters but 3 parameters were supplied", 573 "message": "this function takes 2 parameters but 3 parameters were supplied",
610 "code": { 574 "code": {
@@ -719,16 +683,13 @@ mod tests {
719 "children": [], 683 "children": [],
720 "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n" 684 "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
721 }"##, 685 }"##,
686 expect_file!["crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt"],
722 ); 687 );
723
724 let workspace_root = Path::new("/test/");
725 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
726 insta::assert_debug_snapshot!(diag);
727 } 688 }
728 689
729 #[test] 690 #[test]
730 fn snap_clippy_pass_by_ref() { 691 fn clippy_pass_by_ref() {
731 let diag = parse_diagnostic( 692 check(
732 r##"{ 693 r##"{
733 "message": "this argument is passed by reference, but would be more efficient if passed by value", 694 "message": "this argument is passed by reference, but would be more efficient if passed by value",
734 "code": { 695 "code": {
@@ -839,16 +800,13 @@ mod tests {
839 ], 800 ],
840 "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n" 801 "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
841 }"##, 802 }"##,
803 expect_file!["crates/rust-analyzer/test_data/clippy_pass_by_ref.txt"],
842 ); 804 );
843
844 let workspace_root = Path::new("/test/");
845 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
846 insta::assert_debug_snapshot!(diag);
847 } 805 }
848 806
849 #[test] 807 #[test]
850 fn snap_rustc_mismatched_type() { 808 fn rustc_mismatched_type() {
851 let diag = parse_diagnostic( 809 check(
852 r##"{ 810 r##"{
853 "message": "mismatched types", 811 "message": "mismatched types",
854 "code": { 812 "code": {
@@ -882,16 +840,13 @@ mod tests {
882 "children": [], 840 "children": [],
883 "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n" 841 "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
884 }"##, 842 }"##,
843 expect_file!["crates/rust-analyzer/test_data/rustc_mismatched_type.txt"],
885 ); 844 );
886
887 let workspace_root = Path::new("/test/");
888 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
889 insta::assert_debug_snapshot!(diag);
890 } 845 }
891 846
892 #[test] 847 #[test]
893 fn snap_handles_macro_location() { 848 fn handles_macro_location() {
894 let diag = parse_diagnostic( 849 check(
895 r##"{ 850 r##"{
896 "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n", 851 "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
897 "children": [ 852 "children": [
@@ -1153,16 +1108,13 @@ mod tests {
1153 } 1108 }
1154 ] 1109 ]
1155 }"##, 1110 }"##,
1111 expect_file!["crates/rust-analyzer/test_data/handles_macro_location.txt"],
1156 ); 1112 );
1157
1158 let workspace_root = Path::new("/test/");
1159 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1160 insta::assert_debug_snapshot!(diag);
1161 } 1113 }
1162 1114
1163 #[test] 1115 #[test]
1164 fn snap_macro_compiler_error() { 1116 fn macro_compiler_error() {
1165 let diag = parse_diagnostic( 1117 check(
1166 r##"{ 1118 r##"{
1167 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n", 1119 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n",
1168 "children": [], 1120 "children": [],
@@ -1382,16 +1334,13 @@ mod tests {
1382 ] 1334 ]
1383 } 1335 }
1384 "##, 1336 "##,
1337 expect_file!["crates/rust-analyzer/test_data/macro_compiler_error.txt"],
1385 ); 1338 );
1386
1387 let workspace_root = Path::new("/test/");
1388 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1389 insta::assert_debug_snapshot!(diag);
1390 } 1339 }
1391 1340
1392 #[test] 1341 #[test]
1393 fn snap_multi_line_fix() { 1342 fn snap_multi_line_fix() {
1394 let diag = parse_diagnostic( 1343 check(
1395 r##"{ 1344 r##"{
1396 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n", 1345 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n",
1397 "children": [ 1346 "children": [
@@ -1515,10 +1464,7 @@ mod tests {
1515 ] 1464 ]
1516 } 1465 }
1517 "##, 1466 "##,
1467 expect_file!["crates/rust-analyzer/test_data/snap_multi_line_fix.txt"],
1518 ); 1468 );
1519
1520 let workspace_root = Path::new("/test/");
1521 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1522 insta::assert_debug_snapshot!(diag);
1523 } 1469 }
1524} 1470}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 5900886e7..4e556bd50 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -238,7 +238,7 @@ impl GlobalState {
238 &workspace_root, 238 &workspace_root,
239 ); 239 );
240 for diag in diagnostics { 240 for diag in diagnostics {
241 match url_to_file_id(&self.vfs.read().0, &diag.location.uri) { 241 match url_to_file_id(&self.vfs.read().0, &diag.url) {
242 Ok(file_id) => self.diagnostics.add_check_diagnostic( 242 Ok(file_id) => self.diagnostics.add_check_diagnostic(
243 file_id, 243 file_id,
244 diag.diagnostic, 244 diag.diagnostic,
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap b/crates/rust-analyzer/test_data/clippy_pass_by_ref.txt
index d7f9ec049..d06517126 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap
+++ b/crates/rust-analyzer/test_data/clippy_pass_by_ref.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/compiler/mir/tagset.rs",
8 uri: "file:///test/compiler/mir/tagset.rs",
9 range: Range {
10 start: Position {
11 line: 41,
12 character: 23,
13 },
14 end: Position {
15 line: 41,
16 character: 28,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap b/crates/rust-analyzer/test_data/handles_macro_location.txt
index a59faf254..f5de2f07f 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap
+++ b/crates/rust-analyzer/test_data/handles_macro_location.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/src/main.rs",
8 uri: "file:///test/src/main.rs",
9 range: Range {
10 start: Position {
11 line: 1,
12 character: 4,
13 },
14 end: Position {
15 line: 1,
16 character: 26,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap b/crates/rust-analyzer/test_data/macro_compiler_error.txt
index 3c78e7f36..f695db73c 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap
+++ b/crates/rust-analyzer/test_data/macro_compiler_error.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/crates/ra_hir_def/src/data.rs",
8 uri: "file:///test/crates/ra_hir_def/src/data.rs",
9 range: Range {
10 start: Position {
11 line: 79,
12 character: 15,
13 },
14 end: Position {
15 line: 79,
16 character: 41,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap b/crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt
index 46d0c56d2..fc54440be 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/compiler/ty/list_iter.rs",
8 uri: "file:///test/compiler/ty/list_iter.rs",
9 range: Range {
10 start: Position {
11 line: 51,
12 character: 4,
13 },
14 end: Position {
15 line: 51,
16 character: 47,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap b/crates/rust-analyzer/test_data/rustc_mismatched_type.txt
index 4182929ba..c269af218 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap
+++ b/crates/rust-analyzer/test_data/rustc_mismatched_type.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/runtime/compiler_support.rs",
8 uri: "file:///test/runtime/compiler_support.rs",
9 range: Range {
10 start: Position {
11 line: 47,
12 character: 64,
13 },
14 end: Position {
15 line: 47,
16 character: 69,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/test_data/rustc_unused_variable.txt
index 9a7972ff5..81f180a86 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/test_data/rustc_unused_variable.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/driver/subcommand/repl.rs",
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap b/crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt
index f0273315e..d5d78fd80 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap
+++ b/crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/driver/subcommand/repl.rs",
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap b/crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt
index 85fd050fd..2a7505c59 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap
+++ b/crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/driver/subcommand/repl.rs",
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap b/crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt
index f6ab05004..efe37261d 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/compiler/ty/select.rs",
8 uri: "file:///test/compiler/ty/select.rs",
9 range: Range {
10 start: Position {
11 line: 103,
12 character: 17,
13 },
14 end: Position {
15 line: 103,
16 character: 29,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/test_data/snap_multi_line_fix.txt
index 272057b47..6eca3ae2f 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/test_data/snap_multi_line_fix.txt
@@ -1,22 +1,6 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[ 1[
6 MappedRustDiagnostic { 2 MappedRustDiagnostic {
7 location: Location { 3 url: "file:///test/src/main.rs",
8 uri: "file:///test/src/main.rs",
9 range: Range {
10 start: Position {
11 line: 3,
12 character: 4,
13 },
14 end: Position {
15 line: 3,
16 character: 5,
17 },
18 },
19 },
20 diagnostic: Diagnostic { 4 diagnostic: Diagnostic {
21 range: Range { 5 range: Range {
22 start: Position { 6 start: Position {