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 ;
@@ -13,6 +14,7 @@ pub(crate) use cargo_metadata::diagnostic::{
13
14
Applicability , Diagnostic , DiagnosticCode , DiagnosticLevel , DiagnosticSpan ,
14
15
} ;
15
16
use toolchain:: Tool ;
17
+ use triomphe:: Arc ;
16
18
17
19
use crate :: command:: { CommandHandle , ParseFromLine } ;
18
20
@@ -151,10 +153,19 @@ impl FlycheckHandle {
151
153
152
154
pub ( crate ) enum FlycheckMessage {
153
155
/// Request adding a diagnostic with fixes included to a file
154
- AddDiagnostic { id : usize , workspace_root : AbsPathBuf , diagnostic : Diagnostic } ,
156
+ AddDiagnostic {
157
+ id : usize ,
158
+ workspace_root : Arc < AbsPathBuf > ,
159
+ diagnostic : Diagnostic ,
160
+ package_id : Option < Arc < PackageId > > ,
161
+ } ,
155
162
156
- /// Request clearing all previous diagnostics
157
- ClearDiagnostics { id : usize } ,
163
+ /// Request clearing all outdated diagnostics.
164
+ ClearDiagnostics {
165
+ id : usize ,
166
+ /// The package whose diagnostics to clear, or if unspecified, all diagnostics.
167
+ package_id : Option < Arc < PackageId > > ,
168
+ } ,
158
169
159
170
/// Request check progress notification to client
160
171
Progress {
@@ -167,15 +178,18 @@ pub(crate) enum FlycheckMessage {
167
178
impl fmt:: Debug for FlycheckMessage {
168
179
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
169
180
match self {
170
- FlycheckMessage :: AddDiagnostic { id, workspace_root, diagnostic } => f
181
+ FlycheckMessage :: AddDiagnostic { id, workspace_root, diagnostic, package_id } => f
171
182
. debug_struct ( "AddDiagnostic" )
172
183
. field ( "id" , id)
173
184
. field ( "workspace_root" , workspace_root)
185
+ . field ( "package_id" , package_id)
174
186
. field ( "diagnostic_code" , & diagnostic. code . as_ref ( ) . map ( |it| & it. code ) )
175
187
. finish ( ) ,
176
- FlycheckMessage :: ClearDiagnostics { id } => {
177
- f. debug_struct ( "ClearDiagnostics" ) . field ( "id" , id) . finish ( )
178
- }
188
+ FlycheckMessage :: ClearDiagnostics { id, package_id } => f
189
+ . debug_struct ( "ClearDiagnostics" )
190
+ . field ( "id" , id)
191
+ . field ( "package_id" , package_id)
192
+ . finish ( ) ,
179
193
FlycheckMessage :: Progress { id, progress } => {
180
194
f. debug_struct ( "Progress" ) . field ( "id" , id) . field ( "progress" , progress) . finish ( )
181
195
}
@@ -201,12 +215,13 @@ enum StateChange {
201
215
struct FlycheckActor {
202
216
/// The workspace id of this flycheck instance.
203
217
id : usize ,
218
+
204
219
sender : Sender < FlycheckMessage > ,
205
220
config : FlycheckConfig ,
206
221
manifest_path : Option < AbsPathBuf > ,
207
222
/// Either the workspace root of the workspace we are flychecking,
208
223
/// or the project root of the project.
209
- root : AbsPathBuf ,
224
+ root : Arc < AbsPathBuf > ,
210
225
sysroot_root : Option < AbsPathBuf > ,
211
226
/// CargoHandle exists to wrap around the communication needed to be able to
212
227
/// run `cargo check` without blocking. Currently the Rust standard library
@@ -216,8 +231,13 @@ struct FlycheckActor {
216
231
command_handle : Option < CommandHandle < CargoCheckMessage > > ,
217
232
/// The receiver side of the channel mentioned above.
218
233
command_receiver : Option < Receiver < CargoCheckMessage > > ,
234
+ package_status : FxHashMap < Arc < PackageId > , DiagnosticReceived > ,
235
+ }
219
236
220
- status : FlycheckStatus ,
237
+ #[ derive( PartialEq , Eq , Copy , Clone , Debug ) ]
238
+ enum DiagnosticReceived {
239
+ Yes ,
240
+ No ,
221
241
}
222
242
223
243
#[ allow( clippy:: large_enum_variant) ]
@@ -226,13 +246,6 @@ enum Event {
226
246
CheckEvent ( Option < CargoCheckMessage > ) ,
227
247
}
228
248
229
- #[ derive( PartialEq ) ]
230
- enum FlycheckStatus {
231
- Started ,
232
- DiagnosticSent ,
233
- Finished ,
234
- }
235
-
236
249
pub ( crate ) const SAVED_FILE_PLACEHOLDER : & str = "$saved_file" ;
237
250
238
251
impl FlycheckActor {
@@ -250,11 +263,11 @@ impl FlycheckActor {
250
263
sender,
251
264
config,
252
265
sysroot_root,
253
- root : workspace_root,
266
+ root : Arc :: new ( workspace_root) ,
254
267
manifest_path,
255
268
command_handle : None ,
256
269
command_receiver : None ,
257
- status : FlycheckStatus :: Finished ,
270
+ package_status : FxHashMap :: default ( ) ,
258
271
}
259
272
}
260
273
@@ -307,13 +320,11 @@ impl FlycheckActor {
307
320
self . command_handle = Some ( command_handle) ;
308
321
self . command_receiver = Some ( receiver) ;
309
322
self . report_progress ( Progress :: DidStart ) ;
310
- self . status = FlycheckStatus :: Started ;
311
323
}
312
324
Err ( error) => {
313
325
self . report_progress ( Progress :: DidFailToRestart ( format ! (
314
326
"Failed to run the following command: {formatted_command} error={error}"
315
327
) ) ) ;
316
- self . status = FlycheckStatus :: Finished ;
317
328
}
318
329
}
319
330
}
@@ -333,11 +344,25 @@ impl FlycheckActor {
333
344
error
334
345
) ;
335
346
}
336
- if self . status == FlycheckStatus :: Started {
337
- self . send ( FlycheckMessage :: ClearDiagnostics { id : self . id } ) ;
347
+ if self . package_status . is_empty ( ) {
348
+ // We finished without receiving any diagnostics.
349
+ // That means all of them are stale.
350
+ self . send ( FlycheckMessage :: ClearDiagnostics {
351
+ id : self . id ,
352
+ package_id : None ,
353
+ } ) ;
354
+ } else {
355
+ for ( package_id, status) in mem:: take ( & mut self . package_status ) {
356
+ if let DiagnosticReceived :: No = status {
357
+ self . send ( FlycheckMessage :: ClearDiagnostics {
358
+ id : self . id ,
359
+ package_id : Some ( package_id) ,
360
+ } ) ;
361
+ }
362
+ }
338
363
}
364
+
339
365
self . report_progress ( Progress :: DidFinish ( res) ) ;
340
- self . status = FlycheckStatus :: Finished ;
341
366
}
342
367
Event :: CheckEvent ( Some ( message) ) => match message {
343
368
CargoCheckMessage :: CompilerArtifact ( msg) => {
@@ -347,23 +372,32 @@ impl FlycheckActor {
347
372
"artifact received"
348
373
) ;
349
374
self . report_progress ( Progress :: DidCheckCrate ( msg. target . name ) ) ;
375
+ self . package_status
376
+ . entry ( Arc :: new ( msg. package_id ) )
377
+ . or_insert ( DiagnosticReceived :: No ) ;
350
378
}
351
-
352
- CargoCheckMessage :: Diagnostic ( msg) => {
379
+ CargoCheckMessage :: Diagnostic { diagnostic, package_id } => {
353
380
tracing:: trace!(
354
381
flycheck_id = self . id,
355
- message = msg . message,
382
+ message = diagnostic . message,
356
383
"diagnostic received"
357
384
) ;
358
- if self . status == FlycheckStatus :: Started {
359
- self . send ( FlycheckMessage :: ClearDiagnostics { id : self . id } ) ;
385
+ if let Some ( package_id) = & package_id {
386
+ if !self . package_status . contains_key ( package_id) {
387
+ self . package_status
388
+ . insert ( package_id. clone ( ) , DiagnosticReceived :: Yes ) ;
389
+ self . send ( FlycheckMessage :: ClearDiagnostics {
390
+ id : self . id ,
391
+ package_id : Some ( package_id. clone ( ) ) ,
392
+ } ) ;
393
+ }
360
394
}
361
395
self . send ( FlycheckMessage :: AddDiagnostic {
362
396
id : self . id ,
397
+ package_id,
363
398
workspace_root : self . root . clone ( ) ,
364
- diagnostic : msg ,
399
+ diagnostic,
365
400
} ) ;
366
- self . status = FlycheckStatus :: DiagnosticSent ;
367
401
}
368
402
} ,
369
403
}
@@ -381,7 +415,7 @@ impl FlycheckActor {
381
415
command_handle. cancel ( ) ;
382
416
self . command_receiver . take ( ) ;
383
417
self . report_progress ( Progress :: DidCancel ) ;
384
- self . status = FlycheckStatus :: Finished ;
418
+ self . package_status . clear ( ) ;
385
419
}
386
420
}
387
421
@@ -401,7 +435,7 @@ impl FlycheckActor {
401
435
cmd. env ( "RUSTUP_TOOLCHAIN" , AsRef :: < std:: path:: Path > :: as_ref ( sysroot_root) ) ;
402
436
}
403
437
cmd. arg ( command) ;
404
- cmd. current_dir ( & self . root ) ;
438
+ cmd. current_dir ( & * self . root ) ;
405
439
406
440
match package {
407
441
Some ( pkg) => cmd. arg ( "-p" ) . arg ( pkg) ,
@@ -443,11 +477,11 @@ impl FlycheckActor {
443
477
444
478
match invocation_strategy {
445
479
InvocationStrategy :: Once => {
446
- cmd. current_dir ( & self . root ) ;
480
+ cmd. current_dir ( & * self . root ) ;
447
481
}
448
482
InvocationStrategy :: PerWorkspace => {
449
483
// FIXME: cmd.current_dir(&affected_workspace);
450
- cmd. current_dir ( & self . root ) ;
484
+ cmd. current_dir ( & * self . root ) ;
451
485
}
452
486
}
453
487
@@ -487,7 +521,7 @@ impl FlycheckActor {
487
521
#[ allow( clippy:: large_enum_variant) ]
488
522
enum CargoCheckMessage {
489
523
CompilerArtifact ( cargo_metadata:: Artifact ) ,
490
- Diagnostic ( Diagnostic ) ,
524
+ Diagnostic { diagnostic : Diagnostic , package_id : Option < Arc < PackageId > > } ,
491
525
}
492
526
493
527
impl ParseFromLine for CargoCheckMessage {
@@ -502,11 +536,16 @@ impl ParseFromLine for CargoCheckMessage {
502
536
Some ( CargoCheckMessage :: CompilerArtifact ( artifact) )
503
537
}
504
538
cargo_metadata:: Message :: CompilerMessage ( msg) => {
505
- Some ( CargoCheckMessage :: Diagnostic ( msg. message ) )
539
+ Some ( CargoCheckMessage :: Diagnostic {
540
+ diagnostic : msg. message ,
541
+ package_id : Some ( Arc :: new ( msg. package_id ) ) ,
542
+ } )
506
543
}
507
544
_ => None ,
508
545
} ,
509
- JsonMessage :: Rustc ( message) => Some ( CargoCheckMessage :: Diagnostic ( message) ) ,
546
+ JsonMessage :: Rustc ( message) => {
547
+ Some ( CargoCheckMessage :: Diagnostic { diagnostic : message, package_id : None } )
548
+ }
510
549
} ;
511
550
}
512
551
0 commit comments