mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 04:44:44 +00:00
Improve type mismatch messages
This commit is contained in:
parent
14bb674068
commit
d26909c317
7 changed files with 50 additions and 27 deletions
|
@ -874,7 +874,7 @@ impl Context {
|
||||||
&proj,
|
&proj,
|
||||||
t_loc,
|
t_loc,
|
||||||
self.caused_by(),
|
self.caused_by(),
|
||||||
self.get_no_candidate_hint(&proj),
|
Self::get_no_candidate_hint(&proj),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1169,7 +1169,7 @@ impl Context {
|
||||||
&proj,
|
&proj,
|
||||||
t_loc,
|
t_loc,
|
||||||
self.caused_by(),
|
self.caused_by(),
|
||||||
self.get_no_candidate_hint(&proj),
|
Self::get_no_candidate_hint(&proj),
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use erg_common::enum_unwrap;
|
use erg_common::enum_unwrap;
|
||||||
|
|
||||||
use crate::ty::typaram::TyParam;
|
use crate::ty::typaram::TyParam;
|
||||||
use crate::ty::Type;
|
use crate::ty::{HasType, Type};
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
|
|
||||||
|
@ -11,8 +11,9 @@ enum Sequence {
|
||||||
Backward,
|
Backward,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: these should not be in Context
|
||||||
impl Context {
|
impl Context {
|
||||||
fn readable_type(&self, typ: &Type) -> Type {
|
fn readable_type(typ: &Type) -> Type {
|
||||||
match typ {
|
match typ {
|
||||||
Type::FreeVar(fv) if fv.constraint_is_sandwiched() => {
|
Type::FreeVar(fv) if fv.constraint_is_sandwiched() => {
|
||||||
let (sub, sup) = fv.get_subsup().unwrap();
|
let (sub, sup) = fv.get_subsup().unwrap();
|
||||||
|
@ -25,7 +26,20 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_type_mismatch_hint(&self, expected: &Type, found: &Type) -> Option<String> {
|
/// TODO: custom types
|
||||||
|
fn get_verb_and_preposition(trait_: &Type) -> Option<(&str, &str, Sequence)> {
|
||||||
|
match &trait_.qual_name()[..] {
|
||||||
|
"Add" => Some(("add", "and", Sequence::Forward)),
|
||||||
|
"Sub" => Some(("subtract", "from", Sequence::Backward)),
|
||||||
|
"Mul" => Some(("multiply", "and", Sequence::Forward)),
|
||||||
|
"Div" => Some(("divide", "by", Sequence::Forward)),
|
||||||
|
"Eq" => Some(("compare", "and", Sequence::Forward)),
|
||||||
|
"Ord" => Some(("compare", "and", Sequence::Forward)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_type_mismatch_hint(expected: &Type, found: &Type) -> Option<String> {
|
||||||
let expected = if let Type::FreeVar(fv) = expected {
|
let expected = if let Type::FreeVar(fv) = expected {
|
||||||
if fv.is_linked() {
|
if fv.is_linked() {
|
||||||
fv.crack().clone()
|
fv.crack().clone()
|
||||||
|
@ -38,27 +52,26 @@ impl Context {
|
||||||
};
|
};
|
||||||
match (&expected.qual_name()[..], &found.qual_name()[..]) {
|
match (&expected.qual_name()[..], &found.qual_name()[..]) {
|
||||||
("Eq", "Float") => Some(String::from("Float has no equivalence relation defined. you should use `l - r <= Float.EPSILON` instead of `l == r`.")),
|
("Eq", "Float") => Some(String::from("Float has no equivalence relation defined. you should use `l - r <= Float.EPSILON` instead of `l == r`.")),
|
||||||
_ => None,
|
_ => {
|
||||||
|
let (verb, preposition, _sequence) = Self::get_verb_and_preposition(&expected)?;
|
||||||
|
found.union_types()
|
||||||
|
.map(|(t1, t2)| format!("cannot {verb} {t1} {preposition} {t2}"))
|
||||||
|
.or_else(|| {
|
||||||
|
let ts = expected.inner_ts();
|
||||||
|
Some(format!("cannot {verb} {found} {preposition} {}", ts[0]))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_no_candidate_hint(&self, proj: &Type) -> Option<String> {
|
pub(crate) fn get_no_candidate_hint(proj: &Type) -> Option<String> {
|
||||||
match proj {
|
match proj {
|
||||||
Type::Proj { lhs, rhs: _ } => {
|
Type::Proj { lhs, rhs: _ } => {
|
||||||
if let Type::FreeVar(fv) = lhs.as_ref() {
|
if let Type::FreeVar(fv) = lhs.as_ref() {
|
||||||
let (sub, sup) = fv.get_subsup()?;
|
let (sub, sup) = fv.get_subsup()?;
|
||||||
// TODO: automating
|
let (verb, preposition, sequence) = Self::get_verb_and_preposition(&sup)?;
|
||||||
let (verb, preposition, sequence) = match &sup.qual_name()[..] {
|
|
||||||
"Add" => Some(("add", "and", Sequence::Forward)),
|
|
||||||
"Sub" => Some(("subtract", "from", Sequence::Backward)),
|
|
||||||
"Mul" => Some(("multiply", "and", Sequence::Forward)),
|
|
||||||
"Div" => Some(("divide", "by", Sequence::Forward)),
|
|
||||||
"Eq" => Some(("compare", "and", Sequence::Forward)),
|
|
||||||
"Ord" => Some(("compare", "and", Sequence::Forward)),
|
|
||||||
_ => None,
|
|
||||||
}?;
|
|
||||||
let sup = enum_unwrap!(sup.typarams().remove(0), TyParam::Type);
|
let sup = enum_unwrap!(sup.typarams().remove(0), TyParam::Type);
|
||||||
let sup = self.readable_type(&sup);
|
let sup = Self::readable_type(&sup);
|
||||||
let (l, r) = if sequence == Sequence::Forward {
|
let (l, r) = if sequence == Sequence::Forward {
|
||||||
(sub, sup)
|
(sub, sup)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl Context {
|
||||||
&spec_t,
|
&spec_t,
|
||||||
body_t,
|
body_t,
|
||||||
self.get_candidates(body_t),
|
self.get_candidates(body_t),
|
||||||
self.get_type_mismatch_hint(&spec_t, body_t),
|
Self::get_type_mismatch_hint(&spec_t, body_t),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -256,7 +256,7 @@ impl Context {
|
||||||
&mono("LambdaFunc"),
|
&mono("LambdaFunc"),
|
||||||
t,
|
t,
|
||||||
self.get_candidates(t),
|
self.get_candidates(t),
|
||||||
self.get_type_mismatch_hint(&mono("LambdaFunc"), t),
|
Self::get_type_mismatch_hint(&mono("LambdaFunc"), t),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1075,7 +1075,7 @@ impl Context {
|
||||||
param_t,
|
param_t,
|
||||||
arg_t,
|
arg_t,
|
||||||
self.get_candidates(arg_t),
|
self.get_candidates(arg_t),
|
||||||
self.get_type_mismatch_hint(param_t, arg_t),
|
Self::get_type_mismatch_hint(param_t, arg_t),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -1130,7 +1130,7 @@ impl Context {
|
||||||
param_t,
|
param_t,
|
||||||
arg_t,
|
arg_t,
|
||||||
self.get_candidates(arg_t),
|
self.get_candidates(arg_t),
|
||||||
self.get_type_mismatch_hint(param_t, arg_t),
|
Self::get_type_mismatch_hint(param_t, arg_t),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -1187,7 +1187,7 @@ impl Context {
|
||||||
pt.typ(),
|
pt.typ(),
|
||||||
arg_t,
|
arg_t,
|
||||||
self.get_candidates(arg_t),
|
self.get_candidates(arg_t),
|
||||||
self.get_type_mismatch_hint(pt.typ(), arg_t),
|
Self::get_type_mismatch_hint(pt.typ(), arg_t),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
|
@ -1285,7 +1285,7 @@ impl Context {
|
||||||
maybe_sup,
|
maybe_sup,
|
||||||
maybe_sub,
|
maybe_sub,
|
||||||
self.get_candidates(maybe_sub),
|
self.get_candidates(maybe_sub),
|
||||||
self.get_type_mismatch_hint(maybe_sup, maybe_sub),
|
Self::get_type_mismatch_hint(maybe_sup, maybe_sub),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
match (maybe_sub, maybe_sup) {
|
match (maybe_sub, maybe_sup) {
|
||||||
|
|
|
@ -16,9 +16,9 @@ use erg_common::{
|
||||||
|
|
||||||
use erg_parser::error::{ParserRunnerError, ParserRunnerErrors};
|
use erg_parser::error::{ParserRunnerError, ParserRunnerErrors};
|
||||||
|
|
||||||
use crate::ty::{Predicate, Type};
|
use crate::context::Context;
|
||||||
|
|
||||||
use crate::hir::{Expr, Identifier, Signature};
|
use crate::hir::{Expr, Identifier, Signature};
|
||||||
|
use crate::ty::{Predicate, Type};
|
||||||
|
|
||||||
pub fn ordinal_num(n: usize) -> String {
|
pub fn ordinal_num(n: usize) -> String {
|
||||||
match n.to_string().chars().last().unwrap() {
|
match n.to_string().chars().last().unwrap() {
|
||||||
|
@ -972,6 +972,7 @@ passed keyword args: {kw_args_len}"
|
||||||
caused_by: String,
|
caused_by: String,
|
||||||
hint: Option<String>,
|
hint: Option<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let hint = hint.or_else(|| Context::get_type_mismatch_hint(trait_, class));
|
||||||
Self::new(
|
Self::new(
|
||||||
ErrorCore::new(
|
ErrorCore::new(
|
||||||
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
|
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
|
||||||
|
|
|
@ -168,7 +168,7 @@ impl ASTLowerer {
|
||||||
expect,
|
expect,
|
||||||
found,
|
found,
|
||||||
self.ctx.get_candidates(found),
|
self.ctx.get_candidates(found),
|
||||||
self.ctx.get_type_mismatch_hint(expect, found),
|
Context::get_type_mismatch_hint(expect, found),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1779,6 +1779,15 @@ impl Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn union_types(&self) -> Option<(Type, Type)> {
|
||||||
|
match self {
|
||||||
|
Type::FreeVar(fv) if fv.is_linked() => fv.crack().union_types(),
|
||||||
|
Type::Refinement(refine) => refine.t.union_types(),
|
||||||
|
Type::Or(t1, t2) => Some((*t1.clone(), *t2.clone())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// assert!((A or B).contains_union(B))
|
/// assert!((A or B).contains_union(B))
|
||||||
pub fn contains_union(&self, typ: &Type) -> bool {
|
pub fn contains_union(&self, typ: &Type) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue