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