aboutsummaryrefslogtreecommitdiff
path: root/crates/rust-analyzer/src/diagnostics
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-05-15 00:51:48 +0100
committerAleksey Kladov <[email protected]>2020-05-15 00:52:25 +0100
commit220813dcb0881ff199619c11eb34a39a6de0f67a (patch)
treebf2bcdce03889a356b37bfe29253de4228ded6ea /crates/rust-analyzer/src/diagnostics
parent12d82687cd600ec81bf3027661135e5f0d4ad4bd (diff)
Move LSP bits from flycheck to rust-analyzer
There should be only one place that knows about LSP, and that place is right before we spit JSON on stdout.
Diffstat (limited to 'crates/rust-analyzer/src/diagnostics')
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap83
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap48
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap63
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap114
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap48
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap48
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap86
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap67
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs1408
9 files changed, 1965 insertions, 0 deletions
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/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap
new file mode 100644
index 000000000..d7f9ec049
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap
@@ -0,0 +1,83 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 41,
24 character: 23,
25 },
26 end: Position {
27 line: 41,
28 character: 28,
29 },
30 },
31 severity: Some(
32 Warning,
33 ),
34 code: Some(
35 String(
36 "trivially_copy_pass_by_ref",
37 ),
38 ),
39 source: Some(
40 "clippy",
41 ),
42 message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
43 related_information: Some(
44 [
45 DiagnosticRelatedInformation {
46 location: Location {
47 uri: "file:///test/compiler/lib.rs",
48 range: Range {
49 start: Position {
50 line: 0,
51 character: 8,
52 },
53 end: Position {
54 line: 0,
55 character: 19,
56 },
57 },
58 },
59 message: "lint level defined here",
60 },
61 DiagnosticRelatedInformation {
62 location: Location {
63 uri: "file:///test/compiler/mir/tagset.rs",
64 range: Range {
65 start: Position {
66 line: 41,
67 character: 23,
68 },
69 end: Position {
70 line: 41,
71 character: 28,
72 },
73 },
74 },
75 message: "consider passing by value instead",
76 },
77 ],
78 ),
79 tags: None,
80 },
81 fixes: [],
82 },
83]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap
new file mode 100644
index 000000000..a59faf254
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap
@@ -0,0 +1,48 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 1,
24 character: 4,
25 },
26 end: Position {
27 line: 1,
28 character: 26,
29 },
30 },
31 severity: Some(
32 Error,
33 ),
34 code: Some(
35 String(
36 "E0277",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
43 related_information: None,
44 tags: None,
45 },
46 fixes: [],
47 },
48]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap
new file mode 100644
index 000000000..3c78e7f36
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap
@@ -0,0 +1,63 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 79,
24 character: 15,
25 },
26 end: Position {
27 line: 79,
28 character: 41,
29 },
30 },
31 severity: Some(
32 Error,
33 ),
34 code: None,
35 source: Some(
36 "rustc",
37 ),
38 message: "Please register your known path in the path module",
39 related_information: Some(
40 [
41 DiagnosticRelatedInformation {
42 location: Location {
43 uri: "file:///test/crates/ra_hir_def/src/path.rs",
44 range: Range {
45 start: Position {
46 line: 264,
47 character: 8,
48 },
49 end: Position {
50 line: 264,
51 character: 76,
52 },
53 },
54 },
55 message: "Error originated from macro here",
56 },
57 ],
58 ),
59 tags: None,
60 },
61 fixes: [],
62 },
63]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
new file mode 100644
index 000000000..076b3cf27
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
@@ -0,0 +1,114 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 3,
24 character: 4,
25 },
26 end: Position {
27 line: 3,
28 character: 5,
29 },
30 },
31 severity: Some(
32 Warning,
33 ),
34 code: Some(
35 String(
36 "let_and_return",
37 ),
38 ),
39 source: Some(
40 "clippy",
41 ),
42 message: "returning the result of a let binding from a block\n`#[warn(clippy::let_and_return)]` on by default\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
43 related_information: Some(
44 [
45 DiagnosticRelatedInformation {
46 location: Location {
47 uri: "file:///test/src/main.rs",
48 range: Range {
49 start: Position {
50 line: 2,
51 character: 4,
52 },
53 end: Position {
54 line: 2,
55 character: 30,
56 },
57 },
58 },
59 message: "unnecessary let binding",
60 },
61 ],
62 ),
63 tags: None,
64 },
65 fixes: [
66 CodeAction {
67 title: "return the expression directly",
68 kind: Some(
69 "quickfix",
70 ),
71 diagnostics: None,
72 edit: Some(
73 WorkspaceEdit {
74 changes: Some(
75 {
76 "file:///test/src/main.rs": [
77 TextEdit {
78 range: Range {
79 start: Position {
80 line: 2,
81 character: 4,
82 },
83 end: Position {
84 line: 2,
85 character: 30,
86 },
87 },
88 new_text: "",
89 },
90 TextEdit {
91 range: Range {
92 start: Position {
93 line: 3,
94 character: 4,
95 },
96 end: Position {
97 line: 3,
98 character: 5,
99 },
100 },
101 new_text: "(0..10).collect()",
102 },
103 ],
104 },
105 ),
106 document_changes: None,
107 },
108 ),
109 command: None,
110 is_preferred: None,
111 },
112 ],
113 },
114]
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/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap
new file mode 100644
index 000000000..46d0c56d2
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap
@@ -0,0 +1,48 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 51,
24 character: 4,
25 },
26 end: Position {
27 line: 51,
28 character: 47,
29 },
30 },
31 severity: Some(
32 Error,
33 ),
34 code: Some(
35 String(
36 "E0053",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "method `next` has an incompatible type for trait\nexpected 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>>`",
43 related_information: None,
44 tags: None,
45 },
46 fixes: [],
47 },
48]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap
new file mode 100644
index 000000000..4182929ba
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap
@@ -0,0 +1,48 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 47,
24 character: 64,
25 },
26 end: Position {
27 line: 47,
28 character: 69,
29 },
30 },
31 severity: Some(
32 Error,
33 ),
34 code: Some(
35 String(
36 "E0308",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "mismatched types\nexpected usize, found u32",
43 related_information: None,
44 tags: None,
45 },
46 fixes: [],
47 },
48]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
new file mode 100644
index 000000000..69138c15b
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -0,0 +1,86 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 290,
24 character: 8,
25 },
26 end: Position {
27 line: 290,
28 character: 11,
29 },
30 },
31 severity: Some(
32 Warning,
33 ),
34 code: Some(
35 String(
36 "unused_variables",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
43 related_information: None,
44 tags: Some(
45 [
46 Unnecessary,
47 ],
48 ),
49 },
50 fixes: [
51 CodeAction {
52 title: "consider prefixing with an underscore",
53 kind: Some(
54 "quickfix",
55 ),
56 diagnostics: None,
57 edit: Some(
58 WorkspaceEdit {
59 changes: Some(
60 {
61 "file:///test/driver/subcommand/repl.rs": [
62 TextEdit {
63 range: Range {
64 start: Position {
65 line: 290,
66 character: 8,
67 },
68 end: Position {
69 line: 290,
70 character: 11,
71 },
72 },
73 new_text: "_foo",
74 },
75 ],
76 },
77 ),
78 document_changes: None,
79 },
80 ),
81 command: None,
82 is_preferred: None,
83 },
84 ],
85 },
86]
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/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap
new file mode 100644
index 000000000..f6ab05004
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap
@@ -0,0 +1,67 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
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 {
21 range: Range {
22 start: Position {
23 line: 103,
24 character: 17,
25 },
26 end: Position {
27 line: 103,
28 character: 29,
29 },
30 },
31 severity: Some(
32 Error,
33 ),
34 code: Some(
35 String(
36 "E0061",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters",
43 related_information: Some(
44 [
45 DiagnosticRelatedInformation {
46 location: Location {
47 uri: "file:///test/compiler/ty/select.rs",
48 range: Range {
49 start: Position {
50 line: 218,
51 character: 4,
52 },
53 end: Position {
54 line: 230,
55 character: 5,
56 },
57 },
58 },
59 message: "defined here",
60 },
61 ],
62 ),
63 tags: None,
64 },
65 fixes: [],
66 },
67]
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
new file mode 100644
index 000000000..0c8108591
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -0,0 +1,1408 @@
1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format.
3use cargo_metadata::diagnostic::{
4 Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan,
5 DiagnosticSpanMacroExpansion,
6};
7use lsp_types::{
8 CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag,
9 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit,
10};
11use std::{
12 collections::HashMap,
13 fmt::Write,
14 path::{Component, Path, PathBuf, Prefix},
15 str::FromStr,
16};
17
18/// Converts a Rust level string to a LSP severity
19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
20 match val {
21 DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error),
22 DiagnosticLevel::Error => Some(DiagnosticSeverity::Error),
23 DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning),
24 DiagnosticLevel::Note => Some(DiagnosticSeverity::Information),
25 DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint),
26 DiagnosticLevel::Unknown => None,
27 }
28}
29
30/// Check whether a file name is from macro invocation
31fn is_from_macro(file_name: &str) -> bool {
32 file_name.starts_with('<') && file_name.ends_with('>')
33}
34
35/// Converts a Rust macro span to a LSP location recursively
36fn map_macro_span_to_location(
37 span_macro: &DiagnosticSpanMacroExpansion,
38 workspace_root: &PathBuf,
39) -> Option<Location> {
40 if !is_from_macro(&span_macro.span.file_name) {
41 return Some(map_span_to_location(&span_macro.span, workspace_root));
42 }
43
44 if let Some(expansion) = &span_macro.span.expansion {
45 return map_macro_span_to_location(&expansion, workspace_root);
46 }
47
48 None
49}
50
51/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
52fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
53 if span.expansion.is_some() {
54 let expansion = span.expansion.as_ref().unwrap();
55 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
56 return macro_range;
57 }
58 }
59
60 map_span_to_location_naive(span, workspace_root)
61}
62
63/// Converts a Rust span to a LSP location
64fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
65 let mut file_name = workspace_root.clone();
66 file_name.push(&span.file_name);
67 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap();
68
69 let range = Range::new(
70 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
71 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
72 );
73
74 Location { uri, range }
75}
76
77/// Converts a secondary Rust span to a LSP related information
78///
79/// If the span is unlabelled this will return `None`.
80fn map_secondary_span_to_related(
81 span: &DiagnosticSpan,
82 workspace_root: &PathBuf,
83) -> Option<DiagnosticRelatedInformation> {
84 if let Some(label) = &span.label {
85 let location = map_span_to_location(span, workspace_root);
86 Some(DiagnosticRelatedInformation { location, message: label.clone() })
87 } else {
88 // Nothing to label this with
89 None
90 }
91}
92
93/// Determines if diagnostic is related to unused code
94fn is_unused_or_unnecessary(rd: &RustDiagnostic) -> bool {
95 if let Some(code) = &rd.code {
96 match code.code.as_str() {
97 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
98 | "unused_imports" | "unused_macros" | "unused_variables" => true,
99 _ => false,
100 }
101 } else {
102 false
103 }
104}
105
106/// Determines if diagnostic is related to deprecated code
107fn is_deprecated(rd: &RustDiagnostic) -> bool {
108 if let Some(code) = &rd.code {
109 match code.code.as_str() {
110 "deprecated" => true,
111 _ => false,
112 }
113 } else {
114 false
115 }
116}
117
118enum MappedRustChildDiagnostic {
119 Related(DiagnosticRelatedInformation),
120 SuggestedFix(CodeAction),
121 MessageLine(String),
122}
123
124fn map_rust_child_diagnostic(
125 rd: &RustDiagnostic,
126 workspace_root: &PathBuf,
127) -> MappedRustChildDiagnostic {
128 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
129 if spans.is_empty() {
130 // `rustc` uses these spanless children as a way to print multi-line
131 // messages
132 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
133 }
134
135 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new();
136 for &span in &spans {
137 match (&span.suggestion_applicability, &span.suggested_replacement) {
138 (Some(Applicability::MachineApplicable), Some(suggested_replacement)) => {
139 let location = map_span_to_location(span, workspace_root);
140 let edit = TextEdit::new(location.range, suggested_replacement.clone());
141 edit_map.entry(location.uri).or_default().push(edit);
142 }
143 _ => {}
144 }
145 }
146
147 if !edit_map.is_empty() {
148 MappedRustChildDiagnostic::SuggestedFix(CodeAction {
149 title: rd.message.clone(),
150 kind: Some("quickfix".to_string()),
151 diagnostics: None,
152 edit: Some(WorkspaceEdit::new(edit_map)),
153 command: None,
154 is_preferred: None,
155 })
156 } else {
157 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
158 location: map_span_to_location(spans[0], workspace_root),
159 message: rd.message.clone(),
160 })
161 }
162}
163
164#[derive(Debug)]
165pub(crate) struct MappedRustDiagnostic {
166 pub location: Location,
167 pub diagnostic: Diagnostic,
168 pub fixes: Vec<CodeAction>,
169}
170
171/// Converts a Rust root diagnostic to LSP form
172///
173/// This flattens the Rust diagnostic by:
174///
175/// 1. Creating a LSP diagnostic with the root message and primary span.
176/// 2. Adding any labelled secondary spans to `relatedInformation`
177/// 3. Categorising child diagnostics as either `SuggestedFix`es,
178/// `relatedInformation` or additional message lines.
179///
180/// If the diagnostic has no primary span this will return `None`
181pub(crate) fn map_rust_diagnostic_to_lsp(
182 rd: &RustDiagnostic,
183 workspace_root: &PathBuf,
184) -> Vec<MappedRustDiagnostic> {
185 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
186 if primary_spans.is_empty() {
187 return vec![];
188 }
189
190 let severity = map_level_to_severity(rd.level);
191
192 let mut source = String::from("rustc");
193 let mut code = rd.code.as_ref().map(|c| c.code.clone());
194 if let Some(code_val) = &code {
195 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
196 let scoped_code: Vec<&str> = code_val.split("::").collect();
197 if scoped_code.len() == 2 {
198 source = String::from(scoped_code[0]);
199 code = Some(String::from(scoped_code[1]));
200 }
201 }
202
203 let mut needs_primary_span_label = true;
204 let mut related_information = vec![];
205 let mut tags = vec![];
206
207 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
208 let related = map_secondary_span_to_related(secondary_span, workspace_root);
209 if let Some(related) = related {
210 related_information.push(related);
211 }
212 }
213
214 let mut fixes = vec![];
215 let mut message = rd.message.clone();
216 for child in &rd.children {
217 let child = map_rust_child_diagnostic(&child, workspace_root);
218 match child {
219 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
220 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
221 MappedRustChildDiagnostic::MessageLine(message_line) => {
222 write!(&mut message, "\n{}", message_line).unwrap();
223
224 // These secondary messages usually duplicate the content of the
225 // primary span label.
226 needs_primary_span_label = false;
227 }
228 }
229 }
230
231 if is_unused_or_unnecessary(rd) {
232 tags.push(DiagnosticTag::Unnecessary);
233 }
234
235 if is_deprecated(rd) {
236 tags.push(DiagnosticTag::Deprecated);
237 }
238
239 primary_spans
240 .iter()
241 .map(|primary_span| {
242 let location = map_span_to_location(&primary_span, workspace_root);
243
244 let mut message = message.clone();
245 if needs_primary_span_label {
246 if let Some(primary_span_label) = &primary_span.label {
247 write!(&mut message, "\n{}", primary_span_label).unwrap();
248 }
249 }
250
251 // If error occurs from macro expansion, add related info pointing to
252 // where the error originated
253 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
254 let def_loc = map_span_to_location_naive(&primary_span, workspace_root);
255 related_information.push(DiagnosticRelatedInformation {
256 location: def_loc,
257 message: "Error originated from macro here".to_string(),
258 });
259 }
260
261 let diagnostic = Diagnostic {
262 range: location.range,
263 severity,
264 code: code.clone().map(NumberOrString::String),
265 source: Some(source.clone()),
266 message,
267 related_information: if !related_information.is_empty() {
268 Some(related_information.clone())
269 } else {
270 None
271 },
272 tags: if !tags.is_empty() { Some(tags.clone()) } else { None },
273 };
274
275 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() }
276 })
277 .collect()
278}
279
280/// Returns a `Url` object from a given path, will lowercase drive letters if present.
281/// This will only happen when processing windows paths.
282///
283/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
284pub fn url_from_path_with_drive_lowercasing(
285 path: impl AsRef<Path>,
286) -> Result<Url, Box<dyn std::error::Error + Send + Sync>> {
287 let component_has_windows_drive = path.as_ref().components().any(|comp| {
288 if let Component::Prefix(c) = comp {
289 match c.kind() {
290 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
291 _ => return false,
292 }
293 }
294 false
295 });
296
297 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
298 if component_has_windows_drive {
299 let url_original = Url::from_file_path(&path)
300 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
301
302 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
303
304 // There is a drive partition, but we never found a colon.
305 // This should not happen, but in this case we just pass it through.
306 if drive_partition.len() == 1 {
307 return Ok(url_original);
308 }
309
310 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
311 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
312
313 Ok(url)
314 } else {
315 Ok(Url::from_file_path(&path)
316 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 // `Url` is not able to parse windows paths on unix machines.
325 #[test]
326 #[cfg(target_os = "windows")]
327 fn test_lowercase_drive_letter_with_drive() {
328 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
329
330 assert_eq!(url.to_string(), "file:///c:/Test");
331 }
332
333 #[test]
334 #[cfg(target_os = "windows")]
335 fn test_drive_without_colon_passthrough() {
336 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
337
338 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
339 }
340
341 #[cfg(not(windows))]
342 fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic {
343 serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap()
344 }
345
346 #[test]
347 #[cfg(not(windows))]
348 fn snap_rustc_incompatible_type_for_trait() {
349 let diag = parse_diagnostic(
350 r##"{
351 "message": "method `next` has an incompatible type for trait",
352 "code": {
353 "code": "E0053",
354 "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n"
355 },
356 "level": "error",
357 "spans": [
358 {
359 "file_name": "compiler/ty/list_iter.rs",
360 "byte_start": 1307,
361 "byte_end": 1350,
362 "line_start": 52,
363 "line_end": 52,
364 "column_start": 5,
365 "column_end": 48,
366 "is_primary": true,
367 "text": [
368 {
369 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
370 "highlight_start": 5,
371 "highlight_end": 48
372 }
373 ],
374 "label": "types differ in mutability",
375 "suggested_replacement": null,
376 "suggestion_applicability": null,
377 "expansion": null
378 }
379 ],
380 "children": [
381 {
382 "message": "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>>`",
383 "code": null,
384 "level": "note",
385 "spans": [],
386 "children": [],
387 "rendered": null
388 }
389 ],
390 "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"
391 }
392 "##,
393 );
394
395 let workspace_root = PathBuf::from("/test/");
396 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
397 insta::assert_debug_snapshot!(diag);
398 }
399
400 #[test]
401 #[cfg(not(windows))]
402 fn snap_rustc_unused_variable() {
403 let diag = parse_diagnostic(
404 r##"{
405 "message": "unused variable: `foo`",
406 "code": {
407 "code": "unused_variables",
408 "explanation": null
409 },
410 "level": "warning",
411 "spans": [
412 {
413 "file_name": "driver/subcommand/repl.rs",
414 "byte_start": 9228,
415 "byte_end": 9231,
416 "line_start": 291,
417 "line_end": 291,
418 "column_start": 9,
419 "column_end": 12,
420 "is_primary": true,
421 "text": [
422 {
423 "text": " let foo = 42;",
424 "highlight_start": 9,
425 "highlight_end": 12
426 }
427 ],
428 "label": null,
429 "suggested_replacement": null,
430 "suggestion_applicability": null,
431 "expansion": null
432 }
433 ],
434 "children": [
435 {
436 "message": "#[warn(unused_variables)] on by default",
437 "code": null,
438 "level": "note",
439 "spans": [],
440 "children": [],
441 "rendered": null
442 },
443 {
444 "message": "consider prefixing with an underscore",
445 "code": null,
446 "level": "help",
447 "spans": [
448 {
449 "file_name": "driver/subcommand/repl.rs",
450 "byte_start": 9228,
451 "byte_end": 9231,
452 "line_start": 291,
453 "line_end": 291,
454 "column_start": 9,
455 "column_end": 12,
456 "is_primary": true,
457 "text": [
458 {
459 "text": " let foo = 42;",
460 "highlight_start": 9,
461 "highlight_end": 12
462 }
463 ],
464 "label": null,
465 "suggested_replacement": "_foo",
466 "suggestion_applicability": "MachineApplicable",
467 "expansion": null
468 }
469 ],
470 "children": [],
471 "rendered": null
472 }
473 ],
474 "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"
475 }"##,
476 );
477
478 let workspace_root = PathBuf::from("/test/");
479 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
480 insta::assert_debug_snapshot!(diag);
481 }
482
483 #[test]
484 #[cfg(not(windows))]
485 fn snap_rustc_wrong_number_of_parameters() {
486 let diag = parse_diagnostic(
487 r##"{
488 "message": "this function takes 2 parameters but 3 parameters were supplied",
489 "code": {
490 "code": "E0061",
491 "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n"
492 },
493 "level": "error",
494 "spans": [
495 {
496 "file_name": "compiler/ty/select.rs",
497 "byte_start": 8787,
498 "byte_end": 9241,
499 "line_start": 219,
500 "line_end": 231,
501 "column_start": 5,
502 "column_end": 6,
503 "is_primary": false,
504 "text": [
505 {
506 "text": " pub fn add_evidence(",
507 "highlight_start": 5,
508 "highlight_end": 25
509 },
510 {
511 "text": " &mut self,",
512 "highlight_start": 1,
513 "highlight_end": 19
514 },
515 {
516 "text": " target_poly: &ty::Ref<ty::Poly>,",
517 "highlight_start": 1,
518 "highlight_end": 41
519 },
520 {
521 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
522 "highlight_start": 1,
523 "highlight_end": 43
524 },
525 {
526 "text": " ) {",
527 "highlight_start": 1,
528 "highlight_end": 8
529 },
530 {
531 "text": " match target_poly {",
532 "highlight_start": 1,
533 "highlight_end": 28
534 },
535 {
536 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
537 "highlight_start": 1,
538 "highlight_end": 81
539 },
540 {
541 "text": " ty::Ref::Fixed(target_ty) => {",
542 "highlight_start": 1,
543 "highlight_end": 43
544 },
545 {
546 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
547 "highlight_start": 1,
548 "highlight_end": 65
549 },
550 {
551 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
552 "highlight_start": 1,
553 "highlight_end": 76
554 },
555 {
556 "text": " }",
557 "highlight_start": 1,
558 "highlight_end": 14
559 },
560 {
561 "text": " }",
562 "highlight_start": 1,
563 "highlight_end": 10
564 },
565 {
566 "text": " }",
567 "highlight_start": 1,
568 "highlight_end": 6
569 }
570 ],
571 "label": "defined here",
572 "suggested_replacement": null,
573 "suggestion_applicability": null,
574 "expansion": null
575 },
576 {
577 "file_name": "compiler/ty/select.rs",
578 "byte_start": 4045,
579 "byte_end": 4057,
580 "line_start": 104,
581 "line_end": 104,
582 "column_start": 18,
583 "column_end": 30,
584 "is_primary": true,
585 "text": [
586 {
587 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
588 "highlight_start": 18,
589 "highlight_end": 30
590 }
591 ],
592 "label": "expected 2 parameters",
593 "suggested_replacement": null,
594 "suggestion_applicability": null,
595 "expansion": null
596 }
597 ],
598 "children": [],
599 "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"
600 }"##,
601 );
602
603 let workspace_root = PathBuf::from("/test/");
604 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
605 insta::assert_debug_snapshot!(diag);
606 }
607
608 #[test]
609 #[cfg(not(windows))]
610 fn snap_clippy_pass_by_ref() {
611 let diag = parse_diagnostic(
612 r##"{
613 "message": "this argument is passed by reference, but would be more efficient if passed by value",
614 "code": {
615 "code": "clippy::trivially_copy_pass_by_ref",
616 "explanation": null
617 },
618 "level": "warning",
619 "spans": [
620 {
621 "file_name": "compiler/mir/tagset.rs",
622 "byte_start": 941,
623 "byte_end": 946,
624 "line_start": 42,
625 "line_end": 42,
626 "column_start": 24,
627 "column_end": 29,
628 "is_primary": true,
629 "text": [
630 {
631 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
632 "highlight_start": 24,
633 "highlight_end": 29
634 }
635 ],
636 "label": null,
637 "suggested_replacement": null,
638 "suggestion_applicability": null,
639 "expansion": null
640 }
641 ],
642 "children": [
643 {
644 "message": "lint level defined here",
645 "code": null,
646 "level": "note",
647 "spans": [
648 {
649 "file_name": "compiler/lib.rs",
650 "byte_start": 8,
651 "byte_end": 19,
652 "line_start": 1,
653 "line_end": 1,
654 "column_start": 9,
655 "column_end": 20,
656 "is_primary": true,
657 "text": [
658 {
659 "text": "#![warn(clippy::all)]",
660 "highlight_start": 9,
661 "highlight_end": 20
662 }
663 ],
664 "label": null,
665 "suggested_replacement": null,
666 "suggestion_applicability": null,
667 "expansion": null
668 }
669 ],
670 "children": [],
671 "rendered": null
672 },
673 {
674 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
675 "code": null,
676 "level": "note",
677 "spans": [],
678 "children": [],
679 "rendered": null
680 },
681 {
682 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
683 "code": null,
684 "level": "help",
685 "spans": [],
686 "children": [],
687 "rendered": null
688 },
689 {
690 "message": "consider passing by value instead",
691 "code": null,
692 "level": "help",
693 "spans": [
694 {
695 "file_name": "compiler/mir/tagset.rs",
696 "byte_start": 941,
697 "byte_end": 946,
698 "line_start": 42,
699 "line_end": 42,
700 "column_start": 24,
701 "column_end": 29,
702 "is_primary": true,
703 "text": [
704 {
705 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
706 "highlight_start": 24,
707 "highlight_end": 29
708 }
709 ],
710 "label": null,
711 "suggested_replacement": "self",
712 "suggestion_applicability": "Unspecified",
713 "expansion": null
714 }
715 ],
716 "children": [],
717 "rendered": null
718 }
719 ],
720 "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"
721 }"##,
722 );
723
724 let workspace_root = PathBuf::from("/test/");
725 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
726 insta::assert_debug_snapshot!(diag);
727 }
728
729 #[test]
730 #[cfg(not(windows))]
731 fn snap_rustc_mismatched_type() {
732 let diag = parse_diagnostic(
733 r##"{
734 "message": "mismatched types",
735 "code": {
736 "code": "E0308",
737 "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
738 },
739 "level": "error",
740 "spans": [
741 {
742 "file_name": "runtime/compiler_support.rs",
743 "byte_start": 1589,
744 "byte_end": 1594,
745 "line_start": 48,
746 "line_end": 48,
747 "column_start": 65,
748 "column_end": 70,
749 "is_primary": true,
750 "text": [
751 {
752 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
753 "highlight_start": 65,
754 "highlight_end": 70
755 }
756 ],
757 "label": "expected usize, found u32",
758 "suggested_replacement": null,
759 "suggestion_applicability": null,
760 "expansion": null
761 }
762 ],
763 "children": [],
764 "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"
765 }"##,
766 );
767
768 let workspace_root = PathBuf::from("/test/");
769 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
770 insta::assert_debug_snapshot!(diag);
771 }
772
773 #[test]
774 #[cfg(not(windows))]
775 fn snap_handles_macro_location() {
776 let diag = parse_diagnostic(
777 r##"{
778 "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",
779 "children": [
780 {
781 "children": [],
782 "code": null,
783 "level": "help",
784 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
785 "rendered": null,
786 "spans": []
787 }
788 ],
789 "code": {
790 "code": "E0277",
791 "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
792 },
793 "level": "error",
794 "message": "can't compare `{integer}` with `&str`",
795 "spans": [
796 {
797 "byte_end": 155,
798 "byte_start": 153,
799 "column_end": 33,
800 "column_start": 31,
801 "expansion": {
802 "def_site_span": {
803 "byte_end": 940,
804 "byte_start": 0,
805 "column_end": 6,
806 "column_start": 1,
807 "expansion": null,
808 "file_name": "<::core::macros::assert_eq macros>",
809 "is_primary": false,
810 "label": null,
811 "line_end": 36,
812 "line_start": 1,
813 "suggested_replacement": null,
814 "suggestion_applicability": null,
815 "text": [
816 {
817 "highlight_end": 35,
818 "highlight_start": 1,
819 "text": "($ left : expr, $ right : expr) =>"
820 },
821 {
822 "highlight_end": 3,
823 "highlight_start": 1,
824 "text": "({"
825 },
826 {
827 "highlight_end": 33,
828 "highlight_start": 1,
829 "text": " match (& $ left, & $ right)"
830 },
831 {
832 "highlight_end": 7,
833 "highlight_start": 1,
834 "text": " {"
835 },
836 {
837 "highlight_end": 34,
838 "highlight_start": 1,
839 "text": " (left_val, right_val) =>"
840 },
841 {
842 "highlight_end": 11,
843 "highlight_start": 1,
844 "text": " {"
845 },
846 {
847 "highlight_end": 46,
848 "highlight_start": 1,
849 "text": " if ! (* left_val == * right_val)"
850 },
851 {
852 "highlight_end": 15,
853 "highlight_start": 1,
854 "text": " {"
855 },
856 {
857 "highlight_end": 25,
858 "highlight_start": 1,
859 "text": " panic !"
860 },
861 {
862 "highlight_end": 57,
863 "highlight_start": 1,
864 "text": " (r#\"assertion failed: `(left == right)`"
865 },
866 {
867 "highlight_end": 16,
868 "highlight_start": 1,
869 "text": " left: `{:?}`,"
870 },
871 {
872 "highlight_end": 18,
873 "highlight_start": 1,
874 "text": " right: `{:?}`\"#,"
875 },
876 {
877 "highlight_end": 47,
878 "highlight_start": 1,
879 "text": " & * left_val, & * right_val)"
880 },
881 {
882 "highlight_end": 15,
883 "highlight_start": 1,
884 "text": " }"
885 },
886 {
887 "highlight_end": 11,
888 "highlight_start": 1,
889 "text": " }"
890 },
891 {
892 "highlight_end": 7,
893 "highlight_start": 1,
894 "text": " }"
895 },
896 {
897 "highlight_end": 42,
898 "highlight_start": 1,
899 "text": " }) ; ($ left : expr, $ right : expr,) =>"
900 },
901 {
902 "highlight_end": 49,
903 "highlight_start": 1,
904 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
905 },
906 {
907 "highlight_end": 53,
908 "highlight_start": 1,
909 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
910 },
911 {
912 "highlight_end": 3,
913 "highlight_start": 1,
914 "text": "({"
915 },
916 {
917 "highlight_end": 37,
918 "highlight_start": 1,
919 "text": " match (& ($ left), & ($ right))"
920 },
921 {
922 "highlight_end": 7,
923 "highlight_start": 1,
924 "text": " {"
925 },
926 {
927 "highlight_end": 34,
928 "highlight_start": 1,
929 "text": " (left_val, right_val) =>"
930 },
931 {
932 "highlight_end": 11,
933 "highlight_start": 1,
934 "text": " {"
935 },
936 {
937 "highlight_end": 46,
938 "highlight_start": 1,
939 "text": " if ! (* left_val == * right_val)"
940 },
941 {
942 "highlight_end": 15,
943 "highlight_start": 1,
944 "text": " {"
945 },
946 {
947 "highlight_end": 25,
948 "highlight_start": 1,
949 "text": " panic !"
950 },
951 {
952 "highlight_end": 57,
953 "highlight_start": 1,
954 "text": " (r#\"assertion failed: `(left == right)`"
955 },
956 {
957 "highlight_end": 16,
958 "highlight_start": 1,
959 "text": " left: `{:?}`,"
960 },
961 {
962 "highlight_end": 22,
963 "highlight_start": 1,
964 "text": " right: `{:?}`: {}\"#,"
965 },
966 {
967 "highlight_end": 72,
968 "highlight_start": 1,
969 "text": " & * left_val, & * right_val, $ crate :: format_args !"
970 },
971 {
972 "highlight_end": 33,
973 "highlight_start": 1,
974 "text": " ($ ($ arg) +))"
975 },
976 {
977 "highlight_end": 15,
978 "highlight_start": 1,
979 "text": " }"
980 },
981 {
982 "highlight_end": 11,
983 "highlight_start": 1,
984 "text": " }"
985 },
986 {
987 "highlight_end": 7,
988 "highlight_start": 1,
989 "text": " }"
990 },
991 {
992 "highlight_end": 6,
993 "highlight_start": 1,
994 "text": " }) ;"
995 }
996 ]
997 },
998 "macro_decl_name": "assert_eq!",
999 "span": {
1000 "byte_end": 38,
1001 "byte_start": 16,
1002 "column_end": 27,
1003 "column_start": 5,
1004 "expansion": null,
1005 "file_name": "src/main.rs",
1006 "is_primary": false,
1007 "label": null,
1008 "line_end": 2,
1009 "line_start": 2,
1010 "suggested_replacement": null,
1011 "suggestion_applicability": null,
1012 "text": [
1013 {
1014 "highlight_end": 27,
1015 "highlight_start": 5,
1016 "text": " assert_eq!(1, \"love\");"
1017 }
1018 ]
1019 }
1020 },
1021 "file_name": "<::core::macros::assert_eq macros>",
1022 "is_primary": true,
1023 "label": "no implementation for `{integer} == &str`",
1024 "line_end": 7,
1025 "line_start": 7,
1026 "suggested_replacement": null,
1027 "suggestion_applicability": null,
1028 "text": [
1029 {
1030 "highlight_end": 33,
1031 "highlight_start": 31,
1032 "text": " if ! (* left_val == * right_val)"
1033 }
1034 ]
1035 }
1036 ]
1037 }"##,
1038 );
1039
1040 let workspace_root = PathBuf::from("/test/");
1041 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
1042 insta::assert_debug_snapshot!(diag);
1043 }
1044
1045 #[test]
1046 #[cfg(not(windows))]
1047 fn snap_macro_compiler_error() {
1048 let diag = parse_diagnostic(
1049 r##"{
1050 "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",
1051 "children": [],
1052 "code": null,
1053 "level": "error",
1054 "message": "Please register your known path in the path module",
1055 "spans": [
1056 {
1057 "byte_end": 8285,
1058 "byte_start": 8217,
1059 "column_end": 77,
1060 "column_start": 9,
1061 "expansion": {
1062 "def_site_span": {
1063 "byte_end": 8294,
1064 "byte_start": 7858,
1065 "column_end": 2,
1066 "column_start": 1,
1067 "expansion": null,
1068 "file_name": "crates/ra_hir_def/src/path.rs",
1069 "is_primary": false,
1070 "label": null,
1071 "line_end": 267,
1072 "line_start": 254,
1073 "suggested_replacement": null,
1074 "suggestion_applicability": null,
1075 "text": [
1076 {
1077 "highlight_end": 28,
1078 "highlight_start": 1,
1079 "text": "macro_rules! __known_path {"
1080 },
1081 {
1082 "highlight_end": 37,
1083 "highlight_start": 1,
1084 "text": " (std::iter::IntoIterator) => {};"
1085 },
1086 {
1087 "highlight_end": 33,
1088 "highlight_start": 1,
1089 "text": " (std::result::Result) => {};"
1090 },
1091 {
1092 "highlight_end": 29,
1093 "highlight_start": 1,
1094 "text": " (std::ops::Range) => {};"
1095 },
1096 {
1097 "highlight_end": 33,
1098 "highlight_start": 1,
1099 "text": " (std::ops::RangeFrom) => {};"
1100 },
1101 {
1102 "highlight_end": 33,
1103 "highlight_start": 1,
1104 "text": " (std::ops::RangeFull) => {};"
1105 },
1106 {
1107 "highlight_end": 31,
1108 "highlight_start": 1,
1109 "text": " (std::ops::RangeTo) => {};"
1110 },
1111 {
1112 "highlight_end": 40,
1113 "highlight_start": 1,
1114 "text": " (std::ops::RangeToInclusive) => {};"
1115 },
1116 {
1117 "highlight_end": 38,
1118 "highlight_start": 1,
1119 "text": " (std::ops::RangeInclusive) => {};"
1120 },
1121 {
1122 "highlight_end": 27,
1123 "highlight_start": 1,
1124 "text": " (std::ops::Try) => {};"
1125 },
1126 {
1127 "highlight_end": 22,
1128 "highlight_start": 1,
1129 "text": " ($path:path) => {"
1130 },
1131 {
1132 "highlight_end": 77,
1133 "highlight_start": 1,
1134 "text": " compile_error!(\"Please register your known path in the path module\")"
1135 },
1136 {
1137 "highlight_end": 7,
1138 "highlight_start": 1,
1139 "text": " };"
1140 },
1141 {
1142 "highlight_end": 2,
1143 "highlight_start": 1,
1144 "text": "}"
1145 }
1146 ]
1147 },
1148 "macro_decl_name": "$crate::__known_path!",
1149 "span": {
1150 "byte_end": 8427,
1151 "byte_start": 8385,
1152 "column_end": 51,
1153 "column_start": 9,
1154 "expansion": {
1155 "def_site_span": {
1156 "byte_end": 8611,
1157 "byte_start": 8312,
1158 "column_end": 2,
1159 "column_start": 1,
1160 "expansion": null,
1161 "file_name": "crates/ra_hir_def/src/path.rs",
1162 "is_primary": false,
1163 "label": null,
1164 "line_end": 277,
1165 "line_start": 270,
1166 "suggested_replacement": null,
1167 "suggestion_applicability": null,
1168 "text": [
1169 {
1170 "highlight_end": 22,
1171 "highlight_start": 1,
1172 "text": "macro_rules! __path {"
1173 },
1174 {
1175 "highlight_end": 43,
1176 "highlight_start": 1,
1177 "text": " ($start:ident $(:: $seg:ident)*) => ({"
1178 },
1179 {
1180 "highlight_end": 51,
1181 "highlight_start": 1,
1182 "text": " $crate::__known_path!($start $(:: $seg)*);"
1183 },
1184 {
1185 "highlight_end": 87,
1186 "highlight_start": 1,
1187 "text": " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
1188 },
1189 {
1190 "highlight_end": 76,
1191 "highlight_start": 1,
1192 "text": " $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
1193 },
1194 {
1195 "highlight_end": 11,
1196 "highlight_start": 1,
1197 "text": " ])"
1198 },
1199 {
1200 "highlight_end": 8,
1201 "highlight_start": 1,
1202 "text": " });"
1203 },
1204 {
1205 "highlight_end": 2,
1206 "highlight_start": 1,
1207 "text": "}"
1208 }
1209 ]
1210 },
1211 "macro_decl_name": "path!",
1212 "span": {
1213 "byte_end": 2966,
1214 "byte_start": 2940,
1215 "column_end": 42,
1216 "column_start": 16,
1217 "expansion": null,
1218 "file_name": "crates/ra_hir_def/src/data.rs",
1219 "is_primary": false,
1220 "label": null,
1221 "line_end": 80,
1222 "line_start": 80,
1223 "suggested_replacement": null,
1224 "suggestion_applicability": null,
1225 "text": [
1226 {
1227 "highlight_end": 42,
1228 "highlight_start": 16,
1229 "text": " let path = path![std::future::Future];"
1230 }
1231 ]
1232 }
1233 },
1234 "file_name": "crates/ra_hir_def/src/path.rs",
1235 "is_primary": false,
1236 "label": null,
1237 "line_end": 272,
1238 "line_start": 272,
1239 "suggested_replacement": null,
1240 "suggestion_applicability": null,
1241 "text": [
1242 {
1243 "highlight_end": 51,
1244 "highlight_start": 9,
1245 "text": " $crate::__known_path!($start $(:: $seg)*);"
1246 }
1247 ]
1248 }
1249 },
1250 "file_name": "crates/ra_hir_def/src/path.rs",
1251 "is_primary": true,
1252 "label": null,
1253 "line_end": 265,
1254 "line_start": 265,
1255 "suggested_replacement": null,
1256 "suggestion_applicability": null,
1257 "text": [
1258 {
1259 "highlight_end": 77,
1260 "highlight_start": 9,
1261 "text": " compile_error!(\"Please register your known path in the path module\")"
1262 }
1263 ]
1264 }
1265 ]
1266 }
1267 "##,
1268 );
1269
1270 let workspace_root = PathBuf::from("/test/");
1271 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
1272 insta::assert_debug_snapshot!(diag);
1273 }
1274
1275 #[test]
1276 #[cfg(not(windows))]
1277 fn snap_multi_line_fix() {
1278 let diag = parse_diagnostic(
1279 r##"{
1280 "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",
1281 "children": [
1282 {
1283 "children": [],
1284 "code": null,
1285 "level": "note",
1286 "message": "`#[warn(clippy::let_and_return)]` on by default",
1287 "rendered": null,
1288 "spans": []
1289 },
1290 {
1291 "children": [],
1292 "code": null,
1293 "level": "help",
1294 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
1295 "rendered": null,
1296 "spans": []
1297 },
1298 {
1299 "children": [],
1300 "code": null,
1301 "level": "help",
1302 "message": "return the expression directly",
1303 "rendered": null,
1304 "spans": [
1305 {
1306 "byte_end": 55,
1307 "byte_start": 29,
1308 "column_end": 31,
1309 "column_start": 5,
1310 "expansion": null,
1311 "file_name": "src/main.rs",
1312 "is_primary": true,
1313 "label": null,
1314 "line_end": 3,
1315 "line_start": 3,
1316 "suggested_replacement": "",
1317 "suggestion_applicability": "MachineApplicable",
1318 "text": [
1319 {
1320 "highlight_end": 31,
1321 "highlight_start": 5,
1322 "text": " let a = (0..10).collect();"
1323 }
1324 ]
1325 },
1326 {
1327 "byte_end": 61,
1328 "byte_start": 60,
1329 "column_end": 6,
1330 "column_start": 5,
1331 "expansion": null,
1332 "file_name": "src/main.rs",
1333 "is_primary": true,
1334 "label": null,
1335 "line_end": 4,
1336 "line_start": 4,
1337 "suggested_replacement": "(0..10).collect()",
1338 "suggestion_applicability": "MachineApplicable",
1339 "text": [
1340 {
1341 "highlight_end": 6,
1342 "highlight_start": 5,
1343 "text": " a"
1344 }
1345 ]
1346 }
1347 ]
1348 }
1349 ],
1350 "code": {
1351 "code": "clippy::let_and_return",
1352 "explanation": null
1353 },
1354 "level": "warning",
1355 "message": "returning the result of a let binding from a block",
1356 "spans": [
1357 {
1358 "byte_end": 55,
1359 "byte_start": 29,
1360 "column_end": 31,
1361 "column_start": 5,
1362 "expansion": null,
1363 "file_name": "src/main.rs",
1364 "is_primary": false,
1365 "label": "unnecessary let binding",
1366 "line_end": 3,
1367 "line_start": 3,
1368 "suggested_replacement": null,
1369 "suggestion_applicability": null,
1370 "text": [
1371 {
1372 "highlight_end": 31,
1373 "highlight_start": 5,
1374 "text": " let a = (0..10).collect();"
1375 }
1376 ]
1377 },
1378 {
1379 "byte_end": 61,
1380 "byte_start": 60,
1381 "column_end": 6,
1382 "column_start": 5,
1383 "expansion": null,
1384 "file_name": "src/main.rs",
1385 "is_primary": true,
1386 "label": null,
1387 "line_end": 4,
1388 "line_start": 4,
1389 "suggested_replacement": null,
1390 "suggestion_applicability": null,
1391 "text": [
1392 {
1393 "highlight_end": 6,
1394 "highlight_start": 5,
1395 "text": " a"
1396 }
1397 ]
1398 }
1399 ]
1400 }
1401 "##,
1402 );
1403
1404 let workspace_root = PathBuf::from("/test/");
1405 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
1406 insta::assert_debug_snapshot!(diag);
1407 }
1408}