aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src
diff options
context:
space:
mode:
authorEmil Lauridsen <[email protected]>2019-12-27 10:10:07 +0000
committerEmil Lauridsen <[email protected]>2019-12-27 10:10:07 +0000
commit428a6ff5b8bad2c80a3522599195bf2a393f744e (patch)
tree9707dd343b8f00f01041a7995536aac84b137291 /crates/ra_lsp_server/src
parent0cdbd0814958e174c5481d6bf16bd2a7e53ec981 (diff)
Move cargo watch functionality to separate crate
Diffstat (limited to 'crates/ra_lsp_server/src')
-rw-r--r--crates/ra_lsp_server/src/cargo_check.rs1316
-rw-r--r--crates/ra_lsp_server/src/lib.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs12
-rw-r--r--crates/ra_lsp_server/src/snapshots/test__snap_clippy_pass_by_ref.snap85
-rw-r--r--crates/ra_lsp_server/src/snapshots/test__snap_handles_macro_location.snap46
-rw-r--r--crates/ra_lsp_server/src/snapshots/test__snap_rustc_incompatible_type_for_trait.snap46
-rw-r--r--crates/ra_lsp_server/src/snapshots/test__snap_rustc_mismatched_type.snap46
-rw-r--r--crates/ra_lsp_server/src/snapshots/test__snap_rustc_unused_variable.snap70
-rw-r--r--crates/ra_lsp_server/src/snapshots/test__snap_rustc_wrong_number_of_parameters.snap65
-rw-r--r--crates/ra_lsp_server/src/world.rs10
10 files changed, 11 insertions, 1686 deletions
diff --git a/crates/ra_lsp_server/src/cargo_check.rs b/crates/ra_lsp_server/src/cargo_check.rs
deleted file mode 100644
index 70c723b19..000000000
--- a/crates/ra_lsp_server/src/cargo_check.rs
+++ /dev/null
@@ -1,1316 +0,0 @@
1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command.
4use crate::world::Options;
5use cargo_metadata::{
6 diagnostic::{
7 Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan,
8 DiagnosticSpanMacroExpansion,
9 },
10 Message,
11};
12use crossbeam_channel::{select, unbounded, Receiver, RecvError, Sender, TryRecvError};
13use lsp_types::{
14 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
15 NumberOrString, Position, Range, Url, WorkDoneProgress, WorkDoneProgressBegin,
16 WorkDoneProgressEnd, WorkDoneProgressReport,
17};
18use parking_lot::RwLock;
19use std::{
20 collections::HashMap,
21 fmt::Write,
22 path::PathBuf,
23 process::{Command, Stdio},
24 sync::Arc,
25 thread::JoinHandle,
26 time::Instant,
27};
28
29/// CheckWatcher wraps the shared state and communication machinery used for
30/// running `cargo check` (or other compatible command) and providing
31/// diagnostics based on the output.
32#[derive(Debug)]
33pub struct CheckWatcher {
34 pub task_recv: Receiver<CheckTask>,
35 pub cmd_send: Sender<CheckCommand>,
36 pub shared: Arc<RwLock<CheckWatcherSharedState>>,
37 handle: JoinHandle<()>,
38}
39
40impl CheckWatcher {
41 pub fn new(options: &Options, workspace_root: PathBuf) -> CheckWatcher {
42 let options = options.clone();
43 let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new()));
44
45 let (task_send, task_recv) = unbounded::<CheckTask>();
46 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
47 let shared_ = shared.clone();
48 let handle = std::thread::spawn(move || {
49 let mut check = CheckWatcherState::new(options, workspace_root, shared_);
50 check.run(&task_send, &cmd_recv);
51 });
52
53 CheckWatcher { task_recv, cmd_send, handle, shared }
54 }
55
56 /// Schedule a re-start of the cargo check worker.
57 pub fn update(&self) {
58 self.cmd_send.send(CheckCommand::Update).unwrap();
59 }
60}
61
62pub struct CheckWatcherState {
63 options: Options,
64 workspace_root: PathBuf,
65 running: bool,
66 watcher: WatchThread,
67 last_update_req: Option<Instant>,
68 shared: Arc<RwLock<CheckWatcherSharedState>>,
69}
70
71#[derive(Debug)]
72pub struct CheckWatcherSharedState {
73 diagnostic_collection: HashMap<Url, Vec<Diagnostic>>,
74 suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>,
75}
76
77impl CheckWatcherSharedState {
78 fn new() -> CheckWatcherSharedState {
79 CheckWatcherSharedState {
80 diagnostic_collection: HashMap::new(),
81 suggested_fix_collection: HashMap::new(),
82 }
83 }
84
85 /// Clear the cached diagnostics, and schedule updating diagnostics by the
86 /// server, to clear stale results.
87 pub fn clear(&mut self, task_send: &Sender<CheckTask>) {
88 let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect();
89
90 self.diagnostic_collection.clear();
91 self.suggested_fix_collection.clear();
92
93 for uri in cleared_files {
94 task_send.send(CheckTask::Update(uri.clone())).unwrap();
95 }
96 }
97
98 pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> {
99 self.diagnostic_collection.get(uri).map(|d| d.as_slice())
100 }
101
102 pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> {
103 self.suggested_fix_collection.get(uri).map(|d| d.as_slice())
104 }
105
106 fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) {
107 let diagnostics = self.diagnostic_collection.entry(file_uri).or_default();
108
109 // If we're building multiple targets it's possible we've already seen this diagnostic
110 let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic));
111 if is_duplicate {
112 return;
113 }
114
115 diagnostics.push(diagnostic);
116 }
117
118 fn add_suggested_fix_for_diagnostic(
119 &mut self,
120 mut suggested_fix: SuggestedFix,
121 diagnostic: &Diagnostic,
122 ) {
123 let file_uri = suggested_fix.location.uri.clone();
124 let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default();
125
126 let existing_suggestion: Option<&mut SuggestedFix> =
127 file_suggestions.iter_mut().find(|s| s == &&suggested_fix);
128 if let Some(existing_suggestion) = existing_suggestion {
129 // The existing suggestion also applies to this new diagnostic
130 existing_suggestion.diagnostics.push(diagnostic.clone());
131 } else {
132 // We haven't seen this suggestion before
133 suggested_fix.diagnostics.push(diagnostic.clone());
134 file_suggestions.push(suggested_fix);
135 }
136 }
137}
138
139#[derive(Debug)]
140pub enum CheckTask {
141 /// Request a update of the given files diagnostics
142 Update(Url),
143
144 /// Request check progress notification to client
145 Status(WorkDoneProgress),
146}
147
148pub enum CheckCommand {
149 /// Request re-start of check thread
150 Update,
151}
152
153impl CheckWatcherState {
154 pub fn new(
155 options: Options,
156 workspace_root: PathBuf,
157 shared: Arc<RwLock<CheckWatcherSharedState>>,
158 ) -> CheckWatcherState {
159 let watcher = WatchThread::new(&options, &workspace_root);
160 CheckWatcherState {
161 options,
162 workspace_root,
163 running: false,
164 watcher,
165 last_update_req: None,
166 shared,
167 }
168 }
169
170 pub fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
171 self.running = true;
172 while self.running {
173 select! {
174 recv(&cmd_recv) -> cmd => match cmd {
175 Ok(cmd) => self.handle_command(cmd),
176 Err(RecvError) => {
177 // Command channel has closed, so shut down
178 self.running = false;
179 },
180 },
181 recv(self.watcher.message_recv) -> msg => match msg {
182 Ok(msg) => self.handle_message(msg, task_send),
183 Err(RecvError) => {},
184 }
185 };
186
187 if self.should_recheck() {
188 self.last_update_req.take();
189 self.shared.write().clear(task_send);
190
191 self.watcher.cancel();
192 self.watcher = WatchThread::new(&self.options, &self.workspace_root);
193 }
194 }
195 }
196
197 fn should_recheck(&mut self) -> bool {
198 if let Some(_last_update_req) = &self.last_update_req {
199 // We currently only request an update on save, as we need up to
200 // date source on disk for cargo check to do it's magic, so we
201 // don't really need to debounce the requests at this point.
202 return true;
203 }
204 false
205 }
206
207 fn handle_command(&mut self, cmd: CheckCommand) {
208 match cmd {
209 CheckCommand::Update => self.last_update_req = Some(Instant::now()),
210 }
211 }
212
213 fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
214 match msg {
215 CheckEvent::Begin => {
216 task_send
217 .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
218 title: "Running 'cargo check'".to_string(),
219 cancellable: Some(false),
220 message: None,
221 percentage: None,
222 })))
223 .unwrap();
224 }
225
226 CheckEvent::End => {
227 task_send
228 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
229 message: None,
230 })))
231 .unwrap();
232 }
233
234 CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
235 task_send
236 .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
237 cancellable: Some(false),
238 message: Some(msg.target.name),
239 percentage: None,
240 })))
241 .unwrap();
242 }
243
244 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
245 let map_result =
246 match map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root) {
247 Some(map_result) => map_result,
248 None => return,
249 };
250
251 let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result;
252 let file_uri = location.uri.clone();
253
254 if !suggested_fixes.is_empty() {
255 for suggested_fix in suggested_fixes {
256 self.shared
257 .write()
258 .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic);
259 }
260 }
261 self.shared.write().add_diagnostic(file_uri, diagnostic);
262
263 task_send.send(CheckTask::Update(location.uri)).unwrap();
264 }
265
266 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
267 CheckEvent::Msg(Message::Unknown) => {}
268 }
269 }
270}
271
272/// WatchThread exists to wrap around the communication needed to be able to
273/// run `cargo check` without blocking. Currently the Rust standard library
274/// doesn't provide a way to read sub-process output without blocking, so we
275/// have to wrap sub-processes output handling in a thread and pass messages
276/// back over a channel.
277struct WatchThread {
278 message_recv: Receiver<CheckEvent>,
279 cancel_send: Sender<()>,
280}
281
282enum CheckEvent {
283 Begin,
284 Msg(cargo_metadata::Message),
285 End,
286}
287
288impl WatchThread {
289 fn new(options: &Options, workspace_root: &PathBuf) -> WatchThread {
290 let mut args: Vec<String> = vec![
291 options.cargo_watch_command.clone(),
292 "--message-format=json".to_string(),
293 "--manifest-path".to_string(),
294 format!("{}/Cargo.toml", workspace_root.to_string_lossy()),
295 ];
296 if options.cargo_watch_all_targets {
297 args.push("--all-targets".to_string());
298 }
299 args.extend(options.cargo_watch_args.iter().cloned());
300
301 let (message_send, message_recv) = unbounded();
302 let (cancel_send, cancel_recv) = unbounded();
303 let enabled = options.cargo_watch_enable;
304 std::thread::spawn(move || {
305 if !enabled {
306 return;
307 }
308
309 let mut command = Command::new("cargo")
310 .args(&args)
311 .stdout(Stdio::piped())
312 .stderr(Stdio::null())
313 .spawn()
314 .expect("couldn't launch cargo");
315
316 message_send.send(CheckEvent::Begin).unwrap();
317 for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) {
318 match cancel_recv.try_recv() {
319 Ok(()) | Err(TryRecvError::Disconnected) => {
320 command.kill().expect("couldn't kill command");
321 }
322 Err(TryRecvError::Empty) => (),
323 }
324
325 message_send.send(CheckEvent::Msg(message.unwrap())).unwrap();
326 }
327 message_send.send(CheckEvent::End).unwrap();
328 });
329 WatchThread { message_recv, cancel_send }
330 }
331
332 fn cancel(&self) {
333 let _ = self.cancel_send.send(());
334 }
335}
336
337/// Converts a Rust level string to a LSP severity
338fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
339 match val {
340 DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error),
341 DiagnosticLevel::Error => Some(DiagnosticSeverity::Error),
342 DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning),
343 DiagnosticLevel::Note => Some(DiagnosticSeverity::Information),
344 DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint),
345 DiagnosticLevel::Unknown => None,
346 }
347}
348
349/// Check whether a file name is from macro invocation
350fn is_from_macro(file_name: &str) -> bool {
351 file_name.starts_with('<') && file_name.ends_with('>')
352}
353
354/// Converts a Rust macro span to a LSP location recursively
355fn map_macro_span_to_location(
356 span_macro: &DiagnosticSpanMacroExpansion,
357 workspace_root: &PathBuf,
358) -> Option<Location> {
359 if !is_from_macro(&span_macro.span.file_name) {
360 return Some(map_span_to_location(&span_macro.span, workspace_root));
361 }
362
363 if let Some(expansion) = &span_macro.span.expansion {
364 return map_macro_span_to_location(&expansion, workspace_root);
365 }
366
367 None
368}
369
370/// Converts a Rust span to a LSP location
371fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
372 if is_from_macro(&span.file_name) && span.expansion.is_some() {
373 let expansion = span.expansion.as_ref().unwrap();
374 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
375 return macro_range;
376 }
377 }
378
379 let mut file_name = workspace_root.clone();
380 file_name.push(&span.file_name);
381 let uri = Url::from_file_path(file_name).unwrap();
382
383 let range = Range::new(
384 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
385 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
386 );
387
388 Location { uri, range }
389}
390
391/// Converts a secondary Rust span to a LSP related information
392///
393/// If the span is unlabelled this will return `None`.
394fn map_secondary_span_to_related(
395 span: &DiagnosticSpan,
396 workspace_root: &PathBuf,
397) -> Option<DiagnosticRelatedInformation> {
398 if let Some(label) = &span.label {
399 let location = map_span_to_location(span, workspace_root);
400 Some(DiagnosticRelatedInformation { location, message: label.clone() })
401 } else {
402 // Nothing to label this with
403 None
404 }
405}
406
407/// Determines if diagnostic is related to unused code
408fn is_unused_or_unnecessary(rd: &RustDiagnostic) -> bool {
409 if let Some(code) = &rd.code {
410 match code.code.as_str() {
411 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
412 | "unused_imports" | "unused_macros" | "unused_variables" => true,
413 _ => false,
414 }
415 } else {
416 false
417 }
418}
419
420/// Determines if diagnostic is related to deprecated code
421fn is_deprecated(rd: &RustDiagnostic) -> bool {
422 if let Some(code) = &rd.code {
423 match code.code.as_str() {
424 "deprecated" => true,
425 _ => false,
426 }
427 } else {
428 false
429 }
430}
431
432#[derive(Debug)]
433pub struct SuggestedFix {
434 pub title: String,
435 pub location: Location,
436 pub replacement: String,
437 pub applicability: Applicability,
438 pub diagnostics: Vec<Diagnostic>,
439}
440
441impl std::cmp::PartialEq<SuggestedFix> for SuggestedFix {
442 fn eq(&self, other: &SuggestedFix) -> bool {
443 if self.title == other.title
444 && self.location == other.location
445 && self.replacement == other.replacement
446 {
447 // Applicability doesn't impl PartialEq...
448 match (&self.applicability, &other.applicability) {
449 (Applicability::MachineApplicable, Applicability::MachineApplicable) => true,
450 (Applicability::HasPlaceholders, Applicability::HasPlaceholders) => true,
451 (Applicability::MaybeIncorrect, Applicability::MaybeIncorrect) => true,
452 (Applicability::Unspecified, Applicability::Unspecified) => true,
453 _ => false,
454 }
455 } else {
456 false
457 }
458 }
459}
460
461enum MappedRustChildDiagnostic {
462 Related(DiagnosticRelatedInformation),
463 SuggestedFix(SuggestedFix),
464 MessageLine(String),
465}
466
467fn map_rust_child_diagnostic(
468 rd: &RustDiagnostic,
469 workspace_root: &PathBuf,
470) -> MappedRustChildDiagnostic {
471 let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) {
472 Some(span) => span,
473 None => {
474 // `rustc` uses these spanless children as a way to print multi-line
475 // messages
476 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
477 }
478 };
479
480 // If we have a primary span use its location, otherwise use the parent
481 let location = map_span_to_location(&span, workspace_root);
482
483 if let Some(suggested_replacement) = &span.suggested_replacement {
484 // Include our replacement in the title unless it's empty
485 let title = if !suggested_replacement.is_empty() {
486 format!("{}: '{}'", rd.message, suggested_replacement)
487 } else {
488 rd.message.clone()
489 };
490
491 MappedRustChildDiagnostic::SuggestedFix(SuggestedFix {
492 title,
493 location,
494 replacement: suggested_replacement.clone(),
495 applicability: span.suggestion_applicability.clone().unwrap_or(Applicability::Unknown),
496 diagnostics: vec![],
497 })
498 } else {
499 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
500 location,
501 message: rd.message.clone(),
502 })
503 }
504}
505
506#[derive(Debug)]
507struct MappedRustDiagnostic {
508 location: Location,
509 diagnostic: Diagnostic,
510 suggested_fixes: Vec<SuggestedFix>,
511}
512
513/// Converts a Rust root diagnostic to LSP form
514///
515/// This flattens the Rust diagnostic by:
516///
517/// 1. Creating a LSP diagnostic with the root message and primary span.
518/// 2. Adding any labelled secondary spans to `relatedInformation`
519/// 3. Categorising child diagnostics as either `SuggestedFix`es,
520/// `relatedInformation` or additional message lines.
521///
522/// If the diagnostic has no primary span this will return `None`
523fn map_rust_diagnostic_to_lsp(
524 rd: &RustDiagnostic,
525 workspace_root: &PathBuf,
526) -> Option<MappedRustDiagnostic> {
527 let primary_span = rd.spans.iter().find(|s| s.is_primary)?;
528
529 let location = map_span_to_location(&primary_span, workspace_root);
530
531 let severity = map_level_to_severity(rd.level);
532 let mut primary_span_label = primary_span.label.as_ref();
533
534 let mut source = String::from("rustc");
535 let mut code = rd.code.as_ref().map(|c| c.code.clone());
536 if let Some(code_val) = &code {
537 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
538 let scoped_code: Vec<&str> = code_val.split("::").collect();
539 if scoped_code.len() == 2 {
540 source = String::from(scoped_code[0]);
541 code = Some(String::from(scoped_code[1]));
542 }
543 }
544
545 let mut related_information = vec![];
546 let mut tags = vec![];
547
548 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
549 let related = map_secondary_span_to_related(secondary_span, workspace_root);
550 if let Some(related) = related {
551 related_information.push(related);
552 }
553 }
554
555 let mut suggested_fixes = vec![];
556 let mut message = rd.message.clone();
557 for child in &rd.children {
558 let child = map_rust_child_diagnostic(&child, workspace_root);
559 match child {
560 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
561 MappedRustChildDiagnostic::SuggestedFix(suggested_fix) => {
562 suggested_fixes.push(suggested_fix)
563 }
564 MappedRustChildDiagnostic::MessageLine(message_line) => {
565 write!(&mut message, "\n{}", message_line).unwrap();
566
567 // These secondary messages usually duplicate the content of the
568 // primary span label.
569 primary_span_label = None;
570 }
571 }
572 }
573
574 if let Some(primary_span_label) = primary_span_label {
575 write!(&mut message, "\n{}", primary_span_label).unwrap();
576 }
577
578 if is_unused_or_unnecessary(rd) {
579 tags.push(DiagnosticTag::Unnecessary);
580 }
581
582 if is_deprecated(rd) {
583 tags.push(DiagnosticTag::Deprecated);
584 }
585
586 let diagnostic = Diagnostic {
587 range: location.range,
588 severity,
589 code: code.map(NumberOrString::String),
590 source: Some(source),
591 message,
592 related_information: if !related_information.is_empty() {
593 Some(related_information)
594 } else {
595 None
596 },
597 tags: if !tags.is_empty() { Some(tags) } else { None },
598 };
599
600 Some(MappedRustDiagnostic { location, diagnostic, suggested_fixes })
601}
602
603fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool {
604 left.source == right.source
605 && left.severity == right.severity
606 && left.range == right.range
607 && left.message == right.message
608}
609
610#[cfg(test)]
611mod test {
612 use super::*;
613
614 fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic {
615 serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap()
616 }
617
618 #[test]
619 fn snap_rustc_incompatible_type_for_trait() {
620 let diag = parse_diagnostic(
621 r##"{
622 "message": "method `next` has an incompatible type for trait",
623 "code": {
624 "code": "E0053",
625 "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"
626 },
627 "level": "error",
628 "spans": [
629 {
630 "file_name": "compiler/ty/list_iter.rs",
631 "byte_start": 1307,
632 "byte_end": 1350,
633 "line_start": 52,
634 "line_end": 52,
635 "column_start": 5,
636 "column_end": 48,
637 "is_primary": true,
638 "text": [
639 {
640 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
641 "highlight_start": 5,
642 "highlight_end": 48
643 }
644 ],
645 "label": "types differ in mutability",
646 "suggested_replacement": null,
647 "suggestion_applicability": null,
648 "expansion": null
649 }
650 ],
651 "children": [
652 {
653 "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>>`",
654 "code": null,
655 "level": "note",
656 "spans": [],
657 "children": [],
658 "rendered": null
659 }
660 ],
661 "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"
662 }
663 "##,
664 );
665
666 let workspace_root = PathBuf::from("/test/");
667 let diag =
668 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
669 insta::assert_debug_snapshot!(diag);
670 }
671
672 #[test]
673 fn snap_rustc_unused_variable() {
674 let diag = parse_diagnostic(
675 r##"{
676 "message": "unused variable: `foo`",
677 "code": {
678 "code": "unused_variables",
679 "explanation": null
680 },
681 "level": "warning",
682 "spans": [
683 {
684 "file_name": "driver/subcommand/repl.rs",
685 "byte_start": 9228,
686 "byte_end": 9231,
687 "line_start": 291,
688 "line_end": 291,
689 "column_start": 9,
690 "column_end": 12,
691 "is_primary": true,
692 "text": [
693 {
694 "text": " let foo = 42;",
695 "highlight_start": 9,
696 "highlight_end": 12
697 }
698 ],
699 "label": null,
700 "suggested_replacement": null,
701 "suggestion_applicability": null,
702 "expansion": null
703 }
704 ],
705 "children": [
706 {
707 "message": "#[warn(unused_variables)] on by default",
708 "code": null,
709 "level": "note",
710 "spans": [],
711 "children": [],
712 "rendered": null
713 },
714 {
715 "message": "consider prefixing with an underscore",
716 "code": null,
717 "level": "help",
718 "spans": [
719 {
720 "file_name": "driver/subcommand/repl.rs",
721 "byte_start": 9228,
722 "byte_end": 9231,
723 "line_start": 291,
724 "line_end": 291,
725 "column_start": 9,
726 "column_end": 12,
727 "is_primary": true,
728 "text": [
729 {
730 "text": " let foo = 42;",
731 "highlight_start": 9,
732 "highlight_end": 12
733 }
734 ],
735 "label": null,
736 "suggested_replacement": "_foo",
737 "suggestion_applicability": "MachineApplicable",
738 "expansion": null
739 }
740 ],
741 "children": [],
742 "rendered": null
743 }
744 ],
745 "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"
746}"##,
747 );
748
749 let workspace_root = PathBuf::from("/test/");
750 let diag =
751 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
752 insta::assert_debug_snapshot!(diag);
753 }
754
755 #[test]
756 fn snap_rustc_wrong_number_of_parameters() {
757 let diag = parse_diagnostic(
758 r##"{
759 "message": "this function takes 2 parameters but 3 parameters were supplied",
760 "code": {
761 "code": "E0061",
762 "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"
763 },
764 "level": "error",
765 "spans": [
766 {
767 "file_name": "compiler/ty/select.rs",
768 "byte_start": 8787,
769 "byte_end": 9241,
770 "line_start": 219,
771 "line_end": 231,
772 "column_start": 5,
773 "column_end": 6,
774 "is_primary": false,
775 "text": [
776 {
777 "text": " pub fn add_evidence(",
778 "highlight_start": 5,
779 "highlight_end": 25
780 },
781 {
782 "text": " &mut self,",
783 "highlight_start": 1,
784 "highlight_end": 19
785 },
786 {
787 "text": " target_poly: &ty::Ref<ty::Poly>,",
788 "highlight_start": 1,
789 "highlight_end": 41
790 },
791 {
792 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
793 "highlight_start": 1,
794 "highlight_end": 43
795 },
796 {
797 "text": " ) {",
798 "highlight_start": 1,
799 "highlight_end": 8
800 },
801 {
802 "text": " match target_poly {",
803 "highlight_start": 1,
804 "highlight_end": 28
805 },
806 {
807 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
808 "highlight_start": 1,
809 "highlight_end": 81
810 },
811 {
812 "text": " ty::Ref::Fixed(target_ty) => {",
813 "highlight_start": 1,
814 "highlight_end": 43
815 },
816 {
817 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
818 "highlight_start": 1,
819 "highlight_end": 65
820 },
821 {
822 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
823 "highlight_start": 1,
824 "highlight_end": 76
825 },
826 {
827 "text": " }",
828 "highlight_start": 1,
829 "highlight_end": 14
830 },
831 {
832 "text": " }",
833 "highlight_start": 1,
834 "highlight_end": 10
835 },
836 {
837 "text": " }",
838 "highlight_start": 1,
839 "highlight_end": 6
840 }
841 ],
842 "label": "defined here",
843 "suggested_replacement": null,
844 "suggestion_applicability": null,
845 "expansion": null
846 },
847 {
848 "file_name": "compiler/ty/select.rs",
849 "byte_start": 4045,
850 "byte_end": 4057,
851 "line_start": 104,
852 "line_end": 104,
853 "column_start": 18,
854 "column_end": 30,
855 "is_primary": true,
856 "text": [
857 {
858 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
859 "highlight_start": 18,
860 "highlight_end": 30
861 }
862 ],
863 "label": "expected 2 parameters",
864 "suggested_replacement": null,
865 "suggestion_applicability": null,
866 "expansion": null
867 }
868 ],
869 "children": [],
870 "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"
871}"##,
872 );
873
874 let workspace_root = PathBuf::from("/test/");
875 let diag =
876 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
877 insta::assert_debug_snapshot!(diag);
878 }
879
880 #[test]
881 fn snap_clippy_pass_by_ref() {
882 let diag = parse_diagnostic(
883 r##"{
884 "message": "this argument is passed by reference, but would be more efficient if passed by value",
885 "code": {
886 "code": "clippy::trivially_copy_pass_by_ref",
887 "explanation": null
888 },
889 "level": "warning",
890 "spans": [
891 {
892 "file_name": "compiler/mir/tagset.rs",
893 "byte_start": 941,
894 "byte_end": 946,
895 "line_start": 42,
896 "line_end": 42,
897 "column_start": 24,
898 "column_end": 29,
899 "is_primary": true,
900 "text": [
901 {
902 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
903 "highlight_start": 24,
904 "highlight_end": 29
905 }
906 ],
907 "label": null,
908 "suggested_replacement": null,
909 "suggestion_applicability": null,
910 "expansion": null
911 }
912 ],
913 "children": [
914 {
915 "message": "lint level defined here",
916 "code": null,
917 "level": "note",
918 "spans": [
919 {
920 "file_name": "compiler/lib.rs",
921 "byte_start": 8,
922 "byte_end": 19,
923 "line_start": 1,
924 "line_end": 1,
925 "column_start": 9,
926 "column_end": 20,
927 "is_primary": true,
928 "text": [
929 {
930 "text": "#![warn(clippy::all)]",
931 "highlight_start": 9,
932 "highlight_end": 20
933 }
934 ],
935 "label": null,
936 "suggested_replacement": null,
937 "suggestion_applicability": null,
938 "expansion": null
939 }
940 ],
941 "children": [],
942 "rendered": null
943 },
944 {
945 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
946 "code": null,
947 "level": "note",
948 "spans": [],
949 "children": [],
950 "rendered": null
951 },
952 {
953 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
954 "code": null,
955 "level": "help",
956 "spans": [],
957 "children": [],
958 "rendered": null
959 },
960 {
961 "message": "consider passing by value instead",
962 "code": null,
963 "level": "help",
964 "spans": [
965 {
966 "file_name": "compiler/mir/tagset.rs",
967 "byte_start": 941,
968 "byte_end": 946,
969 "line_start": 42,
970 "line_end": 42,
971 "column_start": 24,
972 "column_end": 29,
973 "is_primary": true,
974 "text": [
975 {
976 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
977 "highlight_start": 24,
978 "highlight_end": 29
979 }
980 ],
981 "label": null,
982 "suggested_replacement": "self",
983 "suggestion_applicability": "Unspecified",
984 "expansion": null
985 }
986 ],
987 "children": [],
988 "rendered": null
989 }
990 ],
991 "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"
992}"##,
993 );
994
995 let workspace_root = PathBuf::from("/test/");
996 let diag =
997 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
998 insta::assert_debug_snapshot!(diag);
999 }
1000
1001 #[test]
1002 fn snap_rustc_mismatched_type() {
1003 let diag = parse_diagnostic(
1004 r##"{
1005 "message": "mismatched types",
1006 "code": {
1007 "code": "E0308",
1008 "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"
1009 },
1010 "level": "error",
1011 "spans": [
1012 {
1013 "file_name": "runtime/compiler_support.rs",
1014 "byte_start": 1589,
1015 "byte_end": 1594,
1016 "line_start": 48,
1017 "line_end": 48,
1018 "column_start": 65,
1019 "column_end": 70,
1020 "is_primary": true,
1021 "text": [
1022 {
1023 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
1024 "highlight_start": 65,
1025 "highlight_end": 70
1026 }
1027 ],
1028 "label": "expected usize, found u32",
1029 "suggested_replacement": null,
1030 "suggestion_applicability": null,
1031 "expansion": null
1032 }
1033 ],
1034 "children": [],
1035 "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"
1036}"##,
1037 );
1038
1039 let workspace_root = PathBuf::from("/test/");
1040 let diag =
1041 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
1042 insta::assert_debug_snapshot!(diag);
1043 }
1044
1045 #[test]
1046 fn snap_handles_macro_location() {
1047 let diag = parse_diagnostic(
1048 r##"{
1049 "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",
1050 "children": [
1051 {
1052 "children": [],
1053 "code": null,
1054 "level": "help",
1055 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
1056 "rendered": null,
1057 "spans": []
1058 }
1059 ],
1060 "code": {
1061 "code": "E0277",
1062 "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"
1063 },
1064 "level": "error",
1065 "message": "can't compare `{integer}` with `&str`",
1066 "spans": [
1067 {
1068 "byte_end": 155,
1069 "byte_start": 153,
1070 "column_end": 33,
1071 "column_start": 31,
1072 "expansion": {
1073 "def_site_span": {
1074 "byte_end": 940,
1075 "byte_start": 0,
1076 "column_end": 6,
1077 "column_start": 1,
1078 "expansion": null,
1079 "file_name": "<::core::macros::assert_eq macros>",
1080 "is_primary": false,
1081 "label": null,
1082 "line_end": 36,
1083 "line_start": 1,
1084 "suggested_replacement": null,
1085 "suggestion_applicability": null,
1086 "text": [
1087 {
1088 "highlight_end": 35,
1089 "highlight_start": 1,
1090 "text": "($ left : expr, $ right : expr) =>"
1091 },
1092 {
1093 "highlight_end": 3,
1094 "highlight_start": 1,
1095 "text": "({"
1096 },
1097 {
1098 "highlight_end": 33,
1099 "highlight_start": 1,
1100 "text": " match (& $ left, & $ right)"
1101 },
1102 {
1103 "highlight_end": 7,
1104 "highlight_start": 1,
1105 "text": " {"
1106 },
1107 {
1108 "highlight_end": 34,
1109 "highlight_start": 1,
1110 "text": " (left_val, right_val) =>"
1111 },
1112 {
1113 "highlight_end": 11,
1114 "highlight_start": 1,
1115 "text": " {"
1116 },
1117 {
1118 "highlight_end": 46,
1119 "highlight_start": 1,
1120 "text": " if ! (* left_val == * right_val)"
1121 },
1122 {
1123 "highlight_end": 15,
1124 "highlight_start": 1,
1125 "text": " {"
1126 },
1127 {
1128 "highlight_end": 25,
1129 "highlight_start": 1,
1130 "text": " panic !"
1131 },
1132 {
1133 "highlight_end": 57,
1134 "highlight_start": 1,
1135 "text": " (r#\"assertion failed: `(left == right)`"
1136 },
1137 {
1138 "highlight_end": 16,
1139 "highlight_start": 1,
1140 "text": " left: `{:?}`,"
1141 },
1142 {
1143 "highlight_end": 18,
1144 "highlight_start": 1,
1145 "text": " right: `{:?}`\"#,"
1146 },
1147 {
1148 "highlight_end": 47,
1149 "highlight_start": 1,
1150 "text": " & * left_val, & * right_val)"
1151 },
1152 {
1153 "highlight_end": 15,
1154 "highlight_start": 1,
1155 "text": " }"
1156 },
1157 {
1158 "highlight_end": 11,
1159 "highlight_start": 1,
1160 "text": " }"
1161 },
1162 {
1163 "highlight_end": 7,
1164 "highlight_start": 1,
1165 "text": " }"
1166 },
1167 {
1168 "highlight_end": 42,
1169 "highlight_start": 1,
1170 "text": " }) ; ($ left : expr, $ right : expr,) =>"
1171 },
1172 {
1173 "highlight_end": 49,
1174 "highlight_start": 1,
1175 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
1176 },
1177 {
1178 "highlight_end": 53,
1179 "highlight_start": 1,
1180 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
1181 },
1182 {
1183 "highlight_end": 3,
1184 "highlight_start": 1,
1185 "text": "({"
1186 },
1187 {
1188 "highlight_end": 37,
1189 "highlight_start": 1,
1190 "text": " match (& ($ left), & ($ right))"
1191 },
1192 {
1193 "highlight_end": 7,
1194 "highlight_start": 1,
1195 "text": " {"
1196 },
1197 {
1198 "highlight_end": 34,
1199 "highlight_start": 1,
1200 "text": " (left_val, right_val) =>"
1201 },
1202 {
1203 "highlight_end": 11,
1204 "highlight_start": 1,
1205 "text": " {"
1206 },
1207 {
1208 "highlight_end": 46,
1209 "highlight_start": 1,
1210 "text": " if ! (* left_val == * right_val)"
1211 },
1212 {
1213 "highlight_end": 15,
1214 "highlight_start": 1,
1215 "text": " {"
1216 },
1217 {
1218 "highlight_end": 25,
1219 "highlight_start": 1,
1220 "text": " panic !"
1221 },
1222 {
1223 "highlight_end": 57,
1224 "highlight_start": 1,
1225 "text": " (r#\"assertion failed: `(left == right)`"
1226 },
1227 {
1228 "highlight_end": 16,
1229 "highlight_start": 1,
1230 "text": " left: `{:?}`,"
1231 },
1232 {
1233 "highlight_end": 22,
1234 "highlight_start": 1,
1235 "text": " right: `{:?}`: {}\"#,"
1236 },
1237 {
1238 "highlight_end": 72,
1239 "highlight_start": 1,
1240 "text": " & * left_val, & * right_val, $ crate :: format_args !"
1241 },
1242 {
1243 "highlight_end": 33,
1244 "highlight_start": 1,
1245 "text": " ($ ($ arg) +))"
1246 },
1247 {
1248 "highlight_end": 15,
1249 "highlight_start": 1,
1250 "text": " }"
1251 },
1252 {
1253 "highlight_end": 11,
1254 "highlight_start": 1,
1255 "text": " }"
1256 },
1257 {
1258 "highlight_end": 7,
1259 "highlight_start": 1,
1260 "text": " }"
1261 },
1262 {
1263 "highlight_end": 6,
1264 "highlight_start": 1,
1265 "text": " }) ;"
1266 }
1267 ]
1268 },
1269 "macro_decl_name": "assert_eq!",
1270 "span": {
1271 "byte_end": 38,
1272 "byte_start": 16,
1273 "column_end": 27,
1274 "column_start": 5,
1275 "expansion": null,
1276 "file_name": "src/main.rs",
1277 "is_primary": false,
1278 "label": null,
1279 "line_end": 2,
1280 "line_start": 2,
1281 "suggested_replacement": null,
1282 "suggestion_applicability": null,
1283 "text": [
1284 {
1285 "highlight_end": 27,
1286 "highlight_start": 5,
1287 "text": " assert_eq!(1, \"love\");"
1288 }
1289 ]
1290 }
1291 },
1292 "file_name": "<::core::macros::assert_eq macros>",
1293 "is_primary": true,
1294 "label": "no implementation for `{integer} == &str`",
1295 "line_end": 7,
1296 "line_start": 7,
1297 "suggested_replacement": null,
1298 "suggestion_applicability": null,
1299 "text": [
1300 {
1301 "highlight_end": 33,
1302 "highlight_start": 31,
1303 "text": " if ! (* left_val == * right_val)"
1304 }
1305 ]
1306 }
1307 ]
1308}"##,
1309 );
1310
1311 let workspace_root = PathBuf::from("/test/");
1312 let diag =
1313 map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic");
1314 insta::assert_debug_snapshot!(diag);
1315 }
1316}
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs
index 2811231fa..2ca149fd5 100644
--- a/crates/ra_lsp_server/src/lib.rs
+++ b/crates/ra_lsp_server/src/lib.rs
@@ -22,7 +22,6 @@ macro_rules! print {
22} 22}
23 23
24mod caps; 24mod caps;
25mod cargo_check;
26mod cargo_target_spec; 25mod cargo_target_spec;
27mod conv; 26mod conv;
28mod main_loop; 27mod main_loop;
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index c58af7e47..e66b8f9eb 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -10,6 +10,7 @@ use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant};
10use crossbeam_channel::{select, unbounded, RecvError, Sender}; 10use crossbeam_channel::{select, unbounded, RecvError, Sender};
11use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 11use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
12use lsp_types::{ClientCapabilities, NumberOrString}; 12use lsp_types::{ClientCapabilities, NumberOrString};
13use ra_cargo_watch::{CheckOptions, CheckTask};
13use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; 14use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId};
14use ra_prof::profile; 15use ra_prof::profile;
15use ra_vfs::{VfsTask, Watch}; 16use ra_vfs::{VfsTask, Watch};
@@ -19,7 +20,6 @@ use serde::{de::DeserializeOwned, Serialize};
19use threadpool::ThreadPool; 20use threadpool::ThreadPool;
20 21
21use crate::{ 22use crate::{
22 cargo_check::CheckTask,
23 main_loop::{ 23 main_loop::{
24 pending_requests::{PendingRequest, PendingRequests}, 24 pending_requests::{PendingRequest, PendingRequests},
25 subscriptions::Subscriptions, 25 subscriptions::Subscriptions,
@@ -127,10 +127,12 @@ pub fn main_loop(
127 .and_then(|it| it.line_folding_only) 127 .and_then(|it| it.line_folding_only)
128 .unwrap_or(false), 128 .unwrap_or(false),
129 max_inlay_hint_length: config.max_inlay_hint_length, 129 max_inlay_hint_length: config.max_inlay_hint_length,
130 cargo_watch_enable: config.cargo_watch_enable, 130 cargo_watch: CheckOptions {
131 cargo_watch_args: config.cargo_watch_args, 131 enable: config.cargo_watch_enable,
132 cargo_watch_command: config.cargo_watch_command, 132 args: config.cargo_watch_args,
133 cargo_watch_all_targets: config.cargo_watch_all_targets, 133 command: config.cargo_watch_command,
134 all_targets: config.cargo_watch_all_targets,
135 },
134 } 136 }
135 }; 137 };
136 138
diff --git a/crates/ra_lsp_server/src/snapshots/test__snap_clippy_pass_by_ref.snap b/crates/ra_lsp_server/src/snapshots/test__snap_clippy_pass_by_ref.snap
deleted file mode 100644
index a5ce29157..000000000
--- a/crates/ra_lsp_server/src/snapshots/test__snap_clippy_pass_by_ref.snap
+++ /dev/null
@@ -1,85 +0,0 @@
1---
2source: crates/ra_lsp_server/src/cargo_check.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/mir/tagset.rs",
8 range: Range {
9 start: Position {
10 line: 41,
11 character: 23,
12 },
13 end: Position {
14 line: 41,
15 character: 28,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 41,
23 character: 23,
24 },
25 end: Position {
26 line: 41,
27 character: 28,
28 },
29 },
30 severity: Some(
31 Warning,
32 ),
33 code: Some(
34 String(
35 "trivially_copy_pass_by_ref",
36 ),
37 ),
38 source: Some(
39 "clippy",
40 ),
41 message: "this argument is passed by reference, but would be more efficient if passed by value\n#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
42 related_information: Some(
43 [
44 DiagnosticRelatedInformation {
45 location: Location {
46 uri: "file:///test/compiler/lib.rs",
47 range: Range {
48 start: Position {
49 line: 0,
50 character: 8,
51 },
52 end: Position {
53 line: 0,
54 character: 19,
55 },
56 },
57 },
58 message: "lint level defined here",
59 },
60 ],
61 ),
62 tags: None,
63 },
64 suggested_fixes: [
65 SuggestedFix {
66 title: "consider passing by value instead: \'self\'",
67 location: Location {
68 uri: "file:///test/compiler/mir/tagset.rs",
69 range: Range {
70 start: Position {
71 line: 41,
72 character: 23,
73 },
74 end: Position {
75 line: 41,
76 character: 28,
77 },
78 },
79 },
80 replacement: "self",
81 applicability: Unspecified,
82 diagnostics: [],
83 },
84 ],
85}
diff --git a/crates/ra_lsp_server/src/snapshots/test__snap_handles_macro_location.snap b/crates/ra_lsp_server/src/snapshots/test__snap_handles_macro_location.snap
deleted file mode 100644
index 07e363ebf..000000000
--- a/crates/ra_lsp_server/src/snapshots/test__snap_handles_macro_location.snap
+++ /dev/null
@@ -1,46 +0,0 @@
1---
2source: crates/ra_lsp_server/src/cargo_check.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/src/main.rs",
8 range: Range {
9 start: Position {
10 line: 1,
11 character: 4,
12 },
13 end: Position {
14 line: 1,
15 character: 26,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 1,
23 character: 4,
24 },
25 end: Position {
26 line: 1,
27 character: 26,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0277",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "can\'t compare `{integer}` with `&str`\nthe trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_lsp_server/src/snapshots/test__snap_rustc_incompatible_type_for_trait.snap
deleted file mode 100644
index 85a87db0b..000000000
--- a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_incompatible_type_for_trait.snap
+++ /dev/null
@@ -1,46 +0,0 @@
1---
2source: crates/ra_lsp_server/src/cargo_check.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/ty/list_iter.rs",
8 range: Range {
9 start: Position {
10 line: 51,
11 character: 4,
12 },
13 end: Position {
14 line: 51,
15 character: 47,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 51,
23 character: 4,
24 },
25 end: Position {
26 line: 51,
27 character: 47,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0053",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "method `next` has an incompatible type for trait\nexpected 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>>`",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_mismatched_type.snap b/crates/ra_lsp_server/src/snapshots/test__snap_rustc_mismatched_type.snap
deleted file mode 100644
index 69cb8badf..000000000
--- a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_mismatched_type.snap
+++ /dev/null
@@ -1,46 +0,0 @@
1---
2source: crates/ra_lsp_server/src/cargo_check.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/runtime/compiler_support.rs",
8 range: Range {
9 start: Position {
10 line: 47,
11 character: 64,
12 },
13 end: Position {
14 line: 47,
15 character: 69,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 47,
23 character: 64,
24 },
25 end: Position {
26 line: 47,
27 character: 69,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0308",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "mismatched types\nexpected usize, found u32",
42 related_information: None,
43 tags: None,
44 },
45 suggested_fixes: [],
46}
diff --git a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_unused_variable.snap b/crates/ra_lsp_server/src/snapshots/test__snap_rustc_unused_variable.snap
deleted file mode 100644
index 33a3e3034..000000000
--- a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_unused_variable.snap
+++ /dev/null
@@ -1,70 +0,0 @@
1---
2source: crates/ra_lsp_server/src/cargo_check.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/driver/subcommand/repl.rs",
8 range: Range {
9 start: Position {
10 line: 290,
11 character: 8,
12 },
13 end: Position {
14 line: 290,
15 character: 11,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 290,
23 character: 8,
24 },
25 end: Position {
26 line: 290,
27 character: 11,
28 },
29 },
30 severity: Some(
31 Warning,
32 ),
33 code: Some(
34 String(
35 "unused_variables",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
42 related_information: None,
43 tags: Some(
44 [
45 Unnecessary,
46 ],
47 ),
48 },
49 suggested_fixes: [
50 SuggestedFix {
51 title: "consider prefixing with an underscore: \'_foo\'",
52 location: Location {
53 uri: "file:///test/driver/subcommand/repl.rs",
54 range: Range {
55 start: Position {
56 line: 290,
57 character: 8,
58 },
59 end: Position {
60 line: 290,
61 character: 11,
62 },
63 },
64 },
65 replacement: "_foo",
66 applicability: MachineApplicable,
67 diagnostics: [],
68 },
69 ],
70}
diff --git a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_lsp_server/src/snapshots/test__snap_rustc_wrong_number_of_parameters.snap
deleted file mode 100644
index ced6fa4df..000000000
--- a/crates/ra_lsp_server/src/snapshots/test__snap_rustc_wrong_number_of_parameters.snap
+++ /dev/null
@@ -1,65 +0,0 @@
1---
2source: crates/ra_lsp_server/src/cargo_check.rs
3expression: diag
4---
5MappedRustDiagnostic {
6 location: Location {
7 uri: "file:///test/compiler/ty/select.rs",
8 range: Range {
9 start: Position {
10 line: 103,
11 character: 17,
12 },
13 end: Position {
14 line: 103,
15 character: 29,
16 },
17 },
18 },
19 diagnostic: Diagnostic {
20 range: Range {
21 start: Position {
22 line: 103,
23 character: 17,
24 },
25 end: Position {
26 line: 103,
27 character: 29,
28 },
29 },
30 severity: Some(
31 Error,
32 ),
33 code: Some(
34 String(
35 "E0061",
36 ),
37 ),
38 source: Some(
39 "rustc",
40 ),
41 message: "this function takes 2 parameters but 3 parameters were supplied\nexpected 2 parameters",
42 related_information: Some(
43 [
44 DiagnosticRelatedInformation {
45 location: Location {
46 uri: "file:///test/compiler/ty/select.rs",
47 range: Range {
48 start: Position {
49 line: 218,
50 character: 4,
51 },
52 end: Position {
53 line: 230,
54 character: 5,
55 },
56 },
57 },
58 message: "defined here",
59 },
60 ],
61 ),
62 tags: None,
63 },
64 suggested_fixes: [],
65}
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 39a07c01a..4b3959e38 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -12,6 +12,7 @@ use crossbeam_channel::{unbounded, Receiver};
12use lsp_server::ErrorCode; 12use lsp_server::ErrorCode;
13use lsp_types::Url; 13use lsp_types::Url;
14use parking_lot::RwLock; 14use parking_lot::RwLock;
15use ra_cargo_watch::{CheckOptions, CheckWatcher, CheckWatcherSharedState};
15use ra_ide::{ 16use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, 17 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
17 SourceRootId, 18 SourceRootId,
@@ -23,7 +24,6 @@ use relative_path::RelativePathBuf;
23use std::path::{Component, Prefix}; 24use std::path::{Component, Prefix};
24 25
25use crate::{ 26use crate::{
26 cargo_check::{CheckWatcher, CheckWatcherSharedState},
27 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 27 main_loop::pending_requests::{CompletedRequest, LatestRequests},
28 LspError, Result, 28 LspError, Result,
29}; 29};
@@ -35,10 +35,7 @@ pub struct Options {
35 pub supports_location_link: bool, 35 pub supports_location_link: bool,
36 pub line_folding_only: bool, 36 pub line_folding_only: bool,
37 pub max_inlay_hint_length: Option<usize>, 37 pub max_inlay_hint_length: Option<usize>,
38 pub cargo_watch_enable: bool, 38 pub cargo_watch: CheckOptions,
39 pub cargo_watch_args: Vec<String>,
40 pub cargo_watch_command: String,
41 pub cargo_watch_all_targets: bool,
42} 39}
43 40
44/// `WorldState` is the primary mutable state of the language server 41/// `WorldState` is the primary mutable state of the language server
@@ -135,7 +132,8 @@ impl WorldState {
135 change.set_crate_graph(crate_graph); 132 change.set_crate_graph(crate_graph);
136 133
137 // FIXME: Figure out the multi-workspace situation 134 // FIXME: Figure out the multi-workspace situation
138 let check_watcher = CheckWatcher::new(&options, folder_roots.first().cloned().unwrap()); 135 let check_watcher =
136 CheckWatcher::new(&options.cargo_watch, folder_roots.first().cloned().unwrap());
139 137
140 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); 138 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags);
141 analysis_host.apply_change(change); 139 analysis_host.apply_change(change);