feat: Elm-quality error messages with error codes
- Add ErrorCode enum with categorized codes (E01xx parse, E02xx type,
E03xx name, E04xx effect, E05xx pattern, E06xx module, E07xx behavioral)
- Extend Diagnostic struct with error code, expected/actual types, and
secondary spans
- Add format_type_diff() for visual type comparison in error messages
- Add help URLs linking to lux-lang.dev/errors/{code}
- Update typechecker, parser, and interpreter to use error codes
- Categorize errors with specific codes and helpful hints
Error messages now show:
- Error code in header: -- ERROR[E0301] ──
- Clear error category title
- Visual type diff for type mismatches
- Context-aware hints
- "Learn more" URL for documentation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::diagnostics::{Diagnostic, Severity};
|
||||
use crate::diagnostics::{Diagnostic, ErrorCode, Severity};
|
||||
use crate::lexer::{LexError, Lexer, StringPart, Token, TokenKind};
|
||||
use std::fmt;
|
||||
|
||||
@@ -27,57 +27,79 @@ impl fmt::Display for ParseError {
|
||||
impl ParseError {
|
||||
/// Convert to a rich diagnostic for Elm-style error display
|
||||
pub fn to_diagnostic(&self) -> Diagnostic {
|
||||
let (title, hints) = categorize_parse_error(&self.message);
|
||||
let (code, title, hints) = categorize_parse_error(&self.message);
|
||||
|
||||
Diagnostic {
|
||||
severity: Severity::Error,
|
||||
code,
|
||||
title,
|
||||
message: self.message.clone(),
|
||||
span: self.span,
|
||||
hints,
|
||||
expected_type: None,
|
||||
actual_type: None,
|
||||
secondary_spans: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Categorize parse errors to provide better titles and hints
|
||||
fn categorize_parse_error(message: &str) -> (String, Vec<String>) {
|
||||
/// Categorize parse errors to provide better titles, hints, and error codes
|
||||
fn categorize_parse_error(message: &str) -> (Option<ErrorCode>, String, Vec<String>) {
|
||||
let message_lower = message.to_lowercase();
|
||||
|
||||
if message_lower.contains("unexpected") && message_lower.contains("expected") {
|
||||
(
|
||||
Some(ErrorCode::E0101),
|
||||
"Unexpected Token".to_string(),
|
||||
vec!["Check for missing or misplaced punctuation.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("expected") && message_lower.contains("expression") {
|
||||
(
|
||||
Some(ErrorCode::E0101),
|
||||
"Missing Expression".to_string(),
|
||||
vec!["An expression was expected here.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("expected") && message_lower.contains(":") {
|
||||
(
|
||||
Some(ErrorCode::E0104),
|
||||
"Missing Type Annotation".to_string(),
|
||||
vec!["A type annotation is required here.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("unclosed") || message_lower.contains("unterminated") {
|
||||
(
|
||||
Some(ErrorCode::E0102),
|
||||
"Unclosed Delimiter".to_string(),
|
||||
vec![
|
||||
"Check for matching opening and closing brackets.".to_string(),
|
||||
"Make sure all strings are properly closed with quotes.".to_string(),
|
||||
],
|
||||
)
|
||||
} else if message_lower.contains("invalid") && message_lower.contains("literal") {
|
||||
(
|
||||
Some(ErrorCode::E0103),
|
||||
"Invalid Literal".to_string(),
|
||||
vec!["Check the format of the literal value.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("invalid") && message_lower.contains("operator") {
|
||||
(
|
||||
Some(ErrorCode::E0105),
|
||||
"Invalid Operator".to_string(),
|
||||
vec!["Check the operator syntax.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("invalid") {
|
||||
(
|
||||
Some(ErrorCode::E0100),
|
||||
"Invalid Syntax".to_string(),
|
||||
vec!["Check the syntax of this construct.".to_string()],
|
||||
)
|
||||
} else if message_lower.contains("identifier") {
|
||||
(
|
||||
Some(ErrorCode::E0100),
|
||||
"Invalid Identifier".to_string(),
|
||||
vec!["Identifiers must start with a letter and contain only letters, numbers, and underscores.".to_string()],
|
||||
)
|
||||
} else {
|
||||
("Parse Error".to_string(), vec![])
|
||||
(Some(ErrorCode::E0100), "Parse Error".to_string(), vec![])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user