1use 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
18fn 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 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 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
53fn is_dummy_macro_file(file_name: &str) -> bool {
55 file_name.starts_with('<') && file_name.ends_with('>')
57}
58
59fn 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 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
118fn 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 let last_span = span_stack.last().unwrap();
138 location(config, workspace_root, last_span, snap)
139}
140
141fn 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
155fn 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 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 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 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 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
271pub(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 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 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 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 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 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 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 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 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 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, 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}