@@ -252,6 +252,7 @@ impl Project {
252
252
. map ( IOErrorDiagnostic :: to_diagnostic) ,
253
253
) ;
254
254
255
+ let open_files = self . open_files ( db) ;
255
256
let check_start = ruff_db:: Instant :: now ( ) ;
256
257
let file_diagnostics = std:: sync:: Mutex :: new ( vec ! [ ] ) ;
257
258
@@ -269,11 +270,30 @@ impl Project {
269
270
tracing:: debug_span!( parent: project_span, "check_file" , ?file) ;
270
271
let _entered = check_file_span. entered ( ) ;
271
272
272
- let result = check_file_impl ( & db, file) ;
273
- file_diagnostics
274
- . lock ( )
275
- . unwrap ( )
276
- . extend ( result. iter ( ) . map ( Clone :: clone) ) ;
273
+ match check_file_impl ( & db, file) {
274
+ Ok ( diagnostics) => {
275
+ file_diagnostics
276
+ . lock ( )
277
+ . unwrap ( )
278
+ . extend ( diagnostics. iter ( ) . map ( Clone :: clone) ) ;
279
+
280
+ // This is outside `check_file_impl` to avoid that opening or closing
281
+ // a file invalidates the `check_file_impl` query of every file!
282
+ if !open_files. contains ( & file) {
283
+ // The module has already been parsed by `check_file_impl`.
284
+ // We only retrieve it here so that we can call `clear` on it.
285
+ let parsed = parsed_module ( & db, file) ;
286
+
287
+ // Drop the AST now that we are done checking this file. It is not currently open,
288
+ // so it is unlikely to be accessed again soon. If any queries need to access the AST
289
+ // from across files, it will be re-parsed.
290
+ parsed. clear ( ) ;
291
+ }
292
+ }
293
+ Err ( io_error) => {
294
+ file_diagnostics. lock ( ) . unwrap ( ) . push ( io_error. clone ( ) ) ;
295
+ }
296
+ }
277
297
278
298
reporter. report_file ( & file) ;
279
299
} ) ;
@@ -300,7 +320,10 @@ impl Project {
300
320
return Vec :: new ( ) ;
301
321
}
302
322
303
- check_file_impl ( db, file) . iter ( ) . map ( Clone :: clone) . collect ( )
323
+ match check_file_impl ( db, file) {
324
+ Ok ( diagnostics) => diagnostics. to_vec ( ) ,
325
+ Err ( diagnostic) => vec ! [ diagnostic. clone( ) ] ,
326
+ }
304
327
}
305
328
306
329
/// Opens a file in the project.
@@ -484,22 +507,19 @@ impl Project {
484
507
}
485
508
}
486
509
487
- #[ salsa:: tracked( returns( deref ) , heap_size=get_size2:: GetSize :: get_heap_size) ]
488
- pub ( crate ) fn check_file_impl ( db : & dyn Db , file : File ) -> Box < [ Diagnostic ] > {
510
+ #[ salsa:: tracked( returns( ref ) , heap_size=get_size2:: GetSize :: get_heap_size) ]
511
+ pub ( crate ) fn check_file_impl ( db : & dyn Db , file : File ) -> Result < Box < [ Diagnostic ] > , Diagnostic > {
489
512
let mut diagnostics: Vec < Diagnostic > = Vec :: new ( ) ;
490
513
491
514
// Abort checking if there are IO errors.
492
515
let source = source_text ( db, file) ;
493
516
494
517
if let Some ( read_error) = source. read_error ( ) {
495
- diagnostics. push (
496
- IOErrorDiagnostic {
497
- file : Some ( file) ,
498
- error : read_error. clone ( ) . into ( ) ,
499
- }
500
- . to_diagnostic ( ) ,
501
- ) ;
502
- return diagnostics. into_boxed_slice ( ) ;
518
+ return Err ( IOErrorDiagnostic {
519
+ file : Some ( file) ,
520
+ error : read_error. clone ( ) . into ( ) ,
521
+ }
522
+ . to_diagnostic ( ) ) ;
503
523
}
504
524
505
525
let parsed = parsed_module ( db, file) ;
@@ -529,13 +549,6 @@ pub(crate) fn check_file_impl(db: &dyn Db, file: File) -> Box<[Diagnostic]> {
529
549
}
530
550
}
531
551
532
- if !db. project ( ) . open_fileset ( db) . contains ( & file) {
533
- // Drop the AST now that we are done checking this file. It is not currently open,
534
- // so it is unlikely to be accessed again soon. If any queries need to access the AST
535
- // from across files, it will be re-parsed.
536
- parsed. clear ( ) ;
537
- }
538
-
539
552
diagnostics. sort_unstable_by_key ( |diagnostic| {
540
553
diagnostic
541
554
. primary_span ( )
@@ -544,7 +557,7 @@ pub(crate) fn check_file_impl(db: &dyn Db, file: File) -> Box<[Diagnostic]> {
544
557
. start ( )
545
558
} ) ;
546
559
547
- diagnostics. into_boxed_slice ( )
560
+ Ok ( diagnostics. into_boxed_slice ( ) )
548
561
}
549
562
550
563
#[ derive( Debug ) ]
@@ -762,10 +775,11 @@ mod tests {
762
775
assert_eq ! ( source_text( & db, file) . as_str( ) , "" ) ;
763
776
assert_eq ! (
764
777
check_file_impl( & db, file)
765
- . iter( )
766
- . map( |diagnostic| diagnostic. primary_message( ) . to_string( ) )
767
- . collect:: <Vec <_>>( ) ,
768
- vec![ "Failed to read file: No such file or directory" . to_string( ) ]
778
+ . as_ref( )
779
+ . unwrap_err( )
780
+ . primary_message( )
781
+ . to_string( ) ,
782
+ "Failed to read file: No such file or directory" . to_string( )
769
783
) ;
770
784
771
785
let events = db. take_salsa_events ( ) ;
@@ -778,6 +792,8 @@ mod tests {
778
792
assert_eq ! ( source_text( & db, file) . as_str( ) , "" ) ;
779
793
assert_eq ! (
780
794
check_file_impl( & db, file)
795
+ . as_ref( )
796
+ . unwrap( )
781
797
. iter( )
782
798
. map( |diagnostic| diagnostic. primary_message( ) . to_string( ) )
783
799
. collect:: <Vec <_>>( ) ,
0 commit comments