]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/diagnostics/to_proto.rs
Update lsp-types to account for new CodeActionKind structure
[rust.git] / crates / rust-analyzer / src / diagnostics / to_proto.rs
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};
4
5 use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
6 use stdx::format_to;
7
8 use crate::{lsp_ext, to_proto::url_from_abs_path};
9
10 use super::DiagnosticsConfig;
11
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
24             }
25             Some(code) if config.warnings_as_info.contains(&code.code) => {
26                 lsp_types::DiagnosticSeverity::Information
27             }
28             _ => lsp_types::DiagnosticSeverity::Warning,
29         },
30         DiagnosticLevel::Note => lsp_types::DiagnosticSeverity::Information,
31         DiagnosticLevel::Help => lsp_types::DiagnosticSeverity::Hint,
32         DiagnosticLevel::Unknown => return None,
33     };
34     Some(res)
35 }
36
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('>')
40 }
41
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;
47     }
48     return location_naive(workspace_root, &span);
49 }
50
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);
55
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),
60     );
61
62     lsp_types::Location { uri, range }
63 }
64
65 /// Converts a secondary Rust span to a LSP related inflocation(ormation
66 ///
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 })
75 }
76
77 enum MappedRustChildDiagnostic {
78     Related(lsp_types::DiagnosticRelatedInformation),
79     SuggestedFix(lsp_ext::CodeAction),
80     MessageLine(String),
81 }
82
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();
88     if spans.is_empty() {
89         // `rustc` uses these spanless children as a way to print multi-line
90         // messages
91         return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
92     }
93
94     let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
95     for &span in &spans {
96         if let (Some(Applicability::MachineApplicable), Some(suggested_replacement)) =
97             (&span.suggestion_applicability, &span.suggested_replacement)
98         {
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);
102         }
103     }
104
105     if edit_map.is_empty() {
106         MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation {
107             location: location(workspace_root, spans[0]),
108             message: rd.message.clone(),
109         })
110     } else {
111         MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction {
112             title: rd.message.clone(),
113             id: None,
114             group: None,
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,
120             }),
121         })
122     }
123 }
124
125 #[derive(Debug)]
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>,
130 }
131
132 /// Converts a Rust root diagnostic to LSP form
133 ///
134 /// This flattens the Rust diagnostic by:
135 ///
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.
140 ///
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() {
149         return Vec::new();
150     }
151
152     let severity = diagnostic_severity(config, rd.level.clone(), rd.code.clone());
153
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]));
162         }
163     }
164
165     let mut needs_primary_span_label = true;
166     let mut related_information = Vec::new();
167     let mut tags = Vec::new();
168
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);
173         }
174     }
175
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);
180         match 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);
185
186                 // These secondary messages usually duplicate the content of the
187                 // primary span label.
188                 needs_primary_span_label = false;
189             }
190         }
191     }
192
193     if let Some(code) = &rd.code {
194         let code = code.code.as_str();
195         if matches!(
196             code,
197             "dead_code"
198                 | "unknown_lints"
199                 | "unreachable_code"
200                 | "unused_attributes"
201                 | "unused_imports"
202                 | "unused_macros"
203                 | "unused_variables"
204         ) {
205             tags.push(lsp_types::DiagnosticTag::Unnecessary);
206         }
207
208         if matches!(code, "deprecated") {
209             tags.push(lsp_types::DiagnosticTag::Deprecated);
210         }
211     }
212
213     primary_spans
214         .iter()
215         .map(|primary_span| {
216             let location = location(workspace_root, &primary_span);
217
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);
222                 }
223             }
224
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(),
231                 });
232             }
233
234             let diagnostic = lsp_types::Diagnostic {
235                 range: location.range,
236                 severity,
237                 code: code.clone().map(lsp_types::NumberOrString::String),
238                 source: Some(source.clone()),
239                 message,
240                 related_information: if related_information.is_empty() {
241                     None
242                 } else {
243                     Some(related_information.clone())
244                 },
245                 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
246             };
247
248             MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }
249         })
250         .collect()
251 }
252
253 #[cfg(test)]
254 #[cfg(not(windows))]
255 mod tests {
256     use super::*;
257
258     use expect::{expect_file, ExpectFile};
259
260     fn check(diagnostics_json: &str, expect: ExpectFile) {
261         check_with_config(DiagnosticsConfig::default(), diagnostics_json, expect)
262     }
263
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)
269     }
270
271     #[test]
272     fn rustc_incompatible_type_for_trait() {
273         check(
274             r##"{
275                 "message": "method `next` has an incompatible type for trait",
276                 "code": {
277                     "code": "E0053",
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"
279                 },
280                 "level": "error",
281                 "spans": [
282                     {
283                         "file_name": "compiler/ty/list_iter.rs",
284                         "byte_start": 1307,
285                         "byte_end": 1350,
286                         "line_start": 52,
287                         "line_end": 52,
288                         "column_start": 5,
289                         "column_end": 48,
290                         "is_primary": true,
291                         "text": [
292                             {
293                                 "text": "    fn next(&self) -> Option<&'list ty::Ref<M>> {",
294                                 "highlight_start": 5,
295                                 "highlight_end": 48
296                             }
297                         ],
298                         "label": "types differ in mutability",
299                         "suggested_replacement": null,
300                         "suggestion_applicability": null,
301                         "expansion": null
302                     }
303                 ],
304                 "children": [
305                     {
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>>`",
307                         "code": null,
308                         "level": "note",
309                         "spans": [],
310                         "children": [],
311                         "rendered": null
312                     }
313                 ],
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"
315             }
316             "##,
317             expect_file!["crates/rust-analyzer/test_data/rustc_incompatible_type_for_trait.txt"],
318         );
319     }
320
321     #[test]
322     fn rustc_unused_variable() {
323         check(
324             r##"{
325     "message": "unused variable: `foo`",
326     "code": {
327         "code": "unused_variables",
328         "explanation": null
329     },
330     "level": "warning",
331     "spans": [
332         {
333             "file_name": "driver/subcommand/repl.rs",
334             "byte_start": 9228,
335             "byte_end": 9231,
336             "line_start": 291,
337             "line_end": 291,
338             "column_start": 9,
339             "column_end": 12,
340             "is_primary": true,
341             "text": [
342                 {
343                     "text": "    let foo = 42;",
344                     "highlight_start": 9,
345                     "highlight_end": 12
346                 }
347             ],
348             "label": null,
349             "suggested_replacement": null,
350             "suggestion_applicability": null,
351             "expansion": null
352         }
353     ],
354     "children": [
355         {
356             "message": "#[warn(unused_variables)] on by default",
357             "code": null,
358             "level": "note",
359             "spans": [],
360             "children": [],
361             "rendered": null
362         },
363         {
364             "message": "consider prefixing with an underscore",
365             "code": null,
366             "level": "help",
367             "spans": [
368                 {
369                     "file_name": "driver/subcommand/repl.rs",
370                     "byte_start": 9228,
371                     "byte_end": 9231,
372                     "line_start": 291,
373                     "line_end": 291,
374                     "column_start": 9,
375                     "column_end": 12,
376                     "is_primary": true,
377                     "text": [
378                         {
379                             "text": "    let foo = 42;",
380                             "highlight_start": 9,
381                             "highlight_end": 12
382                         }
383                     ],
384                     "label": null,
385                     "suggested_replacement": "_foo",
386                     "suggestion_applicability": "MachineApplicable",
387                     "expansion": null
388                 }
389             ],
390             "children": [],
391             "rendered": null
392         }
393     ],
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"
395     }"##,
396             expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable.txt"],
397         );
398     }
399
400     #[test]
401     #[cfg(not(windows))]
402     fn rustc_unused_variable_as_info() {
403         check_with_config(
404             DiagnosticsConfig {
405                 warnings_as_info: vec!["unused_variables".to_string()],
406                 ..DiagnosticsConfig::default()
407             },
408             r##"{
409     "message": "unused variable: `foo`",
410     "code": {
411         "code": "unused_variables",
412         "explanation": null
413     },
414     "level": "warning",
415     "spans": [
416         {
417             "file_name": "driver/subcommand/repl.rs",
418             "byte_start": 9228,
419             "byte_end": 9231,
420             "line_start": 291,
421             "line_end": 291,
422             "column_start": 9,
423             "column_end": 12,
424             "is_primary": true,
425             "text": [
426                 {
427                     "text": "    let foo = 42;",
428                     "highlight_start": 9,
429                     "highlight_end": 12
430                 }
431             ],
432             "label": null,
433             "suggested_replacement": null,
434             "suggestion_applicability": null,
435             "expansion": null
436         }
437     ],
438     "children": [
439         {
440             "message": "#[warn(unused_variables)] on by default",
441             "code": null,
442             "level": "note",
443             "spans": [],
444             "children": [],
445             "rendered": null
446         },
447         {
448             "message": "consider prefixing with an underscore",
449             "code": null,
450             "level": "help",
451             "spans": [
452                 {
453                     "file_name": "driver/subcommand/repl.rs",
454                     "byte_start": 9228,
455                     "byte_end": 9231,
456                     "line_start": 291,
457                     "line_end": 291,
458                     "column_start": 9,
459                     "column_end": 12,
460                     "is_primary": true,
461                     "text": [
462                         {
463                             "text": "    let foo = 42;",
464                             "highlight_start": 9,
465                             "highlight_end": 12
466                         }
467                     ],
468                     "label": null,
469                     "suggested_replacement": "_foo",
470                     "suggestion_applicability": "MachineApplicable",
471                     "expansion": null
472                 }
473             ],
474             "children": [],
475             "rendered": null
476         }
477     ],
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"
479     }"##,
480             expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_info.txt"],
481         );
482     }
483
484     #[test]
485     #[cfg(not(windows))]
486     fn rustc_unused_variable_as_hint() {
487         check_with_config(
488             DiagnosticsConfig {
489                 warnings_as_hint: vec!["unused_variables".to_string()],
490                 ..DiagnosticsConfig::default()
491             },
492             r##"{
493     "message": "unused variable: `foo`",
494     "code": {
495         "code": "unused_variables",
496         "explanation": null
497     },
498     "level": "warning",
499     "spans": [
500         {
501             "file_name": "driver/subcommand/repl.rs",
502             "byte_start": 9228,
503             "byte_end": 9231,
504             "line_start": 291,
505             "line_end": 291,
506             "column_start": 9,
507             "column_end": 12,
508             "is_primary": true,
509             "text": [
510                 {
511                     "text": "    let foo = 42;",
512                     "highlight_start": 9,
513                     "highlight_end": 12
514                 }
515             ],
516             "label": null,
517             "suggested_replacement": null,
518             "suggestion_applicability": null,
519             "expansion": null
520         }
521     ],
522     "children": [
523         {
524             "message": "#[warn(unused_variables)] on by default",
525             "code": null,
526             "level": "note",
527             "spans": [],
528             "children": [],
529             "rendered": null
530         },
531         {
532             "message": "consider prefixing with an underscore",
533             "code": null,
534             "level": "help",
535             "spans": [
536                 {
537                     "file_name": "driver/subcommand/repl.rs",
538                     "byte_start": 9228,
539                     "byte_end": 9231,
540                     "line_start": 291,
541                     "line_end": 291,
542                     "column_start": 9,
543                     "column_end": 12,
544                     "is_primary": true,
545                     "text": [
546                         {
547                             "text": "    let foo = 42;",
548                             "highlight_start": 9,
549                             "highlight_end": 12
550                         }
551                     ],
552                     "label": null,
553                     "suggested_replacement": "_foo",
554                     "suggestion_applicability": "MachineApplicable",
555                     "expansion": null
556                 }
557             ],
558             "children": [],
559             "rendered": null
560         }
561     ],
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"
563     }"##,
564             expect_file!["crates/rust-analyzer/test_data/rustc_unused_variable_as_hint.txt"],
565         );
566     }
567
568     #[test]
569     fn rustc_wrong_number_of_parameters() {
570         check(
571             r##"{
572     "message": "this function takes 2 parameters but 3 parameters were supplied",
573     "code": {
574         "code": "E0061",
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"
576     },
577     "level": "error",
578     "spans": [
579         {
580             "file_name": "compiler/ty/select.rs",
581             "byte_start": 8787,
582             "byte_end": 9241,
583             "line_start": 219,
584             "line_end": 231,
585             "column_start": 5,
586             "column_end": 6,
587             "is_primary": false,
588             "text": [
589                 {
590                     "text": "    pub fn add_evidence(",
591                     "highlight_start": 5,
592                     "highlight_end": 25
593                 },
594                 {
595                     "text": "        &mut self,",
596                     "highlight_start": 1,
597                     "highlight_end": 19
598                 },
599                 {
600                     "text": "        target_poly: &ty::Ref<ty::Poly>,",
601                     "highlight_start": 1,
602                     "highlight_end": 41
603                 },
604                 {
605                     "text": "        evidence_poly: &ty::Ref<ty::Poly>,",
606                     "highlight_start": 1,
607                     "highlight_end": 43
608                 },
609                 {
610                     "text": "    ) {",
611                     "highlight_start": 1,
612                     "highlight_end": 8
613                 },
614                 {
615                     "text": "        match target_poly {",
616                     "highlight_start": 1,
617                     "highlight_end": 28
618                 },
619                 {
620                     "text": "            ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
621                     "highlight_start": 1,
622                     "highlight_end": 81
623                 },
624                 {
625                     "text": "            ty::Ref::Fixed(target_ty) => {",
626                     "highlight_start": 1,
627                     "highlight_end": 43
628                 },
629                 {
630                     "text": "                let evidence_ty = evidence_poly.resolve_to_ty();",
631                     "highlight_start": 1,
632                     "highlight_end": 65
633                 },
634                 {
635                     "text": "                self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
636                     "highlight_start": 1,
637                     "highlight_end": 76
638                 },
639                 {
640                     "text": "            }",
641                     "highlight_start": 1,
642                     "highlight_end": 14
643                 },
644                 {
645                     "text": "        }",
646                     "highlight_start": 1,
647                     "highlight_end": 10
648                 },
649                 {
650                     "text": "    }",
651                     "highlight_start": 1,
652                     "highlight_end": 6
653                 }
654             ],
655             "label": "defined here",
656             "suggested_replacement": null,
657             "suggestion_applicability": null,
658             "expansion": null
659         },
660         {
661             "file_name": "compiler/ty/select.rs",
662             "byte_start": 4045,
663             "byte_end": 4057,
664             "line_start": 104,
665             "line_end": 104,
666             "column_start": 18,
667             "column_end": 30,
668             "is_primary": true,
669             "text": [
670                 {
671                     "text": "            self.add_evidence(target_fixed, evidence_fixed, false);",
672                     "highlight_start": 18,
673                     "highlight_end": 30
674                 }
675             ],
676             "label": "expected 2 parameters",
677             "suggested_replacement": null,
678             "suggestion_applicability": null,
679             "expansion": null
680         }
681     ],
682     "children": [],
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"
684     }"##,
685             expect_file!["crates/rust-analyzer/test_data/rustc_wrong_number_of_parameters.txt"],
686         );
687     }
688
689     #[test]
690     fn clippy_pass_by_ref() {
691         check(
692             r##"{
693     "message": "this argument is passed by reference, but would be more efficient if passed by value",
694     "code": {
695         "code": "clippy::trivially_copy_pass_by_ref",
696         "explanation": null
697     },
698     "level": "warning",
699     "spans": [
700         {
701             "file_name": "compiler/mir/tagset.rs",
702             "byte_start": 941,
703             "byte_end": 946,
704             "line_start": 42,
705             "line_end": 42,
706             "column_start": 24,
707             "column_end": 29,
708             "is_primary": true,
709             "text": [
710                 {
711                     "text": "    pub fn is_disjoint(&self, other: Self) -> bool {",
712                     "highlight_start": 24,
713                     "highlight_end": 29
714                 }
715             ],
716             "label": null,
717             "suggested_replacement": null,
718             "suggestion_applicability": null,
719             "expansion": null
720         }
721     ],
722     "children": [
723         {
724             "message": "lint level defined here",
725             "code": null,
726             "level": "note",
727             "spans": [
728                 {
729                     "file_name": "compiler/lib.rs",
730                     "byte_start": 8,
731                     "byte_end": 19,
732                     "line_start": 1,
733                     "line_end": 1,
734                     "column_start": 9,
735                     "column_end": 20,
736                     "is_primary": true,
737                     "text": [
738                         {
739                             "text": "#![warn(clippy::all)]",
740                             "highlight_start": 9,
741                             "highlight_end": 20
742                         }
743                     ],
744                     "label": null,
745                     "suggested_replacement": null,
746                     "suggestion_applicability": null,
747                     "expansion": null
748                 }
749             ],
750             "children": [],
751             "rendered": null
752         },
753         {
754             "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
755             "code": null,
756             "level": "note",
757             "spans": [],
758             "children": [],
759             "rendered": null
760         },
761         {
762             "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
763             "code": null,
764             "level": "help",
765             "spans": [],
766             "children": [],
767             "rendered": null
768         },
769         {
770             "message": "consider passing by value instead",
771             "code": null,
772             "level": "help",
773             "spans": [
774                 {
775                     "file_name": "compiler/mir/tagset.rs",
776                     "byte_start": 941,
777                     "byte_end": 946,
778                     "line_start": 42,
779                     "line_end": 42,
780                     "column_start": 24,
781                     "column_end": 29,
782                     "is_primary": true,
783                     "text": [
784                         {
785                             "text": "    pub fn is_disjoint(&self, other: Self) -> bool {",
786                             "highlight_start": 24,
787                             "highlight_end": 29
788                         }
789                     ],
790                     "label": null,
791                     "suggested_replacement": "self",
792                     "suggestion_applicability": "Unspecified",
793                     "expansion": null
794                 }
795             ],
796             "children": [],
797             "rendered": null
798         }
799     ],
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"
801     }"##,
802             expect_file!["crates/rust-analyzer/test_data/clippy_pass_by_ref.txt"],
803         );
804     }
805
806     #[test]
807     fn rustc_mismatched_type() {
808         check(
809             r##"{
810     "message": "mismatched types",
811     "code": {
812         "code": "E0308",
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"
814     },
815     "level": "error",
816     "spans": [
817         {
818             "file_name": "runtime/compiler_support.rs",
819             "byte_start": 1589,
820             "byte_end": 1594,
821             "line_start": 48,
822             "line_end": 48,
823             "column_start": 65,
824             "column_end": 70,
825             "is_primary": true,
826             "text": [
827                 {
828                     "text": "    let layout = alloc::Layout::from_size_align_unchecked(size, align);",
829                     "highlight_start": 65,
830                     "highlight_end": 70
831                 }
832             ],
833             "label": "expected usize, found u32",
834             "suggested_replacement": null,
835             "suggestion_applicability": null,
836             "expansion": null
837         }
838     ],
839     "children": [],
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"
841     }"##,
842             expect_file!["crates/rust-analyzer/test_data/rustc_mismatched_type.txt"],
843         );
844     }
845
846     #[test]
847     fn handles_macro_location() {
848         check(
849             r##"{
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",
851     "children": [
852         {
853             "children": [],
854             "code": null,
855             "level": "help",
856             "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
857             "rendered": null,
858             "spans": []
859         }
860     ],
861     "code": {
862         "code": "E0277",
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"
864     },
865     "level": "error",
866     "message": "can't compare `{integer}` with `&str`",
867     "spans": [
868         {
869             "byte_end": 155,
870             "byte_start": 153,
871             "column_end": 33,
872             "column_start": 31,
873             "expansion": {
874                 "def_site_span": {
875                     "byte_end": 940,
876                     "byte_start": 0,
877                     "column_end": 6,
878                     "column_start": 1,
879                     "expansion": null,
880                     "file_name": "<::core::macros::assert_eq macros>",
881                     "is_primary": false,
882                     "label": null,
883                     "line_end": 36,
884                     "line_start": 1,
885                     "suggested_replacement": null,
886                     "suggestion_applicability": null,
887                     "text": [
888                         {
889                             "highlight_end": 35,
890                             "highlight_start": 1,
891                             "text": "($ left : expr, $ right : expr) =>"
892                         },
893                         {
894                             "highlight_end": 3,
895                             "highlight_start": 1,
896                             "text": "({"
897                         },
898                         {
899                             "highlight_end": 33,
900                             "highlight_start": 1,
901                             "text": "     match (& $ left, & $ right)"
902                         },
903                         {
904                             "highlight_end": 7,
905                             "highlight_start": 1,
906                             "text": "     {"
907                         },
908                         {
909                             "highlight_end": 34,
910                             "highlight_start": 1,
911                             "text": "         (left_val, right_val) =>"
912                         },
913                         {
914                             "highlight_end": 11,
915                             "highlight_start": 1,
916                             "text": "         {"
917                         },
918                         {
919                             "highlight_end": 46,
920                             "highlight_start": 1,
921                             "text": "             if ! (* left_val == * right_val)"
922                         },
923                         {
924                             "highlight_end": 15,
925                             "highlight_start": 1,
926                             "text": "             {"
927                         },
928                         {
929                             "highlight_end": 25,
930                             "highlight_start": 1,
931                             "text": "                 panic !"
932                         },
933                         {
934                             "highlight_end": 57,
935                             "highlight_start": 1,
936                             "text": "                 (r#\"assertion failed: `(left == right)`"
937                         },
938                         {
939                             "highlight_end": 16,
940                             "highlight_start": 1,
941                             "text": "  left: `{:?}`,"
942                         },
943                         {
944                             "highlight_end": 18,
945                             "highlight_start": 1,
946                             "text": " right: `{:?}`\"#,"
947                         },
948                         {
949                             "highlight_end": 47,
950                             "highlight_start": 1,
951                             "text": "                  & * left_val, & * right_val)"
952                         },
953                         {
954                             "highlight_end": 15,
955                             "highlight_start": 1,
956                             "text": "             }"
957                         },
958                         {
959                             "highlight_end": 11,
960                             "highlight_start": 1,
961                             "text": "         }"
962                         },
963                         {
964                             "highlight_end": 7,
965                             "highlight_start": 1,
966                             "text": "     }"
967                         },
968                         {
969                             "highlight_end": 42,
970                             "highlight_start": 1,
971                             "text": " }) ; ($ left : expr, $ right : expr,) =>"
972                         },
973                         {
974                             "highlight_end": 49,
975                             "highlight_start": 1,
976                             "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
977                         },
978                         {
979                             "highlight_end": 53,
980                             "highlight_start": 1,
981                             "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
982                         },
983                         {
984                             "highlight_end": 3,
985                             "highlight_start": 1,
986                             "text": "({"
987                         },
988                         {
989                             "highlight_end": 37,
990                             "highlight_start": 1,
991                             "text": "     match (& ($ left), & ($ right))"
992                         },
993                         {
994                             "highlight_end": 7,
995                             "highlight_start": 1,
996                             "text": "     {"
997                         },
998                         {
999                             "highlight_end": 34,
1000                             "highlight_start": 1,
1001                             "text": "         (left_val, right_val) =>"
1002                         },
1003                         {
1004                             "highlight_end": 11,
1005                             "highlight_start": 1,
1006                             "text": "         {"
1007                         },
1008                         {
1009                             "highlight_end": 46,
1010                             "highlight_start": 1,
1011                             "text": "             if ! (* left_val == * right_val)"
1012                         },
1013                         {
1014                             "highlight_end": 15,
1015                             "highlight_start": 1,
1016                             "text": "             {"
1017                         },
1018                         {
1019                             "highlight_end": 25,
1020                             "highlight_start": 1,
1021                             "text": "                 panic !"
1022                         },
1023                         {
1024                             "highlight_end": 57,
1025                             "highlight_start": 1,
1026                             "text": "                 (r#\"assertion failed: `(left == right)`"
1027                         },
1028                         {
1029                             "highlight_end": 16,
1030                             "highlight_start": 1,
1031                             "text": "  left: `{:?}`,"
1032                         },
1033                         {
1034                             "highlight_end": 22,
1035                             "highlight_start": 1,
1036                             "text": " right: `{:?}`: {}\"#,"
1037                         },
1038                         {
1039                             "highlight_end": 72,
1040                             "highlight_start": 1,
1041                             "text": "                  & * left_val, & * right_val, $ crate :: format_args !"
1042                         },
1043                         {
1044                             "highlight_end": 33,
1045                             "highlight_start": 1,
1046                             "text": "                  ($ ($ arg) +))"
1047                         },
1048                         {
1049                             "highlight_end": 15,
1050                             "highlight_start": 1,
1051                             "text": "             }"
1052                         },
1053                         {
1054                             "highlight_end": 11,
1055                             "highlight_start": 1,
1056                             "text": "         }"
1057                         },
1058                         {
1059                             "highlight_end": 7,
1060                             "highlight_start": 1,
1061                             "text": "     }"
1062                         },
1063                         {
1064                             "highlight_end": 6,
1065                             "highlight_start": 1,
1066                             "text": " }) ;"
1067                         }
1068                     ]
1069                 },
1070                 "macro_decl_name": "assert_eq!",
1071                 "span": {
1072                     "byte_end": 38,
1073                     "byte_start": 16,
1074                     "column_end": 27,
1075                     "column_start": 5,
1076                     "expansion": null,
1077                     "file_name": "src/main.rs",
1078                     "is_primary": false,
1079                     "label": null,
1080                     "line_end": 2,
1081                     "line_start": 2,
1082                     "suggested_replacement": null,
1083                     "suggestion_applicability": null,
1084                     "text": [
1085                         {
1086                             "highlight_end": 27,
1087                             "highlight_start": 5,
1088                             "text": "    assert_eq!(1, \"love\");"
1089                         }
1090                     ]
1091                 }
1092             },
1093             "file_name": "<::core::macros::assert_eq macros>",
1094             "is_primary": true,
1095             "label": "no implementation for `{integer} == &str`",
1096             "line_end": 7,
1097             "line_start": 7,
1098             "suggested_replacement": null,
1099             "suggestion_applicability": null,
1100             "text": [
1101                 {
1102                     "highlight_end": 33,
1103                     "highlight_start": 31,
1104                     "text": "             if ! (* left_val == * right_val)"
1105                 }
1106             ]
1107         }
1108     ]
1109     }"##,
1110             expect_file!["crates/rust-analyzer/test_data/handles_macro_location.txt"],
1111         );
1112     }
1113
1114     #[test]
1115     fn macro_compiler_error() {
1116         check(
1117             r##"{
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",
1119         "children": [],
1120         "code": null,
1121         "level": "error",
1122         "message": "Please register your known path in the path module",
1123         "spans": [
1124             {
1125                 "byte_end": 8285,
1126                 "byte_start": 8217,
1127                 "column_end": 77,
1128                 "column_start": 9,
1129                 "expansion": {
1130                     "def_site_span": {
1131                         "byte_end": 8294,
1132                         "byte_start": 7858,
1133                         "column_end": 2,
1134                         "column_start": 1,
1135                         "expansion": null,
1136                         "file_name": "crates/ra_hir_def/src/path.rs",
1137                         "is_primary": false,
1138                         "label": null,
1139                         "line_end": 267,
1140                         "line_start": 254,
1141                         "suggested_replacement": null,
1142                         "suggestion_applicability": null,
1143                         "text": [
1144                             {
1145                                 "highlight_end": 28,
1146                                 "highlight_start": 1,
1147                                 "text": "macro_rules! __known_path {"
1148                             },
1149                             {
1150                                 "highlight_end": 37,
1151                                 "highlight_start": 1,
1152                                 "text": "    (std::iter::IntoIterator) => {};"
1153                             },
1154                             {
1155                                 "highlight_end": 33,
1156                                 "highlight_start": 1,
1157                                 "text": "    (std::result::Result) => {};"
1158                             },
1159                             {
1160                                 "highlight_end": 29,
1161                                 "highlight_start": 1,
1162                                 "text": "    (std::ops::Range) => {};"
1163                             },
1164                             {
1165                                 "highlight_end": 33,
1166                                 "highlight_start": 1,
1167                                 "text": "    (std::ops::RangeFrom) => {};"
1168                             },
1169                             {
1170                                 "highlight_end": 33,
1171                                 "highlight_start": 1,
1172                                 "text": "    (std::ops::RangeFull) => {};"
1173                             },
1174                             {
1175                                 "highlight_end": 31,
1176                                 "highlight_start": 1,
1177                                 "text": "    (std::ops::RangeTo) => {};"
1178                             },
1179                             {
1180                                 "highlight_end": 40,
1181                                 "highlight_start": 1,
1182                                 "text": "    (std::ops::RangeToInclusive) => {};"
1183                             },
1184                             {
1185                                 "highlight_end": 38,
1186                                 "highlight_start": 1,
1187                                 "text": "    (std::ops::RangeInclusive) => {};"
1188                             },
1189                             {
1190                                 "highlight_end": 27,
1191                                 "highlight_start": 1,
1192                                 "text": "    (std::ops::Try) => {};"
1193                             },
1194                             {
1195                                 "highlight_end": 22,
1196                                 "highlight_start": 1,
1197                                 "text": "    ($path:path) => {"
1198                             },
1199                             {
1200                                 "highlight_end": 77,
1201                                 "highlight_start": 1,
1202                                 "text": "        compile_error!(\"Please register your known path in the path module\")"
1203                             },
1204                             {
1205                                 "highlight_end": 7,
1206                                 "highlight_start": 1,
1207                                 "text": "    };"
1208                             },
1209                             {
1210                                 "highlight_end": 2,
1211                                 "highlight_start": 1,
1212                                 "text": "}"
1213                             }
1214                         ]
1215                     },
1216                     "macro_decl_name": "$crate::__known_path!",
1217                     "span": {
1218                         "byte_end": 8427,
1219                         "byte_start": 8385,
1220                         "column_end": 51,
1221                         "column_start": 9,
1222                         "expansion": {
1223                             "def_site_span": {
1224                                 "byte_end": 8611,
1225                                 "byte_start": 8312,
1226                                 "column_end": 2,
1227                                 "column_start": 1,
1228                                 "expansion": null,
1229                                 "file_name": "crates/ra_hir_def/src/path.rs",
1230                                 "is_primary": false,
1231                                 "label": null,
1232                                 "line_end": 277,
1233                                 "line_start": 270,
1234                                 "suggested_replacement": null,
1235                                 "suggestion_applicability": null,
1236                                 "text": [
1237                                     {
1238                                         "highlight_end": 22,
1239                                         "highlight_start": 1,
1240                                         "text": "macro_rules! __path {"
1241                                     },
1242                                     {
1243                                         "highlight_end": 43,
1244                                         "highlight_start": 1,
1245                                         "text": "    ($start:ident $(:: $seg:ident)*) => ({"
1246                                     },
1247                                     {
1248                                         "highlight_end": 51,
1249                                         "highlight_start": 1,
1250                                         "text": "        $crate::__known_path!($start $(:: $seg)*);"
1251                                     },
1252                                     {
1253                                         "highlight_end": 87,
1254                                         "highlight_start": 1,
1255                                         "text": "        $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
1256                                     },
1257                                     {
1258                                         "highlight_end": 76,
1259                                         "highlight_start": 1,
1260                                         "text": "            $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
1261                                     },
1262                                     {
1263                                         "highlight_end": 11,
1264                                         "highlight_start": 1,
1265                                         "text": "        ])"
1266                                     },
1267                                     {
1268                                         "highlight_end": 8,
1269                                         "highlight_start": 1,
1270                                         "text": "    });"
1271                                     },
1272                                     {
1273                                         "highlight_end": 2,
1274                                         "highlight_start": 1,
1275                                         "text": "}"
1276                                     }
1277                                 ]
1278                             },
1279                             "macro_decl_name": "path!",
1280                             "span": {
1281                                 "byte_end": 2966,
1282                                 "byte_start": 2940,
1283                                 "column_end": 42,
1284                                 "column_start": 16,
1285                                 "expansion": null,
1286                                 "file_name": "crates/ra_hir_def/src/data.rs",
1287                                 "is_primary": false,
1288                                 "label": null,
1289                                 "line_end": 80,
1290                                 "line_start": 80,
1291                                 "suggested_replacement": null,
1292                                 "suggestion_applicability": null,
1293                                 "text": [
1294                                     {
1295                                         "highlight_end": 42,
1296                                         "highlight_start": 16,
1297                                         "text": "    let path = path![std::future::Future];"
1298                                     }
1299                                 ]
1300                             }
1301                         },
1302                         "file_name": "crates/ra_hir_def/src/path.rs",
1303                         "is_primary": false,
1304                         "label": null,
1305                         "line_end": 272,
1306                         "line_start": 272,
1307                         "suggested_replacement": null,
1308                         "suggestion_applicability": null,
1309                         "text": [
1310                             {
1311                                 "highlight_end": 51,
1312                                 "highlight_start": 9,
1313                                 "text": "        $crate::__known_path!($start $(:: $seg)*);"
1314                             }
1315                         ]
1316                     }
1317                 },
1318                 "file_name": "crates/ra_hir_def/src/path.rs",
1319                 "is_primary": true,
1320                 "label": null,
1321                 "line_end": 265,
1322                 "line_start": 265,
1323                 "suggested_replacement": null,
1324                 "suggestion_applicability": null,
1325                 "text": [
1326                     {
1327                         "highlight_end": 77,
1328                         "highlight_start": 9,
1329                         "text": "        compile_error!(\"Please register your known path in the path module\")"
1330                     }
1331                 ]
1332             }
1333         ]
1334     }
1335             "##,
1336             expect_file!["crates/rust-analyzer/test_data/macro_compiler_error.txt"],
1337         );
1338     }
1339
1340     #[test]
1341     fn snap_multi_line_fix() {
1342         check(
1343             r##"{
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",
1345                 "children": [
1346                     {
1347                     "children": [],
1348                     "code": null,
1349                     "level": "note",
1350                     "message": "`#[warn(clippy::let_and_return)]` on by default",
1351                     "rendered": null,
1352                     "spans": []
1353                     },
1354                     {
1355                     "children": [],
1356                     "code": null,
1357                     "level": "help",
1358                     "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
1359                     "rendered": null,
1360                     "spans": []
1361                     },
1362                     {
1363                     "children": [],
1364                     "code": null,
1365                     "level": "help",
1366                     "message": "return the expression directly",
1367                     "rendered": null,
1368                     "spans": [
1369                         {
1370                         "byte_end": 55,
1371                         "byte_start": 29,
1372                         "column_end": 31,
1373                         "column_start": 5,
1374                         "expansion": null,
1375                         "file_name": "src/main.rs",
1376                         "is_primary": true,
1377                         "label": null,
1378                         "line_end": 3,
1379                         "line_start": 3,
1380                         "suggested_replacement": "",
1381                         "suggestion_applicability": "MachineApplicable",
1382                         "text": [
1383                             {
1384                             "highlight_end": 31,
1385                             "highlight_start": 5,
1386                             "text": "    let a = (0..10).collect();"
1387                             }
1388                         ]
1389                         },
1390                         {
1391                         "byte_end": 61,
1392                         "byte_start": 60,
1393                         "column_end": 6,
1394                         "column_start": 5,
1395                         "expansion": null,
1396                         "file_name": "src/main.rs",
1397                         "is_primary": true,
1398                         "label": null,
1399                         "line_end": 4,
1400                         "line_start": 4,
1401                         "suggested_replacement": "(0..10).collect()",
1402                         "suggestion_applicability": "MachineApplicable",
1403                         "text": [
1404                             {
1405                             "highlight_end": 6,
1406                             "highlight_start": 5,
1407                             "text": "    a"
1408                             }
1409                         ]
1410                         }
1411                     ]
1412                     }
1413                 ],
1414                 "code": {
1415                     "code": "clippy::let_and_return",
1416                     "explanation": null
1417                 },
1418                 "level": "warning",
1419                 "message": "returning the result of a let binding from a block",
1420                 "spans": [
1421                     {
1422                     "byte_end": 55,
1423                     "byte_start": 29,
1424                     "column_end": 31,
1425                     "column_start": 5,
1426                     "expansion": null,
1427                     "file_name": "src/main.rs",
1428                     "is_primary": false,
1429                     "label": "unnecessary let binding",
1430                     "line_end": 3,
1431                     "line_start": 3,
1432                     "suggested_replacement": null,
1433                     "suggestion_applicability": null,
1434                     "text": [
1435                         {
1436                         "highlight_end": 31,
1437                         "highlight_start": 5,
1438                         "text": "    let a = (0..10).collect();"
1439                         }
1440                     ]
1441                     },
1442                     {
1443                     "byte_end": 61,
1444                     "byte_start": 60,
1445                     "column_end": 6,
1446                     "column_start": 5,
1447                     "expansion": null,
1448                     "file_name": "src/main.rs",
1449                     "is_primary": true,
1450                     "label": null,
1451                     "line_end": 4,
1452                     "line_start": 4,
1453                     "suggested_replacement": null,
1454                     "suggestion_applicability": null,
1455                     "text": [
1456                         {
1457                         "highlight_end": 6,
1458                         "highlight_start": 5,
1459                         "text": "    a"
1460                         }
1461                     ]
1462                     }
1463                 ]
1464             }
1465             "##,
1466             expect_file!["crates/rust-analyzer/test_data/snap_multi_line_fix.txt"],
1467         );
1468     }
1469 }