1 use crate::world::Options;
4 Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan,
5 DiagnosticSpanMacroExpansion,
9 use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender, TryRecvError};
11 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
12 NumberOrString, Position, Range, Url,
14 use parking_lot::RwLock;
19 process::{Command, Stdio},
26 pub struct CheckWatcher {
27 pub task_recv: Receiver<CheckTask>,
28 pub cmd_send: Sender<CheckCommand>,
29 pub shared: Arc<RwLock<CheckWatcherSharedState>>,
30 handle: JoinHandle<()>,
34 pub fn new(options: &Options, workspace_root: PathBuf) -> CheckWatcher {
35 let check_command = options.cargo_check_command.clone();
36 let check_args = options.cargo_check_args.clone();
37 let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new()));
39 let (task_send, task_recv) = unbounded::<CheckTask>();
40 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
41 let shared_ = shared.clone();
42 let handle = std::thread::spawn(move || {
44 CheckWatcherState::new(check_command, check_args, workspace_root, shared_);
45 check.run(&task_send, &cmd_recv);
48 CheckWatcher { task_recv, cmd_send, handle, shared }
51 pub fn update(&self) {
52 self.cmd_send.send(CheckCommand::Update).unwrap();
56 pub struct CheckWatcherState {
57 check_command: Option<String>,
58 check_args: Vec<String>,
59 workspace_root: PathBuf,
62 last_update_req: Option<Instant>,
63 shared: Arc<RwLock<CheckWatcherSharedState>>,
67 pub struct CheckWatcherSharedState {
68 diagnostic_collection: HashMap<Url, Vec<Diagnostic>>,
69 suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>,
72 impl CheckWatcherSharedState {
73 fn new() -> CheckWatcherSharedState {
74 CheckWatcherSharedState {
75 diagnostic_collection: HashMap::new(),
76 suggested_fix_collection: HashMap::new(),
80 pub fn clear(&mut self, task_send: &Sender<CheckTask>) {
81 let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect();
83 self.diagnostic_collection.clear();
84 self.suggested_fix_collection.clear();
86 for uri in cleared_files {
87 task_send.send(CheckTask::Update(uri.clone())).unwrap();
91 pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> {
92 self.diagnostic_collection.get(uri).map(|d| d.as_slice())
95 pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> {
96 self.suggested_fix_collection.get(uri).map(|d| d.as_slice())
99 fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) {
100 let diagnostics = self.diagnostic_collection.entry(file_uri).or_default();
102 // If we're building multiple targets it's possible we've already seen this diagnostic
103 let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic));
108 diagnostics.push(diagnostic);
111 fn add_suggested_fix_for_diagnostic(
113 mut suggested_fix: SuggestedFix,
114 diagnostic: &Diagnostic,
116 let file_uri = suggested_fix.location.uri.clone();
117 let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default();
119 let existing_suggestion: Option<&mut SuggestedFix> =
120 file_suggestions.iter_mut().find(|s| s == &&suggested_fix);
121 if let Some(existing_suggestion) = existing_suggestion {
122 // The existing suggestion also applies to this new diagnostic
123 existing_suggestion.diagnostics.push(diagnostic.clone());
125 // We haven't seen this suggestion before
126 suggested_fix.diagnostics.push(diagnostic.clone());
127 file_suggestions.push(suggested_fix);
137 pub enum CheckCommand {
141 impl CheckWatcherState {
143 check_command: Option<String>,
144 check_args: Vec<String>,
145 workspace_root: PathBuf,
146 shared: Arc<RwLock<CheckWatcherSharedState>>,
147 ) -> CheckWatcherState {
148 let watcher = WatchThread::new(check_command.as_ref(), &check_args, &workspace_root);
155 last_update_req: None,
160 pub fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
164 recv(&cmd_recv) -> cmd => match cmd {
165 Ok(cmd) => self.handle_command(cmd),
167 // Command channel has closed, so shut down
168 self.running = false;
171 recv(self.watcher.message_recv) -> msg => match msg {
172 Ok(msg) => self.handle_message(msg, task_send),
173 Err(RecvError) => {},
177 if self.should_recheck() {
178 self.last_update_req.take();
179 self.shared.write().clear(task_send);
181 self.watcher.cancel();
182 self.watcher = WatchThread::new(
183 self.check_command.as_ref(),
185 &self.workspace_root,
191 fn should_recheck(&mut self) -> bool {
192 if let Some(_last_update_req) = &self.last_update_req {
193 // We currently only request an update on save, as we need up to
194 // date source on disk for cargo check to do it's magic, so we
195 // don't really need to debounce the requests at this point.
201 fn handle_command(&mut self, cmd: CheckCommand) {
203 CheckCommand::Update => self.last_update_req = Some(Instant::now()),
207 fn handle_message(&mut self, msg: cargo_metadata::Message, task_send: &Sender<CheckTask>) {
209 Message::CompilerArtifact(_msg) => {
210 // TODO: Status display
213 Message::CompilerMessage(msg) => {
215 match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) {
216 Some(map_result) => map_result,
220 let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result;
221 let file_uri = location.uri.clone();
223 if !suggested_fixes.is_empty() {
224 for suggested_fix in suggested_fixes {
227 .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic);
230 self.shared.write().add_diagnostic(file_uri, diagnostic);
232 task_send.send(CheckTask::Update(location.uri)).unwrap();
235 Message::BuildScriptExecuted(_msg) => {}
236 Message::Unknown => {}
241 /// WatchThread exists to wrap around the communication needed to be able to
242 /// run `cargo check` without blocking. Currently the Rust standard library
243 /// doesn't provide a way to read sub-process output without blocking, so we
244 /// have to wrap sub-processes output handling in a thread and pass messages
245 /// back over a channel.
247 message_recv: Receiver<cargo_metadata::Message>,
248 cancel_send: Sender<()>,
253 check_command: Option<&String>,
254 check_args: &[String],
255 workspace_root: &PathBuf,
257 let check_command = check_command.cloned().unwrap_or("check".to_string());
258 let mut args: Vec<String> = vec![
260 "--message-format=json".to_string(),
261 "--manifest-path".to_string(),
262 format!("{}/Cargo.toml", workspace_root.to_string_lossy()),
264 args.extend(check_args.iter().cloned());
266 let (message_send, message_recv) = unbounded();
267 let (cancel_send, cancel_recv) = unbounded();
268 std::thread::spawn(move || {
269 let mut command = Command::new("cargo")
271 .stdout(Stdio::piped())
272 .stderr(Stdio::null())
274 .expect("couldn't launch cargo");
276 for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) {
277 match cancel_recv.try_recv() {
278 Ok(()) | Err(TryRecvError::Disconnected) => {
279 command.kill().expect("couldn't kill command");
281 Err(TryRecvError::Empty) => (),
284 message_send.send(message.unwrap()).unwrap();
287 WatchThread { message_recv, cancel_send }
291 let _ = self.cancel_send.send(());
295 /// Converts a Rust level string to a LSP severity
296 fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
298 DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error),
299 DiagnosticLevel::Error => Some(DiagnosticSeverity::Error),
300 DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning),
301 DiagnosticLevel::Note => Some(DiagnosticSeverity::Information),
302 DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint),
303 DiagnosticLevel::Unknown => None,
307 /// Check whether a file name is from macro invocation
308 fn is_from_macro(file_name: &str) -> bool {
309 file_name.starts_with('<') && file_name.ends_with('>')
312 /// Converts a Rust macro span to a LSP location recursively
313 fn map_macro_span_to_location(
314 span_macro: &DiagnosticSpanMacroExpansion,
315 workspace_root: &PathBuf,
316 ) -> Option<Location> {
317 if !is_from_macro(&span_macro.span.file_name) {
318 return Some(map_span_to_location(&span_macro.span, workspace_root));
321 if let Some(expansion) = &span_macro.span.expansion {
322 return map_macro_span_to_location(&expansion, workspace_root);
328 /// Converts a Rust span to a LSP location
329 fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
330 if is_from_macro(&span.file_name) && span.expansion.is_some() {
331 let expansion = span.expansion.as_ref().unwrap();
332 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
337 let mut file_name = workspace_root.clone();
338 file_name.push(&span.file_name);
339 let uri = Url::from_file_path(file_name).unwrap();
341 let range = Range::new(
342 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
343 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
346 Location { uri, range }
349 /// Converts a secondary Rust span to a LSP related information
351 /// If the span is unlabelled this will return `None`.
352 fn map_secondary_span_to_related(
353 span: &DiagnosticSpan,
354 workspace_root: &PathBuf,
355 ) -> Option<DiagnosticRelatedInformation> {
356 if let Some(label) = &span.label {
357 let location = map_span_to_location(span, workspace_root);
358 Some(DiagnosticRelatedInformation { location, message: label.clone() })
360 // Nothing to label this with
365 /// Determines if diagnostic is related to unused code
366 fn is_unused_or_unnecessary(rd: &RustDiagnostic) -> bool {
367 if let Some(code) = &rd.code {
368 match code.code.as_str() {
369 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
370 | "unused_imports" | "unused_macros" | "unused_variables" => true,
378 /// Determines if diagnostic is related to deprecated code
379 fn is_deprecated(rd: &RustDiagnostic) -> bool {
380 if let Some(code) = &rd.code {
381 match code.code.as_str() {
382 "deprecated" => true,
391 pub struct SuggestedFix {
393 pub location: Location,
394 pub replacement: String,
395 pub applicability: Applicability,
396 pub diagnostics: Vec<Diagnostic>,
399 impl std::cmp::PartialEq<SuggestedFix> for SuggestedFix {
400 fn eq(&self, other: &SuggestedFix) -> bool {
401 if self.title == other.title
402 && self.location == other.location
403 && self.replacement == other.replacement
405 // Applicability doesn't impl PartialEq...
406 match (&self.applicability, &other.applicability) {
407 (Applicability::MachineApplicable, Applicability::MachineApplicable) => true,
408 (Applicability::HasPlaceholders, Applicability::HasPlaceholders) => true,
409 (Applicability::MaybeIncorrect, Applicability::MaybeIncorrect) => true,
410 (Applicability::Unspecified, Applicability::Unspecified) => true,
419 enum MappedRustChildDiagnostic {
420 Related(DiagnosticRelatedInformation),
421 SuggestedFix(SuggestedFix),
425 fn map_rust_child_diagnostic(
427 workspace_root: &PathBuf,
428 ) -> MappedRustChildDiagnostic {
429 let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) {
432 // `rustc` uses these spanless children as a way to print multi-line
434 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
438 // If we have a primary span use its location, otherwise use the parent
439 let location = map_span_to_location(&span, workspace_root);
441 if let Some(suggested_replacement) = &span.suggested_replacement {
442 // Include our replacement in the title unless it's empty
443 let title = if !suggested_replacement.is_empty() {
444 format!("{}: '{}'", rd.message, suggested_replacement)
449 MappedRustChildDiagnostic::SuggestedFix(SuggestedFix {
452 replacement: suggested_replacement.clone(),
453 applicability: span.suggestion_applicability.clone().unwrap_or(Applicability::Unknown),
457 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
459 message: rd.message.clone(),
465 struct MappedRustDiagnostic {
467 diagnostic: Diagnostic,
468 suggested_fixes: Vec<SuggestedFix>,
471 /// Converts a Rust root diagnostic to LSP form
473 /// This flattens the Rust diagnostic by:
475 /// 1. Creating a LSP diagnostic with the root message and primary span.
476 /// 2. Adding any labelled secondary spans to `relatedInformation`
477 /// 3. Categorising child diagnostics as either `SuggestedFix`es,
478 /// `relatedInformation` or additional message lines.
480 /// If the diagnostic has no primary span this will return `None`
481 fn map_rust_diagnostic_to_lsp(
483 workspace_root: &PathBuf,
484 ) -> Option<MappedRustDiagnostic> {
485 let primary_span = rd.spans.iter().find(|s| s.is_primary)?;
487 let location = map_span_to_location(&primary_span, workspace_root);
489 let severity = map_level_to_severity(rd.level);
490 let mut primary_span_label = primary_span.label.as_ref();
492 let mut source = String::from("rustc");
493 let mut code = rd.code.as_ref().map(|c| c.code.clone());
494 if let Some(code_val) = &code {
495 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
496 let scoped_code: Vec<&str> = code_val.split("::").collect();
497 if scoped_code.len() == 2 {
498 source = String::from(scoped_code[0]);
499 code = Some(String::from(scoped_code[1]));
503 let mut related_information = vec![];
504 let mut tags = vec![];
506 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
507 let related = map_secondary_span_to_related(secondary_span, workspace_root);
508 if let Some(related) = related {
509 related_information.push(related);
513 let mut suggested_fixes = vec![];
514 let mut message = rd.message.clone();
515 for child in &rd.children {
516 let child = map_rust_child_diagnostic(&child, workspace_root);
518 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
519 MappedRustChildDiagnostic::SuggestedFix(suggested_fix) => {
520 suggested_fixes.push(suggested_fix)
522 MappedRustChildDiagnostic::MessageLine(message_line) => {
523 write!(&mut message, "\n{}", message_line).unwrap();
525 // These secondary messages usually duplicate the content of the
526 // primary span label.
527 primary_span_label = None;
532 if let Some(primary_span_label) = primary_span_label {
533 write!(&mut message, "\n{}", primary_span_label).unwrap();
536 if is_unused_or_unnecessary(rd) {
537 tags.push(DiagnosticTag::Unnecessary);
540 if is_deprecated(rd) {
541 tags.push(DiagnosticTag::Deprecated);
544 let diagnostic = Diagnostic {
545 range: location.range,
547 code: code.map(NumberOrString::String),
548 source: Some(source),
550 related_information: if !related_information.is_empty() {
551 Some(related_information)
555 tags: if !tags.is_empty() { Some(tags) } else { None },
558 Some(MappedRustDiagnostic { location, diagnostic, suggested_fixes })
561 fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool {
562 left.source == right.source
563 && left.severity == right.severity
564 && left.range == right.range
565 && left.message == right.message
572 fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic {
573 serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap()
577 fn snap_rustc_incompatible_type_for_trait() {
578 let diag = parse_diagnostic(
580 "message": "method `next` has an incompatible type for trait",
583 "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"
588 "file_name": "compiler/ty/list_iter.rs",
598 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
599 "highlight_start": 5,
603 "label": "types differ in mutability",
604 "suggested_replacement": null,
605 "suggestion_applicability": null,
611 "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>>`",
619 "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"
624 let workspace_root = PathBuf::from("/test/");
626 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
627 insta::assert_debug_snapshot!(diag);
631 fn snap_rustc_unused_variable() {
632 let diag = parse_diagnostic(
634 "message": "unused variable: `foo`",
636 "code": "unused_variables",
642 "file_name": "driver/subcommand/repl.rs",
652 "text": " let foo = 42;",
653 "highlight_start": 9,
658 "suggested_replacement": null,
659 "suggestion_applicability": null,
665 "message": "#[warn(unused_variables)] on by default",
673 "message": "consider prefixing with an underscore",
678 "file_name": "driver/subcommand/repl.rs",
688 "text": " let foo = 42;",
689 "highlight_start": 9,
694 "suggested_replacement": "_foo",
695 "suggestion_applicability": "MachineApplicable",
703 "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"
707 let workspace_root = PathBuf::from("/test/");
709 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
710 insta::assert_debug_snapshot!(diag);
714 fn snap_rustc_wrong_number_of_parameters() {
715 let diag = parse_diagnostic(
717 "message": "this function takes 2 parameters but 3 parameters were supplied",
720 "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"
725 "file_name": "compiler/ty/select.rs",
735 "text": " pub fn add_evidence(",
736 "highlight_start": 5,
740 "text": " &mut self,",
741 "highlight_start": 1,
745 "text": " target_poly: &ty::Ref<ty::Poly>,",
746 "highlight_start": 1,
750 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
751 "highlight_start": 1,
756 "highlight_start": 1,
760 "text": " match target_poly {",
761 "highlight_start": 1,
765 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
766 "highlight_start": 1,
770 "text": " ty::Ref::Fixed(target_ty) => {",
771 "highlight_start": 1,
775 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
776 "highlight_start": 1,
780 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
781 "highlight_start": 1,
786 "highlight_start": 1,
791 "highlight_start": 1,
796 "highlight_start": 1,
800 "label": "defined here",
801 "suggested_replacement": null,
802 "suggestion_applicability": null,
806 "file_name": "compiler/ty/select.rs",
816 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
817 "highlight_start": 18,
821 "label": "expected 2 parameters",
822 "suggested_replacement": null,
823 "suggestion_applicability": null,
828 "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"
832 let workspace_root = PathBuf::from("/test/");
834 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
835 insta::assert_debug_snapshot!(diag);
839 fn snap_clippy_pass_by_ref() {
840 let diag = parse_diagnostic(
842 "message": "this argument is passed by reference, but would be more efficient if passed by value",
844 "code": "clippy::trivially_copy_pass_by_ref",
850 "file_name": "compiler/mir/tagset.rs",
860 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
861 "highlight_start": 24,
866 "suggested_replacement": null,
867 "suggestion_applicability": null,
873 "message": "lint level defined here",
878 "file_name": "compiler/lib.rs",
888 "text": "#![warn(clippy::all)]",
889 "highlight_start": 9,
894 "suggested_replacement": null,
895 "suggestion_applicability": null,
903 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
911 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
919 "message": "consider passing by value instead",
924 "file_name": "compiler/mir/tagset.rs",
934 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
935 "highlight_start": 24,
940 "suggested_replacement": "self",
941 "suggestion_applicability": "Unspecified",
949 "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"
953 let workspace_root = PathBuf::from("/test/");
955 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
956 insta::assert_debug_snapshot!(diag);
960 fn snap_rustc_mismatched_type() {
961 let diag = parse_diagnostic(
963 "message": "mismatched types",
966 "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"
971 "file_name": "runtime/compiler_support.rs",
981 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
982 "highlight_start": 65,
986 "label": "expected usize, found u32",
987 "suggested_replacement": null,
988 "suggestion_applicability": null,
993 "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"
997 let workspace_root = PathBuf::from("/test/");
999 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
1000 insta::assert_debug_snapshot!(diag);
1004 fn snap_handles_macro_location() {
1005 let diag = parse_diagnostic(
1007 "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",
1013 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
1020 "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"
1023 "message": "can't compare `{integer}` with `&str`",
1037 "file_name": "<::core::macros::assert_eq macros>",
1038 "is_primary": false,
1042 "suggested_replacement": null,
1043 "suggestion_applicability": null,
1046 "highlight_end": 35,
1047 "highlight_start": 1,
1048 "text": "($ left : expr, $ right : expr) =>"
1052 "highlight_start": 1,
1056 "highlight_end": 33,
1057 "highlight_start": 1,
1058 "text": " match (& $ left, & $ right)"
1062 "highlight_start": 1,
1066 "highlight_end": 34,
1067 "highlight_start": 1,
1068 "text": " (left_val, right_val) =>"
1071 "highlight_end": 11,
1072 "highlight_start": 1,
1076 "highlight_end": 46,
1077 "highlight_start": 1,
1078 "text": " if ! (* left_val == * right_val)"
1081 "highlight_end": 15,
1082 "highlight_start": 1,
1086 "highlight_end": 25,
1087 "highlight_start": 1,
1091 "highlight_end": 57,
1092 "highlight_start": 1,
1093 "text": " (r#\"assertion failed: `(left == right)`"
1096 "highlight_end": 16,
1097 "highlight_start": 1,
1098 "text": " left: `{:?}`,"
1101 "highlight_end": 18,
1102 "highlight_start": 1,
1103 "text": " right: `{:?}`\"#,"
1106 "highlight_end": 47,
1107 "highlight_start": 1,
1108 "text": " & * left_val, & * right_val)"
1111 "highlight_end": 15,
1112 "highlight_start": 1,
1116 "highlight_end": 11,
1117 "highlight_start": 1,
1122 "highlight_start": 1,
1126 "highlight_end": 42,
1127 "highlight_start": 1,
1128 "text": " }) ; ($ left : expr, $ right : expr,) =>"
1131 "highlight_end": 49,
1132 "highlight_start": 1,
1133 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
1136 "highlight_end": 53,
1137 "highlight_start": 1,
1138 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
1142 "highlight_start": 1,
1146 "highlight_end": 37,
1147 "highlight_start": 1,
1148 "text": " match (& ($ left), & ($ right))"
1152 "highlight_start": 1,
1156 "highlight_end": 34,
1157 "highlight_start": 1,
1158 "text": " (left_val, right_val) =>"
1161 "highlight_end": 11,
1162 "highlight_start": 1,
1166 "highlight_end": 46,
1167 "highlight_start": 1,
1168 "text": " if ! (* left_val == * right_val)"
1171 "highlight_end": 15,
1172 "highlight_start": 1,
1176 "highlight_end": 25,
1177 "highlight_start": 1,
1181 "highlight_end": 57,
1182 "highlight_start": 1,
1183 "text": " (r#\"assertion failed: `(left == right)`"
1186 "highlight_end": 16,
1187 "highlight_start": 1,
1188 "text": " left: `{:?}`,"
1191 "highlight_end": 22,
1192 "highlight_start": 1,
1193 "text": " right: `{:?}`: {}\"#,"
1196 "highlight_end": 72,
1197 "highlight_start": 1,
1198 "text": " & * left_val, & * right_val, $ crate :: format_args !"
1201 "highlight_end": 33,
1202 "highlight_start": 1,
1203 "text": " ($ ($ arg) +))"
1206 "highlight_end": 15,
1207 "highlight_start": 1,
1211 "highlight_end": 11,
1212 "highlight_start": 1,
1217 "highlight_start": 1,
1222 "highlight_start": 1,
1227 "macro_decl_name": "assert_eq!",
1234 "file_name": "src/main.rs",
1235 "is_primary": false,
1239 "suggested_replacement": null,
1240 "suggestion_applicability": null,
1243 "highlight_end": 27,
1244 "highlight_start": 5,
1245 "text": " assert_eq!(1, \"love\");"
1250 "file_name": "<::core::macros::assert_eq macros>",
1252 "label": "no implementation for `{integer} == &str`",
1255 "suggested_replacement": null,
1256 "suggestion_applicability": null,
1259 "highlight_end": 33,
1260 "highlight_start": 31,
1261 "text": " if ! (* left_val == * right_val)"
1269 let workspace_root = PathBuf::from("/test/");
1271 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
1272 insta::assert_debug_snapshot!(diag);