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
;
5 use flycheck
::{Applicability, DiagnosticLevel, DiagnosticSpan}
;
6 use itertools
::Itertools
;
8 use vfs
::{AbsPath, AbsPathBuf}
;
11 global_state
::GlobalStateSnapshot
, line_index
::OffsetEncoding
, lsp_ext
,
12 to_proto
::url_from_abs_path
,
15 use super::{DiagnosticsMapConfig, Fix}
;
17 /// Determines the LSP severity from a diagnostic
18 fn diagnostic_severity(
19 config
: &DiagnosticsMapConfig
,
20 level
: flycheck
::DiagnosticLevel
,
21 code
: Option
<flycheck
::DiagnosticCode
>,
22 ) -> Option
<lsp_types
::DiagnosticSeverity
> {
23 let res
= match level
{
24 DiagnosticLevel
::Ice
=> lsp_types
::DiagnosticSeverity
::ERROR
,
25 DiagnosticLevel
::Error
=> lsp_types
::DiagnosticSeverity
::ERROR
,
26 DiagnosticLevel
::Warning
=> match &code
{
27 // HACK: special case for `warnings` rustc lint.
29 if config
.warnings_as_hint
.iter().any(|lint
| {
30 lint
== "warnings" || ide_db
::helpers
::lint_eq_or_in_group(&code
.code
, lint
)
33 lsp_types
::DiagnosticSeverity
::HINT
35 // HACK: special case for `warnings` rustc lint.
37 if config
.warnings_as_info
.iter().any(|lint
| {
38 lint
== "warnings" || ide_db
::helpers
::lint_eq_or_in_group(&code
.code
, lint
)
41 lsp_types
::DiagnosticSeverity
::INFORMATION
43 _
=> lsp_types
::DiagnosticSeverity
::WARNING
,
45 DiagnosticLevel
::Note
=> lsp_types
::DiagnosticSeverity
::INFORMATION
,
46 DiagnosticLevel
::Help
=> lsp_types
::DiagnosticSeverity
::HINT
,
52 /// Checks whether a file name is from macro invocation and does not refer to an actual file.
53 fn is_dummy_macro_file(file_name
: &str) -> bool
{
54 // FIXME: current rustc does not seem to emit `<macro file>` files anymore?
55 file_name
.starts_with('
<'
) && file_name
.ends_with('
>'
)
58 /// Converts a Rust span to a LSP 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
);
69 let offset_encoding
= snap
.config
.offset_encoding();
70 lsp_types
::Range
::new(
71 position(&offset_encoding
, span
, span
.line_start
, span
.column_start
),
72 position(&offset_encoding
, span
, span
.line_end
, span
.column_end
),
75 lsp_types
::Location
::new(uri
, range
)
79 offset_encoding
: &OffsetEncoding
,
80 span
: &DiagnosticSpan
,
83 ) -> lsp_types
::Position
{
84 let line_index
= line_offset
- span
.line_start
;
86 let mut true_column_offset
= column_offset
;
87 if let Some(line
) = span
.text
.get(line_index
) {
88 if line
.text
.chars().count() == line
.text
.len() {
89 // all one byte utf-8 char
90 return lsp_types
::Position
{
91 line
: (line_offset
as u32).saturating_sub(1),
92 character
: (column_offset
as u32).saturating_sub(1),
95 let mut char_offset
= 0;
96 let len_func
= match offset_encoding
{
97 OffsetEncoding
::Utf8
=> char::len_utf8
,
98 OffsetEncoding
::Utf16
=> char::len_utf16
,
100 for c
in line
.text
.chars() {
102 if char_offset
> column_offset
{
105 true_column_offset
+= len_func(c
) - 1;
109 lsp_types
::Position
{
110 line
: (line_offset
as u32).saturating_sub(1),
111 character
: (true_column_offset
as u32).saturating_sub(1),
115 /// Extracts a suitable "primary" location from a rustc diagnostic.
117 /// This takes locations pointing into the standard library, or generally outside the current
118 /// workspace into account and tries to avoid those, in case macros are involved.
120 config
: &DiagnosticsMapConfig
,
121 workspace_root
: &AbsPath
,
122 span
: &DiagnosticSpan
,
123 snap
: &GlobalStateSnapshot
,
124 ) -> lsp_types
::Location
{
125 let span_stack
= std
::iter
::successors(Some(span
), |span
| Some(&span
.expansion
.as_ref()?
.span
));
126 for span
in span_stack
.clone() {
127 let abs_path
= resolve_path(config
, workspace_root
, &span
.file_name
);
128 if !is_dummy_macro_file(&span
.file_name
) && abs_path
.starts_with(workspace_root
) {
129 return location(config
, workspace_root
, span
, snap
);
133 // Fall back to the outermost macro invocation if no suitable span comes up.
134 let last_span
= span_stack
.last().unwrap();
135 location(config
, workspace_root
, last_span
, snap
)
138 /// Converts a secondary Rust span to a LSP related information
140 /// If the span is unlabelled this will return `None`.
141 fn diagnostic_related_information(
142 config
: &DiagnosticsMapConfig
,
143 workspace_root
: &AbsPath
,
144 span
: &DiagnosticSpan
,
145 snap
: &GlobalStateSnapshot
,
146 ) -> Option
<lsp_types
::DiagnosticRelatedInformation
> {
147 let message
= span
.label
.clone()?
;
148 let location
= location(config
, workspace_root
, span
, snap
);
149 Some(lsp_types
::DiagnosticRelatedInformation { location, message }
)
152 /// Resolves paths applying any matching path prefix remappings, and then
153 /// joining the path to the workspace root.
155 config
: &DiagnosticsMapConfig
,
156 workspace_root
: &AbsPath
,
162 .find_map(|(from
, to
)| file_name
.strip_prefix(from
).map(|file_name
| (to
, file_name
)))
164 Some((to
, file_name
)) => workspace_root
.join(format
!("{}{}", to
, file_name
)),
165 None
=> workspace_root
.join(file_name
),
169 struct SubDiagnostic
{
170 related
: lsp_types
::DiagnosticRelatedInformation
,
171 suggested_fix
: Option
<Fix
>,
174 enum MappedRustChildDiagnostic
{
175 SubDiagnostic(SubDiagnostic
),
179 fn map_rust_child_diagnostic(
180 config
: &DiagnosticsMapConfig
,
181 workspace_root
: &AbsPath
,
182 rd
: &flycheck
::Diagnostic
,
183 snap
: &GlobalStateSnapshot
,
184 ) -> MappedRustChildDiagnostic
{
185 let spans
: Vec
<&DiagnosticSpan
> = rd
.spans
.iter().filter(|s
| s
.is_primary
).collect();
186 if spans
.is_empty() {
187 // `rustc` uses these spanless children as a way to print multi-line
189 return MappedRustChildDiagnostic
::MessageLine(rd
.message
.clone());
192 let mut edit_map
: HashMap
<lsp_types
::Url
, Vec
<lsp_types
::TextEdit
>> = HashMap
::new();
193 let mut suggested_replacements
= Vec
::new();
194 for &span
in &spans
{
195 if let Some(suggested_replacement
) = &span
.suggested_replacement
{
196 if !suggested_replacement
.is_empty() {
197 suggested_replacements
.push(suggested_replacement
);
199 let location
= location(config
, workspace_root
, span
, snap
);
200 let edit
= lsp_types
::TextEdit
::new(location
.range
, suggested_replacement
.clone());
202 // Only actually emit a quickfix if the suggestion is "valid enough".
203 // We accept both "MaybeIncorrect" and "MachineApplicable". "MaybeIncorrect" means that
204 // the suggestion is *complete* (contains no placeholders where code needs to be
205 // inserted), but might not be what the user wants, or might need minor adjustments.
207 span
.suggestion_applicability
,
208 None
| Some(Applicability
::MaybeIncorrect
| Applicability
::MachineApplicable
)
210 edit_map
.entry(location
.uri
).or_default().push(edit
);
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(": ");
221 suggested_replacements
.iter().map(|suggestion
| format
!("`{}`", suggestion
)).join(", ");
222 message
.push_str(&suggestions
);
225 if edit_map
.is_empty() {
226 MappedRustChildDiagnostic
::SubDiagnostic(SubDiagnostic
{
227 related
: lsp_types
::DiagnosticRelatedInformation
{
228 location
: location(config
, workspace_root
, spans
[0], snap
),
234 MappedRustChildDiagnostic
::SubDiagnostic(SubDiagnostic
{
235 related
: lsp_types
::DiagnosticRelatedInformation
{
236 location
: location(config
, workspace_root
, spans
[0], snap
),
237 message
: message
.clone(),
239 suggested_fix
: Some(Fix
{
242 .map(|&span
| location(config
, workspace_root
, span
, snap
).range
)
244 action
: lsp_ext
::CodeAction
{
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
,
254 is_preferred
: Some(true),
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
>,
270 /// Converts a Rust root diagnostic to LSP form
272 /// This flattens the Rust diagnostic by:
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.
279 /// If the diagnostic has no primary span this will return `None`
280 pub(crate) fn map_rust_diagnostic_to_lsp(
281 config
: &DiagnosticsMapConfig
,
282 rd
: &flycheck
::Diagnostic
,
283 workspace_root
: &AbsPath
,
284 snap
: &GlobalStateSnapshot
,
285 ) -> Vec
<MappedRustDiagnostic
> {
286 let primary_spans
: Vec
<&DiagnosticSpan
> = rd
.spans
.iter().filter(|s
| s
.is_primary
).collect();
287 if primary_spans
.is_empty() {
291 let severity
= diagnostic_severity(config
, rd
.level
, rd
.code
.clone());
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]));
304 let mut needs_primary_span_label
= true;
305 let mut subdiagnostics
= Vec
::new();
306 let mut tags
= Vec
::new();
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 }
);
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
);
319 MappedRustChildDiagnostic
::SubDiagnostic(sub
) => {
320 subdiagnostics
.push(sub
);
322 MappedRustChildDiagnostic
::MessageLine(message_line
) => {
323 format_to
!(message
, "\n{}", message_line
);
325 // These secondary messages usually duplicate the content of the
326 // primary span label.
327 needs_primary_span_label
= false;
332 if let Some(code
) = &rd
.code
{
333 let code
= code
.code
.as_str();
339 | "unused_attributes"
344 tags
.push(lsp_types
::DiagnosticTag
::UNNECESSARY
);
347 if matches
!(code
, "deprecated") {
348 tags
.push(lsp_types
::DiagnosticTag
::DEPRECATED
);
352 let code_description
= match source
.as_str() {
353 "rustc" => rustc_code_description(code
.as_deref()),
354 "clippy" => clippy_code_description(code
.as_deref()),
360 .flat_map(|primary_span
| {
361 let primary_location
= primary_location(config
, workspace_root
, primary_span
, snap
);
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
);
370 // Each primary diagnostic span may result in multiple LSP diagnostics.
371 let mut diagnostics
= Vec
::new();
373 let mut related_info_macro_calls
= vec
![];
375 // If error occurs from macro expansion, add related info pointing to
376 // where the error originated
377 // Also, we would generate an additional diagnostic, so that exact place of macro
378 // will be highlighted in the error origin place.
379 let span_stack
= std
::iter
::successors(Some(*primary_span
), |span
| {
380 Some(&span
.expansion
.as_ref()?
.span
)
382 for (i
, span
) in span_stack
.enumerate() {
383 if is_dummy_macro_file(&span
.file_name
) {
387 // First span is the original diagnostic, others are macro call locations that
388 // generated that code.
389 let is_in_macro_call
= i
!= 0;
391 let secondary_location
= location(config
, workspace_root
, span
, snap
);
392 if secondary_location
== primary_location
{
395 related_info_macro_calls
.push(lsp_types
::DiagnosticRelatedInformation
{
396 location
: secondary_location
.clone(),
397 message
: if is_in_macro_call
{
398 "Error originated from macro call here".to_string()
400 "Actual error occurred here".to_string()
403 // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code.
404 let information_for_additional_diagnostic
=
405 vec
![lsp_types
::DiagnosticRelatedInformation
{
406 location
: primary_location
.clone(),
407 message
: "Exact error occurred here".to_string(),
410 let diagnostic
= lsp_types
::Diagnostic
{
411 range
: secondary_location
.range
,
412 // downgrade to hint if we're pointing at the macro
413 severity
: Some(lsp_types
::DiagnosticSeverity
::HINT
),
414 code
: code
.clone().map(lsp_types
::NumberOrString
::String
),
415 code_description
: code_description
.clone(),
416 source
: Some(source
.clone()),
417 message
: message
.clone(),
418 related_information
: Some(information_for_additional_diagnostic
),
419 tags
: if tags
.is_empty() { None }
else { Some(tags.clone()) }
,
422 diagnostics
.push(MappedRustDiagnostic
{
423 url
: secondary_location
.uri
,
429 // Emit the primary diagnostic.
430 diagnostics
.push(MappedRustDiagnostic
{
431 url
: primary_location
.uri
.clone(),
432 diagnostic
: lsp_types
::Diagnostic
{
433 range
: primary_location
.range
,
435 code
: code
.clone().map(lsp_types
::NumberOrString
::String
),
436 code_description
: code_description
.clone(),
437 source
: Some(source
.clone()),
439 related_information
: {
440 let info
= related_info_macro_calls
443 .chain(subdiagnostics
.iter().map(|sub
| sub
.related
.clone()))
444 .collect
::<Vec
<_
>>();
451 tags
: if tags
.is_empty() { None }
else { Some(tags.clone()) }
,
457 // Emit hint-level diagnostics for all `related_information` entries such as "help"s.
458 // This is useful because they will show up in the user's editor, unlike
459 // `related_information`, which just produces hard-to-read links, at least in VS Code.
460 let back_ref
= lsp_types
::DiagnosticRelatedInformation
{
461 location
: primary_location
,
462 message
: "original diagnostic".to_string(),
464 for sub
in &subdiagnostics
{
465 diagnostics
.push(MappedRustDiagnostic
{
466 url
: sub
.related
.location
.uri
.clone(),
467 fix
: sub
.suggested_fix
.clone(),
468 diagnostic
: lsp_types
::Diagnostic
{
469 range
: sub
.related
.location
.range
,
470 severity
: Some(lsp_types
::DiagnosticSeverity
::HINT
),
471 code
: code
.clone().map(lsp_types
::NumberOrString
::String
),
472 code_description
: code_description
.clone(),
473 source
: Some(source
.clone()),
474 message
: sub
.related
.message
.clone(),
475 related_information
: Some(vec
![back_ref
.clone()]),
476 tags
: None
, // don't apply modifiers again
487 fn rustc_code_description(code
: Option
<&str>) -> Option
<lsp_types
::CodeDescription
> {
489 let mut chars
= code
.chars();
490 chars
.next().map_or(false, |c
| c
== 'E'
)
491 && chars
.by_ref().take(4).all(|c
| c
.is_ascii_digit())
492 && chars
.next().is_none()
495 lsp_types
::Url
::parse(&format
!("https://doc.rust-lang.org/error-index.html#{}", code
))
497 .map(|href
| lsp_types
::CodeDescription { href }
)
501 fn clippy_code_description(code
: Option
<&str>) -> Option
<lsp_types
::CodeDescription
> {
502 code
.and_then(|code
| {
503 lsp_types
::Url
::parse(&format
!(
504 "https://rust-lang.github.io/rust-clippy/master/index.html#{}",
508 .map(|href
| lsp_types
::CodeDescription { href }
)
517 use crate::{config::Config, global_state::GlobalState}
;
521 use expect_test
::{expect_file, ExpectFile}
;
522 use lsp_types
::ClientCapabilities
;
524 fn check(diagnostics_json
: &str, expect
: ExpectFile
) {
525 check_with_config(DiagnosticsMapConfig
::default(), diagnostics_json
, expect
)
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(
534 Config
::new(workspace_root
.to_path_buf(), ClientCapabilities
::default()),
536 let snap
= state
.snapshot();
537 let actual
= map_rust_diagnostic_to_lsp(&config
, &diagnostic
, workspace_root
, &snap
);
538 expect
.assert_debug_eq(&actual
)
542 fn rustc_incompatible_type_for_trait() {
545 "message": "method `next` has an incompatible type for trait",
548 "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n"
553 "file_name": "compiler/ty/list_iter.rs",
563 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
564 "highlight_start": 5,
568 "label": "types differ in mutability",
569 "suggested_replacement": null
,
570 "suggestion_applicability": null
,
576 "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`",
584 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
587 expect_file!["./test_data
/rustc_incompatible_type_for_trait
.txt
"],
592 fn rustc_unused_variable() {
595 "message": "unused variable: `foo`",
597 "code": "unused_variables",
603 "file_name": "driver/subcommand/repl.rs",
613 "text": " let foo = 42;",
614 "highlight_start": 9,
619 "suggested_replacement": null
,
620 "suggestion_applicability": null
,
626 "message": "#[warn(unused_variables)] on by default",
634 "message": "consider prefixing with an underscore",
639 "file_name": "driver/subcommand/repl.rs",
649 "text": " let foo = 42;",
650 "highlight_start": 9,
655 "suggested_replacement": "_foo",
656 "suggestion_applicability": "MachineApplicable",
664 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
666 expect_file!["./test_data
/rustc_unused_variable
.txt
"],
672 fn rustc_unused_variable_as_info() {
674 DiagnosticsMapConfig {
675 warnings_as_info: vec!["unused_variables
".to_string()],
676 ..DiagnosticsMapConfig::default()
679 "message": "unused variable: `foo`",
681 "code": "unused_variables",
687 "file_name": "driver/subcommand/repl.rs",
697 "text": " let foo = 42;",
698 "highlight_start": 9,
703 "suggested_replacement": null
,
704 "suggestion_applicability": null
,
710 "message": "#[warn(unused_variables)] on by default",
718 "message": "consider prefixing with an underscore",
723 "file_name": "driver/subcommand/repl.rs",
733 "text": " let foo = 42;",
734 "highlight_start": 9,
739 "suggested_replacement": "_foo",
740 "suggestion_applicability": "MachineApplicable",
748 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
750 expect_file!["./test_data
/rustc_unused_variable_as_info
.txt
"],
756 fn rustc_unused_variable_as_hint() {
758 DiagnosticsMapConfig {
759 warnings_as_hint: vec!["unused_variables
".to_string()],
760 ..DiagnosticsMapConfig::default()
763 "message": "unused variable: `foo`",
765 "code": "unused_variables",
771 "file_name": "driver/subcommand/repl.rs",
781 "text": " let foo = 42;",
782 "highlight_start": 9,
787 "suggested_replacement": null
,
788 "suggestion_applicability": null
,
794 "message": "#[warn(unused_variables)] on by default",
802 "message": "consider prefixing with an underscore",
807 "file_name": "driver/subcommand/repl.rs",
817 "text": " let foo = 42;",
818 "highlight_start": 9,
823 "suggested_replacement": "_foo",
824 "suggestion_applicability": "MachineApplicable",
832 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
834 expect_file!["./test_data
/rustc_unused_variable_as_hint
.txt
"],
839 fn rustc_wrong_number_of_parameters() {
842 "message": "this function takes 2 parameters but 3 parameters were supplied",
845 "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n"
850 "file_name": "compiler/ty/select.rs",
860 "text": " pub fn add_evidence(",
861 "highlight_start": 5,
865 "text": " &mut self,",
866 "highlight_start": 1,
870 "text": " target_poly: &ty::Ref<ty::Poly>,",
871 "highlight_start": 1,
875 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
876 "highlight_start": 1,
881 "highlight_start": 1,
885 "text": " match target_poly {",
886 "highlight_start": 1,
890 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
891 "highlight_start": 1,
895 "text": " ty::Ref::Fixed(target_ty) => {",
896 "highlight_start": 1,
900 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
901 "highlight_start": 1,
905 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
906 "highlight_start": 1,
911 "highlight_start": 1,
916 "highlight_start": 1,
921 "highlight_start": 1,
925 "label": "defined here",
926 "suggested_replacement": null
,
927 "suggestion_applicability": null
,
931 "file_name": "compiler/ty/select.rs",
941 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
942 "highlight_start": 18,
946 "label": "expected 2 parameters",
947 "suggested_replacement": null
,
948 "suggestion_applicability": null
,
953 "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
955 expect_file!["./test_data
/rustc_wrong_number_of_parameters
.txt
"],
960 fn clippy_pass_by_ref() {
963 "message": "this argument is passed by reference, but would be more efficient if passed by value",
965 "code": "clippy::trivially_copy_pass_by_ref",
971 "file_name": "compiler/mir/tagset.rs",
981 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
982 "highlight_start": 24,
987 "suggested_replacement": null
,
988 "suggestion_applicability": null
,
994 "message": "lint level defined here",
999 "file_name": "compiler/lib.rs",
1009 "text": "#![warn(clippy::all)]",
1010 "highlight_start": 9,
1015 "suggested_replacement": null
,
1016 "suggestion_applicability": null
,
1024 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
1032 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
1040 "message": "consider passing by value instead",
1045 "file_name": "compiler/mir/tagset.rs",
1055 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
1056 "highlight_start": 24,
1061 "suggested_replacement": "self",
1062 "suggestion_applicability": "Unspecified",
1070 "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
1072 expect_file!["./test_data
/clippy_pass_by_ref
.txt
"],
1077 fn rustc_range_map_lsp_position() {
1080 "message": "mismatched types",
1083 "explanation": "Expected type did not match the received type.\n\nErroneous code examples:\n\n```compile_fail,E0308\nfn plus_one(x: i32) -> i32 {\n x + 1\n}\n\nplus_one(\"Not a number\");\n// ^^^^^^^^^^^^^^ expected `i32`, found `&str`\n\nif \"Not a bool\" {\n// ^^^^^^^^^^^^ expected `bool`, found `&str`\n}\n\nlet x: f32 = \"Not a float\";\n// --- ^^^^^^^^^^^^^ expected `f32`, found `&str`\n// |\n// expected due to this\n```\n\nThis error occurs when an expression was used in a place where the compiler\nexpected an expression of a different type. It can occur in several cases, the\nmost common being when calling a function and passing an argument which has a\ndifferent type than the matching type in the function declaration.\n"
1088 "file_name": "crates/test_diagnostics/src/main.rs",
1098 "text": " let x: u32 = \"𐐀𐐀𐐀𐐀\"; // 17-23",
1099 "highlight_start": 18,
1103 "label": "expected `u32`, found `&str`",
1104 "suggested_replacement": null
,
1105 "suggestion_applicability": null
,
1109 "file_name": "crates/test_diagnostics/src/main.rs",
1116 "is_primary": false,
1119 "text": " let x: u32 = \"𐐀𐐀𐐀𐐀\"; // 17-23",
1120 "highlight_start": 12,
1124 "label": "expected due to this",
1125 "suggested_replacement": null
,
1126 "suggestion_applicability": null
,
1131 "rendered": "error[E0308]: mismatched types\n --> crates/test_diagnostics/src/main.rs:4:18\n |\n4 | let x: u32 = \"𐐀𐐀𐐀𐐀\"; // 17-23\n | --- ^^^^^^ expected `u32`, found `&str`\n | |\n | expected due to this\n\n"
1133 expect_file!("./test_data
/rustc_range_map_lsp_position
.txt
"),
1138 fn rustc_mismatched_type() {
1141 "message": "mismatched types",
1144 "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
1149 "file_name": "runtime/compiler_support.rs",
1159 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
1160 "highlight_start": 65,
1164 "label": "expected usize, found u32",
1165 "suggested_replacement": null
,
1166 "suggestion_applicability": null
,
1171 "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
1173 expect_file!["./test_data
/rustc_mismatched_type
.txt
"],
1178 fn handles_macro_location() {
1181 "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
1187 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
1194 "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
1197 "message": "can't compare `{integer}` with `&str`",
1211 "file_name": "<::core::macros::assert_eq macros>",
1212 "is_primary": false,
1216 "suggested_replacement": null
,
1217 "suggestion_applicability": null
,
1220 "highlight_end": 35,
1221 "highlight_start": 1,
1222 "text": "($ left : expr, $ right : expr) =>"
1226 "highlight_start": 1,
1230 "highlight_end": 33,
1231 "highlight_start": 1,
1232 "text": " match (& $ left, & $ right)"
1236 "highlight_start": 1,
1240 "highlight_end": 34,
1241 "highlight_start": 1,
1242 "text": " (left_val, right_val) =>"
1245 "highlight_end": 11,
1246 "highlight_start": 1,
1250 "highlight_end": 46,
1251 "highlight_start": 1,
1252 "text": " if ! (* left_val == * right_val)"
1255 "highlight_end": 15,
1256 "highlight_start": 1,
1260 "highlight_end": 25,
1261 "highlight_start": 1,
1265 "highlight_end": 57,
1266 "highlight_start": 1,
1267 "text": " (r#\"assertion failed: `(left == right)`"
1270 "highlight_end": 16,
1271 "highlight_start": 1,
1272 "text": " left: `{:?}`,"
1275 "highlight_end": 18,
1276 "highlight_start": 1,
1277 "text": " right: `{:?}`\"#,"
1280 "highlight_end": 47,
1281 "highlight_start": 1,
1282 "text": " & * left_val, & * right_val)"
1285 "highlight_end": 15,
1286 "highlight_start": 1,
1290 "highlight_end": 11,
1291 "highlight_start": 1,
1296 "highlight_start": 1,
1300 "highlight_end": 42,
1301 "highlight_start": 1,
1302 "text": " }) ; ($ left : expr, $ right : expr,) =>"
1305 "highlight_end": 49,
1306 "highlight_start": 1,
1307 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
1310 "highlight_end": 53,
1311 "highlight_start": 1,
1312 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
1316 "highlight_start": 1,
1320 "highlight_end": 37,
1321 "highlight_start": 1,
1322 "text": " match (& ($ left), & ($ right))"
1326 "highlight_start": 1,
1330 "highlight_end": 34,
1331 "highlight_start": 1,
1332 "text": " (left_val, right_val) =>"
1335 "highlight_end": 11,
1336 "highlight_start": 1,
1340 "highlight_end": 46,
1341 "highlight_start": 1,
1342 "text": " if ! (* left_val == * right_val)"
1345 "highlight_end": 15,
1346 "highlight_start": 1,
1350 "highlight_end": 25,
1351 "highlight_start": 1,
1355 "highlight_end": 57,
1356 "highlight_start": 1,
1357 "text": " (r#\"assertion failed: `(left == right)`"
1360 "highlight_end": 16,
1361 "highlight_start": 1,
1362 "text": " left: `{:?}`,"
1365 "highlight_end": 22,
1366 "highlight_start": 1,
1367 "text": " right: `{:?}`: {}\"#,"
1370 "highlight_end": 72,
1371 "highlight_start": 1,
1372 "text": " & * left_val, & * right_val, $ crate :: format_args !"
1375 "highlight_end": 33,
1376 "highlight_start": 1,
1377 "text": " ($ ($ arg) +))"
1380 "highlight_end": 15,
1381 "highlight_start": 1,
1385 "highlight_end": 11,
1386 "highlight_start": 1,
1391 "highlight_start": 1,
1396 "highlight_start": 1,
1401 "macro_decl_name": "assert_eq!",
1408 "file_name": "src/main.rs",
1409 "is_primary": false,
1413 "suggested_replacement": null
,
1414 "suggestion_applicability": null
,
1417 "highlight_end": 27,
1418 "highlight_start": 5,
1419 "text": " assert_eq!(1, \"love\");"
1424 "file_name": "<::core::macros::assert_eq macros>",
1426 "label": "no implementation for `{integer} == &str`",
1429 "suggested_replacement": null
,
1430 "suggestion_applicability": null
,
1433 "highlight_end": 33,
1434 "highlight_start": 31,
1435 "text": " if ! (* left_val == * right_val)"
1441 expect_file!["./test_data
/handles_macro_location
.txt
"],
1446 fn macro_compiler_error() {
1449 "rendered": "error: Please register your known path in the path module\n --> crates/hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n",
1453 "message": "Please register your known path in the path module",
1467 "file_name": "crates/hir_def/src/path.rs",
1468 "is_primary": false,
1472 "suggested_replacement": null
,
1473 "suggestion_applicability": null
,
1476 "highlight_end": 28,
1477 "highlight_start": 1,
1478 "text": "macro_rules! __known_path {"
1481 "highlight_end": 37,
1482 "highlight_start": 1,
1483 "text": " (std::iter::IntoIterator) => {};"
1486 "highlight_end": 33,
1487 "highlight_start": 1,
1488 "text": " (std::result::Result) => {};"
1491 "highlight_end": 29,
1492 "highlight_start": 1,
1493 "text": " (std::ops::Range) => {};"
1496 "highlight_end": 33,
1497 "highlight_start": 1,
1498 "text": " (std::ops::RangeFrom) => {};"
1501 "highlight_end": 33,
1502 "highlight_start": 1,
1503 "text": " (std::ops::RangeFull) => {};"
1506 "highlight_end": 31,
1507 "highlight_start": 1,
1508 "text": " (std::ops::RangeTo) => {};"
1511 "highlight_end": 40,
1512 "highlight_start": 1,
1513 "text": " (std::ops::RangeToInclusive) => {};"
1516 "highlight_end": 38,
1517 "highlight_start": 1,
1518 "text": " (std::ops::RangeInclusive) => {};"
1521 "highlight_end": 27,
1522 "highlight_start": 1,
1523 "text": " (std::ops::Try) => {};"
1526 "highlight_end": 22,
1527 "highlight_start": 1,
1528 "text": " ($path:path) => {"
1531 "highlight_end": 77,
1532 "highlight_start": 1,
1533 "text": " compile_error!(\"Please register your known path in the path module\")"
1537 "highlight_start": 1,
1542 "highlight_start": 1,
1547 "macro_decl_name": "$crate::__known_path!",
1560 "file_name": "crates/hir_def/src/path.rs",
1561 "is_primary": false,
1565 "suggested_replacement": null
,
1566 "suggestion_applicability": null
,
1569 "highlight_end": 22,
1570 "highlight_start": 1,
1571 "text": "macro_rules! __path {"
1574 "highlight_end": 43,
1575 "highlight_start": 1,
1576 "text": " ($start:ident $(:: $seg:ident)*) => ({"
1579 "highlight_end": 51,
1580 "highlight_start": 1,
1581 "text": " $crate::__known_path!($start $(:: $seg)*);"
1584 "highlight_end": 87,
1585 "highlight_start": 1,
1586 "text": " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
1589 "highlight_end": 76,
1590 "highlight_start": 1,
1591 "text": " $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
1594 "highlight_end": 11,
1595 "highlight_start": 1,
1600 "highlight_start": 1,
1605 "highlight_start": 1,
1610 "macro_decl_name": "path!",
1617 "file_name": "crates/hir_def/src/data.rs",
1618 "is_primary": false,
1622 "suggested_replacement": null
,
1623 "suggestion_applicability": null
,
1626 "highlight_end": 42,
1627 "highlight_start": 16,
1628 "text": " let path = path![std::future::Future];"
1633 "file_name": "crates/hir_def/src/path.rs",
1634 "is_primary": false,
1638 "suggested_replacement": null
,
1639 "suggestion_applicability": null
,
1642 "highlight_end": 51,
1643 "highlight_start": 9,
1644 "text": " $crate::__known_path!($start $(:: $seg)*);"
1649 "file_name": "crates/hir_def/src/path.rs",
1654 "suggested_replacement": null
,
1655 "suggestion_applicability": null
,
1658 "highlight_end": 77,
1659 "highlight_start": 9,
1660 "text": " compile_error!(\"Please register your known path in the path module\")"
1667 expect_file!["./test_data
/macro_compiler_error
.txt
"],
1672 fn snap_multi_line_fix() {
1675 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n",
1681 "message": "`#[warn(clippy::let_and_return)]` on by default",
1689 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
1697 "message": "return the expression directly",
1706 "file_name": "src/main.rs",
1711 "suggested_replacement": "",
1712 "suggestion_applicability": "MachineApplicable",
1715 "highlight_end": 31,
1716 "highlight_start": 5,
1717 "text": " let a = (0..10).collect();"
1727 "file_name": "src/main.rs",
1732 "suggested_replacement": "(0..10).collect()",
1733 "suggestion_applicability": "MachineApplicable",
1737 "highlight_start": 5,
1746 "code": "clippy::let_and_return",
1750 "message": "returning the result of a let binding from a block",
1758 "file_name": "src/main.rs",
1759 "is_primary": false,
1760 "label": "unnecessary let binding",
1763 "suggested_replacement": null
,
1764 "suggestion_applicability": null
,
1767 "highlight_end": 31,
1768 "highlight_start": 5,
1769 "text": " let a = (0..10).collect();"
1779 "file_name": "src/main.rs",
1784 "suggested_replacement": null
,
1785 "suggestion_applicability": null
,
1789 "highlight_start": 5,
1797 expect_file!["./test_data
/snap_multi_line_fix
.txt
"],
1802 fn reasonable_line_numbers_from_empty_file() {
1805 "message": "`main` function not found in crate `current`",
1808 "explanation": "No `main` function was found in a binary crate.\n\nTo fix this error, add a `main` function:\n\n```\nfn main() {\n // Your program will start here.\n println!(\"Hello world!\");\n}\n```\n\nIf you don't know the basics of Rust, you can look at the\n[Rust Book][rust-book] to get started.\n\n[rust-book]: https://doc.rust-lang.org/book/\n"
1813 "file_name": "src/bin/current.rs",
1823 "suggested_replacement": null
,
1824 "suggestion_applicability": null
,
1830 "message": "consider adding a `main` function to `src/bin/current.rs`",
1838 "rendered": "error[E0601]: `main` function not found in crate `current`\n |\n = note: consider adding a `main` function to `src/bin/current.rs`\n\n"
1840 expect_file!["./test_data
/reasonable_line_numbers_from_empty_file
.txt
"],