1
1
//! Flycheck provides the functionality needed to run `cargo check` to provide
2
2
//! LSP diagnostics based on the output of the command.
3
3
4
- use std::{fmt, io, process::Command, time::Duration};
4
+ use std::{fmt, io, mem, process::Command, time::Duration};
5
5
6
+ use cargo_metadata::PackageId;
6
7
use crossbeam_channel::{select_biased, unbounded, Receiver, Sender};
7
8
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
8
9
use rustc_hash::FxHashMap;
@@ -150,10 +151,19 @@ impl FlycheckHandle {
150
151
151
152
pub(crate) enum FlycheckMessage {
152
153
/// Request adding a diagnostic with fixes included to a file
153
- AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic },
154
+ AddDiagnostic {
155
+ id: usize,
156
+ workspace_root: AbsPathBuf,
157
+ diagnostic: Diagnostic,
158
+ package_id: Option<PackageId>,
159
+ },
154
160
155
- /// Request clearing all previous diagnostics
156
- ClearDiagnostics { id: usize },
161
+ /// Request clearing all outdated diagnostics.
162
+ ClearDiagnostics {
163
+ id: usize,
164
+ /// The pacakge whose diagnostics to clear, or if unspecified, all diagnostics.
165
+ package_id: Option<PackageId>,
166
+ },
157
167
158
168
/// Request check progress notification to client
159
169
Progress {
@@ -166,15 +176,18 @@ pub(crate) enum FlycheckMessage {
166
176
impl fmt::Debug for FlycheckMessage {
167
177
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168
178
match self {
169
- FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => f
179
+ FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => f
170
180
.debug_struct("AddDiagnostic")
171
181
.field("id", id)
172
182
.field("workspace_root", workspace_root)
183
+ .field("package_id", package_id)
173
184
.field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code))
174
185
.finish(),
175
- FlycheckMessage::ClearDiagnostics { id } => {
176
- f.debug_struct("ClearDiagnostics").field("id", id).finish()
177
- }
186
+ FlycheckMessage::ClearDiagnostics { id, package_id } => f
187
+ .debug_struct("ClearDiagnostics")
188
+ .field("id", id)
189
+ .field("package_id", package_id)
190
+ .finish(),
178
191
FlycheckMessage::Progress { id, progress } => {
179
192
f.debug_struct("Progress").field("id", id).field("progress", progress).finish()
180
193
}
@@ -200,6 +213,7 @@ enum StateChange {
200
213
struct FlycheckActor {
201
214
/// The workspace id of this flycheck instance.
202
215
id: usize,
216
+
203
217
sender: Sender<FlycheckMessage>,
204
218
config: FlycheckConfig,
205
219
manifest_path: Option<AbsPathBuf>,
@@ -215,8 +229,13 @@ struct FlycheckActor {
215
229
command_handle: Option<CommandHandle<CargoCheckMessage>>,
216
230
/// The receiver side of the channel mentioned above.
217
231
command_receiver: Option<Receiver<CargoCheckMessage>>,
232
+ package_status: FxHashMap<PackageId, DiagnosticReceived>,
233
+ }
218
234
219
- status: FlycheckStatus,
235
+ #[derive(PartialEq, Eq, Copy, Clone, Debug)]
236
+ enum DiagnosticReceived {
237
+ Yes,
238
+ No,
220
239
}
221
240
222
241
#[allow(clippy::large_enum_variant)]
@@ -225,13 +244,6 @@ enum Event {
225
244
CheckEvent(Option<CargoCheckMessage>),
226
245
}
227
246
228
- #[derive(PartialEq)]
229
- enum FlycheckStatus {
230
- Started,
231
- DiagnosticSent,
232
- Finished,
233
- }
234
-
235
247
pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file";
236
248
237
249
impl FlycheckActor {
@@ -253,7 +265,7 @@ impl FlycheckActor {
253
265
manifest_path,
254
266
command_handle: None,
255
267
command_receiver: None,
256
- status: FlycheckStatus::Finished ,
268
+ package_status: FxHashMap::default() ,
257
269
}
258
270
}
259
271
@@ -306,13 +318,11 @@ impl FlycheckActor {
306
318
self.command_handle = Some(command_handle);
307
319
self.command_receiver = Some(receiver);
308
320
self.report_progress(Progress::DidStart);
309
- self.status = FlycheckStatus::Started;
310
321
}
311
322
Err(error) => {
312
323
self.report_progress(Progress::DidFailToRestart(format!(
313
324
"Failed to run the following command: {formatted_command} error={error}"
314
325
)));
315
- self.status = FlycheckStatus::Finished;
316
326
}
317
327
}
318
328
}
@@ -332,11 +342,25 @@ impl FlycheckActor {
332
342
error
333
343
);
334
344
}
335
- if self.status == FlycheckStatus::Started {
336
- self.send(FlycheckMessage::ClearDiagnostics { id: self.id });
345
+ if self.package_status.is_empty() {
346
+ // We finished without receiving any diagnostics.
347
+ // That means all of them are stale.
348
+ self.send(FlycheckMessage::ClearDiagnostics {
349
+ id: self.id,
350
+ package_id: None,
351
+ });
352
+ } else {
353
+ for (package_id, status) in mem::take(&mut self.package_status) {
354
+ if let DiagnosticReceived::No = status {
355
+ self.send(FlycheckMessage::ClearDiagnostics {
356
+ id: self.id,
357
+ package_id: Some(package_id),
358
+ });
359
+ }
360
+ }
337
361
}
362
+
338
363
self.report_progress(Progress::DidFinish(res));
339
- self.status = FlycheckStatus::Finished;
340
364
}
341
365
Event::CheckEvent(Some(message)) => match message {
342
366
CargoCheckMessage::CompilerArtifact(msg) => {
@@ -346,23 +370,30 @@ impl FlycheckActor {
346
370
"artifact received"
347
371
);
348
372
self.report_progress(Progress::DidCheckCrate(msg.target.name));
373
+ self.package_status.entry(msg.package_id).or_insert(DiagnosticReceived::No);
349
374
}
350
-
351
- CargoCheckMessage::Diagnostic(msg) => {
375
+ CargoCheckMessage::Diagnostic { diagnostic, package_id } => {
352
376
tracing::trace!(
353
377
flycheck_id = self.id,
354
- message = msg .message,
378
+ message = diagnostic .message,
355
379
"diagnostic received"
356
380
);
357
- if self.status == FlycheckStatus::Started {
358
- self.send(FlycheckMessage::ClearDiagnostics { id: self.id });
381
+ if let Some(package_id) = &package_id {
382
+ if !self.package_status.contains_key(package_id) {
383
+ self.package_status
384
+ .insert(package_id.clone(), DiagnosticReceived::Yes);
385
+ self.send(FlycheckMessage::ClearDiagnostics {
386
+ id: self.id,
387
+ package_id: Some(package_id.clone()),
388
+ });
389
+ }
359
390
}
360
391
self.send(FlycheckMessage::AddDiagnostic {
361
392
id: self.id,
393
+ package_id,
362
394
workspace_root: self.root.clone(),
363
- diagnostic: msg ,
395
+ diagnostic,
364
396
});
365
- self.status = FlycheckStatus::DiagnosticSent;
366
397
}
367
398
},
368
399
}
@@ -380,7 +411,7 @@ impl FlycheckActor {
380
411
command_handle.cancel();
381
412
self.command_receiver.take();
382
413
self.report_progress(Progress::DidCancel);
383
- self.status = FlycheckStatus::Finished ;
414
+ self.package_status.clear() ;
384
415
}
385
416
}
386
417
@@ -486,7 +517,7 @@ impl FlycheckActor {
486
517
#[allow(clippy::large_enum_variant)]
487
518
enum CargoCheckMessage {
488
519
CompilerArtifact(cargo_metadata::Artifact),
489
- Diagnostic( Diagnostic) ,
520
+ Diagnostic { diagnostic: Diagnostic, package_id: Option<PackageId> } ,
490
521
}
491
522
492
523
impl ParseFromLine for CargoCheckMessage {
@@ -501,11 +532,16 @@ impl ParseFromLine for CargoCheckMessage {
501
532
Some(CargoCheckMessage::CompilerArtifact(artifact))
502
533
}
503
534
cargo_metadata::Message::CompilerMessage(msg) => {
504
- Some(CargoCheckMessage::Diagnostic(msg.message))
535
+ Some(CargoCheckMessage::Diagnostic {
536
+ diagnostic: msg.message,
537
+ package_id: Some(msg.package_id),
538
+ })
505
539
}
506
540
_ => None,
507
541
},
508
- JsonMessage::Rustc(message) => Some(CargoCheckMessage::Diagnostic(message)),
542
+ JsonMessage::Rustc(message) => {
543
+ Some(CargoCheckMessage::Diagnostic { diagnostic: message, package_id: None })
544
+ }
509
545
};
510
546
}
511
547
0 commit comments