donet_core/parser/
error.rs1use super::lexer::{DCToken, Span};
23use super::pipeline::{PipelineData, PipelineStage};
24use codespan_diag::Label;
25use codespan_diag::LabelStyle;
26use codespan_reporting::diagnostic as codespan_diag;
27use std::mem::discriminant;
28use thiserror::Error;
29
30const ERR_DOCS: &str = "https://docs.donet-server.org/master/dclanguage/error-codes";
31
32pub trait ToErrorCode
35where
36 Self: std::error::Error,
37{
38 fn error_code(&self) -> &str;
39}
40
41#[derive(Debug, Error)]
42#[error(transparent)]
43pub enum DCReadError {
44 #[error("parser error")]
45 Syntax,
46 #[error("semantics error")]
47 Semantic,
48 IO(#[from] std::io::Error),
49}
50
51#[derive(Debug, Error)]
52#[error(transparent)]
53pub enum PipelineError {
54 Parser(#[from] ParseError),
55 Semantics(#[from] SemanticError),
56}
57
58impl ToErrorCode for PipelineError {
59 fn error_code(&self) -> &str {
60 match self {
62 Self::Parser(err) => err.error_code(),
63 Self::Semantics(err) => err.error_code(),
64 }
65 }
66}
67
68#[derive(Debug, Error)]
70pub enum SemanticError {
71 #[error("`{0}` is already defined")]
73 AlreadyDefined(String),
74 #[error("`{0}` is not defined")]
75 NotDefined(String),
76
77 #[error("multiple inheritance is not allowed")]
79 MultipleInheritanceDisabled,
80 #[error("maximum number of dclasses declared")]
81 DClassOverflow,
82 #[error("maximum number of fields declared")]
83 FieldOverflow,
84
85 #[error("redundant view suffix `{0}`")]
87 RedundantViewSuffix(String),
88
89 #[error("redundant keyword `{0}`")]
91 RedundantKeyword(String),
92
93 #[error("dc keywords are not allowed in struct fields")]
95 KeywordsInStructField,
96
97 #[error("duplicate case value")]
99 RedundantCase,
100 #[error("default case already defined")]
101 RedundantDefault,
102 #[error("case value type does not match key value type")]
103 InvalidCaseValueType,
104
105 #[error("`mismatched dc keywords in molecule between `{atom1}` and `{atom2}`")]
107 MismatchedKeywords { atom1: String, atom2: String },
108 #[error("`{0}` is not an atomic field")]
109 ExpectedAtomic(String),
110
111 #[error("invalid range for type")]
113 InvalidRange,
114 #[error("overlapping range")]
115 OverlappingRange,
116 #[error("value out of range")]
117 ValueOutOfRange,
118
119 #[error("invalid divisor")]
121 InvalidDivisor,
122 #[error("invalid modulus")]
123 InvalidModulus,
124
125 #[error("invalid default value for type")]
127 InvalidDefault,
128
129 #[error("`{0}` is not a struct")]
131 ExpectedStruct(String),
132}
133
134impl ToErrorCode for SemanticError {
135 fn error_code(&self) -> &str {
136 match self {
137 Self::AlreadyDefined(_) => "E0200",
139 Self::NotDefined(_) => "E0201",
140 Self::MultipleInheritanceDisabled => "E0210",
142 Self::DClassOverflow => "E0211",
143 Self::FieldOverflow => "E0212",
144 Self::RedundantViewSuffix(_) => "E0220",
146 Self::RedundantKeyword(_) => "E0230",
148 Self::KeywordsInStructField => "E0240",
150 Self::RedundantCase => "E0250",
152 Self::RedundantDefault => "E0251",
153 Self::InvalidCaseValueType => "E0252",
154 Self::MismatchedKeywords { atom1: _, atom2: _ } => "E0260",
156 Self::ExpectedAtomic(_) => "E0261",
157 Self::InvalidRange => "E0270",
159 Self::OverlappingRange => "E0271",
160 Self::ValueOutOfRange => "E0272",
161 Self::InvalidDivisor => "E0280",
163 Self::InvalidModulus => "E0281",
164 Self::InvalidDefault => "E0290",
166 Self::ExpectedStruct(_) => "E0300",
168 }
169 }
170}
171
172#[derive(Debug, Error)]
178pub enum ParseError {
179 #[error("syntax error; {1}, found `{0:?}`")]
180 Error(DCToken, String),
181}
182
183impl ToErrorCode for ParseError {
184 fn error_code(&self) -> &str {
185 match self {
186 Self::Error(_, _) => "E0100",
187 }
188 }
189}
190
191pub(crate) struct Diagnostic {
192 span: Span,
193 stage: PipelineStage,
194 file_id: usize,
195 severity: codespan_diag::Severity,
196 error: PipelineError,
197}
198
199impl Diagnostic {
200 pub fn error(span: Span, pipeline: &mut PipelineData, err: impl Into<PipelineError>) -> Self {
201 Self {
202 span,
203 stage: pipeline.current_stage(),
204 file_id: pipeline.current_file(),
205 severity: codespan_diag::Severity::Error,
206 error: err.into(),
207 }
208 }
209}
210
211impl From<Diagnostic> for codespan_diag::Diagnostic<usize> {
213 fn from(val: Diagnostic) -> codespan_diag::Diagnostic<usize> {
214 codespan_diag::Diagnostic::new(val.severity)
215 .with_message(val.error.to_string())
216 .with_code(val.error.error_code())
217 .with_labels(vec![Label::new(
218 LabelStyle::Primary,
219 val.file_id,
220 val.span.min..val.span.max,
221 )])
222 .with_notes({
223 if discriminant(&val.stage) == discriminant(&PipelineStage::Parser) {
225 vec![
226 "Syntax errors are limited. Please see issue #19.".into(),
227 "https://gitlab.com/donet-server/donet/-/issues/19".into(),
228 ]
229 } else {
231 vec![
232 "For a more detailed explanation see:".into(),
233 format!("{}/#{}", ERR_DOCS, val.error.error_code().to_lowercase()),
234 ]
235 }
236 })
237 }
238}