Skip to main content

rust_analyzer/diagnostics/
flycheck_to_proto.rs

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