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