use std::collections::HashMap;
use std::path::Path;
use cairo_lang_defs::diagnostic_utils::StableLocation;
use cairo_lang_diagnostics::{Diagnostics, PluginFileDiagnosticNotes};
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_semantic::diagnostic::SemanticDiagnosticKind;
use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
use cairo_language_common::CommonGroup;
use cairo_lint::{CairoLintToolMetadata, LinterDiagnosticParams, LinterGroup};
use lsp_types::{Diagnostic, Url};
use salsa::Database;
use tracing::info_span;
use crate::config::Config;
use crate::lang::db::AnalysisDatabase;
use crate::lang::diagnostics::lsp::map_cairo_diagnostics_to_lsp;
use crate::lang::lsp::LsProtoGroup;
use crate::project::ConfigsRegistry;
use crate::toolchain::scarb::ScarbToolchain;
#[derive(Clone, PartialEq, Eq)]
pub struct FilesDiagnostics<'db> {
pub root_on_disk_file: (Url, FileId<'db>),
pub parser: Diagnostics<'db, ParserDiagnostic<'db>>,
pub semantic: Diagnostics<'db, SemanticDiagnostic<'db>>,
pub lowering: Diagnostics<'db, LoweringDiagnostic<'db>>,
pub files_notes: PluginFileDiagnosticNotes<'db>,
}
impl<'db> FilesDiagnostics<'db> {
pub fn collect(
db: &'db AnalysisDatabase,
config: &Config,
config_registry: &ConfigsRegistry,
scarb_toolchain: &ScarbToolchain,
root_on_disk_file: FileId<'db>,
) -> Option<Self> {
let mut files_notes: PluginFileDiagnosticNotes = OrderedHashMap::default();
let root_on_disk_file_url = db.url_for_file(root_on_disk_file)?;
let mut semantic_file_diagnostics: Vec<SemanticDiagnostic> = vec![];
let mut lowering_file_diagnostics: Vec<LoweringDiagnostic> = vec![];
let mut parser_file_diagnostics: Vec<ParserDiagnostic> = vec![];
let root_path_string = root_on_disk_file.full_path(db);
let root_path = Path::new(root_path_string.as_str());
let linter_params = LinterDiagnosticParams {
only_generated_files: false,
tool_metadata: config_registry
.config_for_file(root_path)
.map_or_else(CairoLintToolMetadata::default, |config| config.lint.clone()),
};
let (files_to_process, modules_to_process) =
db.file_and_subfiles_with_corresponding_modules_without_inline(root_on_disk_file)?;
for module_id in modules_to_process.iter().copied() {
if let Ok(notes) = module_id.module_data(db).map(|data| data.diagnostics_notes(db)) {
files_notes.extend(notes.clone());
}
semantic_file_diagnostics.extend(
info_span!("db.module_semantic_diagnostics").in_scope(|| {
db.module_semantic_diagnostics(module_id).unwrap_or_default().get_all()
}),
);
lowering_file_diagnostics.extend(
info_span!("db.module_lowering_diagnostics").in_scope(|| {
db.module_lowering_diagnostics(module_id).unwrap_or_default().get_all()
}),
);
if config.enable_linter && !scarb_toolchain.is_from_scarb_cache(root_path) {
semantic_file_diagnostics.extend(info_span!("db.linter_diagnostics").in_scope(
|| {
db.linter_diagnostics(linter_params.clone(), module_id).iter().map(|diag| {
SemanticDiagnostic::new(
StableLocation::new(diag.stable_ptr),
SemanticDiagnosticKind::PluginDiagnostic(diag.clone()),
module_id,
)
})
},
));
}
}
for file_id in files_to_process.iter() {
parser_file_diagnostics.extend(db.file_syntax_diagnostics(*file_id).get_all());
}
Some(FilesDiagnostics {
root_on_disk_file: (root_on_disk_file_url, root_on_disk_file),
parser: Diagnostics::from_iter(parser_file_diagnostics),
semantic: Diagnostics::from_iter(semantic_file_diagnostics),
lowering: Diagnostics::from_iter(lowering_file_diagnostics),
files_notes,
})
}
pub fn to_lsp(
&self,
db: &'db AnalysisDatabase,
trace_macro_diagnostics: bool,
) -> (Url, HashMap<(Url, FileId<'db>), Vec<Diagnostic>>) {
let mut diagnostics = HashMap::new();
map_cairo_diagnostics_to_lsp(
db as &dyn Database,
&mut diagnostics,
&self.parser,
trace_macro_diagnostics,
&self.files_notes,
);
map_cairo_diagnostics_to_lsp(
db as &dyn Database,
&mut diagnostics,
&self.semantic,
trace_macro_diagnostics,
&self.files_notes,
);
map_cairo_diagnostics_to_lsp(
db as &dyn Database,
&mut diagnostics,
&self.lowering,
trace_macro_diagnostics,
&self.files_notes,
);
if !diagnostics.contains_key(&self.root_on_disk_file) {
diagnostics.insert(self.root_on_disk_file.clone(), Vec::new());
}
(self.root_on_disk_file.0.clone(), diagnostics)
}
}