use super::module::IRModule;
use super::span::*;
use crate::runtime::function::Result;
use crate::runtime::object::{Object, ObjectPtr};
use crate::runtime::{
array::Array,
function::{self, Function, ToFunction},
string::String as TString,
};
use tvm_macros::{external, Object};
pub mod codespan;
external! {
#[name("node.ArrayGetItem")]
fn get_renderer() -> DiagnosticRenderer;
#[name("diagnostics.DiagnosticRenderer")]
fn diagnostic_renderer(func: Function) -> DiagnosticRenderer;
#[name("diagnostics.Emit")]
fn emit(ctx: DiagnosticContext, diagnostic: Diagnostic) -> ();
#[name("diagnostics.DiagnosticContextDefault")]
fn diagnostic_context_default(module: IRModule) -> DiagnosticContext;
#[name("diagnostics.DiagnosticContextRender")]
fn diagnostic_context_render(ctx: DiagnosticContext) -> ();
#[name("diagnostics.DiagnosticRendererRender")]
fn diagnositc_renderer_render(renderer: DiagnosticRenderer,ctx: DiagnosticContext) -> ();
#[name("diagnostics.ClearRenderer")]
fn clear_renderer() -> ();
}
#[repr(C)]
#[derive(PartialEq, Eq, Debug)]
pub enum DiagnosticLevel {
Bug = 10,
Error = 20,
Warning = 30,
Note = 40,
Help = 50,
}
#[repr(C)]
#[derive(Object, Debug)]
#[ref_name = "Diagnostic"]
#[type_key = "Diagnostic"]
pub struct DiagnosticNode {
pub base: Object,
pub level: DiagnosticLevel,
pub span: Span,
pub message: TString,
}
impl Diagnostic {
pub fn new(level: DiagnosticLevel, span: Span, message: TString) -> Diagnostic {
let node = DiagnosticNode {
base: Object::base::<DiagnosticNode>(),
level,
span,
message,
};
ObjectPtr::new(node).into()
}
pub fn bug(span: Span) -> DiagnosticBuilder {
DiagnosticBuilder::new(DiagnosticLevel::Bug, span)
}
pub fn error(span: Span) -> DiagnosticBuilder {
DiagnosticBuilder::new(DiagnosticLevel::Error, span)
}
pub fn warning(span: Span) -> DiagnosticBuilder {
DiagnosticBuilder::new(DiagnosticLevel::Warning, span)
}
pub fn note(span: Span) -> DiagnosticBuilder {
DiagnosticBuilder::new(DiagnosticLevel::Note, span)
}
pub fn help(span: Span) -> DiagnosticBuilder {
DiagnosticBuilder::new(DiagnosticLevel::Help, span)
}
}
pub struct DiagnosticBuilder {
pub level: DiagnosticLevel,
pub span: Span,
pub message: String,
}
impl DiagnosticBuilder {
pub fn new(level: DiagnosticLevel, span: Span) -> DiagnosticBuilder {
DiagnosticBuilder {
level,
span,
message: "".into(),
}
}
}
#[repr(C)]
#[derive(Object, Debug)]
#[ref_name = "DiagnosticRenderer"]
#[type_key = "DiagnosticRenderer"]
pub struct DiagnosticRendererNode {
pub base: Object,
}
impl DiagnosticRenderer {
pub fn render(&self, ctx: DiagnosticContext) -> Result<()> {
diagnositc_renderer_render(self.clone(), ctx)
}
}
#[repr(C)]
#[derive(Object, Debug)]
#[ref_name = "DiagnosticContext"]
#[type_key = "DiagnosticContext"]
pub struct DiagnosticContextNode {
pub base: Object,
pub module: IRModule,
pub diagnostics: Array<Diagnostic>,
pub renderer: DiagnosticRenderer,
}
impl DiagnosticContext {
pub fn new<F>(module: IRModule, render_func: F) -> DiagnosticContext
where
F: Fn(DiagnosticContext) -> () + 'static,
{
let renderer = diagnostic_renderer(render_func.to_function()).unwrap();
let node = DiagnosticContextNode {
base: Object::base::<DiagnosticContextNode>(),
module,
diagnostics: Array::from_vec(vec![]).unwrap(),
renderer,
};
DiagnosticContext(Some(ObjectPtr::new(node)))
}
pub fn default(module: IRModule) -> DiagnosticContext {
diagnostic_context_default(module).unwrap()
}
pub fn emit(&mut self, diagnostic: Diagnostic) -> Result<()> {
emit(self.clone(), diagnostic)
}
pub fn render(&mut self) -> Result<()> {
diagnostic_context_render(self.clone())
}
pub fn emit_fatal(&mut self, diagnostic: Diagnostic) -> Result<()> {
self.emit(diagnostic)?;
self.render()?;
Ok(())
}
}
fn override_renderer<F>(opt_func: Option<F>) -> Result<()>
where
F: Fn(DiagnosticContext) -> () + 'static,
{
match opt_func {
None => clear_renderer(),
Some(func) => {
let func = func.to_function();
let render_factory = move || diagnostic_renderer(func.clone()).unwrap();
function::register_override(render_factory, "diagnostics.OverrideRenderer", true)?;
Ok(())
}
}
}