diff --git a/scarb/src/compiler/structured_diagnostics/core.rs b/scarb/src/compiler/structured_diagnostics/core.rs index 06f439f7f..f41a8d1da 100644 --- a/scarb/src/compiler/structured_diagnostics/core.rs +++ b/scarb/src/compiler/structured_diagnostics/core.rs @@ -1,3 +1,7 @@ +use crate::core::{ + MachineDiagnostic, MachineDiagnosticKind, MachineDiagnosticSeverity, MachineDiagnosticSpan, + MachineRelatedLocation, +}; use cairo_lang_defs::db::DefsGroup; use cairo_lang_defs::ids::ModuleId; use cairo_lang_diagnostics::{ @@ -12,44 +16,12 @@ use cairo_lang_utils::Intern; use cairo_lang_utils::unordered_hash_set::UnorderedHashSet; use itertools::Itertools; use salsa::Database; -use serde::Serialize; - -#[derive(Serialize)] -pub struct StructuredDiagnosticMessage { - r#type: &'static str, - severity: StructuredDiagnosticSeverity, - message: String, - #[serde(skip_serializing_if = "Option::is_none")] - code: Option, - file: String, - span: StructuredDiagnosticSpan, - #[serde(default, skip_serializing_if = "Vec::is_empty")] - related: Vec, -} -#[derive(Clone, Copy, Serialize, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub enum StructuredDiagnosticSeverity { - Error, - Warning, -} +pub type StructuredDiagnosticMessage = MachineDiagnostic; struct StructuredDiagnosticLocation { file: String, - span: StructuredDiagnosticSpan, -} - -#[derive(Serialize)] -struct StructuredDiagnosticSpan { - start: usize, - end: usize, -} - -#[derive(Serialize)] -struct StructuredDiagnosticRelated { - message: String, - file: String, - span: StructuredDiagnosticSpan, + span: MachineDiagnosticSpan, } pub trait StructuredDiagnosticsSink { @@ -68,11 +40,45 @@ impl StructuredDiagnosticsReporter { crates, } } +} + +impl StructuredDiagnosticMessage { + fn error(message: String, file: String) -> Self { + Self::new( + MachineDiagnosticKind::Diagnostic, + message, + MachineDiagnosticSeverity::Error, + file, + MachineDiagnosticSpan { start: 0, end: 0 }, + ) + } +} + +impl StructuredDiagnosticLocation { + fn from_user_location(db: &dyn Database, location: SpanInFile<'_>) -> Self { + Self { + file: location.file_id.full_path(db), + span: MachineDiagnosticSpan { + start: location.span.start.as_u32() as usize, + end: location.span.end.as_u32() as usize, + }, + } + } - pub fn check(&mut self, db: &dyn Database, sink: &mut impl StructuredDiagnosticsSink) -> bool { + fn into_related(self, message: String) -> MachineRelatedLocation { + MachineRelatedLocation { + message, + file: Some(self.file), + span: self.span, + } + } +} + +impl StructuredDiagnosticsReporter { + pub fn check(&self, db: &dyn Database, sink: &mut impl StructuredDiagnosticsSink) -> bool { let mut found_diagnostics = false; - for crate_input in self.crates.clone() { + for crate_input in &self.crates { let crate_id = crate_input.clone().into_crate_long_id(db).intern(db); let Ok(module_file) = db.module_main_file(ModuleId::CrateRoot(crate_id)) else { found_diagnostics = true; @@ -92,7 +98,7 @@ impl StructuredDiagnosticsReporter { found_diagnostics = true; } - let skip_warnings = self.ignore_warnings_crate_ids.contains(&crate_input); + let skip_warnings = self.ignore_warnings_crate_ids.contains(crate_input); let modules = db.crate_modules(crate_id); let mut processed_file_ids = UnorderedHashSet::<_>::default(); for module_id in modules.iter() { @@ -142,7 +148,7 @@ impl StructuredDiagnosticsReporter { } fn check_diag_group<'db, TEntry: DiagnosticEntry<'db> + salsa::Update>( - &mut self, + &self, db: &'db dyn Database, group: Diagnostics<'db, TEntry>, skip_warnings: bool, @@ -164,44 +170,6 @@ impl StructuredDiagnosticsReporter { } } -impl StructuredDiagnosticMessage { - fn error(message: String, file: String) -> Self { - Self { - r#type: "diagnostic", - severity: StructuredDiagnosticSeverity::Error, - message, - code: None, - file, - span: StructuredDiagnosticSpan { start: 0, end: 0 }, - related: vec![], - } - } - - pub fn severity(&self) -> StructuredDiagnosticSeverity { - self.severity - } -} - -impl StructuredDiagnosticLocation { - fn from_user_location(db: &dyn Database, location: SpanInFile<'_>) -> Self { - Self { - file: location.file_id.full_path(db), - span: StructuredDiagnosticSpan { - start: location.span.start.as_u32() as usize, - end: location.span.end.as_u32() as usize, - }, - } - } - - fn into_related(self, message: String) -> StructuredDiagnosticRelated { - StructuredDiagnosticRelated { - message, - file: self.file, - span: self.span, - } - } -} - fn build_structured_diagnostic_message<'db, TEntry: DiagnosticEntry<'db>>( db: &'db dyn Database, entry: &TEntry, @@ -232,12 +200,12 @@ fn build_structured_diagnostic_message<'db, TEntry: DiagnosticEntry<'db>>( } Some(StructuredDiagnosticMessage { - r#type: "diagnostic", + kind: MachineDiagnosticKind::Diagnostic, + message: entry.format(db), severity: match entry.severity() { - Severity::Error => StructuredDiagnosticSeverity::Error, - Severity::Warning => StructuredDiagnosticSeverity::Warning, + Severity::Error => MachineDiagnosticSeverity::Error, + Severity::Warning => MachineDiagnosticSeverity::Warning, }, - message: entry.format(db), code: entry.error_code().map(|code| code.to_string()), file: primary.file, span: primary.span, diff --git a/scarb/src/compiler/structured_diagnostics/mod.rs b/scarb/src/compiler/structured_diagnostics/mod.rs index d2b7e87b2..68709f5fc 100644 --- a/scarb/src/compiler/structured_diagnostics/mod.rs +++ b/scarb/src/compiler/structured_diagnostics/mod.rs @@ -1,34 +1,30 @@ mod core; mod scarb_ui; +use crate::compiler::helpers::{all_crate_inputs, non_main_crate_inputs}; use crate::core::Workspace; use cairo_lang_compiler::diagnostics::DiagnosticsError; -use cairo_lang_filesystem::db::FilesGroup; +use cairo_lang_compiler::diagnostics::DiagnosticsReporter; +use cairo_lang_compiler::ensure_diagnostics; use cairo_lang_filesystem::ids::CrateId; -use itertools::Itertools; -use salsa::Database; +use cairo_lang_utils::CloneableDatabase; use self::core::StructuredDiagnosticsReporter; use self::scarb_ui::ScarbUiStructuredDiagnosticsSink; pub fn ensure_structured_json_diagnostics<'db>( - db: &'db dyn Database, + db: &'db dyn CloneableDatabase, main_crate_ids: &[CrateId<'db>], ws: &Workspace<'_>, -) -> std::result::Result<(), DiagnosticsError> { - let ignore_warnings_crates = db - .crates() - .iter() - .filter(|crate_id| !main_crate_ids.contains(crate_id)) - .map(|c| c.long(db).clone().into_crate_input(db)) - .collect_vec(); - let crates_to_check = db - .crates() - .iter() - .map(|c| c.long(db).clone().into_crate_input(db)) - .collect_vec(); +) -> Result<(), DiagnosticsError> { + let ignore_warnings_crates = non_main_crate_inputs(db, main_crate_ids); + let crates_to_check = all_crate_inputs(db); let mut sink = ScarbUiStructuredDiagnosticsSink::new(ws.config().ui().clone()); - let mut reporter = StructuredDiagnosticsReporter::new(ignore_warnings_crates, crates_to_check); + let reporter = + StructuredDiagnosticsReporter::new(ignore_warnings_crates, crates_to_check.clone()); + let mut warmup_reporter = DiagnosticsReporter::ignoring().with_crates(&crates_to_check); + let _ = ensure_diagnostics(db, &mut warmup_reporter); + if reporter.check(db, &mut sink) { Err(DiagnosticsError) } else { diff --git a/scarb/src/compiler/structured_diagnostics/scarb_ui.rs b/scarb/src/compiler/structured_diagnostics/scarb_ui.rs index 6c5155ca4..a915e682a 100644 --- a/scarb/src/compiler/structured_diagnostics/scarb_ui.rs +++ b/scarb/src/compiler/structured_diagnostics/scarb_ui.rs @@ -1,8 +1,7 @@ use scarb_ui::components::MachineMessage; -use super::core::{ - StructuredDiagnosticMessage, StructuredDiagnosticSeverity, StructuredDiagnosticsSink, -}; +use super::core::{StructuredDiagnosticMessage, StructuredDiagnosticsSink}; +use crate::core::MachineDiagnosticSeverity; pub struct ScarbUiStructuredDiagnosticsSink { ui: scarb_ui::Ui, @@ -18,8 +17,8 @@ impl StructuredDiagnosticsSink for ScarbUiStructuredDiagnosticsSink { fn emit(&mut self, message: StructuredDiagnosticMessage) { let severity = message.severity(); match severity { - StructuredDiagnosticSeverity::Error => self.ui.record_error(), - StructuredDiagnosticSeverity::Warning => self.ui.record_warning(), + MachineDiagnosticSeverity::Error => self.ui.record_error(), + MachineDiagnosticSeverity::Warning => self.ui.record_warning(), } self.ui.print(MachineMessage(message)); } diff --git a/scarb/tests/build.rs b/scarb/tests/build.rs index 15bfabb27..8653985cd 100644 --- a/scarb/tests/build.rs +++ b/scarb/tests/build.rs @@ -114,7 +114,7 @@ fn compile_with_syntax_error_json() { .code(1) .stdout_eq(indoc! {r#" {"status":"checking","message":"hello v0.1.0 ([..]Scarb.toml)"} - {"type":"diagnostic","severity":"error","message":"Skipped tokens. Expected: Const/Enum/ExternFunction/ExternType/Function/Impl/InlineMacro/Module/Struct/Trait/TypeAlias/Use or an attribute.","code":"E1000","file":"[..]/lib.cairo","span":{"start":13,"end":13}} + {"kind":"diagnostic","message":"Skipped tokens. Expected: Const/Enum/ExternFunction/ExternType/Function/Impl/InlineMacro/Module/Struct/Trait/TypeAlias/Use or an attribute.","file":"[..]/lib.cairo","span":{"start":13,"end":13},"severity":"error","code":"E1000"} {"type":"error","message":"could not check `hello` due to [..] previous error"} "#}); }