Add Elm-style error diagnostics
Implement beautiful, informative error messages inspired by Elm: - Rich diagnostic rendering with source code snippets - Colored output with proper underlines showing error locations - Categorized error titles (Type Mismatch, Unknown Name, etc.) - Contextual hints and suggestions for common errors - Support for type errors, runtime errors, and parse errors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ use crate::ast::{
|
||||
LetDecl, Literal, LiteralKind, MatchArm, Parameter, Pattern, Program, Span, Statement,
|
||||
TypeDecl, TypeExpr, UnaryOp, VariantFields,
|
||||
};
|
||||
use crate::diagnostics::{Diagnostic, Severity};
|
||||
use crate::modules::ModuleLoader;
|
||||
use crate::types::{
|
||||
self, unify, EffectDef, EffectOpDef, EffectSet, HandlerDef, PropertySet, Type, TypeEnv,
|
||||
@@ -30,6 +31,72 @@ impl std::fmt::Display for TypeError {
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeError {
|
||||
/// Convert to a rich diagnostic for Elm-style error display
|
||||
pub fn to_diagnostic(&self) -> Diagnostic {
|
||||
// Categorize the error and extract hints
|
||||
let (title, hints) = categorize_type_error(&self.message);
|
||||
|
||||
Diagnostic {
|
||||
severity: Severity::Error,
|
||||
title,
|
||||
message: self.message.clone(),
|
||||
span: self.span,
|
||||
hints,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Categorize a type error message to provide better titles and hints
|
||||
fn categorize_type_error(message: &str) -> (String, Vec<String>) {
|
||||
let message_lower = message.to_lowercase();
|
||||
|
||||
if message_lower.contains("type mismatch") {
|
||||
(
|
||||
"Type Mismatch".to_string(),
|
||||
vec!["Check that the types on both sides of the expression are compatible.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("undefined variable") || message_lower.contains("not found") {
|
||||
(
|
||||
"Unknown Name".to_string(),
|
||||
vec![
|
||||
"Check the spelling of the name.".to_string(),
|
||||
"Make sure the variable is defined before use.".to_string(),
|
||||
],
|
||||
)
|
||||
} else if message_lower.contains("cannot unify") {
|
||||
(
|
||||
"Type Mismatch".to_string(),
|
||||
vec!["The types are not compatible. Check your function arguments and return types.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("expected") && message_lower.contains("argument") {
|
||||
(
|
||||
"Wrong Number of Arguments".to_string(),
|
||||
vec!["Check the function signature and provide the correct number of arguments.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("pure") && message_lower.contains("effect") {
|
||||
(
|
||||
"Purity Violation".to_string(),
|
||||
vec![
|
||||
"Functions marked 'is pure' cannot perform effects.".to_string(),
|
||||
"Remove the 'is pure' annotation or handle the effects.".to_string(),
|
||||
],
|
||||
)
|
||||
} else if message_lower.contains("effect") && message_lower.contains("unhandled") {
|
||||
(
|
||||
"Unhandled Effect".to_string(),
|
||||
vec!["Use a 'handle' expression to provide an implementation for this effect.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("recursive") {
|
||||
(
|
||||
"Invalid Recursion".to_string(),
|
||||
vec!["Check that recursive calls have proper base cases.".to_string()],
|
||||
)
|
||||
} else {
|
||||
("Type Error".to_string(), vec![])
|
||||
}
|
||||
}
|
||||
|
||||
/// Type checker
|
||||
pub struct TypeChecker {
|
||||
env: TypeEnv,
|
||||
|
||||
Reference in New Issue
Block a user