better typechecker errors

This commit is contained in:
Masashi 2025-12-15 21:42:52 +05:30
commit a62432bfbb
3 changed files with 108 additions and 5 deletions

View file

@ -95,6 +95,56 @@ fn format_parse_error(source: &str, error: &suicmez::parser::ParseError) -> Stri
format!("Parse error: {} (at byte {})", error.message, error.span.start)
}
fn format_type_error(source: &str, error: &suicmez::typechecker::TypeError) -> String {
// Find the line containing the error
let lines: Vec<&str> = source.lines().collect();
let mut current_pos = 0;
for (line_idx, line) in lines.iter().enumerate() {
let line_start = current_pos;
let line_end = current_pos + line.len();
// Check if the error span intersects with this line
if error.span.start < line_end && error.span.end > line_start {
let mut result = String::new();
// Print the error message
result.push_str(&format!("Type error: {}\n", error.kind));
// Print the line number and content
result.push_str(&format!("{} | {}\n", line_idx + 1, line));
// Calculate column positions within the line
let line_start_col = error.span.start.saturating_sub(line_start);
let line_end_col = (error.span.end - line_start).min(line.len());
// Print spaces and squiggly line for the span
result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len())));
for _ in 0..line_start_col {
result.push(' ');
}
for _ in line_start_col..line_end_col {
result.push('~');
}
result.push('\n');
// Print caret at the start position
result.push_str(&format!("{} | ", " ".repeat((line_idx + 1).to_string().len())));
for _ in 0..line_start_col {
result.push(' ');
}
result.push('^');
return result;
}
current_pos = line_end + 1; // +1 for the newline character
}
// Fallback if we can't find the line
format!("Type error: {} (at byte {})", error.kind, error.span.start)
}
fn run_file(filename: &str) -> Result<(), String> {
// Read the source file
let source = fs::read_to_string(filename)
@ -134,10 +184,7 @@ fn run_file(filename: &str) -> Result<(), String> {
// Typecheck the AST
let mut typechecker = TypeChecker::new();
let typed_nodes = typechecker.typecheck_program(&lowered_nodes).map_err(|e| {
format!(
"Type error at {}:{}: {:?}",
e.span.file, e.span.start, e.kind
)
format_type_error(&source, &e)
})?;
println!(

View file

@ -1,6 +1,7 @@
// src/typechecker.rs
use crate::ast::*;
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Type {
@ -86,6 +87,61 @@ pub enum TypeErrorKind {
Other(String),
}
impl fmt::Display for TypeErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeErrorKind::TypeMismatch(expected, actual) => {
write!(f, "Type mismatch: expected {}, found {}", expected.to_string(), actual.to_string())
}
TypeErrorKind::UndefinedVariable(name) => {
write!(f, "Undefined variable '{}'", name)
}
TypeErrorKind::UndefinedType(name) => {
write!(f, "Undefined type '{}'", name)
}
TypeErrorKind::UndefinedFunction(name) => {
write!(f, "Undefined function '{}'", name)
}
TypeErrorKind::UndefinedField(field, ty) => {
write!(f, "Undefined field '{}' on type {}", field, ty.to_string())
}
TypeErrorKind::UndefinedVariant(enum_name, variant) => {
write!(f, "Undefined variant '{}' in enum '{}'", variant, enum_name)
}
TypeErrorKind::ArityMismatch(expected, actual) => {
write!(f, "Function expects {} arguments, but {} were provided", expected, actual)
}
TypeErrorKind::NotAFunction(ty) => {
write!(f, "Expected a function, but found {}", ty.to_string())
}
TypeErrorKind::NotAnArray(ty) => {
write!(f, "Expected an array, but found {}", ty.to_string())
}
TypeErrorKind::NotAStruct(ty) => {
write!(f, "Expected a struct, but found {}", ty.to_string())
}
TypeErrorKind::NotAnEnum(ty) => {
write!(f, "Expected an enum, but found {}", ty.to_string())
}
TypeErrorKind::InvalidCast(from, to) => {
write!(f, "Invalid cast from {} to {}", from.to_string(), to.to_string())
}
TypeErrorKind::InvalidPattern(msg) => {
write!(f, "Invalid pattern: {}", msg)
}
TypeErrorKind::MutableityError(msg) => {
write!(f, "Mutability error: {}", msg)
}
TypeErrorKind::LinearityError(msg) => {
write!(f, "Linearity error: {}", msg)
}
TypeErrorKind::Other(msg) => {
write!(f, "{}", msg)
}
}
}
}
#[derive(Clone)]
struct VarInfo {
ty: Type,

View file

@ -5,7 +5,7 @@ fn main() -> int do
else
0
let i = 0;
let mut i = 0;
while i < 5
i = i + 1;
i