1 //! This module provides the functionality needed to convert diagnostics from
2 //! `cargo check` json format to the LSP diagnostic format.
3 use std::{collections::HashMap, path::Path};
5 use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
8 use crate::{lsp_ext, to_proto::url_from_abs_path};
10 use super::DiagnosticsConfig;
12 /// Determines the LSP severity from a diagnostic
13 fn diagnostic_severity(
14 config: &DiagnosticsConfig,
15 level: flycheck::DiagnosticLevel,
16 code: Option<flycheck::DiagnosticCode>,
17 ) -> Option<lsp_types::DiagnosticSeverity> {
18 let res = match level {
19 DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::Error,
20 DiagnosticLevel::Error => lsp_types::DiagnosticSeverity::Error,
21 DiagnosticLevel::Warning => match &code {
22 Some(code) if config.warnings_as_hint.contains(&code.code) => {
23 lsp_types::DiagnosticSeverity::Hint
25 Some(code) if config.warnings_as_info.contains(&code.code) => {
26 lsp_types::DiagnosticSeverity::Information
28 _ => lsp_types::DiagnosticSeverity::Warning,
30 DiagnosticLevel::Note => lsp_types::DiagnosticSeverity::Information,
31 DiagnosticLevel::Help => lsp_types::DiagnosticSeverity::Hint,
32 DiagnosticLevel::Unknown => return None,
37 /// Check whether a file name is from macro invocation
38 fn is_from_macro(file_name: &str) -> bool {
39 file_name.starts_with('<') && file_name.ends_with('>')
42 /// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
43 fn location(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
44 let mut span = span.clone();
45 while let Some(expansion) = span.expansion {
46 span = expansion.span;
48 return location_naive(workspace_root, &span);
51 /// Converts a Rust span to a LSP location
52 fn location_naive(workspace_root: &Path, span: &DiagnosticSpan) -> lsp_types::Location {
53 let file_name = workspace_root.join(&span.file_name);
54 let uri = url_from_abs_path(&file_name);
56 // FIXME: this doesn't handle UTF16 offsets correctly
57 let range = lsp_types::Range::new(
58 lsp_types::Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
59 lsp_types::Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
62 lsp_types::Location { uri, range }
65 /// Converts a secondary Rust span to a LSP related inflocation(ormation
67 /// If the span is unlabelled this will return `None`.
68 fn diagnostic_related_information(
69 workspace_root: &Path,
70 span: &DiagnosticSpan,
71 ) -> Option<lsp_types::DiagnosticRelatedInformation> {
72 let message = span.label.clone()?;
73 let location = location(workspace_root, span);
74 Some(lsp_types::DiagnosticRelatedInformation { location, message })
77 enum MappedRustChildDiagnostic {
78 Related(lsp_types::DiagnosticRelatedInformation),
79 SuggestedFix(lsp_ext::CodeAction),
83 fn map_rust_child_diagnostic(
84 workspace_root: &Path,
85 rd: &flycheck::Diagnostic,
86 ) -> MappedRustChildDiagnostic {
87 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
89 // `rustc` uses these spanless children as a way to print multi-line
91 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
94 let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
96 if let (Some(Applicability::MachineApplicable), Some(suggested_replacement)) =
97 (&span.suggestion_applicability, &span.suggested_replacement)
99 let location = location(workspace_root, span);
100 let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
101 edit_map.entry(location.uri).or_default().push(edit);
105 if edit_map.is_empty() {
106 MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
107 location: location(workspace_root, spans[0]),
108 message: rd.message.clone(),
111 MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
112 title: rd.message.clone(),
115 kind: Some(lsp_types::CodeActionKind::QUICKFIX),
116 edit: Some(lsp_ext::SnippetWorkspaceEdit {
117 // FIXME: there's no good reason to use edit_map here....
118 changes: Some(edit_map),
119 document_changes: None,
126 pub(crate) struct MappedRustDiagnostic {
127 pub(crate) url: lsp_types::Url,
128 pub(crate) diagnostic: lsp_types::Diagnostic,
129 pub(crate) fixes: Vec<lsp_ext::CodeAction>,
132 /// Converts a Rust root diagnostic to LSP form
134 /// This flattens the Rust diagnostic by:
136 /// 1. Creating a LSP diagnostic with the root message and primary span.
137 /// 2. Adding any labelled secondary spans to `relatedInformation`
138 /// 3. Categorising child diagnostics as either `SuggestedFix`es,
139 /// `relatedInformation` or additional message lines.
141 /// If the diagnostic has no primary span this will return `None`
142 pub(crate) fn map_rust_diagnostic_to_lsp(
143 config: &DiagnosticsConfig,
144 rd: &flycheck::Diagnostic,
145 workspace_root: &Path,
146 ) -> Vec<MappedRustDiagnostic> {
147 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
148 if primary_spans.is_empty() {
152 let severity = diagnostic_severity(config, rd.level.clone(), rd.code.clone());
154 let mut source = String::from("rustc");
155 let mut code = rd.code.as_ref().map(|c| c.code.clone());
156 if let Some(code_val) = &code {
157 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
158 let scoped_code: Vec<&str> = code_val.split("::").collect();
159 if scoped_code.len() == 2 {
160 source = String::from(scoped_code[0]);
161 code = Some(String::from(scoped_code[1]));
165 let mut needs_primary_span_label = true;
166 let mut related_information = Vec::new();
167 let mut tags = Vec::new();
169 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
170 let related = diagnostic_related_information(workspace_root, secondary_span);
171 if let Some(related) = related {
172 related_information.push(related);
176 let mut fixes = Vec::new();
177 let mut message = rd.message.clone();
178 for child in &rd.children {
179 let child = map_rust_child_diagnostic(workspace_root, &child);
181 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
182 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
183 MappedRustChildDiagnostic::MessageLine(message_line) => {
184 format_to!(message, "\n{}", message_line);
186 // These secondary messages usually duplicate the content of the
187 // primary span label.
188 needs_primary_span_label = false;
193 if let Some(code) = &rd.code {
194 let code = code.code.as_str();
200 | "unused_attributes"
205 tags.push(lsp_types::DiagnosticTag::Unnecessary);
208 if matches!(code, "deprecated") {
209 tags.push(lsp_types::DiagnosticTag::Deprecated);
215 .map(|primary_span| {
216 let location = location(workspace_root, &primary_span);
218 let mut message = message.clone();
219 if needs_primary_span_label {
220 if let Some(primary_span_label) = &primary_span.label {
221 format_to!(message, "\n{}", primary_span_label);
225 // If error occurs from macro expansion, add related info pointing to
226 // where the error originated
227 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
228 related_information.push(lsp_types::DiagnosticRelatedInformation {
229 location: location_naive(workspace_root, &primary_span),
230 message: "Error originated from macro here".to_string(),
234 let diagnostic = lsp_types::Diagnostic {
235 range: location.range,
237 code: code.clone().map(lsp_types::NumberOrString::String),
238 source: Some(source.clone()),
240 related_information: if related_information.is_empty() {
243 Some(related_information.clone())
245 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
248 MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }
258 use expect::{expect_file, ExpectFile};
260 fn check(diagnostics_json: &str, expect: ExpectFile) {
261 check_with_config(DiagnosticsConfig::default(), diagnostics_json, expect)
264 fn check_with_config(config: DiagnosticsConfig, diagnostics_json: &str, expect: ExpectFile) {
265 let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap();
266 let workspace_root = Path::new("/test/");
267 let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root);
268 expect.assert_debug_eq(&actual)
272 fn rustc_incompatible_type_for_trait() {
275 "message": "method `next` has an incompatible type for trait",
278 "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"
283 "file_name": "compiler/ty/list_iter.rs",
293 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
294 "highlight_start": 5,
298 "label": "types differ in mutability",
299 "suggested_replacement": null,
300 "suggestion_applicability": null,
306 "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>>`",
314 "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"
317 expect_file!["crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt"],
322 fn rustc_unused_variable() {
325 "message": "unused variable: `foo`",
327 "code": "unused_variables",
333 "file_name": "driver/subcommand/repl.rs",
343 "text": " let foo = 42;",
344 "highlight_start": 9,
349 "suggested_replacement": null,
350 "suggestion_applicability": null,
356 "message": "#[warn(unused_variables)] on by default",
364 "message": "consider prefixing with an underscore",
369 "file_name": "driver/subcommand/repl.rs",
379 "text": " let foo = 42;",
380 "highlight_start": 9,
385 "suggested_replacement": "_foo",
386 "suggestion_applicability": "MachineApplicable",
394 "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"
396 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable.txt"],
402 fn rustc_unused_variable_as_info() {
405 warnings_as_info: vec!["unused_variables".to_string()],
406 ..DiagnosticsConfig::default()
409 "message": "unused variable: `foo`",
411 "code": "unused_variables",
417 "file_name": "driver/subcommand/repl.rs",
427 "text": " let foo = 42;",
428 "highlight_start": 9,
433 "suggested_replacement": null,
434 "suggestion_applicability": null,
440 "message": "#[warn(unused_variables)] on by default",
448 "message": "consider prefixing with an underscore",
453 "file_name": "driver/subcommand/repl.rs",
463 "text": " let foo = 42;",
464 "highlight_start": 9,
469 "suggested_replacement": "_foo",
470 "suggestion_applicability": "MachineApplicable",
478 "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"
480 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt"],
486 fn rustc_unused_variable_as_hint() {
489 warnings_as_hint: vec!["unused_variables".to_string()],
490 ..DiagnosticsConfig::default()
493 "message": "unused variable: `foo`",
495 "code": "unused_variables",
501 "file_name": "driver/subcommand/repl.rs",
511 "text": " let foo = 42;",
512 "highlight_start": 9,
517 "suggested_replacement": null,
518 "suggestion_applicability": null,
524 "message": "#[warn(unused_variables)] on by default",
532 "message": "consider prefixing with an underscore",
537 "file_name": "driver/subcommand/repl.rs",
547 "text": " let foo = 42;",
548 "highlight_start": 9,
553 "suggested_replacement": "_foo",
554 "suggestion_applicability": "MachineApplicable",
562 "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"
564 expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt"],
569 fn rustc_wrong_number_of_parameters() {
572 "message": "this function takes 2 parameters but 3 parameters were supplied",
575 "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"
580 "file_name": "compiler/ty/select.rs",
590 "text": " pub fn add_evidence(",
591 "highlight_start": 5,
595 "text": " &mut self,",
596 "highlight_start": 1,
600 "text": " target_poly: &ty::Ref<ty::Poly>,",
601 "highlight_start": 1,
605 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
606 "highlight_start": 1,
611 "highlight_start": 1,
615 "text": " match target_poly {",
616 "highlight_start": 1,
620 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
621 "highlight_start": 1,
625 "text": " ty::Ref::Fixed(target_ty) => {",
626 "highlight_start": 1,
630 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
631 "highlight_start": 1,
635 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
636 "highlight_start": 1,
641 "highlight_start": 1,
646 "highlight_start": 1,
651 "highlight_start": 1,
655 "label": "defined here",
656 "suggested_replacement": null,
657 "suggestion_applicability": null,
661 "file_name": "compiler/ty/select.rs",
671 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
672 "highlight_start": 18,
676 "label": "expected 2 parameters",
677 "suggested_replacement": null,
678 "suggestion_applicability": null,
683 "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"
685 expect_file!["crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt"],
690 fn clippy_pass_by_ref() {
693 "message": "this argument is passed by reference, but would be more efficient if passed by value",
695 "code": "clippy::trivially_copy_pass_by_ref",
701 "file_name": "compiler/mir/tagset.rs",
711 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
712 "highlight_start": 24,
717 "suggested_replacement": null,
718 "suggestion_applicability": null,
724 "message": "lint level defined here",
729 "file_name": "compiler/lib.rs",
739 "text": "#![warn(clippy::all)]",
740 "highlight_start": 9,
745 "suggested_replacement": null,
746 "suggestion_applicability": null,
754 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
762 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
770 "message": "consider passing by value instead",
775 "file_name": "compiler/mir/tagset.rs",
785 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
786 "highlight_start": 24,
791 "suggested_replacement": "self",
792 "suggestion_applicability": "Unspecified",
800 "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"
802 expect_file!["crates/rust-analyzer/test_data/clippy_pass_by_ref.txt"],
807 fn rustc_mismatched_type() {
810 "message": "mismatched types",
813 "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"
818 "file_name": "runtime/compiler_support.rs",
828 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
829 "highlight_start": 65,
833 "label": "expected usize, found u32",
834 "suggested_replacement": null,
835 "suggestion_applicability": null,
840 "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"
842 expect_file!["crates/rust-analyzer/test_data/rustc_mismatched_type.txt"],
847 fn handles_macro_location() {
850 "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",
856 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
863 "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"
866 "message": "can't compare `{integer}` with `&str`",
880 "file_name": "<::core::macros::assert_eq macros>",
885 "suggested_replacement": null,
886 "suggestion_applicability": null,
890 "highlight_start": 1,
891 "text": "($ left : expr, $ right : expr) =>"
895 "highlight_start": 1,
900 "highlight_start": 1,
901 "text": " match (& $ left, & $ right)"
905 "highlight_start": 1,
910 "highlight_start": 1,
911 "text": " (left_val, right_val) =>"
915 "highlight_start": 1,
920 "highlight_start": 1,
921 "text": " if ! (* left_val == * right_val)"
925 "highlight_start": 1,
930 "highlight_start": 1,
935 "highlight_start": 1,
936 "text": " (r#\"assertion failed: `(left == right)`"
940 "highlight_start": 1,
941 "text": " left: `{:?}`,"
945 "highlight_start": 1,
946 "text": " right: `{:?}`\"#,"
950 "highlight_start": 1,
951 "text": " & * left_val, & * right_val)"
955 "highlight_start": 1,
960 "highlight_start": 1,
965 "highlight_start": 1,
970 "highlight_start": 1,
971 "text": " }) ; ($ left : expr, $ right : expr,) =>"
975 "highlight_start": 1,
976 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
980 "highlight_start": 1,
981 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
985 "highlight_start": 1,
990 "highlight_start": 1,
991 "text": " match (& ($ left), & ($ right))"
995 "highlight_start": 1,
1000 "highlight_start": 1,
1001 "text": " (left_val, right_val) =>"
1004 "highlight_end": 11,
1005 "highlight_start": 1,
1009 "highlight_end": 46,
1010 "highlight_start": 1,
1011 "text": " if ! (* left_val == * right_val)"
1014 "highlight_end": 15,
1015 "highlight_start": 1,
1019 "highlight_end": 25,
1020 "highlight_start": 1,
1024 "highlight_end": 57,
1025 "highlight_start": 1,
1026 "text": " (r#\"assertion failed: `(left == right)`"
1029 "highlight_end": 16,
1030 "highlight_start": 1,
1031 "text": " left: `{:?}`,"
1034 "highlight_end": 22,
1035 "highlight_start": 1,
1036 "text": " right: `{:?}`: {}\"#,"
1039 "highlight_end": 72,
1040 "highlight_start": 1,
1041 "text": " & * left_val, & * right_val, $ crate :: format_args !"
1044 "highlight_end": 33,
1045 "highlight_start": 1,
1046 "text": " ($ ($ arg) +))"
1049 "highlight_end": 15,
1050 "highlight_start": 1,
1054 "highlight_end": 11,
1055 "highlight_start": 1,
1060 "highlight_start": 1,
1065 "highlight_start": 1,
1070 "macro_decl_name": "assert_eq!",
1077 "file_name": "src/main.rs",
1078 "is_primary": false,
1082 "suggested_replacement": null,
1083 "suggestion_applicability": null,
1086 "highlight_end": 27,
1087 "highlight_start": 5,
1088 "text": " assert_eq!(1, \"love\");"
1093 "file_name": "<::core::macros::assert_eq macros>",
1095 "label": "no implementation for `{integer} == &str`",
1098 "suggested_replacement": null,
1099 "suggestion_applicability": null,
1102 "highlight_end": 33,
1103 "highlight_start": 31,
1104 "text": " if ! (* left_val == * right_val)"
1110 expect_file!["crates/rust-analyzer/test_data/handles_macro_location.txt"],
1115 fn macro_compiler_error() {
1118 "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",
1122 "message": "Please register your known path in the path module",
1136 "file_name": "crates/ra_hir_def/src/path.rs",
1137 "is_primary": false,
1141 "suggested_replacement": null,
1142 "suggestion_applicability": null,
1145 "highlight_end": 28,
1146 "highlight_start": 1,
1147 "text": "macro_rules! __known_path {"
1150 "highlight_end": 37,
1151 "highlight_start": 1,
1152 "text": " (std::iter::IntoIterator) => {};"
1155 "highlight_end": 33,
1156 "highlight_start": 1,
1157 "text": " (std::result::Result) => {};"
1160 "highlight_end": 29,
1161 "highlight_start": 1,
1162 "text": " (std::ops::Range) => {};"
1165 "highlight_end": 33,
1166 "highlight_start": 1,
1167 "text": " (std::ops::RangeFrom) => {};"
1170 "highlight_end": 33,
1171 "highlight_start": 1,
1172 "text": " (std::ops::RangeFull) => {};"
1175 "highlight_end": 31,
1176 "highlight_start": 1,
1177 "text": " (std::ops::RangeTo) => {};"
1180 "highlight_end": 40,
1181 "highlight_start": 1,
1182 "text": " (std::ops::RangeToInclusive) => {};"
1185 "highlight_end": 38,
1186 "highlight_start": 1,
1187 "text": " (std::ops::RangeInclusive) => {};"
1190 "highlight_end": 27,
1191 "highlight_start": 1,
1192 "text": " (std::ops::Try) => {};"
1195 "highlight_end": 22,
1196 "highlight_start": 1,
1197 "text": " ($path:path) => {"
1200 "highlight_end": 77,
1201 "highlight_start": 1,
1202 "text": " compile_error!(\"Please register your known path in the path module\")"
1206 "highlight_start": 1,
1211 "highlight_start": 1,
1216 "macro_decl_name": "$crate::__known_path!",
1229 "file_name": "crates/ra_hir_def/src/path.rs",
1230 "is_primary": false,
1234 "suggested_replacement": null,
1235 "suggestion_applicability": null,
1238 "highlight_end": 22,
1239 "highlight_start": 1,
1240 "text": "macro_rules! __path {"
1243 "highlight_end": 43,
1244 "highlight_start": 1,
1245 "text": " ($start:ident $(:: $seg:ident)*) => ({"
1248 "highlight_end": 51,
1249 "highlight_start": 1,
1250 "text": " $crate::__known_path!($start $(:: $seg)*);"
1253 "highlight_end": 87,
1254 "highlight_start": 1,
1255 "text": " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
1258 "highlight_end": 76,
1259 "highlight_start": 1,
1260 "text": " $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
1263 "highlight_end": 11,
1264 "highlight_start": 1,
1269 "highlight_start": 1,
1274 "highlight_start": 1,
1279 "macro_decl_name": "path!",
1286 "file_name": "crates/ra_hir_def/src/data.rs",
1287 "is_primary": false,
1291 "suggested_replacement": null,
1292 "suggestion_applicability": null,
1295 "highlight_end": 42,
1296 "highlight_start": 16,
1297 "text": " let path = path![std::future::Future];"
1302 "file_name": "crates/ra_hir_def/src/path.rs",
1303 "is_primary": false,
1307 "suggested_replacement": null,
1308 "suggestion_applicability": null,
1311 "highlight_end": 51,
1312 "highlight_start": 9,
1313 "text": " $crate::__known_path!($start $(:: $seg)*);"
1318 "file_name": "crates/ra_hir_def/src/path.rs",
1323 "suggested_replacement": null,
1324 "suggestion_applicability": null,
1327 "highlight_end": 77,
1328 "highlight_start": 9,
1329 "text": " compile_error!(\"Please register your known path in the path module\")"
1336 expect_file!["crates/rust-analyzer/test_data/macro_compiler_error.txt"],
1341 fn snap_multi_line_fix() {
1344 "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",
1350 "message": "`#[warn(clippy::let_and_return)]` on by default",
1358 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
1366 "message": "return the expression directly",
1375 "file_name": "src/main.rs",
1380 "suggested_replacement": "",
1381 "suggestion_applicability": "MachineApplicable",
1384 "highlight_end": 31,
1385 "highlight_start": 5,
1386 "text": " let a = (0..10).collect();"
1396 "file_name": "src/main.rs",
1401 "suggested_replacement": "(0..10).collect()",
1402 "suggestion_applicability": "MachineApplicable",
1406 "highlight_start": 5,
1415 "code": "clippy::let_and_return",
1419 "message": "returning the result of a let binding from a block",
1427 "file_name": "src/main.rs",
1428 "is_primary": false,
1429 "label": "unnecessary let binding",
1432 "suggested_replacement": null,
1433 "suggestion_applicability": null,
1436 "highlight_end": 31,
1437 "highlight_start": 5,
1438 "text": " let a = (0..10).collect();"
1448 "file_name": "src/main.rs",
1453 "suggested_replacement": null,
1454 "suggestion_applicability": null,
1458 "highlight_start": 5,
1466 expect_file!["crates/rust-analyzer/test_data/snap_multi_line_fix.txt"],