use super::lexer::{DCToken, Span};
use super::pipeline::{PipelineData, PipelineStage};
use codespan_diag::Label;
use codespan_diag::LabelStyle;
use codespan_reporting::diagnostic as codespan_diag;
use std::mem::discriminant;
use thiserror::Error;
pub trait ToErrorCode
where
Self: std::error::Error,
{
fn error_code(&self) -> &str;
}
#[derive(Debug, Error)]
#[error(transparent)]
pub enum DCReadError {
#[error("parser error")]
Syntax,
#[error("semantics error")]
Semantic,
IO(#[from] std::io::Error),
}
#[derive(Debug, Error)]
#[error(transparent)]
pub enum PipelineError {
Parser(#[from] ParseError),
Semantics(#[from] SemanticError),
}
impl ToErrorCode for PipelineError {
fn error_code(&self) -> &str {
match self {
Self::Parser(err) => err.error_code(),
Self::Semantics(err) => err.error_code(),
}
}
}
#[derive(Debug, Error)]
pub enum SemanticError {
#[error("`{0}` is already defined")]
AlreadyDefined(String),
#[error("`{0}` is not defined")]
NotDefined(String),
#[error("multiple inheritance is not allowed")]
MultipleInheritanceDisabled,
#[error("maximum number of dclasses declared")]
DClassOverflow,
#[error("maximum number of fields declared")]
FieldOverflow,
#[error("redundant view suffix `{0}`")]
RedundantViewSuffix(String),
#[error("redundant keyword `{0}`")]
RedundantKeyword(String),
#[error("dc keywords are not allowed in struct fields")]
KeywordsInStructField,
#[error("duplicate case value")]
RedundantCase,
#[error("default case already defined")]
RedundantDefault,
#[error("case value type does not match key value type")]
InvalidCaseValueType,
#[error("`mismatched dc keywords in molecule between `{atom1}` and `{atom2}`")]
MismatchedKeywords { atom1: String, atom2: String },
#[error("`{0}` is not an atomic field")]
ExpectedAtomic(String),
#[error("invalid range for type")]
InvalidRange,
#[error("overlapping range")]
OverlappingRange,
#[error("value out of range")]
ValueOutOfRange,
#[error("invalid divisor")]
InvalidDivisor,
#[error("invalid modulus")]
InvalidModulus,
#[error("invalid default value for type")]
InvalidDefault,
#[error("`{0}` is not a struct")]
ExpectedStruct(String),
}
impl ToErrorCode for SemanticError {
fn error_code(&self) -> &str {
match self {
Self::AlreadyDefined(_) => "E0200",
Self::NotDefined(_) => "E0201",
Self::MultipleInheritanceDisabled => "E0210",
Self::DClassOverflow => "E0211",
Self::FieldOverflow => "E0212",
Self::RedundantViewSuffix(_) => "E0220",
Self::RedundantKeyword(_) => "E0230",
Self::KeywordsInStructField => "E0240",
Self::RedundantCase => "E0250",
Self::RedundantDefault => "E0251",
Self::InvalidCaseValueType => "E0252",
Self::MismatchedKeywords { atom1: _, atom2: _ } => "E0260",
Self::ExpectedAtomic(_) => "E0261",
Self::InvalidRange => "E0270",
Self::OverlappingRange => "E0271",
Self::ValueOutOfRange => "E0272",
Self::InvalidDivisor => "E0280",
Self::InvalidModulus => "E0281",
Self::InvalidDefault => "E0290",
Self::ExpectedStruct(_) => "E0300",
}
}
}
#[derive(Debug, Error)]
pub enum ParseError {
#[error("syntax error; {1}, found `{0:?}`")]
Error(DCToken, String),
}
impl ToErrorCode for ParseError {
fn error_code(&self) -> &str {
match self {
Self::Error(_, _) => "E0100",
}
}
}
pub(crate) struct Diagnostic {
span: Span,
stage: PipelineStage,
file_id: usize,
severity: codespan_diag::Severity,
error: PipelineError,
}
impl Diagnostic {
pub fn error(span: Span, pipeline: &mut PipelineData, err: impl Into<PipelineError>) -> Self {
Self {
span,
stage: pipeline.current_stage(),
file_id: pipeline.current_file(),
severity: codespan_diag::Severity::Error,
error: err.into(),
}
}
}
impl From<Diagnostic> for codespan_diag::Diagnostic<usize> {
fn from(val: Diagnostic) -> codespan_diag::Diagnostic<usize> {
codespan_diag::Diagnostic::new(val.severity)
.with_message(val.error.to_string())
.with_code(val.error.error_code())
.with_labels(vec![Label::new(
LabelStyle::Primary,
val.file_id,
val.span.min..val.span.max,
)])
.with_notes({
if discriminant(&val.stage) == discriminant(&PipelineStage::Parser) {
vec![
"Syntax errors are limited. Please see issue #19.".into(),
"https://gitlab.com/donet-server/donet/-/issues/19".into(),
]
} else {
vec![]
}
})
}
}