use std::collections::HashSet;
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use cairo_lang_defs::db::DefsGroup;
use cairo_lang_defs::ids::ModuleId;
use cairo_lang_diagnostics::Diagnostics;
use cairo_lang_filesystem::ids::FileId;
use cairo_lang_lowering::db::LoweringGroup;
use cairo_lang_lowering::diagnostic::LoweringDiagnostic;
use cairo_lang_parser::ParserDiagnostic;
use cairo_lang_parser::db::ParserGroup;
use cairo_lang_semantic::SemanticDiagnostic;
use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_utils::{LookupIntern, Upcast};
use lsp_types::Url;
use tracing::{error, info_span};
use crate::lang::db::AnalysisDatabase;
use crate::lang::diagnostics::lsp::map_cairo_diagnostics_to_lsp;
use crate::lang::lsp::LsProtoGroup;
use crate::server::panic::is_cancelled;
#[derive(Clone, PartialEq, Eq)]
pub struct FileDiagnostics {
pub url: Url,
pub parser: Diagnostics<ParserDiagnostic>,
pub semantic: Diagnostics<SemanticDiagnostic>,
pub lowering: Diagnostics<LoweringDiagnostic>,
}
impl FileDiagnostics {
pub fn collect(
db: &AnalysisDatabase,
file: FileId,
processed_modules: &mut HashSet<ModuleId>,
) -> Option<Self> {
macro_rules! query {
($query:expr) => {
info_span!(stringify!($query)).in_scope(|| {
catch_unwind(AssertUnwindSafe(|| $query)).map_err(|err| {
if is_cancelled(err.as_ref()) {
resume_unwind(err);
} else {
error!(
"caught panic when computing diagnostics for file: {:?}",
file.lookup_intern(db)
);
err
}
})
})
};
}
let url = query!(db.url_for_file(file)).ok()??;
let module_ids = query!(db.file_modules(file)).ok()?.ok()?;
let mut semantic_file_diagnostics: Vec<SemanticDiagnostic> = vec![];
let mut lowering_file_diagnostics: Vec<LoweringDiagnostic> = vec![];
for &module_id in module_ids.iter() {
if !processed_modules.contains(&module_id) {
semantic_file_diagnostics.extend(
query!(db.module_semantic_diagnostics(module_id))
.map(Result::unwrap_or_default)
.unwrap_or_default()
.get_all(),
);
lowering_file_diagnostics.extend(
query!(db.module_lowering_diagnostics(module_id))
.map(Result::unwrap_or_default)
.unwrap_or_default()
.get_all(),
);
processed_modules.insert(module_id);
}
}
let parser_file_diagnostics = query!(db.file_syntax_diagnostics(file)).unwrap_or_default();
Some(FileDiagnostics {
url,
parser: parser_file_diagnostics,
semantic: Diagnostics::from_iter(semantic_file_diagnostics),
lowering: Diagnostics::from_iter(lowering_file_diagnostics),
})
}
pub fn clear(&mut self) {
self.parser = Diagnostics::default();
self.semantic = Diagnostics::default();
self.lowering = Diagnostics::default();
}
pub fn to_lsp(
&self,
db: &AnalysisDatabase,
trace_macro_diagnostics: bool,
) -> Option<lsp_types::PublishDiagnosticsParams> {
let file = db.file_for_url(&self.url)?;
let mut diagnostics = Vec::new();
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diagnostics,
&self.parser,
file,
trace_macro_diagnostics,
);
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diagnostics,
&self.semantic,
file,
trace_macro_diagnostics,
);
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diagnostics,
&self.lowering,
file,
trace_macro_diagnostics,
);
Some(lsp_types::PublishDiagnosticsParams {
uri: self.url.clone(),
diagnostics,
version: None,
})
}
}