diff options
Diffstat (limited to 'src/diagnostic.rs')
| -rw-r--r-- | src/diagnostic.rs | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/src/diagnostic.rs b/src/diagnostic.rs new file mode 100644 index 00000000..a5c5d0b8 --- /dev/null +++ b/src/diagnostic.rs @@ -0,0 +1,91 @@ +//! Diagnostics (errors / warnings) in source code. +//! +//! There are no fatal errors. The document will always compile and yield a +//! layout. However, this is a best effort process and bad things will still +//! generate errors and warnings. + +use serde::Serialize; +use crate::syntax::span::SpanVec; + +/// A list of spanned diagnostics. +pub type Diagnostics = SpanVec<Diagnostic>; + +/// A diagnostic that arose in parsing or layouting. +#[derive(Debug, Clone, Eq, PartialEq, Serialize)] +pub struct Diagnostic { + /// How severe / important the diagnostic is. + pub level: Level, + /// A message describing the diagnostic. + pub message: String, +} + +/// How severe / important a diagnostic is. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum Level { + Warning, + Error, +} + +impl Diagnostic { + /// Create a new diagnostic from message and level. + pub fn new(message: impl Into<String>, level: Level) -> Self { + Self { message: message.into(), level } + } +} + +/// Construct a diagnostic with `Error` level. +/// +/// ``` +/// # use typstc::error; +/// # use typstc::syntax::span::Span; +/// # use typstc::Feedback; +/// # let span = Span::ZERO; +/// # let mut feedback = Feedback::new(); +/// # let name = ""; +/// // Create formatted error values. +/// let error = error!("expected {}", name); +/// +/// // Create spanned errors. +/// let spanned = error!(span, "there is an error here"); +/// +/// // Create an error and directly add it to existing feedback. +/// error!(@feedback, span, "oh no!"); +/// ``` +#[macro_export] +macro_rules! error { + ($($tts:tt)*) => { + $crate::__impl_diagnostic!($crate::diagnostic::Level::Error; $($tts)*) + }; +} + +/// Construct a diagnostic with `Warning` level. +/// +/// This works exactly like `error!`. See its documentation for more +/// information. +#[macro_export] +macro_rules! warning { + ($($tts:tt)*) => { + $crate::__impl_diagnostic!($crate::diagnostic::Level::Warning; $($tts)*) + }; +} + +/// Backs the `error!` and `warning!` macros. +#[macro_export] +#[doc(hidden)] +macro_rules! __impl_diagnostic { + ($level:expr; @$feedback:expr, $($tts:tt)*) => { + $feedback.diagnostics.push($crate::__impl_diagnostic!($level; $($tts)*)); + }; + + ($level:expr; $fmt:literal $($tts:tt)*) => { + $crate::diagnostic::Diagnostic::new(format!($fmt $($tts)*), $level) + }; + + ($level:expr; $span:expr, $fmt:literal $($tts:tt)*) => { + $crate::syntax::span::Spanned::new( + $crate::__impl_diagnostic!($level; $fmt $($tts)*), + $span, + ) + }; +} |
