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;
4 
5 use flycheck::{DiagnosticLevel, DiagnosticSpan};
6 use itertools::Itertools;
7 use stdx::format_to;
8 use vfs::{AbsPath, AbsPathBuf};
9 
10 use crate::{lsp_ext, to_proto::url_from_abs_path};
11 
12 use super::{DiagnosticsMapConfig, Fix};
13 
14 /// Determines the LSP severity from a diagnostic
diagnostic_severity( config: &DiagnosticsMapConfig, level: flycheck::DiagnosticLevel, code: Option<flycheck::DiagnosticCode>, ) -> Option<lsp_types::DiagnosticSeverity>15 fn diagnostic_severity(
16     config: &DiagnosticsMapConfig,
17     level: flycheck::DiagnosticLevel,
18     code: Option<flycheck::DiagnosticCode>,
19 ) -> Option<lsp_types::DiagnosticSeverity> {
20     let res = match level {
21         DiagnosticLevel::Ice => lsp_types::DiagnosticSeverity::ERROR,
22         DiagnosticLevel::Error => lsp_types::DiagnosticSeverity::ERROR,
23         DiagnosticLevel::Warning => match &code {
24             // HACK: special case for `warnings` rustc lint.
25             Some(code)
26                 if config.warnings_as_hint.iter().any(|lint| {
27                     lint == "warnings" || ide_db::helpers::lint_eq_or_in_group(&code.code, &lint)
28                 }) =>
29             {
30                 lsp_types::DiagnosticSeverity::HINT
31             }
32             // HACK: special case for `warnings` rustc lint.
33             Some(code)
34                 if config.warnings_as_info.iter().any(|lint| {
35                     lint == "warnings" || ide_db::helpers::lint_eq_or_in_group(&code.code, &lint)
36                 }) =>
37             {
38                 lsp_types::DiagnosticSeverity::INFORMATION
39             }
40             _ => lsp_types::DiagnosticSeverity::WARNING,
41         },
42         DiagnosticLevel::Note => lsp_types::DiagnosticSeverity::INFORMATION,
43         DiagnosticLevel::Help => lsp_types::DiagnosticSeverity::HINT,
44         _ => return None,
45     };
46     Some(res)
47 }
48 
49 /// Checks whether a file name is from macro invocation and does not refer to an actual file.
is_dummy_macro_file(file_name: &str) -> bool50 fn is_dummy_macro_file(file_name: &str) -> bool {
51     // FIXME: current rustc does not seem to emit `<macro file>` files anymore?
52     file_name.starts_with('<') && file_name.ends_with('>')
53 }
54 
55 /// Converts a Rust span to a LSP location
location( config: &DiagnosticsMapConfig, workspace_root: &AbsPath, span: &DiagnosticSpan, ) -> lsp_types::Location56 fn location(
57     config: &DiagnosticsMapConfig,
58     workspace_root: &AbsPath,
59     span: &DiagnosticSpan,
60 ) -> lsp_types::Location {
61     let file_name = resolve_path(config, workspace_root, &span.file_name);
62     let uri = url_from_abs_path(&file_name);
63 
64     // FIXME: this doesn't handle UTF16 offsets correctly
65     let range = lsp_types::Range::new(
66         lsp_types::Position::new(span.line_start as u32 - 1, span.column_start as u32 - 1),
67         lsp_types::Position::new(span.line_end as u32 - 1, span.column_end as u32 - 1),
68     );
69 
70     lsp_types::Location { uri, range }
71 }
72 
73 /// Extracts a suitable "primary" location from a rustc diagnostic.
74 ///
75 /// This takes locations pointing into the standard library, or generally outside the current
76 /// workspace into account and tries to avoid those, in case macros are involved.
primary_location( config: &DiagnosticsMapConfig, workspace_root: &AbsPath, span: &DiagnosticSpan, ) -> lsp_types::Location77 fn primary_location(
78     config: &DiagnosticsMapConfig,
79     workspace_root: &AbsPath,
80     span: &DiagnosticSpan,
81 ) -> lsp_types::Location {
82     let span_stack = std::iter::successors(Some(span), |span| Some(&span.expansion.as_ref()?.span));
83     for span in span_stack.clone() {
84         let abs_path = resolve_path(config, workspace_root, &span.file_name);
85         if !is_dummy_macro_file(&span.file_name) && abs_path.starts_with(workspace_root) {
86             return location(config, workspace_root, span);
87         }
88     }
89 
90     // Fall back to the outermost macro invocation if no suitable span comes up.
91     let last_span = span_stack.last().unwrap();
92     location(config, workspace_root, last_span)
93 }
94 
95 /// Converts a secondary Rust span to a LSP related information
96 ///
97 /// If the span is unlabelled this will return `None`.
diagnostic_related_information( config: &DiagnosticsMapConfig, workspace_root: &AbsPath, span: &DiagnosticSpan, ) -> Option<lsp_types::DiagnosticRelatedInformation>98 fn diagnostic_related_information(
99     config: &DiagnosticsMapConfig,
100     workspace_root: &AbsPath,
101     span: &DiagnosticSpan,
102 ) -> Option<lsp_types::DiagnosticRelatedInformation> {
103     let message = span.label.clone()?;
104     let location = location(config, workspace_root, span);
105     Some(lsp_types::DiagnosticRelatedInformation { location, message })
106 }
107 
108 /// Resolves paths applying any matching path prefix remappings, and then
109 /// joining the path to the workspace root.
resolve_path( config: &DiagnosticsMapConfig, workspace_root: &AbsPath, file_name: &str, ) -> AbsPathBuf110 fn resolve_path(
111     config: &DiagnosticsMapConfig,
112     workspace_root: &AbsPath,
113     file_name: &str,
114 ) -> AbsPathBuf {
115     match config
116         .remap_prefix
117         .iter()
118         .find_map(|(from, to)| file_name.strip_prefix(from).map(|file_name| (to, file_name)))
119     {
120         Some((to, file_name)) => workspace_root.join(format!("{}{}", to, file_name)),
121         None => workspace_root.join(file_name),
122     }
123 }
124 
125 struct SubDiagnostic {
126     related: lsp_types::DiagnosticRelatedInformation,
127     suggested_fix: Option<Fix>,
128 }
129 
130 enum MappedRustChildDiagnostic {
131     SubDiagnostic(SubDiagnostic),
132     MessageLine(String),
133 }
134 
map_rust_child_diagnostic( config: &DiagnosticsMapConfig, workspace_root: &AbsPath, rd: &flycheck::Diagnostic, ) -> MappedRustChildDiagnostic135 fn map_rust_child_diagnostic(
136     config: &DiagnosticsMapConfig,
137     workspace_root: &AbsPath,
138     rd: &flycheck::Diagnostic,
139 ) -> MappedRustChildDiagnostic {
140     let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
141     if spans.is_empty() {
142         // `rustc` uses these spanless children as a way to print multi-line
143         // messages
144         return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
145     }
146 
147     let mut edit_map: HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>> = HashMap::new();
148     let mut suggested_replacements = Vec::new();
149     for &span in &spans {
150         if let Some(suggested_replacement) = &span.suggested_replacement {
151             if !suggested_replacement.is_empty() {
152                 suggested_replacements.push(suggested_replacement);
153             }
154             let location = location(config, workspace_root, span);
155             let edit = lsp_types::TextEdit::new(location.range, suggested_replacement.clone());
156             edit_map.entry(location.uri).or_default().push(edit);
157         }
158     }
159 
160     // rustc renders suggestion diagnostics by appending the suggested replacement, so do the same
161     // here, otherwise the diagnostic text is missing useful information.
162     let mut message = rd.message.clone();
163     if !suggested_replacements.is_empty() {
164         message.push_str(": ");
165         let suggestions =
166             suggested_replacements.iter().map(|suggestion| format!("`{}`", suggestion)).join(", ");
167         message.push_str(&suggestions);
168     }
169 
170     if edit_map.is_empty() {
171         MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
172             related: lsp_types::DiagnosticRelatedInformation {
173                 location: location(config, workspace_root, spans[0]),
174                 message,
175             },
176             suggested_fix: None,
177         })
178     } else {
179         MappedRustChildDiagnostic::SubDiagnostic(SubDiagnostic {
180             related: lsp_types::DiagnosticRelatedInformation {
181                 location: location(config, workspace_root, spans[0]),
182                 message: message.clone(),
183             },
184             suggested_fix: Some(Fix {
185                 ranges: spans
186                     .iter()
187                     .map(|&span| location(config, workspace_root, span).range)
188                     .collect(),
189                 action: lsp_ext::CodeAction {
190                     title: message,
191                     group: None,
192                     kind: Some(lsp_types::CodeActionKind::QUICKFIX),
193                     edit: Some(lsp_ext::SnippetWorkspaceEdit {
194                         // FIXME: there's no good reason to use edit_map here....
195                         changes: Some(edit_map),
196                         document_changes: None,
197                         change_annotations: None,
198                     }),
199                     is_preferred: Some(true),
200                     data: None,
201                 },
202             }),
203         })
204     }
205 }
206 
207 #[derive(Debug)]
208 pub(crate) struct MappedRustDiagnostic {
209     pub(crate) url: lsp_types::Url,
210     pub(crate) diagnostic: lsp_types::Diagnostic,
211     pub(crate) fix: Option<Fix>,
212 }
213 
214 /// Converts a Rust root diagnostic to LSP form
215 ///
216 /// This flattens the Rust diagnostic by:
217 ///
218 /// 1. Creating a LSP diagnostic with the root message and primary span.
219 /// 2. Adding any labelled secondary spans to `relatedInformation`
220 /// 3. Categorising child diagnostics as either `SuggestedFix`es,
221 ///    `relatedInformation` or additional message lines.
222 ///
223 /// If the diagnostic has no primary span this will return `None`
map_rust_diagnostic_to_lsp( config: &DiagnosticsMapConfig, rd: &flycheck::Diagnostic, workspace_root: &AbsPath, ) -> Vec<MappedRustDiagnostic>224 pub(crate) fn map_rust_diagnostic_to_lsp(
225     config: &DiagnosticsMapConfig,
226     rd: &flycheck::Diagnostic,
227     workspace_root: &AbsPath,
228 ) -> Vec<MappedRustDiagnostic> {
229     let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
230     if primary_spans.is_empty() {
231         return Vec::new();
232     }
233 
234     let severity = diagnostic_severity(config, rd.level, rd.code.clone());
235 
236     let mut source = String::from("rustc");
237     let mut code = rd.code.as_ref().map(|c| c.code.clone());
238     if let Some(code_val) = &code {
239         // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
240         let scoped_code: Vec<&str> = code_val.split("::").collect();
241         if scoped_code.len() == 2 {
242             source = String::from(scoped_code[0]);
243             code = Some(String::from(scoped_code[1]));
244         }
245     }
246 
247     let mut needs_primary_span_label = true;
248     let mut subdiagnostics = Vec::new();
249     let mut tags = Vec::new();
250 
251     for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
252         let related = diagnostic_related_information(config, workspace_root, secondary_span);
253         if let Some(related) = related {
254             subdiagnostics.push(SubDiagnostic { related, suggested_fix: None });
255         }
256     }
257 
258     let mut message = rd.message.clone();
259     for child in &rd.children {
260         let child = map_rust_child_diagnostic(config, workspace_root, child);
261         match child {
262             MappedRustChildDiagnostic::SubDiagnostic(sub) => {
263                 subdiagnostics.push(sub);
264             }
265             MappedRustChildDiagnostic::MessageLine(message_line) => {
266                 format_to!(message, "\n{}", message_line);
267 
268                 // These secondary messages usually duplicate the content of the
269                 // primary span label.
270                 needs_primary_span_label = false;
271             }
272         }
273     }
274 
275     if let Some(code) = &rd.code {
276         let code = code.code.as_str();
277         if matches!(
278             code,
279             "dead_code"
280                 | "unknown_lints"
281                 | "unreachable_code"
282                 | "unused_attributes"
283                 | "unused_imports"
284                 | "unused_macros"
285                 | "unused_variables"
286         ) {
287             tags.push(lsp_types::DiagnosticTag::UNNECESSARY);
288         }
289 
290         if matches!(code, "deprecated") {
291             tags.push(lsp_types::DiagnosticTag::DEPRECATED);
292         }
293     }
294 
295     let code_description = match source.as_str() {
296         "rustc" => rustc_code_description(code.as_deref()),
297         "clippy" => clippy_code_description(code.as_deref()),
298         _ => None,
299     };
300 
301     primary_spans
302         .iter()
303         .flat_map(|primary_span| {
304             let primary_location = primary_location(config, workspace_root, primary_span);
305 
306             let mut message = message.clone();
307             if needs_primary_span_label {
308                 if let Some(primary_span_label) = &primary_span.label {
309                     format_to!(message, "\n{}", primary_span_label);
310                 }
311             }
312 
313             // Each primary diagnostic span may result in multiple LSP diagnostics.
314             let mut diagnostics = Vec::new();
315 
316             let mut related_info_macro_calls = vec![];
317 
318             // If error occurs from macro expansion, add related info pointing to
319             // where the error originated
320             // Also, we would generate an additional diagnostic, so that exact place of macro
321             // will be highlighted in the error origin place.
322             let span_stack = std::iter::successors(Some(*primary_span), |span| {
323                 Some(&span.expansion.as_ref()?.span)
324             });
325             for (i, span) in span_stack.enumerate() {
326                 if is_dummy_macro_file(&span.file_name) {
327                     continue;
328                 }
329 
330                 // First span is the original diagnostic, others are macro call locations that
331                 // generated that code.
332                 let is_in_macro_call = i != 0;
333 
334                 let secondary_location = location(config, workspace_root, span);
335                 if secondary_location == primary_location {
336                     continue;
337                 }
338                 related_info_macro_calls.push(lsp_types::DiagnosticRelatedInformation {
339                     location: secondary_location.clone(),
340                     message: if is_in_macro_call {
341                         "Error originated from macro call here".to_string()
342                     } else {
343                         "Actual error occurred here".to_string()
344                     },
345                 });
346                 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
347                 let information_for_additional_diagnostic =
348                     vec![lsp_types::DiagnosticRelatedInformation {
349                         location: primary_location.clone(),
350                         message: "Exact error occurred here".to_string(),
351                     }];
352 
353                 let diagnostic = lsp_types::Diagnostic {
354                     range: secondary_location.range,
355                     // downgrade to hint if we're pointing at the macro
356                     severity: Some(lsp_types::DiagnosticSeverity::HINT),
357                     code: code.clone().map(lsp_types::NumberOrString::String),
358                     code_description: code_description.clone(),
359                     source: Some(source.clone()),
360                     message: message.clone(),
361                     related_information: Some(information_for_additional_diagnostic),
362                     tags: if tags.is_empty() { None } else { Some(tags.clone()) },
363                     data: None,
364                 };
365                 diagnostics.push(MappedRustDiagnostic {
366                     url: secondary_location.uri,
367                     diagnostic,
368                     fix: None,
369                 });
370             }
371 
372             // Emit the primary diagnostic.
373             diagnostics.push(MappedRustDiagnostic {
374                 url: primary_location.uri.clone(),
375                 diagnostic: lsp_types::Diagnostic {
376                     range: primary_location.range,
377                     severity,
378                     code: code.clone().map(lsp_types::NumberOrString::String),
379                     code_description: code_description.clone(),
380                     source: Some(source.clone()),
381                     message,
382                     related_information: {
383                         let info = related_info_macro_calls
384                             .iter()
385                             .cloned()
386                             .chain(subdiagnostics.iter().map(|sub| sub.related.clone()))
387                             .collect::<Vec<_>>();
388                         if info.is_empty() {
389                             None
390                         } else {
391                             Some(info)
392                         }
393                     },
394                     tags: if tags.is_empty() { None } else { Some(tags.clone()) },
395                     data: None,
396                 },
397                 fix: None,
398             });
399 
400             // Emit hint-level diagnostics for all `related_information` entries such as "help"s.
401             // This is useful because they will show up in the user's editor, unlike
402             // `related_information`, which just produces hard-to-read links, at least in VS Code.
403             let back_ref = lsp_types::DiagnosticRelatedInformation {
404                 location: primary_location,
405                 message: "original diagnostic".to_string(),
406             };
407             for sub in &subdiagnostics {
408                 // Filter out empty/non-existent messages, as they greatly confuse VS Code.
409                 if sub.related.message.is_empty() {
410                     continue;
411                 }
412                 diagnostics.push(MappedRustDiagnostic {
413                     url: sub.related.location.uri.clone(),
414                     fix: sub.suggested_fix.clone(),
415                     diagnostic: lsp_types::Diagnostic {
416                         range: sub.related.location.range,
417                         severity: Some(lsp_types::DiagnosticSeverity::HINT),
418                         code: code.clone().map(lsp_types::NumberOrString::String),
419                         code_description: code_description.clone(),
420                         source: Some(source.clone()),
421                         message: sub.related.message.clone(),
422                         related_information: Some(vec![back_ref.clone()]),
423                         tags: None, // don't apply modifiers again
424                         data: None,
425                     },
426                 });
427             }
428 
429             diagnostics
430         })
431         .collect()
432 }
433 
rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription>434 fn rustc_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> {
435     code.filter(|code| {
436         let mut chars = code.chars();
437         chars.next().map_or(false, |c| c == 'E')
438             && chars.by_ref().take(4).all(|c| c.is_ascii_digit())
439             && chars.next().is_none()
440     })
441     .and_then(|code| {
442         lsp_types::Url::parse(&format!("https://doc.rust-lang.org/error-index.html#{}", code))
443             .ok()
444             .map(|href| lsp_types::CodeDescription { href })
445     })
446 }
447 
clippy_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription>448 fn clippy_code_description(code: Option<&str>) -> Option<lsp_types::CodeDescription> {
449     code.and_then(|code| {
450         lsp_types::Url::parse(&format!(
451             "https://rust-lang.github.io/rust-clippy/master/index.html#{}",
452             code
453         ))
454         .ok()
455         .map(|href| lsp_types::CodeDescription { href })
456     })
457 }
458 
459 #[cfg(test)]
460 #[cfg(not(windows))]
461 mod tests {
462     use std::{convert::TryInto, path::Path};
463 
464     use super::*;
465 
466     use expect_test::{expect_file, ExpectFile};
467 
check(diagnostics_json: &str, expect: ExpectFile)468     fn check(diagnostics_json: &str, expect: ExpectFile) {
469         check_with_config(DiagnosticsMapConfig::default(), diagnostics_json, expect)
470     }
471 
check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile)472     fn check_with_config(config: DiagnosticsMapConfig, diagnostics_json: &str, expect: ExpectFile) {
473         let diagnostic: flycheck::Diagnostic = serde_json::from_str(diagnostics_json).unwrap();
474         let workspace_root: &AbsPath = Path::new("/test/").try_into().unwrap();
475         let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root);
476         expect.assert_debug_eq(&actual)
477     }
478 
479     #[test]
rustc_incompatible_type_for_trait()480     fn rustc_incompatible_type_for_trait() {
481         check(
482             r##"{
483                 "message": "method `next` has an incompatible type for trait",
484                 "code": {
485                     "code": "E0053",
486                     "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"
487                 },
488                 "level": "error",
489                 "spans": [
490                     {
491                         "file_name": "compiler/ty/list_iter.rs",
492                         "byte_start": 1307,
493                         "byte_end": 1350,
494                         "line_start": 52,
495                         "line_end": 52,
496                         "column_start": 5,
497                         "column_end": 48,
498                         "is_primary": true,
499                         "text": [
500                             {
501                                 "text": "    fn next(&self) -> Option<&'list ty::Ref<M>> {",
502                                 "highlight_start": 5,
503                                 "highlight_end": 48
504                             }
505                         ],
506                         "label": "types differ in mutability",
507                         "suggested_replacement": null,
508                         "suggestion_applicability": null,
509                         "expansion": null
510                     }
511                 ],
512                 "children": [
513                     {
514                         "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>>`",
515                         "code": null,
516                         "level": "note",
517                         "spans": [],
518                         "children": [],
519                         "rendered": null
520                     }
521                 ],
522                 "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"
523             }
524             "##,
525             expect_file!["./test_data/rustc_incompatible_type_for_trait.txt"],
526         );
527     }
528 
529     #[test]
rustc_unused_variable()530     fn rustc_unused_variable() {
531         check(
532             r##"{
533     "message": "unused variable: `foo`",
534     "code": {
535         "code": "unused_variables",
536         "explanation": null
537     },
538     "level": "warning",
539     "spans": [
540         {
541             "file_name": "driver/subcommand/repl.rs",
542             "byte_start": 9228,
543             "byte_end": 9231,
544             "line_start": 291,
545             "line_end": 291,
546             "column_start": 9,
547             "column_end": 12,
548             "is_primary": true,
549             "text": [
550                 {
551                     "text": "    let foo = 42;",
552                     "highlight_start": 9,
553                     "highlight_end": 12
554                 }
555             ],
556             "label": null,
557             "suggested_replacement": null,
558             "suggestion_applicability": null,
559             "expansion": null
560         }
561     ],
562     "children": [
563         {
564             "message": "#[warn(unused_variables)] on by default",
565             "code": null,
566             "level": "note",
567             "spans": [],
568             "children": [],
569             "rendered": null
570         },
571         {
572             "message": "consider prefixing with an underscore",
573             "code": null,
574             "level": "help",
575             "spans": [
576                 {
577                     "file_name": "driver/subcommand/repl.rs",
578                     "byte_start": 9228,
579                     "byte_end": 9231,
580                     "line_start": 291,
581                     "line_end": 291,
582                     "column_start": 9,
583                     "column_end": 12,
584                     "is_primary": true,
585                     "text": [
586                         {
587                             "text": "    let foo = 42;",
588                             "highlight_start": 9,
589                             "highlight_end": 12
590                         }
591                     ],
592                     "label": null,
593                     "suggested_replacement": "_foo",
594                     "suggestion_applicability": "MachineApplicable",
595                     "expansion": null
596                 }
597             ],
598             "children": [],
599             "rendered": null
600         }
601     ],
602     "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"
603     }"##,
604             expect_file!["./test_data/rustc_unused_variable.txt"],
605         );
606     }
607 
608     #[test]
609     #[cfg(not(windows))]
rustc_unused_variable_as_info()610     fn rustc_unused_variable_as_info() {
611         check_with_config(
612             DiagnosticsMapConfig {
613                 warnings_as_info: vec!["unused_variables".to_string()],
614                 ..DiagnosticsMapConfig::default()
615             },
616             r##"{
617     "message": "unused variable: `foo`",
618     "code": {
619         "code": "unused_variables",
620         "explanation": null
621     },
622     "level": "warning",
623     "spans": [
624         {
625             "file_name": "driver/subcommand/repl.rs",
626             "byte_start": 9228,
627             "byte_end": 9231,
628             "line_start": 291,
629             "line_end": 291,
630             "column_start": 9,
631             "column_end": 12,
632             "is_primary": true,
633             "text": [
634                 {
635                     "text": "    let foo = 42;",
636                     "highlight_start": 9,
637                     "highlight_end": 12
638                 }
639             ],
640             "label": null,
641             "suggested_replacement": null,
642             "suggestion_applicability": null,
643             "expansion": null
644         }
645     ],
646     "children": [
647         {
648             "message": "#[warn(unused_variables)] on by default",
649             "code": null,
650             "level": "note",
651             "spans": [],
652             "children": [],
653             "rendered": null
654         },
655         {
656             "message": "consider prefixing with an underscore",
657             "code": null,
658             "level": "help",
659             "spans": [
660                 {
661                     "file_name": "driver/subcommand/repl.rs",
662                     "byte_start": 9228,
663                     "byte_end": 9231,
664                     "line_start": 291,
665                     "line_end": 291,
666                     "column_start": 9,
667                     "column_end": 12,
668                     "is_primary": true,
669                     "text": [
670                         {
671                             "text": "    let foo = 42;",
672                             "highlight_start": 9,
673                             "highlight_end": 12
674                         }
675                     ],
676                     "label": null,
677                     "suggested_replacement": "_foo",
678                     "suggestion_applicability": "MachineApplicable",
679                     "expansion": null
680                 }
681             ],
682             "children": [],
683             "rendered": null
684         }
685     ],
686     "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"
687     }"##,
688             expect_file!["./test_data/rustc_unused_variable_as_info.txt"],
689         );
690     }
691 
692     #[test]
693     #[cfg(not(windows))]
rustc_unused_variable_as_hint()694     fn rustc_unused_variable_as_hint() {
695         check_with_config(
696             DiagnosticsMapConfig {
697                 warnings_as_hint: vec!["unused_variables".to_string()],
698                 ..DiagnosticsMapConfig::default()
699             },
700             r##"{
701     "message": "unused variable: `foo`",
702     "code": {
703         "code": "unused_variables",
704         "explanation": null
705     },
706     "level": "warning",
707     "spans": [
708         {
709             "file_name": "driver/subcommand/repl.rs",
710             "byte_start": 9228,
711             "byte_end": 9231,
712             "line_start": 291,
713             "line_end": 291,
714             "column_start": 9,
715             "column_end": 12,
716             "is_primary": true,
717             "text": [
718                 {
719                     "text": "    let foo = 42;",
720                     "highlight_start": 9,
721                     "highlight_end": 12
722                 }
723             ],
724             "label": null,
725             "suggested_replacement": null,
726             "suggestion_applicability": null,
727             "expansion": null
728         }
729     ],
730     "children": [
731         {
732             "message": "#[warn(unused_variables)] on by default",
733             "code": null,
734             "level": "note",
735             "spans": [],
736             "children": [],
737             "rendered": null
738         },
739         {
740             "message": "consider prefixing with an underscore",
741             "code": null,
742             "level": "help",
743             "spans": [
744                 {
745                     "file_name": "driver/subcommand/repl.rs",
746                     "byte_start": 9228,
747                     "byte_end": 9231,
748                     "line_start": 291,
749                     "line_end": 291,
750                     "column_start": 9,
751                     "column_end": 12,
752                     "is_primary": true,
753                     "text": [
754                         {
755                             "text": "    let foo = 42;",
756                             "highlight_start": 9,
757                             "highlight_end": 12
758                         }
759                     ],
760                     "label": null,
761                     "suggested_replacement": "_foo",
762                     "suggestion_applicability": "MachineApplicable",
763                     "expansion": null
764                 }
765             ],
766             "children": [],
767             "rendered": null
768         }
769     ],
770     "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"
771     }"##,
772             expect_file!["./test_data/rustc_unused_variable_as_hint.txt"],
773         );
774     }
775 
776     #[test]
rustc_wrong_number_of_parameters()777     fn rustc_wrong_number_of_parameters() {
778         check(
779             r##"{
780     "message": "this function takes 2 parameters but 3 parameters were supplied",
781     "code": {
782         "code": "E0061",
783         "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"
784     },
785     "level": "error",
786     "spans": [
787         {
788             "file_name": "compiler/ty/select.rs",
789             "byte_start": 8787,
790             "byte_end": 9241,
791             "line_start": 219,
792             "line_end": 231,
793             "column_start": 5,
794             "column_end": 6,
795             "is_primary": false,
796             "text": [
797                 {
798                     "text": "    pub fn add_evidence(",
799                     "highlight_start": 5,
800                     "highlight_end": 25
801                 },
802                 {
803                     "text": "        &mut self,",
804                     "highlight_start": 1,
805                     "highlight_end": 19
806                 },
807                 {
808                     "text": "        target_poly: &ty::Ref<ty::Poly>,",
809                     "highlight_start": 1,
810                     "highlight_end": 41
811                 },
812                 {
813                     "text": "        evidence_poly: &ty::Ref<ty::Poly>,",
814                     "highlight_start": 1,
815                     "highlight_end": 43
816                 },
817                 {
818                     "text": "    ) {",
819                     "highlight_start": 1,
820                     "highlight_end": 8
821                 },
822                 {
823                     "text": "        match target_poly {",
824                     "highlight_start": 1,
825                     "highlight_end": 28
826                 },
827                 {
828                     "text": "            ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
829                     "highlight_start": 1,
830                     "highlight_end": 81
831                 },
832                 {
833                     "text": "            ty::Ref::Fixed(target_ty) => {",
834                     "highlight_start": 1,
835                     "highlight_end": 43
836                 },
837                 {
838                     "text": "                let evidence_ty = evidence_poly.resolve_to_ty();",
839                     "highlight_start": 1,
840                     "highlight_end": 65
841                 },
842                 {
843                     "text": "                self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
844                     "highlight_start": 1,
845                     "highlight_end": 76
846                 },
847                 {
848                     "text": "            }",
849                     "highlight_start": 1,
850                     "highlight_end": 14
851                 },
852                 {
853                     "text": "        }",
854                     "highlight_start": 1,
855                     "highlight_end": 10
856                 },
857                 {
858                     "text": "    }",
859                     "highlight_start": 1,
860                     "highlight_end": 6
861                 }
862             ],
863             "label": "defined here",
864             "suggested_replacement": null,
865             "suggestion_applicability": null,
866             "expansion": null
867         },
868         {
869             "file_name": "compiler/ty/select.rs",
870             "byte_start": 4045,
871             "byte_end": 4057,
872             "line_start": 104,
873             "line_end": 104,
874             "column_start": 18,
875             "column_end": 30,
876             "is_primary": true,
877             "text": [
878                 {
879                     "text": "            self.add_evidence(target_fixed, evidence_fixed, false);",
880                     "highlight_start": 18,
881                     "highlight_end": 30
882                 }
883             ],
884             "label": "expected 2 parameters",
885             "suggested_replacement": null,
886             "suggestion_applicability": null,
887             "expansion": null
888         }
889     ],
890     "children": [],
891     "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"
892     }"##,
893             expect_file!["./test_data/rustc_wrong_number_of_parameters.txt"],
894         );
895     }
896 
897     #[test]
clippy_pass_by_ref()898     fn clippy_pass_by_ref() {
899         check(
900             r##"{
901     "message": "this argument is passed by reference, but would be more efficient if passed by value",
902     "code": {
903         "code": "clippy::trivially_copy_pass_by_ref",
904         "explanation": null
905     },
906     "level": "warning",
907     "spans": [
908         {
909             "file_name": "compiler/mir/tagset.rs",
910             "byte_start": 941,
911             "byte_end": 946,
912             "line_start": 42,
913             "line_end": 42,
914             "column_start": 24,
915             "column_end": 29,
916             "is_primary": true,
917             "text": [
918                 {
919                     "text": "    pub fn is_disjoint(&self, other: Self) -> bool {",
920                     "highlight_start": 24,
921                     "highlight_end": 29
922                 }
923             ],
924             "label": null,
925             "suggested_replacement": null,
926             "suggestion_applicability": null,
927             "expansion": null
928         }
929     ],
930     "children": [
931         {
932             "message": "lint level defined here",
933             "code": null,
934             "level": "note",
935             "spans": [
936                 {
937                     "file_name": "compiler/lib.rs",
938                     "byte_start": 8,
939                     "byte_end": 19,
940                     "line_start": 1,
941                     "line_end": 1,
942                     "column_start": 9,
943                     "column_end": 20,
944                     "is_primary": true,
945                     "text": [
946                         {
947                             "text": "#![warn(clippy::all)]",
948                             "highlight_start": 9,
949                             "highlight_end": 20
950                         }
951                     ],
952                     "label": null,
953                     "suggested_replacement": null,
954                     "suggestion_applicability": null,
955                     "expansion": null
956                 }
957             ],
958             "children": [],
959             "rendered": null
960         },
961         {
962             "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
963             "code": null,
964             "level": "note",
965             "spans": [],
966             "children": [],
967             "rendered": null
968         },
969         {
970             "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
971             "code": null,
972             "level": "help",
973             "spans": [],
974             "children": [],
975             "rendered": null
976         },
977         {
978             "message": "consider passing by value instead",
979             "code": null,
980             "level": "help",
981             "spans": [
982                 {
983                     "file_name": "compiler/mir/tagset.rs",
984                     "byte_start": 941,
985                     "byte_end": 946,
986                     "line_start": 42,
987                     "line_end": 42,
988                     "column_start": 24,
989                     "column_end": 29,
990                     "is_primary": true,
991                     "text": [
992                         {
993                             "text": "    pub fn is_disjoint(&self, other: Self) -> bool {",
994                             "highlight_start": 24,
995                             "highlight_end": 29
996                         }
997                     ],
998                     "label": null,
999                     "suggested_replacement": "self",
1000                     "suggestion_applicability": "Unspecified",
1001                     "expansion": null
1002                 }
1003             ],
1004             "children": [],
1005             "rendered": null
1006         }
1007     ],
1008     "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"
1009     }"##,
1010             expect_file!["./test_data/clippy_pass_by_ref.txt"],
1011         );
1012     }
1013 
1014     #[test]
rustc_mismatched_type()1015     fn rustc_mismatched_type() {
1016         check(
1017             r##"{
1018     "message": "mismatched types",
1019     "code": {
1020         "code": "E0308",
1021         "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"
1022     },
1023     "level": "error",
1024     "spans": [
1025         {
1026             "file_name": "runtime/compiler_support.rs",
1027             "byte_start": 1589,
1028             "byte_end": 1594,
1029             "line_start": 48,
1030             "line_end": 48,
1031             "column_start": 65,
1032             "column_end": 70,
1033             "is_primary": true,
1034             "text": [
1035                 {
1036                     "text": "    let layout = alloc::Layout::from_size_align_unchecked(size, align);",
1037                     "highlight_start": 65,
1038                     "highlight_end": 70
1039                 }
1040             ],
1041             "label": "expected usize, found u32",
1042             "suggested_replacement": null,
1043             "suggestion_applicability": null,
1044             "expansion": null
1045         }
1046     ],
1047     "children": [],
1048     "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"
1049     }"##,
1050             expect_file!["./test_data/rustc_mismatched_type.txt"],
1051         );
1052     }
1053 
1054     #[test]
handles_macro_location()1055     fn handles_macro_location() {
1056         check(
1057             r##"{
1058     "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",
1059     "children": [
1060         {
1061             "children": [],
1062             "code": null,
1063             "level": "help",
1064             "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
1065             "rendered": null,
1066             "spans": []
1067         }
1068     ],
1069     "code": {
1070         "code": "E0277",
1071         "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"
1072     },
1073     "level": "error",
1074     "message": "can't compare `{integer}` with `&str`",
1075     "spans": [
1076         {
1077             "byte_end": 155,
1078             "byte_start": 153,
1079             "column_end": 33,
1080             "column_start": 31,
1081             "expansion": {
1082                 "def_site_span": {
1083                     "byte_end": 940,
1084                     "byte_start": 0,
1085                     "column_end": 6,
1086                     "column_start": 1,
1087                     "expansion": null,
1088                     "file_name": "<::core::macros::assert_eq macros>",
1089                     "is_primary": false,
1090                     "label": null,
1091                     "line_end": 36,
1092                     "line_start": 1,
1093                     "suggested_replacement": null,
1094                     "suggestion_applicability": null,
1095                     "text": [
1096                         {
1097                             "highlight_end": 35,
1098                             "highlight_start": 1,
1099                             "text": "($ left : expr, $ right : expr) =>"
1100                         },
1101                         {
1102                             "highlight_end": 3,
1103                             "highlight_start": 1,
1104                             "text": "({"
1105                         },
1106                         {
1107                             "highlight_end": 33,
1108                             "highlight_start": 1,
1109                             "text": "     match (& $ left, & $ right)"
1110                         },
1111                         {
1112                             "highlight_end": 7,
1113                             "highlight_start": 1,
1114                             "text": "     {"
1115                         },
1116                         {
1117                             "highlight_end": 34,
1118                             "highlight_start": 1,
1119                             "text": "         (left_val, right_val) =>"
1120                         },
1121                         {
1122                             "highlight_end": 11,
1123                             "highlight_start": 1,
1124                             "text": "         {"
1125                         },
1126                         {
1127                             "highlight_end": 46,
1128                             "highlight_start": 1,
1129                             "text": "             if ! (* left_val == * right_val)"
1130                         },
1131                         {
1132                             "highlight_end": 15,
1133                             "highlight_start": 1,
1134                             "text": "             {"
1135                         },
1136                         {
1137                             "highlight_end": 25,
1138                             "highlight_start": 1,
1139                             "text": "                 panic !"
1140                         },
1141                         {
1142                             "highlight_end": 57,
1143                             "highlight_start": 1,
1144                             "text": "                 (r#\"assertion failed: `(left == right)`"
1145                         },
1146                         {
1147                             "highlight_end": 16,
1148                             "highlight_start": 1,
1149                             "text": "  left: `{:?}`,"
1150                         },
1151                         {
1152                             "highlight_end": 18,
1153                             "highlight_start": 1,
1154                             "text": " right: `{:?}`\"#,"
1155                         },
1156                         {
1157                             "highlight_end": 47,
1158                             "highlight_start": 1,
1159                             "text": "                  & * left_val, & * right_val)"
1160                         },
1161                         {
1162                             "highlight_end": 15,
1163                             "highlight_start": 1,
1164                             "text": "             }"
1165                         },
1166                         {
1167                             "highlight_end": 11,
1168                             "highlight_start": 1,
1169                             "text": "         }"
1170                         },
1171                         {
1172                             "highlight_end": 7,
1173                             "highlight_start": 1,
1174                             "text": "     }"
1175                         },
1176                         {
1177                             "highlight_end": 42,
1178                             "highlight_start": 1,
1179                             "text": " }) ; ($ left : expr, $ right : expr,) =>"
1180                         },
1181                         {
1182                             "highlight_end": 49,
1183                             "highlight_start": 1,
1184                             "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
1185                         },
1186                         {
1187                             "highlight_end": 53,
1188                             "highlight_start": 1,
1189                             "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
1190                         },
1191                         {
1192                             "highlight_end": 3,
1193                             "highlight_start": 1,
1194                             "text": "({"
1195                         },
1196                         {
1197                             "highlight_end": 37,
1198                             "highlight_start": 1,
1199                             "text": "     match (& ($ left), & ($ right))"
1200                         },
1201                         {
1202                             "highlight_end": 7,
1203                             "highlight_start": 1,
1204                             "text": "     {"
1205                         },
1206                         {
1207                             "highlight_end": 34,
1208                             "highlight_start": 1,
1209                             "text": "         (left_val, right_val) =>"
1210                         },
1211                         {
1212                             "highlight_end": 11,
1213                             "highlight_start": 1,
1214                             "text": "         {"
1215                         },
1216                         {
1217                             "highlight_end": 46,
1218                             "highlight_start": 1,
1219                             "text": "             if ! (* left_val == * right_val)"
1220                         },
1221                         {
1222                             "highlight_end": 15,
1223                             "highlight_start": 1,
1224                             "text": "             {"
1225                         },
1226                         {
1227                             "highlight_end": 25,
1228                             "highlight_start": 1,
1229                             "text": "                 panic !"
1230                         },
1231                         {
1232                             "highlight_end": 57,
1233                             "highlight_start": 1,
1234                             "text": "                 (r#\"assertion failed: `(left == right)`"
1235                         },
1236                         {
1237                             "highlight_end": 16,
1238                             "highlight_start": 1,
1239                             "text": "  left: `{:?}`,"
1240                         },
1241                         {
1242                             "highlight_end": 22,
1243                             "highlight_start": 1,
1244                             "text": " right: `{:?}`: {}\"#,"
1245                         },
1246                         {
1247                             "highlight_end": 72,
1248                             "highlight_start": 1,
1249                             "text": "                  & * left_val, & * right_val, $ crate :: format_args !"
1250                         },
1251                         {
1252                             "highlight_end": 33,
1253                             "highlight_start": 1,
1254                             "text": "                  ($ ($ arg) +))"
1255                         },
1256                         {
1257                             "highlight_end": 15,
1258                             "highlight_start": 1,
1259                             "text": "             }"
1260                         },
1261                         {
1262                             "highlight_end": 11,
1263                             "highlight_start": 1,
1264                             "text": "         }"
1265                         },
1266                         {
1267                             "highlight_end": 7,
1268                             "highlight_start": 1,
1269                             "text": "     }"
1270                         },
1271                         {
1272                             "highlight_end": 6,
1273                             "highlight_start": 1,
1274                             "text": " }) ;"
1275                         }
1276                     ]
1277                 },
1278                 "macro_decl_name": "assert_eq!",
1279                 "span": {
1280                     "byte_end": 38,
1281                     "byte_start": 16,
1282                     "column_end": 27,
1283                     "column_start": 5,
1284                     "expansion": null,
1285                     "file_name": "src/main.rs",
1286                     "is_primary": false,
1287                     "label": null,
1288                     "line_end": 2,
1289                     "line_start": 2,
1290                     "suggested_replacement": null,
1291                     "suggestion_applicability": null,
1292                     "text": [
1293                         {
1294                             "highlight_end": 27,
1295                             "highlight_start": 5,
1296                             "text": "    assert_eq!(1, \"love\");"
1297                         }
1298                     ]
1299                 }
1300             },
1301             "file_name": "<::core::macros::assert_eq macros>",
1302             "is_primary": true,
1303             "label": "no implementation for `{integer} == &str`",
1304             "line_end": 7,
1305             "line_start": 7,
1306             "suggested_replacement": null,
1307             "suggestion_applicability": null,
1308             "text": [
1309                 {
1310                     "highlight_end": 33,
1311                     "highlight_start": 31,
1312                     "text": "             if ! (* left_val == * right_val)"
1313                 }
1314             ]
1315         }
1316     ]
1317     }"##,
1318             expect_file!["./test_data/handles_macro_location.txt"],
1319         );
1320     }
1321 
1322     #[test]
macro_compiler_error()1323     fn macro_compiler_error() {
1324         check(
1325             r##"{
1326         "rendered": "error: Please register your known path in the path module\n   --> crates/hir_def/src/path.rs:265:9\n    |\n265 |         compile_error!(\"Please register your known path in the path module\")\n    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    | \n   ::: crates/hir_def/src/data.rs:80:16\n    |\n80  |     let path = path![std::future::Future];\n    |                -------------------------- in this macro invocation\n\n",
1327         "children": [],
1328         "code": null,
1329         "level": "error",
1330         "message": "Please register your known path in the path module",
1331         "spans": [
1332             {
1333                 "byte_end": 8285,
1334                 "byte_start": 8217,
1335                 "column_end": 77,
1336                 "column_start": 9,
1337                 "expansion": {
1338                     "def_site_span": {
1339                         "byte_end": 8294,
1340                         "byte_start": 7858,
1341                         "column_end": 2,
1342                         "column_start": 1,
1343                         "expansion": null,
1344                         "file_name": "crates/hir_def/src/path.rs",
1345                         "is_primary": false,
1346                         "label": null,
1347                         "line_end": 267,
1348                         "line_start": 254,
1349                         "suggested_replacement": null,
1350                         "suggestion_applicability": null,
1351                         "text": [
1352                             {
1353                                 "highlight_end": 28,
1354                                 "highlight_start": 1,
1355                                 "text": "macro_rules! __known_path {"
1356                             },
1357                             {
1358                                 "highlight_end": 37,
1359                                 "highlight_start": 1,
1360                                 "text": "    (std::iter::IntoIterator) => {};"
1361                             },
1362                             {
1363                                 "highlight_end": 33,
1364                                 "highlight_start": 1,
1365                                 "text": "    (std::result::Result) => {};"
1366                             },
1367                             {
1368                                 "highlight_end": 29,
1369                                 "highlight_start": 1,
1370                                 "text": "    (std::ops::Range) => {};"
1371                             },
1372                             {
1373                                 "highlight_end": 33,
1374                                 "highlight_start": 1,
1375                                 "text": "    (std::ops::RangeFrom) => {};"
1376                             },
1377                             {
1378                                 "highlight_end": 33,
1379                                 "highlight_start": 1,
1380                                 "text": "    (std::ops::RangeFull) => {};"
1381                             },
1382                             {
1383                                 "highlight_end": 31,
1384                                 "highlight_start": 1,
1385                                 "text": "    (std::ops::RangeTo) => {};"
1386                             },
1387                             {
1388                                 "highlight_end": 40,
1389                                 "highlight_start": 1,
1390                                 "text": "    (std::ops::RangeToInclusive) => {};"
1391                             },
1392                             {
1393                                 "highlight_end": 38,
1394                                 "highlight_start": 1,
1395                                 "text": "    (std::ops::RangeInclusive) => {};"
1396                             },
1397                             {
1398                                 "highlight_end": 27,
1399                                 "highlight_start": 1,
1400                                 "text": "    (std::ops::Try) => {};"
1401                             },
1402                             {
1403                                 "highlight_end": 22,
1404                                 "highlight_start": 1,
1405                                 "text": "    ($path:path) => {"
1406                             },
1407                             {
1408                                 "highlight_end": 77,
1409                                 "highlight_start": 1,
1410                                 "text": "        compile_error!(\"Please register your known path in the path module\")"
1411                             },
1412                             {
1413                                 "highlight_end": 7,
1414                                 "highlight_start": 1,
1415                                 "text": "    };"
1416                             },
1417                             {
1418                                 "highlight_end": 2,
1419                                 "highlight_start": 1,
1420                                 "text": "}"
1421                             }
1422                         ]
1423                     },
1424                     "macro_decl_name": "$crate::__known_path!",
1425                     "span": {
1426                         "byte_end": 8427,
1427                         "byte_start": 8385,
1428                         "column_end": 51,
1429                         "column_start": 9,
1430                         "expansion": {
1431                             "def_site_span": {
1432                                 "byte_end": 8611,
1433                                 "byte_start": 8312,
1434                                 "column_end": 2,
1435                                 "column_start": 1,
1436                                 "expansion": null,
1437                                 "file_name": "crates/hir_def/src/path.rs",
1438                                 "is_primary": false,
1439                                 "label": null,
1440                                 "line_end": 277,
1441                                 "line_start": 270,
1442                                 "suggested_replacement": null,
1443                                 "suggestion_applicability": null,
1444                                 "text": [
1445                                     {
1446                                         "highlight_end": 22,
1447                                         "highlight_start": 1,
1448                                         "text": "macro_rules! __path {"
1449                                     },
1450                                     {
1451                                         "highlight_end": 43,
1452                                         "highlight_start": 1,
1453                                         "text": "    ($start:ident $(:: $seg:ident)*) => ({"
1454                                     },
1455                                     {
1456                                         "highlight_end": 51,
1457                                         "highlight_start": 1,
1458                                         "text": "        $crate::__known_path!($start $(:: $seg)*);"
1459                                     },
1460                                     {
1461                                         "highlight_end": 87,
1462                                         "highlight_start": 1,
1463                                         "text": "        $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
1464                                     },
1465                                     {
1466                                         "highlight_end": 76,
1467                                         "highlight_start": 1,
1468                                         "text": "            $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
1469                                     },
1470                                     {
1471                                         "highlight_end": 11,
1472                                         "highlight_start": 1,
1473                                         "text": "        ])"
1474                                     },
1475                                     {
1476                                         "highlight_end": 8,
1477                                         "highlight_start": 1,
1478                                         "text": "    });"
1479                                     },
1480                                     {
1481                                         "highlight_end": 2,
1482                                         "highlight_start": 1,
1483                                         "text": "}"
1484                                     }
1485                                 ]
1486                             },
1487                             "macro_decl_name": "path!",
1488                             "span": {
1489                                 "byte_end": 2966,
1490                                 "byte_start": 2940,
1491                                 "column_end": 42,
1492                                 "column_start": 16,
1493                                 "expansion": null,
1494                                 "file_name": "crates/hir_def/src/data.rs",
1495                                 "is_primary": false,
1496                                 "label": null,
1497                                 "line_end": 80,
1498                                 "line_start": 80,
1499                                 "suggested_replacement": null,
1500                                 "suggestion_applicability": null,
1501                                 "text": [
1502                                     {
1503                                         "highlight_end": 42,
1504                                         "highlight_start": 16,
1505                                         "text": "    let path = path![std::future::Future];"
1506                                     }
1507                                 ]
1508                             }
1509                         },
1510                         "file_name": "crates/hir_def/src/path.rs",
1511                         "is_primary": false,
1512                         "label": null,
1513                         "line_end": 272,
1514                         "line_start": 272,
1515                         "suggested_replacement": null,
1516                         "suggestion_applicability": null,
1517                         "text": [
1518                             {
1519                                 "highlight_end": 51,
1520                                 "highlight_start": 9,
1521                                 "text": "        $crate::__known_path!($start $(:: $seg)*);"
1522                             }
1523                         ]
1524                     }
1525                 },
1526                 "file_name": "crates/hir_def/src/path.rs",
1527                 "is_primary": true,
1528                 "label": null,
1529                 "line_end": 265,
1530                 "line_start": 265,
1531                 "suggested_replacement": null,
1532                 "suggestion_applicability": null,
1533                 "text": [
1534                     {
1535                         "highlight_end": 77,
1536                         "highlight_start": 9,
1537                         "text": "        compile_error!(\"Please register your known path in the path module\")"
1538                     }
1539                 ]
1540             }
1541         ]
1542     }
1543             "##,
1544             expect_file!["./test_data/macro_compiler_error.txt"],
1545         );
1546     }
1547 
1548     #[test]
snap_multi_line_fix()1549     fn snap_multi_line_fix() {
1550         check(
1551             r##"{
1552                 "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",
1553                 "children": [
1554                     {
1555                     "children": [],
1556                     "code": null,
1557                     "level": "note",
1558                     "message": "`#[warn(clippy::let_and_return)]` on by default",
1559                     "rendered": null,
1560                     "spans": []
1561                     },
1562                     {
1563                     "children": [],
1564                     "code": null,
1565                     "level": "help",
1566                     "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
1567                     "rendered": null,
1568                     "spans": []
1569                     },
1570                     {
1571                     "children": [],
1572                     "code": null,
1573                     "level": "help",
1574                     "message": "return the expression directly",
1575                     "rendered": null,
1576                     "spans": [
1577                         {
1578                         "byte_end": 55,
1579                         "byte_start": 29,
1580                         "column_end": 31,
1581                         "column_start": 5,
1582                         "expansion": null,
1583                         "file_name": "src/main.rs",
1584                         "is_primary": true,
1585                         "label": null,
1586                         "line_end": 3,
1587                         "line_start": 3,
1588                         "suggested_replacement": "",
1589                         "suggestion_applicability": "MachineApplicable",
1590                         "text": [
1591                             {
1592                             "highlight_end": 31,
1593                             "highlight_start": 5,
1594                             "text": "    let a = (0..10).collect();"
1595                             }
1596                         ]
1597                         },
1598                         {
1599                         "byte_end": 61,
1600                         "byte_start": 60,
1601                         "column_end": 6,
1602                         "column_start": 5,
1603                         "expansion": null,
1604                         "file_name": "src/main.rs",
1605                         "is_primary": true,
1606                         "label": null,
1607                         "line_end": 4,
1608                         "line_start": 4,
1609                         "suggested_replacement": "(0..10).collect()",
1610                         "suggestion_applicability": "MachineApplicable",
1611                         "text": [
1612                             {
1613                             "highlight_end": 6,
1614                             "highlight_start": 5,
1615                             "text": "    a"
1616                             }
1617                         ]
1618                         }
1619                     ]
1620                     }
1621                 ],
1622                 "code": {
1623                     "code": "clippy::let_and_return",
1624                     "explanation": null
1625                 },
1626                 "level": "warning",
1627                 "message": "returning the result of a let binding from a block",
1628                 "spans": [
1629                     {
1630                     "byte_end": 55,
1631                     "byte_start": 29,
1632                     "column_end": 31,
1633                     "column_start": 5,
1634                     "expansion": null,
1635                     "file_name": "src/main.rs",
1636                     "is_primary": false,
1637                     "label": "unnecessary let binding",
1638                     "line_end": 3,
1639                     "line_start": 3,
1640                     "suggested_replacement": null,
1641                     "suggestion_applicability": null,
1642                     "text": [
1643                         {
1644                         "highlight_end": 31,
1645                         "highlight_start": 5,
1646                         "text": "    let a = (0..10).collect();"
1647                         }
1648                     ]
1649                     },
1650                     {
1651                     "byte_end": 61,
1652                     "byte_start": 60,
1653                     "column_end": 6,
1654                     "column_start": 5,
1655                     "expansion": null,
1656                     "file_name": "src/main.rs",
1657                     "is_primary": true,
1658                     "label": null,
1659                     "line_end": 4,
1660                     "line_start": 4,
1661                     "suggested_replacement": null,
1662                     "suggestion_applicability": null,
1663                     "text": [
1664                         {
1665                         "highlight_end": 6,
1666                         "highlight_start": 5,
1667                         "text": "    a"
1668                         }
1669                     ]
1670                     }
1671                 ]
1672             }
1673             "##,
1674             expect_file!["./test_data/snap_multi_line_fix.txt"],
1675         );
1676     }
1677 }
1678