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