Skip to content

Commit 926e833

Browse files
authored
[ty] Avoid rechecking the entire project when changing the opened files (#19463)
1 parent 5cace28 commit 926e833

File tree

1 file changed

+44
-28
lines changed

1 file changed

+44
-28
lines changed

crates/ty_project/src/lib.rs

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ impl Project {
252252
.map(IOErrorDiagnostic::to_diagnostic),
253253
);
254254

255+
let open_files = self.open_files(db);
255256
let check_start = ruff_db::Instant::now();
256257
let file_diagnostics = std::sync::Mutex::new(vec![]);
257258

@@ -269,11 +270,30 @@ impl Project {
269270
tracing::debug_span!(parent: project_span, "check_file", ?file);
270271
let _entered = check_file_span.entered();
271272

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+
}
277297

278298
reporter.report_file(&file);
279299
});
@@ -300,7 +320,10 @@ impl Project {
300320
return Vec::new();
301321
}
302322

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+
}
304327
}
305328

306329
/// Opens a file in the project.
@@ -484,22 +507,19 @@ impl Project {
484507
}
485508
}
486509

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> {
489512
let mut diagnostics: Vec<Diagnostic> = Vec::new();
490513

491514
// Abort checking if there are IO errors.
492515
let source = source_text(db, file);
493516

494517
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());
503523
}
504524

505525
let parsed = parsed_module(db, file);
@@ -529,13 +549,6 @@ pub(crate) fn check_file_impl(db: &dyn Db, file: File) -> Box<[Diagnostic]> {
529549
}
530550
}
531551

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-
539552
diagnostics.sort_unstable_by_key(|diagnostic| {
540553
diagnostic
541554
.primary_span()
@@ -544,7 +557,7 @@ pub(crate) fn check_file_impl(db: &dyn Db, file: File) -> Box<[Diagnostic]> {
544557
.start()
545558
});
546559

547-
diagnostics.into_boxed_slice()
560+
Ok(diagnostics.into_boxed_slice())
548561
}
549562

550563
#[derive(Debug)]
@@ -762,10 +775,11 @@ mod tests {
762775
assert_eq!(source_text(&db, file).as_str(), "");
763776
assert_eq!(
764777
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()
769783
);
770784

771785
let events = db.take_salsa_events();
@@ -778,6 +792,8 @@ mod tests {
778792
assert_eq!(source_text(&db, file).as_str(), "");
779793
assert_eq!(
780794
check_file_impl(&db, file)
795+
.as_ref()
796+
.unwrap()
781797
.iter()
782798
.map(|diagnostic| diagnostic.primary_message().to_string())
783799
.collect::<Vec<_>>(),

0 commit comments

Comments
 (0)