///|
/// A stable aggregate for a batch of diagnostics.
pub(all) struct DiagnosticReport {
diagnostics : Array[Diagnostic]
errors : Int
warnings : Int
infos : Int
} derive(Debug)
///|
pub fn DiagnosticReport::new(
diagnostics : Array[Diagnostic],
) -> DiagnosticReport {
let mut errors = 0
let mut warnings = 0
let mut infos = 0
for diagnostic in diagnostics {
match diagnostic.severity {
Error => errors += 1
Warning => warnings += 1
Info => infos += 1
}
}
{ diagnostics, errors, warnings, infos }
}
///|
pub fn DiagnosticReport::has_errors(self : DiagnosticReport) -> Bool {
self.errors > 0
}
///|
pub fn DiagnosticReport::total(self : DiagnosticReport) -> Int {
self.diagnostics.length()
}
///|
pub fn DiagnosticReport::for_code(
self : DiagnosticReport,
code : String,
) -> Array[Diagnostic] {
let result : Array[Diagnostic] = []
for diagnostic in self.diagnostics {
if diagnostic.code == code {
result.push(diagnostic)
}
}
result
}
///|
pub fn DiagnosticReport::codes(self : DiagnosticReport) -> Array[String] {
let result : Array[String] = []
for diagnostic in self.diagnostics {
if !result.contains(diagnostic.code) {
result.push(diagnostic.code)
}
}
result
}
///|
pub fn DiagnosticReport::summary(self : DiagnosticReport) -> String {
"errors=" +
self.errors.to_string() +
", warnings=" +
self.warnings.to_string() +
", infos=" +
self.infos.to_string() +
", total=" +
self.total().to_string()
}
///|
/// Add a note while preserving the original diagnostic value.
pub fn Diagnostic::with_note(self : Diagnostic, note : String) -> Diagnostic {
let notes = self.notes.copy()
notes.push(note)
{ ..self, notes, }
}
///|
fn sarif_level(severity : Severity) -> String {
match severity {
Error => "error"
Warning => "warning"
Info => "note"
}
}
///|
/// Render diagnostics in the SARIF 2.1.0 shape used by code scanning tools.
pub fn render_sarif(
diagnostics : Array[Diagnostic],
source : @source.SourceFile,
) -> String {
let builder = StringBuilder()
builder.write_string(
"{\"version\":\"2.1.0\",\"$schema\":\"https://json.schemastore.org/sarif-2.1.0.json\",\"runs\":[{\"tool\":{\"driver\":{\"name\":\"moonbit-rtl\"}},\"results\":[",
)
for index, diagnostic in diagnostics {
if index > 0 {
builder.write_char(',')
}
let (line, column) = diagnostic.at_line_col(source)
builder.write_string("{\"ruleId\":\"")
builder.write_string(structured_escape(diagnostic.code))
builder.write_string("\",\"level\":\"")
builder.write_string(sarif_level(diagnostic.severity))
builder.write_string("\",\"message\":{\"text\":\"")
builder.write_string(structured_escape(diagnostic.message))
builder.write_string(
"\"},\"locations\":[{\"physicalLocation\":{\"artifactLocation\":{\"uri\":\"",
)
builder.write_string(
structured_escape(
if diagnostic.file.is_empty() {
source.name()
} else {
diagnostic.file
},
),
)
builder.write_string("\"},\"region\":{\"startLine\":")
builder.write_object(line + 1)
builder.write_string(",\"startColumn\":")
builder.write_object(column + 1)
builder.write_string("}}}]}")
}
builder.write_string("]}]}\n")
builder.to_string()
}