use crate::position::Position;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum TypeError {
TypeMismatch {
at_node: String,
expected: String,
got: String,
context: Vec<String>,
},
UnknownIdentifier {
at_node: String,
name: String,
},
ArityMismatch {
at_node: String,
expected: usize,
got: usize,
},
NonExhaustiveMatch {
at_node: String,
missing: Vec<String>,
},
UnknownField {
at_node: String,
record_type: String,
field: String,
},
DuplicateField {
at_node: String,
field: String,
},
UnknownVariant {
at_node: String,
constructor: String,
},
EffectNotDeclared {
at_node: String,
effect: String,
},
InfiniteType {
at_node: String,
},
AmbiguousType {
at_node: String,
},
RecursiveTypeWithoutConstructor {
at_node: String,
name: String,
},
RefinementViolation {
at_node: String,
fn_name: String,
param_index: usize,
binding: String,
reason: String,
},
ExamplesOnEffectfulFn {
at_node: String,
fn_name: String,
},
ExampleArityMismatch {
at_node: String,
fn_name: String,
case_index: usize,
expected: usize,
got: usize,
},
ExampleMismatch {
at_node: String,
fn_name: String,
case_index: usize,
expected: String,
got: String,
},
}
impl TypeError {
pub fn node(&self) -> &str {
match self {
TypeError::TypeMismatch { at_node, .. }
| TypeError::UnknownIdentifier { at_node, .. }
| TypeError::ArityMismatch { at_node, .. }
| TypeError::NonExhaustiveMatch { at_node, .. }
| TypeError::UnknownField { at_node, .. }
| TypeError::DuplicateField { at_node, .. }
| TypeError::UnknownVariant { at_node, .. }
| TypeError::EffectNotDeclared { at_node, .. }
| TypeError::InfiniteType { at_node, .. }
| TypeError::AmbiguousType { at_node, .. }
| TypeError::RecursiveTypeWithoutConstructor { at_node, .. }
| TypeError::RefinementViolation { at_node, .. }
| TypeError::ExamplesOnEffectfulFn { at_node, .. }
| TypeError::ExampleArityMismatch { at_node, .. }
| TypeError::ExampleMismatch { at_node, .. } => at_node,
}
}
}
impl std::fmt::Display for TypeError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
TypeError::TypeMismatch { at_node, expected, got, context } => {
write!(f, "type mismatch at {at_node}: expected {expected}, got {got}")?;
if !context.is_empty() { write!(f, " ({})", context.join(" / "))?; }
Ok(())
}
TypeError::UnknownIdentifier { at_node, name } => write!(f, "unknown identifier `{name}` at {at_node}"),
TypeError::ArityMismatch { at_node, expected, got } => write!(f, "arity mismatch at {at_node}: expected {expected}, got {got}"),
TypeError::NonExhaustiveMatch { at_node, missing } => write!(f, "non-exhaustive match at {at_node}: missing {missing:?}"),
TypeError::UnknownField { at_node, record_type, field } => write!(f, "unknown field `{field}` on {record_type} at {at_node}"),
TypeError::DuplicateField { at_node, field } => write!(f, "duplicate field `{field}` at {at_node}"),
TypeError::UnknownVariant { at_node, constructor } => write!(f, "unknown constructor `{constructor}` at {at_node}"),
TypeError::EffectNotDeclared { at_node, effect } => write!(f, "effect `{effect}` not declared at {at_node}"),
TypeError::InfiniteType { at_node } => write!(f, "infinite type (occurs check) at {at_node}"),
TypeError::AmbiguousType { at_node } => write!(f, "ambiguous type at {at_node}"),
TypeError::RecursiveTypeWithoutConstructor { at_node, name } => write!(f, "recursive type {name} has no constructor at {at_node}"),
TypeError::RefinementViolation { at_node, fn_name, param_index, binding, reason } =>
write!(f, "refinement violated at {at_node}: argument {} of `{fn_name}` (binding `{binding}`): {reason}",
param_index + 1),
TypeError::ExamplesOnEffectfulFn { at_node, fn_name } =>
write!(f, "function `{fn_name}` at {at_node} carries `examples` but declares effects; \
v1 restricts examples to pure functions"),
TypeError::ExampleArityMismatch { at_node, fn_name, case_index, expected, got } =>
write!(f, "example #{} of `{fn_name}` at {at_node}: expected {expected} argument(s), got {got}",
case_index + 1),
TypeError::ExampleMismatch { at_node, fn_name, case_index, expected, got } =>
write!(f, "example #{} of `{fn_name}` at {at_node}: expected {expected}, got {got}",
case_index + 1),
}
}
}
impl std::error::Error for TypeError {}
#[derive(Debug, Clone)]
pub struct PositionedError {
pub error: TypeError,
pub position: Option<Position>,
}
impl Serialize for PositionedError {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeMap;
let inner = serde_json::to_value(&self.error)
.map_err(serde::ser::Error::custom)?;
let obj = inner
.as_object()
.ok_or_else(|| serde::ser::Error::custom("TypeError did not serialize as an object"))?;
let extra = if self.position.is_some() { 3 } else { 2 };
let mut map = serializer.serialize_map(Some(obj.len() + extra))?;
for (k, v) in obj {
map.serialize_entry(k, v)?;
}
map.serialize_entry("rule_tag", self.error.rule_tag())?;
map.serialize_entry("rule_explanation", self.error.rule_explanation())?;
if let Some(p) = &self.position {
map.serialize_entry("position", p)?;
}
map.end()
}
}
impl<'de> Deserialize<'de> for PositionedError {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let mut value = serde_json::Value::deserialize(deserializer)?;
let position = value
.as_object_mut()
.and_then(|o| o.remove("position"))
.and_then(|p| serde_json::from_value::<Position>(p).ok());
if let Some(o) = value.as_object_mut() {
o.remove("rule_tag");
o.remove("rule_explanation");
}
let error: TypeError =
serde_json::from_value(value).map_err(serde::de::Error::custom)?;
Ok(PositionedError { error, position })
}
}
impl PositionedError {
pub fn new(error: TypeError, position: Option<Position>) -> Self {
Self { error, position }
}
pub fn without_position(error: TypeError) -> Self {
Self { error, position: None }
}
pub fn node(&self) -> &str {
self.error.node()
}
}
impl std::fmt::Display for PositionedError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self.position {
Some(p) => write!(f, "[{}] {}", p.render(), self.error),
None => self.error.fmt(f),
}
}
}
impl std::error::Error for PositionedError {}
impl From<TypeError> for PositionedError {
fn from(e: TypeError) -> Self {
Self::without_position(e)
}
}