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