Skip to content

Commit c9817d4

Browse files
committed
prettier errors
1 parent 29c5ba8 commit c9817d4

2 files changed

Lines changed: 267 additions & 1 deletion

File tree

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ fn main() {
103103
),
104104
None => format!("{}", module.path.display()),
105105
};
106-
error_messages.push(format!("{location}:\n\n{err}"));
106+
error_messages.push(format!("{location}:\n\n {}", err.format_pretty()));
107107
}
108108
}
109109

src/typechecker/error.rs

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use std::collections::HashMap;
2+
use std::fmt::Write;
3+
14
use thiserror;
25

36
use crate::cst::QualifiedIdent;
@@ -649,4 +652,267 @@ impl TypeError {
649652
}
650653
}
651654
}
655+
656+
/// Format the error with readable multi-line layout and normalized type variables.
657+
/// Unification variables like `?120` become `t0`, `t1`, etc.
658+
pub fn format_pretty(&self) -> String {
659+
let var_map = self.build_var_map();
660+
match self {
661+
TypeError::UnificationError { expected, found, .. } => {
662+
format!(
663+
"Could not match type\n\n {}\n\n with type\n\n {}",
664+
pretty_type(expected, &var_map),
665+
pretty_type(found, &var_map),
666+
)
667+
}
668+
TypeError::KindsDoNotUnify { expected, found, .. } => {
669+
format!(
670+
"Could not match kind\n\n {}\n\n with kind\n\n {}",
671+
pretty_type(expected, &var_map),
672+
pretty_type(found, &var_map),
673+
)
674+
}
675+
TypeError::HoleInferredType { name, ty, .. } => {
676+
format!(
677+
"Hole ?{} has the inferred type\n\n {}",
678+
interner::resolve(*name).unwrap_or_default(),
679+
pretty_type(ty, &var_map),
680+
)
681+
}
682+
TypeError::InfiniteType { var, ty, .. } => {
683+
format!(
684+
"An infinite type was inferred for type variable t{}\n\n {}",
685+
var.0,
686+
pretty_type(ty, &var_map),
687+
)
688+
}
689+
TypeError::InfiniteKind { var, ty, .. } => {
690+
format!(
691+
"An infinite kind was inferred for type t{}\n\n {}",
692+
var.0,
693+
pretty_type(ty, &var_map),
694+
)
695+
}
696+
TypeError::NoInstanceFound { class_name, type_args, .. } => {
697+
let args: Vec<String> = type_args.iter().map(|ty| pretty_type(ty, &var_map)).collect();
698+
format!(
699+
"No type class instance was found for\n\n {} {}",
700+
class_name,
701+
args.join(" "),
702+
)
703+
}
704+
TypeError::CannotGeneralizeRecursiveFunction { name, type_, .. } => {
705+
format!(
706+
"Unable to generalize the type of the recursive function {}.\n The inferred type was\n\n {}\n\n Try adding a type signature.",
707+
interner::resolve(*name).unwrap_or_default(),
708+
pretty_type(type_, &var_map),
709+
)
710+
}
711+
TypeError::MissingClassMember { class_name, members, .. } => {
712+
let mut s = format!("The class {} is missing the following members:\n", class_name);
713+
for (name, ty) in members {
714+
let _ = write!(s, "\n {} :: {}", interner::resolve(*name).unwrap_or_default(), pretty_type(ty, &var_map));
715+
}
716+
s
717+
}
718+
TypeError::EscapedSkolem { name, ty, .. } => {
719+
format!(
720+
"A type variable has escaped its scope: {}\n\n {}",
721+
interner::resolve(*name).unwrap_or_default(),
722+
pretty_type(ty, &var_map),
723+
)
724+
}
725+
TypeError::ExpectedType { found, .. } => {
726+
format!(
727+
"Expected type of kind Type, but found kind\n\n {}",
728+
pretty_type(found, &var_map),
729+
)
730+
}
731+
TypeError::CannotApplyExpressionOfTypeOnType { type_, .. } => {
732+
format!(
733+
"Cannot apply expression of type\n\n {}\n\n to a type argument",
734+
pretty_type(type_, &var_map),
735+
)
736+
}
737+
// For all other errors, use the default Display but with normalized unif vars
738+
_ => {
739+
if var_map.is_empty() {
740+
format!("{self}")
741+
} else {
742+
// Replace ?N patterns in the default display string
743+
let s = format!("{self}");
744+
replace_unif_vars_in_string(&s, &var_map)
745+
}
746+
}
747+
}
748+
}
749+
750+
/// Collect all types referenced by this error and build a normalized var mapping.
751+
fn build_var_map(&self) -> HashMap<u32, usize> {
752+
let mut unif_ids = Vec::new();
753+
self.collect_types(&mut |ty| collect_unif_vars(ty, &mut unif_ids));
754+
let mut map = HashMap::new();
755+
for id in &unif_ids {
756+
let len = map.len();
757+
map.entry(*id).or_insert(len);
758+
}
759+
map
760+
}
761+
762+
/// Visit all Type values in this error variant.
763+
fn collect_types(&self, visitor: &mut dyn FnMut(&Type)) {
764+
match self {
765+
TypeError::UnificationError { expected, found, .. } => { visitor(expected); visitor(found); }
766+
TypeError::InfiniteType { ty, .. } | TypeError::InfiniteKind { ty, .. } => visitor(ty),
767+
TypeError::HoleInferredType { ty, .. } => visitor(ty),
768+
TypeError::NoInstanceFound { type_args, .. }
769+
| TypeError::OverlappingInstances { type_args, .. }
770+
| TypeError::PossiblyInfiniteInstance { type_args, .. }
771+
| TypeError::PossiblyInfiniteCoercibleInstance { type_args, .. } => {
772+
for ty in type_args { visitor(ty); }
773+
}
774+
TypeError::CannotGeneralizeRecursiveFunction { type_, .. }
775+
| TypeError::CannotApplyExpressionOfTypeOnType { type_, .. } => visitor(type_),
776+
TypeError::MissingClassMember { members, .. } => {
777+
for (_, ty) in members { visitor(ty); }
778+
}
779+
TypeError::KindsDoNotUnify { expected, found, .. } => { visitor(expected); visitor(found); }
780+
TypeError::ExpectedType { found, .. } => visitor(found),
781+
TypeError::EscapedSkolem { ty, .. } => visitor(ty),
782+
TypeError::QuantificationCheckFailureInType { ty, .. }
783+
| TypeError::QuantificationCheckFailureInKind { ty, .. } => visitor(ty),
784+
_ => {}
785+
}
786+
}
787+
}
788+
789+
/// Collect unification variable IDs from a type in order of first appearance.
790+
fn collect_unif_vars(ty: &Type, ids: &mut Vec<u32>) {
791+
match ty {
792+
Type::Unif(id) => {
793+
if !ids.contains(&id.0) {
794+
ids.push(id.0);
795+
}
796+
}
797+
Type::App(f, a) => { collect_unif_vars(f, ids); collect_unif_vars(a, ids); }
798+
Type::Fun(a, b) => { collect_unif_vars(a, ids); collect_unif_vars(b, ids); }
799+
Type::Forall(_, t) => collect_unif_vars(t, ids),
800+
Type::Record(fields, tail) => {
801+
for (_, t) in fields { collect_unif_vars(t, ids); }
802+
if let Some(t) = tail { collect_unif_vars(t, ids); }
803+
}
804+
_ => {}
805+
}
806+
}
807+
808+
/// Format a type with normalized unification variable names.
809+
fn pretty_type(ty: &Type, var_map: &HashMap<u32, usize>) -> String {
810+
if var_map.is_empty() {
811+
return format!("{ty}");
812+
}
813+
let mut out = String::new();
814+
fmt_type(&mut out, ty, var_map, false);
815+
out
816+
}
817+
818+
fn fmt_type(out: &mut String, ty: &Type, var_map: &HashMap<u32, usize>, nested: bool) {
819+
match ty {
820+
Type::Unif(id) => {
821+
if let Some(&idx) = var_map.get(&id.0) {
822+
let _ = write!(out, "t{idx}");
823+
} else {
824+
let _ = write!(out, "t{}", id.0);
825+
}
826+
}
827+
Type::Var(sym) => {
828+
let _ = write!(out, "{}", interner::resolve(*sym).unwrap_or_default());
829+
}
830+
Type::Con(sym) => {
831+
let _ = write!(out, "{sym}");
832+
}
833+
Type::App(func, arg) => {
834+
if nested { out.push('('); }
835+
match func.as_ref() {
836+
Type::App(..) | Type::Con(..) | Type::Var(..) | Type::Unif(..) => fmt_type(out, func, var_map, false),
837+
_ => fmt_type(out, func, var_map, true),
838+
}
839+
out.push(' ');
840+
fmt_type(out, arg, var_map, true);
841+
if nested { out.push(')'); }
842+
}
843+
Type::Fun(from, to) => {
844+
if nested { out.push('('); }
845+
fmt_type(out, from, var_map, true);
846+
out.push_str(" -> ");
847+
fmt_type(out, to, var_map, false);
848+
if nested { out.push(')'); }
849+
}
850+
Type::Forall(vars, body) => {
851+
if nested { out.push('('); }
852+
out.push_str("forall");
853+
for (v, visible) in vars {
854+
if *visible {
855+
let _ = write!(out, " @{}", interner::resolve(*v).unwrap_or_default());
856+
} else {
857+
let _ = write!(out, " {}", interner::resolve(*v).unwrap_or_default());
858+
}
859+
}
860+
out.push_str(". ");
861+
fmt_type(out, body, var_map, false);
862+
if nested { out.push(')'); }
863+
}
864+
Type::TypeString(sym) => {
865+
let _ = write!(out, "\"{}\"", interner::resolve(*sym).unwrap_or_default());
866+
}
867+
Type::TypeInt(n) => {
868+
let _ = write!(out, "{n}");
869+
}
870+
Type::Record(fields, tail) => {
871+
out.push_str("{ ");
872+
for (i, (label, field_ty)) in fields.iter().enumerate() {
873+
if i > 0 { out.push_str(", "); }
874+
let _ = write!(out, "{} :: ", interner::resolve(*label).unwrap_or_default());
875+
fmt_type(out, field_ty, var_map, false);
876+
}
877+
if let Some(tail) = tail {
878+
if !fields.is_empty() { out.push_str(" | "); }
879+
fmt_type(out, tail, var_map, false);
880+
}
881+
out.push_str(" }");
882+
}
883+
}
884+
}
885+
886+
/// Replace `?N` patterns in a pre-formatted string with normalized `tN` names.
887+
fn replace_unif_vars_in_string(s: &str, var_map: &HashMap<u32, usize>) -> String {
888+
let mut result = String::with_capacity(s.len());
889+
let mut chars = s.char_indices().peekable();
890+
while let Some((i, c)) = chars.next() {
891+
if c == '?' {
892+
// Try to parse a number after '?'
893+
let start = i + 1;
894+
let mut end = start;
895+
while let Some(&(j, d)) = chars.peek() {
896+
if d.is_ascii_digit() {
897+
end = j + 1;
898+
chars.next();
899+
} else {
900+
break;
901+
}
902+
}
903+
if end > start {
904+
if let Ok(id) = s[start..end].parse::<u32>() {
905+
if let Some(&idx) = var_map.get(&id) {
906+
let _ = write!(result, "t{idx}");
907+
continue;
908+
}
909+
}
910+
}
911+
result.push(c);
912+
result.push_str(&s[start..end]);
913+
} else {
914+
result.push(c);
915+
}
916+
}
917+
result
652918
}

0 commit comments

Comments
 (0)