submilli_engine/
compiler_error.rs1use crate::{Diagnostic, FileId, Severity, Span};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6pub enum CompilerStage {
7 Parse,
8 Infer,
9 Codegen,
10}
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum CompilerFailure {
14 Limit {
15 stage: CompilerStage,
16 span: Option<Span>,
17 message: String,
18 help: Vec<String>,
19 },
20 Internal {
21 stage: CompilerStage,
22 span: Option<Span>,
23 message: String,
24 },
25}
26
27impl CompilerFailure {
28 pub(crate) fn with_span(mut self, source_span: Span) -> Self {
29 let (Self::Limit { span, .. } | Self::Internal { span, .. }) = &mut self;
30 if span.is_none() {
31 *span = Some(source_span);
32 }
33 self
34 }
35
36 pub(crate) fn with_stage(mut self, current_stage: CompilerStage) -> Self {
37 match &mut self {
38 Self::Limit { stage, .. } | Self::Internal { stage, .. } => *stage = current_stage,
39 }
40 self
41 }
42}
43
44impl std::fmt::Display for CompilerFailure {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 let (category, stage, message) = match self {
47 Self::Limit { stage, message, .. } => ("compiler limit", stage, message),
48 Self::Internal { stage, message, .. } => ("internal compiler failure", stage, message),
49 };
50 write!(f, "{category} during {stage:?}: {message}")
51 }
52}
53
54impl std::error::Error for CompilerFailure {}
55
56#[derive(Clone, Debug, PartialEq, Eq)]
59pub struct CompileError {
60 pub diagnostics: Vec<Diagnostic>,
61 pub fatal: Option<CompilerFailure>,
62}
63
64impl CompileError {
65 pub fn with_prior_diagnostics(mut self, diagnostics: &[Diagnostic]) -> Self {
66 self.diagnostics.splice(0..0, diagnostics.iter().cloned());
67 self
68 }
69
70 pub(crate) fn with_limit_span_cut<'a>(
76 mut self,
77 text_of: impl Fn(FileId) -> Option<&'a str>,
78 ) -> Self {
79 if let Some(CompilerFailure::Limit {
80 span: Some(span), ..
81 }) = &mut self.fatal
82 && let Some(text) = text_of(span.file)
83 {
84 *span = span.first_line_of(text);
85 }
86 self
87 }
88
89 pub fn into_diagnostics(mut self, _file: FileId) -> Vec<Diagnostic> {
92 if let Some(fatal) = self.fatal {
93 let span = match &fatal {
94 CompilerFailure::Limit { span, .. } | CompilerFailure::Internal { span, .. } => {
95 *span
96 }
97 };
98 let (message, help) = match &fatal {
99 CompilerFailure::Limit { message, help, .. } => (message.clone(), help.clone()),
100 CompilerFailure::Internal { .. } => (
101 fatal.to_string(),
102 vec!["report this compiler error with the source program".into()],
103 ),
104 };
105 self.diagnostics.push(Diagnostic {
106 severity: Severity::Error,
107 span: span.unwrap_or(Span::at(FileId::COMPILER)),
108 message,
109 help,
110 notes: Vec::new(),
111 });
112 }
113 self.diagnostics
114 }
115}
116
117impl From<Vec<Diagnostic>> for CompileError {
118 fn from(diagnostics: Vec<Diagnostic>) -> Self {
119 Self {
120 diagnostics,
121 fatal: None,
122 }
123 }
124}
125
126impl From<CompilerFailure> for CompileError {
127 fn from(fatal: CompilerFailure) -> Self {
128 Self {
129 diagnostics: Vec::new(),
130 fatal: Some(fatal),
131 }
132 }
133}
134
135impl std::fmt::Display for CompileError {
136 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137 if let Some(fatal) = &self.fatal {
138 return fatal.fmt(f);
139 }
140 write!(
141 f,
142 "compilation failed with {} diagnostics",
143 self.diagnostics.len()
144 )
145 }
146}
147
148impl std::error::Error for CompileError {
149 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
150 self.fatal
151 .as_ref()
152 .map(|error| error as &dyn std::error::Error)
153 }
154}