mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 06:03:58 +00:00
Auto merge of #17775 - ShoyuVanilla:segregate-diags, r=Veykril
perf: Segregate syntax and semantic diagnostics Closes #17731
This commit is contained in:
commit
f62d7b9f11
9 changed files with 255 additions and 146 deletions
|
@ -96,6 +96,7 @@ use syntax::{
|
|||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum DiagnosticCode {
|
||||
RustcHardError(&'static str),
|
||||
SyntaxError,
|
||||
RustcLint(&'static str),
|
||||
Clippy(&'static str),
|
||||
Ra(&'static str, Severity),
|
||||
|
@ -107,6 +108,9 @@ impl DiagnosticCode {
|
|||
DiagnosticCode::RustcHardError(e) => {
|
||||
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) => {
|
||||
format!("https://doc.rust-lang.org/rustc/?search={e}")
|
||||
}
|
||||
|
@ -125,6 +129,7 @@ impl DiagnosticCode {
|
|||
| DiagnosticCode::RustcLint(r)
|
||||
| DiagnosticCode::Clippy(r)
|
||||
| DiagnosticCode::Ra(r, _) => r,
|
||||
DiagnosticCode::SyntaxError => "syntax-error",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +159,7 @@ impl Diagnostic {
|
|||
message,
|
||||
range: range.into(),
|
||||
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.
|
||||
DiagnosticCode::RustcLint(_) => Severity::Warning,
|
||||
// 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.
|
||||
pub fn diagnostics(
|
||||
pub fn semantic_diagnostics(
|
||||
db: &RootDatabase,
|
||||
config: &DiagnosticsConfig,
|
||||
resolve: &AssistResolveStrategy,
|
||||
file_id: FileId,
|
||||
) -> Vec<Diagnostic> {
|
||||
let _p = tracing::info_span!("diagnostics").entered();
|
||||
let _p = tracing::info_span!("semantic_diagnostics").entered();
|
||||
let sema = Semantics::new(db);
|
||||
let file_id = sema
|
||||
.attach_first_edition(file_id)
|
||||
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
|
||||
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);
|
||||
|
||||
// FIXME: This iterates the entire file which is a rather expensive operation.
|
||||
|
@ -341,8 +369,11 @@ pub fn diagnostics(
|
|||
match module {
|
||||
// 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.
|
||||
Some(m) if parse_errors < 16 => m.diagnostics(db, &mut diags, config.style_lints),
|
||||
Some(_) => (),
|
||||
Some(m) => {
|
||||
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()),
|
||||
}
|
||||
|
||||
|
@ -363,7 +394,7 @@ pub fn diagnostics(
|
|||
res.extend(d.errors.iter().take(16).map(|err| {
|
||||
{
|
||||
Diagnostic::new(
|
||||
DiagnosticCode::RustcHardError("syntax-error"),
|
||||
DiagnosticCode::SyntaxError,
|
||||
format!("Syntax Error in Expansion: {err}"),
|
||||
ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
|
||||
)
|
||||
|
@ -464,6 +495,19 @@ pub fn diagnostics(
|
|||
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
|
||||
|
||||
static RUSTC_LINT_GROUPS_DICT: Lazy<FxHashMap<&str, Vec<&str>>> =
|
||||
|
|
|
@ -59,10 +59,14 @@ fn check_nth_fix_with_config(
|
|||
let after = trim_indent(ra_fixture_after);
|
||||
|
||||
let (db, file_position) = RootDatabase::with_position(ra_fixture_before);
|
||||
let diagnostic =
|
||||
super::diagnostics(&db, &config, &AssistResolveStrategy::All, file_position.file_id.into())
|
||||
.pop()
|
||||
.expect("no diagnostics");
|
||||
let diagnostic = super::full_diagnostics(
|
||||
&db,
|
||||
&config,
|
||||
&AssistResolveStrategy::All,
|
||||
file_position.file_id.into(),
|
||||
)
|
||||
.pop()
|
||||
.expect("no diagnostics");
|
||||
let fix = &diagnostic
|
||||
.fixes
|
||||
.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 mut conf = DiagnosticsConfig::test_sample();
|
||||
conf.expr_fill_default = ExprFillDefaultMode::Default;
|
||||
let fix =
|
||||
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into())
|
||||
.into_iter()
|
||||
.find(|d| {
|
||||
d.fixes
|
||||
.as_ref()
|
||||
.and_then(|fixes| {
|
||||
fixes.iter().find(|fix| {
|
||||
if !fix.target.contains_inclusive(file_position.offset) {
|
||||
return false;
|
||||
}
|
||||
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();
|
||||
let fix = super::full_diagnostics(
|
||||
&db,
|
||||
&conf,
|
||||
&AssistResolveStrategy::All,
|
||||
file_position.file_id.into(),
|
||||
)
|
||||
.into_iter()
|
||||
.find(|d| {
|
||||
d.fixes
|
||||
.as_ref()
|
||||
.and_then(|fixes| {
|
||||
fixes.iter().find(|fix| {
|
||||
if !fix.target.contains_inclusive(file_position.offset) {
|
||||
return false;
|
||||
}
|
||||
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()
|
||||
{
|
||||
edit.apply(&mut actual);
|
||||
if let Some(snippet_edit) = snippet_edit {
|
||||
snippet_edit.apply(&mut actual);
|
||||
}
|
||||
}
|
||||
actual
|
||||
};
|
||||
after == actual
|
||||
})
|
||||
})
|
||||
.is_some()
|
||||
});
|
||||
for (edit, snippet_edit) in source_change.source_file_edits.values() {
|
||||
edit.apply(&mut actual);
|
||||
if let Some(snippet_edit) = snippet_edit {
|
||||
snippet_edit.apply(&mut actual);
|
||||
}
|
||||
}
|
||||
actual
|
||||
};
|
||||
after == actual
|
||||
})
|
||||
})
|
||||
.is_some()
|
||||
});
|
||||
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();
|
||||
conf.expr_fill_default = ExprFillDefaultMode::Default;
|
||||
let mut n_fixes = 0;
|
||||
let fix =
|
||||
super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id.into())
|
||||
.into_iter()
|
||||
.find(|d| {
|
||||
d.fixes
|
||||
.as_ref()
|
||||
.and_then(|fixes| {
|
||||
n_fixes += fixes.len();
|
||||
fixes.iter().find(|fix| {
|
||||
if !fix.target.contains_inclusive(file_position.offset) {
|
||||
return false;
|
||||
}
|
||||
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();
|
||||
let fix = super::full_diagnostics(
|
||||
&db,
|
||||
&conf,
|
||||
&AssistResolveStrategy::All,
|
||||
file_position.file_id.into(),
|
||||
)
|
||||
.into_iter()
|
||||
.find(|d| {
|
||||
d.fixes
|
||||
.as_ref()
|
||||
.and_then(|fixes| {
|
||||
n_fixes += fixes.len();
|
||||
fixes.iter().find(|fix| {
|
||||
if !fix.target.contains_inclusive(file_position.offset) {
|
||||
return false;
|
||||
}
|
||||
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()
|
||||
{
|
||||
edit.apply(&mut actual);
|
||||
if let Some(snippet_edit) = snippet_edit {
|
||||
snippet_edit.apply(&mut actual);
|
||||
}
|
||||
}
|
||||
actual
|
||||
};
|
||||
after == actual
|
||||
})
|
||||
})
|
||||
.is_some()
|
||||
});
|
||||
for (edit, snippet_edit) in source_change.source_file_edits.values() {
|
||||
edit.apply(&mut actual);
|
||||
if let Some(snippet_edit) = snippet_edit {
|
||||
snippet_edit.apply(&mut actual);
|
||||
}
|
||||
}
|
||||
actual
|
||||
};
|
||||
after == actual
|
||||
})
|
||||
})
|
||||
.is_some()
|
||||
});
|
||||
assert!(fix.is_some(), "no diagnostic with desired fix");
|
||||
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`.
|
||||
pub(crate) fn check_no_fix(ra_fixture: &str) {
|
||||
let (db, file_position) = RootDatabase::with_position(ra_fixture);
|
||||
let diagnostic = super::diagnostics(
|
||||
let diagnostic = super::full_diagnostics(
|
||||
&db,
|
||||
&DiagnosticsConfig::test_sample(),
|
||||
&AssistResolveStrategy::All,
|
||||
|
@ -215,7 +223,7 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
|
|||
.iter()
|
||||
.copied()
|
||||
.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()
|
||||
.map(|d| {
|
||||
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 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());
|
||||
|
||||
let diagnostics = super::diagnostics(
|
||||
let diagnostics = super::full_diagnostics(
|
||||
&db,
|
||||
&DiagnosticsConfig::test_sample(),
|
||||
&AssistResolveStrategy::All,
|
||||
|
|
|
@ -672,14 +672,33 @@ impl Analysis {
|
|||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Computes the set of diagnostics for the given file.
|
||||
pub fn diagnostics(
|
||||
/// Computes the set of parser level diagnostics for the given file.
|
||||
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,
|
||||
config: &DiagnosticsConfig,
|
||||
resolve: AssistResolveStrategy,
|
||||
file_id: FileId,
|
||||
) -> 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
|
||||
|
@ -697,7 +716,7 @@ impl Analysis {
|
|||
|
||||
self.with_db(|db| {
|
||||
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()
|
||||
.flat_map(|it| it.fixes.unwrap_or_default())
|
||||
.filter(|it| it.target.intersect(frange.range).is_some())
|
||||
|
|
|
@ -976,7 +976,7 @@ impl flags::AnalysisStats {
|
|||
let mut sw = self.stop_watch();
|
||||
|
||||
for &file_id in &file_ids {
|
||||
_ = analysis.diagnostics(
|
||||
_ = analysis.full_diagnostics(
|
||||
&DiagnosticsConfig {
|
||||
enabled: true,
|
||||
proc_macros_enabled: true,
|
||||
|
|
|
@ -63,7 +63,7 @@ impl flags::Diagnostics {
|
|||
_vfs.file_path(file_id.into())
|
||||
);
|
||||
for diagnostic in analysis
|
||||
.diagnostics(
|
||||
.full_diagnostics(
|
||||
&DiagnosticsConfig::test_sample(),
|
||||
AssistResolveStrategy::None,
|
||||
file_id.into(),
|
||||
|
|
|
@ -155,7 +155,7 @@ impl Tester {
|
|||
let root_file = self.root_file;
|
||||
move || {
|
||||
let res = std::panic::catch_unwind(move || {
|
||||
analysis.diagnostics(
|
||||
analysis.full_diagnostics(
|
||||
diagnostic_config,
|
||||
ide::AssistResolveStrategy::None,
|
||||
root_file,
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_hash::FxHashSet;
|
|||
use stdx::iter_eq_by;
|
||||
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>>>>;
|
||||
|
||||
|
@ -28,7 +28,8 @@ pub(crate) type DiagnosticsGeneration = usize;
|
|||
#[derive(Debug, Default, Clone)]
|
||||
pub(crate) struct DiagnosticCollection {
|
||||
// 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>
|
||||
pub(crate) check: IntMap<usize, IntMap<FileId, Vec<lsp_types::Diagnostic>>>,
|
||||
pub(crate) check_fixes: CheckFixes,
|
||||
|
@ -64,7 +65,8 @@ impl DiagnosticCollection {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -88,43 +90,51 @@ impl DiagnosticCollection {
|
|||
self.changes.insert(file_id);
|
||||
}
|
||||
|
||||
pub(crate) fn set_native_diagnostics(
|
||||
&mut self,
|
||||
generation: DiagnosticsGeneration,
|
||||
file_id: FileId,
|
||||
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;
|
||||
pub(crate) fn set_native_diagnostics(&mut self, kind: DiagnosticsTaskKind) {
|
||||
let (generation, diagnostics, target) = match kind {
|
||||
DiagnosticsTaskKind::Syntax(generation, diagnostics) => {
|
||||
(generation, diagnostics, &mut self.native_syntax)
|
||||
}
|
||||
if *old_gen < generation || generation == 0 {
|
||||
self.native.insert(file_id, (generation, diagnostics));
|
||||
DiagnosticsTaskKind::Semantic(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 {
|
||||
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))
|
||||
target.insert(file_id, (generation, diagnostics));
|
||||
}
|
||||
} else {
|
||||
self.native.insert(file_id, (generation, diagnostics));
|
||||
self.changes.insert(file_id);
|
||||
}
|
||||
self.changes.insert(file_id);
|
||||
}
|
||||
|
||||
pub(crate) fn diagnostics_for(
|
||||
&self,
|
||||
file_id: FileId,
|
||||
) -> 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();
|
||||
native.chain(check)
|
||||
native_syntax.chain(native_semantic).chain(check)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub(crate) enum NativeDiagnosticsFetchKind {
|
||||
Syntax,
|
||||
Semantic,
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_native_diagnostics(
|
||||
snapshot: GlobalStateSnapshot,
|
||||
snapshot: &GlobalStateSnapshot,
|
||||
subscriptions: std::sync::Arc<[FileId]>,
|
||||
slice: std::ops::Range<usize>,
|
||||
kind: NativeDiagnosticsFetchKind,
|
||||
) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> {
|
||||
let _p = tracing::info_span!("fetch_native_diagnostics").entered();
|
||||
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 source_root = snapshot.analysis.source_root_id(file_id).ok()?;
|
||||
|
||||
let diagnostics = snapshot
|
||||
.analysis
|
||||
.diagnostics(
|
||||
&snapshot.config.diagnostics(Some(source_root)),
|
||||
ide::AssistResolveStrategy::None,
|
||||
file_id,
|
||||
)
|
||||
.ok()?
|
||||
let config = &snapshot.config.diagnostics(Some(source_root));
|
||||
let diagnostics = match kind {
|
||||
NativeDiagnosticsFetchKind::Syntax => {
|
||||
snapshot.analysis.syntax_diagnostics(config, file_id).ok()?
|
||||
}
|
||||
NativeDiagnosticsFetchKind::Semantic => snapshot
|
||||
.analysis
|
||||
.semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id)
|
||||
.ok()?,
|
||||
};
|
||||
let diagnostics = diagnostics
|
||||
.into_iter()
|
||||
.filter_map(|d| {
|
||||
if d.range.file_id == file_id {
|
||||
|
|
|
@ -325,7 +325,7 @@ fn integrated_diagnostics_benchmark() {
|
|||
term_search_borrowck: true,
|
||||
};
|
||||
host.analysis()
|
||||
.diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
|
||||
.full_diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
|
||||
.unwrap();
|
||||
|
||||
let _g = crate::tracing::hprof::init("*");
|
||||
|
@ -343,7 +343,7 @@ fn integrated_diagnostics_benchmark() {
|
|||
let _p = tracing::info_span!("diagnostics").entered();
|
||||
let _span = profile::cpu_span();
|
||||
host.analysis()
|
||||
.diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
|
||||
.full_diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use vfs::{AbsPathBuf, FileId};
|
|||
|
||||
use crate::{
|
||||
config::Config,
|
||||
diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration},
|
||||
diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind},
|
||||
dispatch::{NotificationDispatcher, RequestDispatcher},
|
||||
global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState},
|
||||
hack_recover_crate_name,
|
||||
|
@ -86,12 +86,18 @@ pub(crate) enum QueuedTask {
|
|||
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)]
|
||||
pub(crate) enum Task {
|
||||
Response(lsp_server::Response),
|
||||
DiscoverLinkedProjects(DiscoverProjectParam),
|
||||
Retry(lsp_server::Request),
|
||||
Diagnostics(DiagnosticsGeneration, Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
|
||||
Diagnostics(DiagnosticsTaskKind),
|
||||
DiscoverTest(lsp_ext::DiscoverTestResults),
|
||||
PrimeCaches(PrimeCachesProgress),
|
||||
FetchWorkspace(ProjectWorkspaceProgress),
|
||||
|
@ -549,14 +555,29 @@ impl GlobalState {
|
|||
}
|
||||
// Diagnostics are triggered by the user typing
|
||||
// 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();
|
||||
move || {
|
||||
Task::Diagnostics(
|
||||
generation,
|
||||
fetch_native_diagnostics(snapshot, subscriptions, slice),
|
||||
)
|
||||
move |sender| {
|
||||
let diags = fetch_native_diagnostics(
|
||||
&snapshot,
|
||||
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;
|
||||
|
@ -644,10 +665,8 @@ impl GlobalState {
|
|||
// 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(_) => (),
|
||||
Task::Diagnostics(generation, diagnostics_per_file) => {
|
||||
for (file_id, diagnostics) in diagnostics_per_file {
|
||||
self.diagnostics.set_native_diagnostics(generation, file_id, diagnostics)
|
||||
}
|
||||
Task::Diagnostics(kind) => {
|
||||
self.diagnostics.set_native_diagnostics(kind);
|
||||
}
|
||||
Task::PrimeCaches(progress) => match progress {
|
||||
PrimeCachesProgress::Begin => prime_caches_progress.push(progress),
|
||||
|
|
Loading…
Reference in a new issue