Auto merge of #17775 - ShoyuVanilla:segregate-diags, r=Veykril

perf: Segregate syntax and semantic diagnostics

Closes #17731
This commit is contained in:
bors 2024-08-05 13:39:30 +00:00
commit f62d7b9f11
9 changed files with 255 additions and 146 deletions

View file

@ -96,6 +96,7 @@ use syntax::{
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum DiagnosticCode { pub enum DiagnosticCode {
RustcHardError(&'static str), RustcHardError(&'static str),
SyntaxError,
RustcLint(&'static str), RustcLint(&'static str),
Clippy(&'static str), Clippy(&'static str),
Ra(&'static str, Severity), Ra(&'static str, Severity),
@ -107,6 +108,9 @@ impl DiagnosticCode {
DiagnosticCode::RustcHardError(e) => { DiagnosticCode::RustcHardError(e) => {
format!("https://doc.rust-lang.org/stable/error_codes/{e}.html") format!("https://doc.rust-lang.org/stable/error_codes/{e}.html")
} }
DiagnosticCode::SyntaxError => {
String::from("https://doc.rust-lang.org/stable/reference/")
}
DiagnosticCode::RustcLint(e) => { DiagnosticCode::RustcLint(e) => {
format!("https://doc.rust-lang.org/rustc/?search={e}") format!("https://doc.rust-lang.org/rustc/?search={e}")
} }
@ -125,6 +129,7 @@ impl DiagnosticCode {
| DiagnosticCode::RustcLint(r) | DiagnosticCode::RustcLint(r)
| DiagnosticCode::Clippy(r) | DiagnosticCode::Clippy(r)
| DiagnosticCode::Ra(r, _) => r, | DiagnosticCode::Ra(r, _) => r,
DiagnosticCode::SyntaxError => "syntax-error",
} }
} }
} }
@ -154,7 +159,7 @@ impl Diagnostic {
message, message,
range: range.into(), range: range.into(),
severity: match code { severity: match code {
DiagnosticCode::RustcHardError(_) => Severity::Error, DiagnosticCode::RustcHardError(_) | DiagnosticCode::SyntaxError => Severity::Error,
// FIXME: Rustc lints are not always warning, but the ones that are currently implemented are all warnings. // FIXME: Rustc lints are not always warning, but the ones that are currently implemented are all warnings.
DiagnosticCode::RustcLint(_) => Severity::Warning, DiagnosticCode::RustcLint(_) => Severity::Warning,
// FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can // FIXME: We can make this configurable, and if the user uses `cargo clippy` on flycheck, we can
@ -297,31 +302,54 @@ impl DiagnosticsContext<'_> {
} }
} }
/// Request diagnostics for the given [`FileId`]. The produced diagnostics may point to other files /// Request parser level diagnostics for the given [`FileId`].
pub fn syntax_diagnostics(
db: &RootDatabase,
config: &DiagnosticsConfig,
file_id: FileId,
) -> Vec<Diagnostic> {
let _p = tracing::info_span!("syntax_diagnostics").entered();
if config.disabled.contains("syntax-error") {
return Vec::new();
}
let sema = Semantics::new(db);
let file_id = sema
.attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
// [#3434] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
db.parse_errors(file_id)
.as_deref()
.into_iter()
.flatten()
.take(128)
.map(|err| {
Diagnostic::new(
DiagnosticCode::SyntaxError,
format!("Syntax Error: {err}"),
FileRange { file_id: file_id.into(), range: err.range() },
)
})
.collect()
}
/// Request semantic diagnostics for the given [`FileId`]. The produced diagnostics may point to other files
/// due to macros. /// due to macros.
pub fn diagnostics( pub fn semantic_diagnostics(
db: &RootDatabase, db: &RootDatabase,
config: &DiagnosticsConfig, config: &DiagnosticsConfig,
resolve: &AssistResolveStrategy, resolve: &AssistResolveStrategy,
file_id: FileId, file_id: FileId,
) -> Vec<Diagnostic> { ) -> Vec<Diagnostic> {
let _p = tracing::info_span!("diagnostics").entered(); let _p = tracing::info_span!("semantic_diagnostics").entered();
let sema = Semantics::new(db); let sema = Semantics::new(db);
let file_id = sema let file_id = sema
.attach_first_edition(file_id) .attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id)); .unwrap_or_else(|| EditionedFileId::current_edition(file_id));
let mut res = Vec::new(); let mut res = Vec::new();
// [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
res.extend(db.parse_errors(file_id).as_deref().into_iter().flatten().take(128).map(|err| {
Diagnostic::new(
DiagnosticCode::RustcHardError("syntax-error"),
format!("Syntax Error: {err}"),
FileRange { file_id: file_id.into(), range: err.range() },
)
}));
let parse_errors = res.len();
let parse = sema.parse(file_id); let parse = sema.parse(file_id);
// FIXME: This iterates the entire file which is a rather expensive operation. // FIXME: This iterates the entire file which is a rather expensive operation.
@ -341,8 +369,11 @@ pub fn diagnostics(
match module { match module {
// A bunch of parse errors in a file indicate some bigger structural parse changes in the // A bunch of parse errors in a file indicate some bigger structural parse changes in the
// file, so we skip semantic diagnostics so we can show these faster. // file, so we skip semantic diagnostics so we can show these faster.
Some(m) if parse_errors < 16 => m.diagnostics(db, &mut diags, config.style_lints), Some(m) => {
Some(_) => (), if !db.parse_errors(file_id).as_deref().is_some_and(|es| es.len() >= 16) {
m.diagnostics(db, &mut diags, config.style_lints);
}
}
None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id.file_id()), None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id.file_id()),
} }
@ -363,7 +394,7 @@ pub fn diagnostics(
res.extend(d.errors.iter().take(16).map(|err| { res.extend(d.errors.iter().take(16).map(|err| {
{ {
Diagnostic::new( Diagnostic::new(
DiagnosticCode::RustcHardError("syntax-error"), DiagnosticCode::SyntaxError,
format!("Syntax Error in Expansion: {err}"), format!("Syntax Error in Expansion: {err}"),
ctx.resolve_precise_location(&d.node.clone(), d.precise_location), ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
) )
@ -464,6 +495,19 @@ pub fn diagnostics(
res res
} }
/// Request both syntax and semantic diagnostics for the given [`FileId`].
pub fn full_diagnostics(
db: &RootDatabase,
config: &DiagnosticsConfig,
resolve: &AssistResolveStrategy,
file_id: FileId,
) -> Vec<Diagnostic> {
let mut res = syntax_diagnostics(db, config, file_id);
let sema = semantic_diagnostics(db, config, resolve, file_id);
res.extend(sema);
res
}
// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros // `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros
static RUSTC_LINT_GROUPS_DICT: Lazy<FxHashMap<&str, Vec<&str>>> = static RUSTC_LINT_GROUPS_DICT: Lazy<FxHashMap<&str, Vec<&str>>> =

View file

@ -59,10 +59,14 @@ fn check_nth_fix_with_config(
let after = trim_indent(ra_fixture_after); let after = trim_indent(ra_fixture_after);
let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
let diagnostic = let diagnostic = super::full_diagnostics(
super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_position.file_id.into()) &db,
.pop() &config,
.expect("no diagnostics"); &AssistResolveStrategy::All,
file_position.file_id.into(),
)
.pop()
.expect("no diagnostics");
let fix = &diagnostic let fix = &diagnostic
.fixes .fixes
.unwrap_or_else(|| panic!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; .unwrap_or_else(|| panic!("{:?} diagnostic misses fixes", diagnostic.code))[nth];
@ -102,37 +106,39 @@ pub(crate) fn check_has_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
let mut conf = DiagnosticsConfig::test_sample(); let mut conf = DiagnosticsConfig::test_sample();
conf.expr_fill_default = ExprFillDefaultMode::Default; conf.expr_fill_default = ExprFillDefaultMode::Default;
let fix = let fix = super::full_diagnostics(
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into()) &db,
.into_iter() &conf,
.find(|d| { &AssistResolveStrategy::All,
d.fixes file_position.file_id.into(),
.as_ref() )
.and_then(|fixes| { .into_iter()
fixes.iter().find(|fix| { .find(|d| {
if !fix.target.contains_inclusive(file_position.offset) { d.fixes
return false; .as_ref()
} .and_then(|fixes| {
let actual = { fixes.iter().find(|fix| {
let source_change = fix.source_change.as_ref().unwrap(); if !fix.target.contains_inclusive(file_position.offset) {
let file_id = return false;
*source_change.source_file_edits.keys().next().unwrap(); }
let mut actual = db.file_text(file_id).to_string(); let actual = {
let source_change = fix.source_change.as_ref().unwrap();
let file_id = *source_change.source_file_edits.keys().next().unwrap();
let mut actual = db.file_text(file_id).to_string();
for (edit, snippet_edit) in source_change.source_file_edits.values() for (edit, snippet_edit) in source_change.source_file_edits.values() {
{ edit.apply(&mut actual);
edit.apply(&mut actual); if let Some(snippet_edit) = snippet_edit {
if let Some(snippet_edit) = snippet_edit { snippet_edit.apply(&mut actual);
snippet_edit.apply(&mut actual); }
} }
} actual
actual };
}; after == actual
after == actual })
}) })
}) .is_some()
.is_some() });
});
assert!(fix.is_some(), "no diagnostic with desired fix"); assert!(fix.is_some(), "no diagnostic with desired fix");
} }
@ -144,38 +150,40 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s
let mut conf = DiagnosticsConfig::test_sample(); let mut conf = DiagnosticsConfig::test_sample();
conf.expr_fill_default = ExprFillDefaultMode::Default; conf.expr_fill_default = ExprFillDefaultMode::Default;
let mut n_fixes = 0; let mut n_fixes = 0;
let fix = let fix = super::full_diagnostics(
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into()) &db,
.into_iter() &conf,
.find(|d| { &AssistResolveStrategy::All,
d.fixes file_position.file_id.into(),
.as_ref() )
.and_then(|fixes| { .into_iter()
n_fixes += fixes.len(); .find(|d| {
fixes.iter().find(|fix| { d.fixes
if !fix.target.contains_inclusive(file_position.offset) { .as_ref()
return false; .and_then(|fixes| {
} n_fixes += fixes.len();
let actual = { fixes.iter().find(|fix| {
let source_change = fix.source_change.as_ref().unwrap(); if !fix.target.contains_inclusive(file_position.offset) {
let file_id = return false;
*source_change.source_file_edits.keys().next().unwrap(); }
let mut actual = db.file_text(file_id).to_string(); let actual = {
let source_change = fix.source_change.as_ref().unwrap();
let file_id = *source_change.source_file_edits.keys().next().unwrap();
let mut actual = db.file_text(file_id).to_string();
for (edit, snippet_edit) in source_change.source_file_edits.values() for (edit, snippet_edit) in source_change.source_file_edits.values() {
{ edit.apply(&mut actual);
edit.apply(&mut actual); if let Some(snippet_edit) = snippet_edit {
if let Some(snippet_edit) = snippet_edit { snippet_edit.apply(&mut actual);
snippet_edit.apply(&mut actual); }
} }
} actual
actual };
}; after == actual
after == actual })
}) })
}) .is_some()
.is_some() });
});
assert!(fix.is_some(), "no diagnostic with desired fix"); assert!(fix.is_some(), "no diagnostic with desired fix");
assert!(n_fixes == 1, "Too many fixes suggested"); assert!(n_fixes == 1, "Too many fixes suggested");
} }
@ -183,7 +191,7 @@ pub(crate) fn check_has_single_fix(ra_fixture_before: &str, ra_fixture_after: &s
/// Checks that there's a diagnostic *without* fix at `$0`. /// Checks that there's a diagnostic *without* fix at `$0`.
pub(crate) fn check_no_fix(ra_fixture: &str) { pub(crate) fn check_no_fix(ra_fixture: &str) {
let (db, file_position) = RootDatabase::with_position(ra_fixture); let (db, file_position) = RootDatabase::with_position(ra_fixture);
let diagnostic = super::diagnostics( let diagnostic = super::full_diagnostics(
&db, &db,
&DiagnosticsConfig::test_sample(), &DiagnosticsConfig::test_sample(),
&AssistResolveStrategy::All, &AssistResolveStrategy::All,
@ -215,7 +223,7 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
.iter() .iter()
.copied() .copied()
.flat_map(|file_id| { .flat_map(|file_id| {
super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.into()) super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.into())
.into_iter() .into_iter()
.map(|d| { .map(|d| {
let mut annotation = String::new(); let mut annotation = String::new();
@ -277,10 +285,10 @@ fn test_disabled_diagnostics() {
let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#);
let file_id = file_id.into(); let file_id = file_id.into();
let diagnostics = super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); let diagnostics = super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id);
assert!(diagnostics.is_empty()); assert!(diagnostics.is_empty());
let diagnostics = super::diagnostics( let diagnostics = super::full_diagnostics(
&db, &db,
&DiagnosticsConfig::test_sample(), &DiagnosticsConfig::test_sample(),
&AssistResolveStrategy::All, &AssistResolveStrategy::All,

View file

@ -672,14 +672,33 @@ impl Analysis {
.unwrap_or_default()) .unwrap_or_default())
} }
/// Computes the set of diagnostics for the given file. /// Computes the set of parser level diagnostics for the given file.
pub fn diagnostics( pub fn syntax_diagnostics(
&self,
config: &DiagnosticsConfig,
file_id: FileId,
) -> Cancellable<Vec<Diagnostic>> {
self.with_db(|db| ide_diagnostics::syntax_diagnostics(db, config, file_id))
}
/// Computes the set of semantic diagnostics for the given file.
pub fn semantic_diagnostics(
&self, &self,
config: &DiagnosticsConfig, config: &DiagnosticsConfig,
resolve: AssistResolveStrategy, resolve: AssistResolveStrategy,
file_id: FileId, file_id: FileId,
) -> Cancellable<Vec<Diagnostic>> { ) -> Cancellable<Vec<Diagnostic>> {
self.with_db(|db| ide_diagnostics::diagnostics(db, config, &resolve, file_id)) self.with_db(|db| ide_diagnostics::semantic_diagnostics(db, config, &resolve, file_id))
}
/// Computes the set of both syntax and semantic diagnostics for the given file.
pub fn full_diagnostics(
&self,
config: &DiagnosticsConfig,
resolve: AssistResolveStrategy,
file_id: FileId,
) -> Cancellable<Vec<Diagnostic>> {
self.with_db(|db| ide_diagnostics::full_diagnostics(db, config, &resolve, file_id))
} }
/// Convenience function to return assists + quick fixes for diagnostics /// Convenience function to return assists + quick fixes for diagnostics
@ -697,7 +716,7 @@ impl Analysis {
self.with_db(|db| { self.with_db(|db| {
let diagnostic_assists = if diagnostics_config.enabled && include_fixes { let diagnostic_assists = if diagnostics_config.enabled && include_fixes {
ide_diagnostics::diagnostics(db, diagnostics_config, &resolve, frange.file_id) ide_diagnostics::full_diagnostics(db, diagnostics_config, &resolve, frange.file_id)
.into_iter() .into_iter()
.flat_map(|it| it.fixes.unwrap_or_default()) .flat_map(|it| it.fixes.unwrap_or_default())
.filter(|it| it.target.intersect(frange.range).is_some()) .filter(|it| it.target.intersect(frange.range).is_some())

View file

@ -976,7 +976,7 @@ impl flags::AnalysisStats {
let mut sw = self.stop_watch(); let mut sw = self.stop_watch();
for &file_id in &file_ids { for &file_id in &file_ids {
_ = analysis.diagnostics( _ = analysis.full_diagnostics(
&DiagnosticsConfig { &DiagnosticsConfig {
enabled: true, enabled: true,
proc_macros_enabled: true, proc_macros_enabled: true,

View file

@ -63,7 +63,7 @@ impl flags::Diagnostics {
_vfs.file_path(file_id.into()) _vfs.file_path(file_id.into())
); );
for diagnostic in analysis for diagnostic in analysis
.diagnostics( .full_diagnostics(
&DiagnosticsConfig::test_sample(), &DiagnosticsConfig::test_sample(),
AssistResolveStrategy::None, AssistResolveStrategy::None,
file_id.into(), file_id.into(),

View file

@ -155,7 +155,7 @@ impl Tester {
let root_file = self.root_file; let root_file = self.root_file;
move || { move || {
let res = std::panic::catch_unwind(move || { let res = std::panic::catch_unwind(move || {
analysis.diagnostics( analysis.full_diagnostics(
diagnostic_config, diagnostic_config,
ide::AssistResolveStrategy::None, ide::AssistResolveStrategy::None,
root_file, root_file,

View file

@ -11,7 +11,7 @@ use rustc_hash::FxHashSet;
use stdx::iter_eq_by; use stdx::iter_eq_by;
use triomphe::Arc; use triomphe::Arc;
use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext}; use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind};
pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>; pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>;
@ -28,7 +28,8 @@ pub(crate) type DiagnosticsGeneration = usize;
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub(crate) struct DiagnosticCollection { pub(crate) struct DiagnosticCollection {
// FIXME: should be IntMap<FileId, Vec<ra_id::Diagnostic>> // FIXME: should be IntMap<FileId, Vec<ra_id::Diagnostic>>
pub(crate) native: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>, pub(crate) native_syntax: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>,
pub(crate) native_semantic: IntMap<FileId, (DiagnosticsGeneration, Vec<lsp_types::Diagnostic>)>,
// FIXME: should be Vec<flycheck::Diagnostic> // FIXME: should be Vec<flycheck::Diagnostic>
pub(crate) check: IntMap<usize, IntMap<FileId, Vec<lsp_types::Diagnostic>>>, pub(crate) check: IntMap<usize, IntMap<FileId, Vec<lsp_types::Diagnostic>>>,
pub(crate) check_fixes: CheckFixes, pub(crate) check_fixes: CheckFixes,
@ -64,7 +65,8 @@ impl DiagnosticCollection {
} }
pub(crate) fn clear_native_for(&mut self, file_id: FileId) { pub(crate) fn clear_native_for(&mut self, file_id: FileId) {
self.native.remove(&file_id); self.native_syntax.remove(&file_id);
self.native_semantic.remove(&file_id);
self.changes.insert(file_id); self.changes.insert(file_id);
} }
@ -88,43 +90,51 @@ impl DiagnosticCollection {
self.changes.insert(file_id); self.changes.insert(file_id);
} }
pub(crate) fn set_native_diagnostics( pub(crate) fn set_native_diagnostics(&mut self, kind: DiagnosticsTaskKind) {
&mut self, let (generation, diagnostics, target) = match kind {
generation: DiagnosticsGeneration, DiagnosticsTaskKind::Syntax(generation, diagnostics) => {
file_id: FileId, (generation, diagnostics, &mut self.native_syntax)
mut diagnostics: Vec<lsp_types::Diagnostic>,
) {
diagnostics.sort_by_key(|it| (it.range.start, it.range.end));
if let Some((old_gen, existing_diagnostics)) = self.native.get_mut(&file_id) {
if existing_diagnostics.len() == diagnostics.len()
&& iter_eq_by(&diagnostics, &*existing_diagnostics, |new, existing| {
are_diagnostics_equal(new, existing)
})
{
// don't signal an update if the diagnostics are the same
return;
} }
if *old_gen < generation || generation == 0 { DiagnosticsTaskKind::Semantic(generation, diagnostics) => {
self.native.insert(file_id, (generation, diagnostics)); (generation, diagnostics, &mut self.native_semantic)
}
};
for (file_id, mut diagnostics) in diagnostics {
diagnostics.sort_by_key(|it| (it.range.start, it.range.end));
if let Some((old_gen, existing_diagnostics)) = target.get_mut(&file_id) {
if existing_diagnostics.len() == diagnostics.len()
&& iter_eq_by(&diagnostics, &*existing_diagnostics, |new, existing| {
are_diagnostics_equal(new, existing)
})
{
// don't signal an update if the diagnostics are the same
return;
}
if *old_gen < generation || generation == 0 {
target.insert(file_id, (generation, diagnostics));
} else {
existing_diagnostics.extend(diagnostics);
// FIXME: Doing the merge step of a merge sort here would be a bit more performant
// but eh
existing_diagnostics.sort_by_key(|it| (it.range.start, it.range.end))
}
} else { } else {
existing_diagnostics.extend(diagnostics); target.insert(file_id, (generation, diagnostics));
// FIXME: Doing the merge step of a merge sort here would be a bit more performant
// but eh
existing_diagnostics.sort_by_key(|it| (it.range.start, it.range.end))
} }
} else { self.changes.insert(file_id);
self.native.insert(file_id, (generation, diagnostics));
} }
self.changes.insert(file_id);
} }
pub(crate) fn diagnostics_for( pub(crate) fn diagnostics_for(
&self, &self,
file_id: FileId, file_id: FileId,
) -> impl Iterator<Item = &lsp_types::Diagnostic> { ) -> impl Iterator<Item = &lsp_types::Diagnostic> {
let native = self.native.get(&file_id).into_iter().flat_map(|(_, d)| d); let native_syntax = self.native_syntax.get(&file_id).into_iter().flat_map(|(_, d)| d);
let native_semantic = self.native_semantic.get(&file_id).into_iter().flat_map(|(_, d)| d);
let check = self.check.values().filter_map(move |it| it.get(&file_id)).flatten(); let check = self.check.values().filter_map(move |it| it.get(&file_id)).flatten();
native.chain(check) native_syntax.chain(native_semantic).chain(check)
} }
pub(crate) fn take_changes(&mut self) -> Option<IntSet<FileId>> { pub(crate) fn take_changes(&mut self) -> Option<IntSet<FileId>> {
@ -147,10 +157,16 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno
&& left.message == right.message && left.message == right.message
} }
pub(crate) enum NativeDiagnosticsFetchKind {
Syntax,
Semantic,
}
pub(crate) fn fetch_native_diagnostics( pub(crate) fn fetch_native_diagnostics(
snapshot: GlobalStateSnapshot, snapshot: &GlobalStateSnapshot,
subscriptions: std::sync::Arc<[FileId]>, subscriptions: std::sync::Arc<[FileId]>,
slice: std::ops::Range<usize>, slice: std::ops::Range<usize>,
kind: NativeDiagnosticsFetchKind,
) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> { ) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> {
let _p = tracing::info_span!("fetch_native_diagnostics").entered(); let _p = tracing::info_span!("fetch_native_diagnostics").entered();
let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned()); let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
@ -180,14 +196,17 @@ pub(crate) fn fetch_native_diagnostics(
let line_index = snapshot.file_line_index(file_id).ok()?; let line_index = snapshot.file_line_index(file_id).ok()?;
let source_root = snapshot.analysis.source_root_id(file_id).ok()?; let source_root = snapshot.analysis.source_root_id(file_id).ok()?;
let diagnostics = snapshot let config = &snapshot.config.diagnostics(Some(source_root));
.analysis let diagnostics = match kind {
.diagnostics( NativeDiagnosticsFetchKind::Syntax => {
&snapshot.config.diagnostics(Some(source_root)), snapshot.analysis.syntax_diagnostics(config, file_id).ok()?
ide::AssistResolveStrategy::None, }
file_id, NativeDiagnosticsFetchKind::Semantic => snapshot
) .analysis
.ok()? .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id)
.ok()?,
};
let diagnostics = diagnostics
.into_iter() .into_iter()
.filter_map(|d| { .filter_map(|d| {
if d.range.file_id == file_id { if d.range.file_id == file_id {

View file

@ -325,7 +325,7 @@ fn integrated_diagnostics_benchmark() {
term_search_borrowck: true, term_search_borrowck: true,
}; };
host.analysis() host.analysis()
.diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) .full_diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
.unwrap(); .unwrap();
let _g = crate::tracing::hprof::init("*"); let _g = crate::tracing::hprof::init("*");
@ -343,7 +343,7 @@ fn integrated_diagnostics_benchmark() {
let _p = tracing::info_span!("diagnostics").entered(); let _p = tracing::info_span!("diagnostics").entered();
let _span = profile::cpu_span(); let _span = profile::cpu_span();
host.analysis() host.analysis()
.diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id) .full_diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
.unwrap(); .unwrap();
} }
} }

View file

@ -19,7 +19,7 @@ use vfs::{AbsPathBuf, FileId};
use crate::{ use crate::{
config::Config, config::Config,
diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration}, diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind},
dispatch::{NotificationDispatcher, RequestDispatcher}, dispatch::{NotificationDispatcher, RequestDispatcher},
global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState}, global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState},
hack_recover_crate_name, hack_recover_crate_name,
@ -86,12 +86,18 @@ pub(crate) enum QueuedTask {
CheckProcMacroSources(Vec<FileId>), CheckProcMacroSources(Vec<FileId>),
} }
#[derive(Debug)]
pub(crate) enum DiagnosticsTaskKind {
Syntax(DiagnosticsGeneration, Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
Semantic(DiagnosticsGeneration, Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum Task { pub(crate) enum Task {
Response(lsp_server::Response), Response(lsp_server::Response),
DiscoverLinkedProjects(DiscoverProjectParam), DiscoverLinkedProjects(DiscoverProjectParam),
Retry(lsp_server::Request), Retry(lsp_server::Request),
Diagnostics(DiagnosticsGeneration, Vec<(FileId, Vec<lsp_types::Diagnostic>)>), Diagnostics(DiagnosticsTaskKind),
DiscoverTest(lsp_ext::DiscoverTestResults), DiscoverTest(lsp_ext::DiscoverTestResults),
PrimeCaches(PrimeCachesProgress), PrimeCaches(PrimeCachesProgress),
FetchWorkspace(ProjectWorkspaceProgress), FetchWorkspace(ProjectWorkspaceProgress),
@ -549,14 +555,29 @@ impl GlobalState {
} }
// Diagnostics are triggered by the user typing // Diagnostics are triggered by the user typing
// so we run them on a latency sensitive thread. // so we run them on a latency sensitive thread.
self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, { let snapshot = self.snapshot();
let snapshot = self.snapshot(); self.task_pool.handle.spawn_with_sender(ThreadIntent::LatencySensitive, {
let subscriptions = subscriptions.clone(); let subscriptions = subscriptions.clone();
move || { move |sender| {
Task::Diagnostics( let diags = fetch_native_diagnostics(
generation, &snapshot,
fetch_native_diagnostics(snapshot, subscriptions, slice), subscriptions.clone(),
) slice.clone(),
NativeDiagnosticsFetchKind::Syntax,
);
sender
.send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags)))
.unwrap();
let diags = fetch_native_diagnostics(
&snapshot,
subscriptions,
slice,
NativeDiagnosticsFetchKind::Semantic,
);
sender
.send(Task::Diagnostics(DiagnosticsTaskKind::Semantic(generation, diags)))
.unwrap();
} }
}); });
start = end; start = end;
@ -644,10 +665,8 @@ impl GlobalState {
// Only retry requests that haven't been cancelled. Otherwise we do unnecessary work. // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work.
Task::Retry(req) if !self.is_completed(&req) => self.on_request(req), Task::Retry(req) if !self.is_completed(&req) => self.on_request(req),
Task::Retry(_) => (), Task::Retry(_) => (),
Task::Diagnostics(generation, diagnostics_per_file) => { Task::Diagnostics(kind) => {
for (file_id, diagnostics) in diagnostics_per_file { self.diagnostics.set_native_diagnostics(kind);
self.diagnostics.set_native_diagnostics(generation, file_id, diagnostics)
}
} }
Task::PrimeCaches(progress) => match progress { Task::PrimeCaches(progress) => match progress {
PrimeCachesProgress::Begin => prime_caches_progress.push(progress), PrimeCachesProgress::Begin => prime_caches_progress.push(progress),