mirror of
https://github.com/typst/typst
synced 2025-05-21 12:35:29 +08:00
This also makes serialization support non-optional since it's too much feature-management for too little benefit.
114 lines
2.9 KiB
Rust
114 lines
2.9 KiB
Rust
//! Diagnostics for source code.
|
|
//!
|
|
//! Errors are never fatal, the document will always compile and yield a layout.
|
|
|
|
use std::collections::BTreeSet;
|
|
use std::fmt::{self, Display, Formatter};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::syntax::Span;
|
|
|
|
/// The result of some pass: Some output `T` and diagnostics.
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
pub struct Pass<T> {
|
|
/// The output of this compilation pass.
|
|
pub output: T,
|
|
/// User diagnostics accumulated in this pass.
|
|
pub diags: DiagSet,
|
|
}
|
|
|
|
impl<T> Pass<T> {
|
|
/// Create a new pass from output and diagnostics.
|
|
pub fn new(output: T, diags: DiagSet) -> Self {
|
|
Self { output, diags }
|
|
}
|
|
}
|
|
|
|
/// A set of diagnostics.
|
|
///
|
|
/// Since this is a [`BTreeSet`], there cannot be two equal (up to span)
|
|
/// diagnostics and you can quickly iterate diagnostics in source location
|
|
/// order.
|
|
pub type DiagSet = BTreeSet<Diag>;
|
|
|
|
/// A diagnostic with severity level and message.
|
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
|
pub struct Diag {
|
|
/// The source code location.
|
|
pub span: Span,
|
|
/// How severe / important the diagnostic is.
|
|
pub level: Level,
|
|
/// A message describing the diagnostic.
|
|
pub message: String,
|
|
}
|
|
|
|
impl Diag {
|
|
/// Create a new diagnostic from message and level.
|
|
pub fn new(span: impl Into<Span>, level: Level, message: impl Into<String>) -> Self {
|
|
Self {
|
|
span: span.into(),
|
|
level,
|
|
message: message.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for Diag {
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
write!(f, "{}: {}", self.level, self.message)
|
|
}
|
|
}
|
|
|
|
/// How severe / important a diagnostic is.
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
|
#[derive(Serialize, Deserialize)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum Level {
|
|
Warning,
|
|
Error,
|
|
}
|
|
|
|
impl Display for Level {
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
f.pad(match self {
|
|
Self::Warning => "warning",
|
|
Self::Error => "error",
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Construct a diagnostic with [`Error`](Level::Error) level.
|
|
///
|
|
/// ```
|
|
/// # use typst::error;
|
|
/// # use typst::syntax::Span;
|
|
/// # let span = Span::ZERO;
|
|
/// # let thing = "";
|
|
/// let error = error!(span, "there is an error with {}", thing);
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! error {
|
|
($span:expr, $($tts:tt)*) => {
|
|
$crate::diag::Diag::new(
|
|
$span,
|
|
$crate::diag::Level::Error,
|
|
format!($($tts)*),
|
|
)
|
|
};
|
|
}
|
|
|
|
/// Construct a diagnostic with [`Warning`](Level::Warning) level.
|
|
///
|
|
/// This works exactly like [`error!`].
|
|
#[macro_export]
|
|
macro_rules! warning {
|
|
($span:expr, $($tts:tt)*) => {
|
|
$crate::diag::Diag::new(
|
|
$span,
|
|
$crate::diag::Level::Warning,
|
|
format!($($tts)*),
|
|
)
|
|
};
|
|
}
|