add When pattern errors

This commit is contained in:
Folkert 2020-04-07 18:06:00 +02:00
parent 2d1a1621c3
commit 7f999a06f3
7 changed files with 276 additions and 35 deletions

View File

@ -517,7 +517,7 @@ pub fn constrain_expr(
PExpected::ForReason(
PReason::WhenMatch { index },
cond_type.clone(),
region,
Region::across_all(when_branch.patterns.iter().map(|v| &v.region)),
),
FromAnnotation(
name.clone(),
@ -543,7 +543,7 @@ pub fn constrain_expr(
PExpected::ForReason(
PReason::WhenMatch { index },
cond_type.clone(),
region,
Region::across_all(when_branch.patterns.iter().map(|v| &v.region)),
),
ForReason(
Reason::WhenBranch { index },

View File

@ -38,6 +38,25 @@ impl Region {
end_col: end.end_col,
}
}
pub fn across_all<'a, I>(regions: I) -> Self
where
I: IntoIterator<Item = &'a Region>,
{
let mut it = regions.into_iter();
if let Some(first) = it.next() {
let mut result = *first;
for r in it {
result = Self::span_across(&result, r);
}
result
} else {
Self::zero()
}
}
}
#[test]

View File

@ -652,12 +652,14 @@ impl ReportText {
.into_iter()
.map(|rep| rep.pretty(alloc, subs, home, src_lines, interns)),
),
Stack(report_texts) => alloc.intersperse(
report_texts
.into_iter()
.map(|rep| (rep.pretty(alloc, subs, home, src_lines, interns))),
alloc.hardline(),
),
Stack(report_texts) => alloc
.intersperse(
report_texts
.into_iter()
.map(|rep| (rep.pretty(alloc, subs, home, src_lines, interns))),
alloc.hardline(),
)
.append(alloc.hardline()),
Intersperse { separator, items } => alloc.intersperse(
items
.into_iter()

View File

@ -558,14 +558,17 @@ fn type_comparison(
) -> ReportText {
let comparison = to_comparison(actual, expected);
ReportText::Stack(vec![
let mut lines = vec![
i_am_seeing,
comparison.actual,
instead_of,
comparison.expected,
context_hints,
problems_to_hint(comparison.problems),
])
];
lines.extend(problems_to_hint(comparison.problems));
ReportText::Stack(lines)
}
fn lone_type(
@ -576,12 +579,11 @@ fn lone_type(
) -> ReportText {
let comparison = to_comparison(actual, expected);
ReportText::Stack(vec![
i_am_seeing,
comparison.actual,
further_details,
problems_to_hint(comparison.problems),
])
let mut lines = vec![i_am_seeing, comparison.actual, further_details];
lines.extend(problems_to_hint(comparison.problems));
ReportText::Stack(lines)
}
fn add_category(this_is: ReportText, category: &Category) -> ReportText {
@ -654,13 +656,155 @@ fn add_category(this_is: ReportText, category: &Category) -> ReportText {
}
fn to_pattern_report(
_filename: PathBuf,
_expr_region: roc_region::all::Region,
_category: PatternCategory,
_found: ErrorType,
_expected: PExpected<ErrorType>,
filename: PathBuf,
expr_region: roc_region::all::Region,
category: PatternCategory,
found: ErrorType,
expected: PExpected<ErrorType>,
) -> Report {
todo!()
use roc_types::types::PReason;
use ReportText::*;
match expected {
PExpected::NoExpectation(expected_type) => {
let text = Concat(vec![
plain_text("This pattern is being used in an unexpected way:"),
Region(expr_region),
pattern_type_comparision(
found,
expected_type,
add_pattern_category(plain_text("It is"), &category),
plain_text("But it needs to match:"),
vec![],
),
]);
Report {
filename,
title: "TYPE MISMATCH".to_string(),
text,
}
}
PExpected::ForReason(reason, expected_type, region) => match reason {
PReason::WhenMatch { index } => {
if index == 0 {
let text = Concat(vec![
plain_text("The 1st pattern in this "),
keyword_text("when"),
plain_text(" is causing a mismatch:"),
Region(region),
pattern_type_comparision(
found,
expected_type,
add_pattern_category(
plain_text("The first pattern is trying to match"),
&category,
),
Concat(vec![
plain_text("But the expression between "),
keyword_text("when"),
plain_text(" and "),
keyword_text("is"),
plain_text(" has the type:"),
]),
vec![],
),
]);
Report {
filename,
title: "TYPE MISMATCH".to_string(),
text,
}
} else {
let text = Concat(vec![
plain_text(&format!(
"The {} pattern in this ",
int_to_ordinal(index + 1)
)),
keyword_text("when"),
plain_text(" does not match the previous ones:"),
Region(region),
pattern_type_comparision(
found,
expected_type,
add_pattern_category(
plain_text(&format!(
"The {} pattern is trying to match",
int_to_ordinal(index + 1)
)),
&category,
),
plain_text("But all the previous branches match:"),
vec![],
),
]);
Report {
filename,
title: "TYPE MISMATCH".to_string(),
text,
}
}
}
_ => {
// TypedArg { name: Box<str>, index: usize },
// WhenMatch { index: usize },
// CtorArg { name: Box<str>, index: usize },
// ListEntry { index: usize },
// Tail,
todo!()
}
},
}
}
fn pattern_type_comparision(
actual: ErrorType,
expected: ErrorType,
i_am_seeing: ReportText,
instead_of: ReportText,
reason_hints: Vec<ReportText>,
) -> ReportText {
let comparison = to_comparison(actual, expected);
let mut lines = vec![
i_am_seeing,
comparison.actual,
instead_of,
comparison.expected,
];
lines.extend(problems_to_hint(comparison.problems));
lines.extend(reason_hints);
ReportText::Stack(lines)
}
fn add_pattern_category(
i_am_trying_to_match: ReportText,
category: &PatternCategory,
) -> ReportText {
use PatternCategory::*;
let rest = match category {
Record => plain_text(" record values of type:"),
EmptyRecord => plain_text(" an empty record:"),
PatternGuard => plain_text(" a pattern guard of type:"),
Set => plain_text(" sets of type:"),
Map => plain_text(" maps of type:"),
Ctor(tag_name) => ReportText::Concat(vec![
tag_name_text(tag_name.clone()),
plain_text(" values of type:"),
]),
Str => plain_text(" strings:"),
Num => plain_text(" numbers:"),
Int => plain_text(" integers:"),
Float => plain_text(" floats"),
};
ReportText::Concat(vec![i_am_trying_to_match, rest])
}
fn to_circular_report(
@ -703,9 +847,9 @@ pub enum Problem {
BadRigidVar(Lowercase, ErrorType),
}
fn problems_to_hint(_problems: Vec<Problem>) -> ReportText {
fn problems_to_hint(_problems: Vec<Problem>) -> Vec<ReportText> {
// TODO
ReportText::Concat(vec![])
vec![]
}
pub struct Comparison {

View File

@ -1132,6 +1132,7 @@ mod test_reporting {
Here is my best effort at writing down the type. You will see for parts of the type that repeat something already printed out infinitely.
-> a
"#
),
)
@ -1157,6 +1158,7 @@ mod test_reporting {
Here is my best effort at writing down the type. You will see for parts of the type that repeat something already printed out infinitely.
List -> a
"#
),
)
@ -1298,7 +1300,7 @@ mod test_reporting {
indoc!(
r#"
x : Int
x =
x =
when True is
_ -> 3.14
@ -1426,4 +1428,63 @@ mod test_reporting {
),
)
}
#[test]
fn pattern_when_condition() {
report_problem_as(
indoc!(
r#"
when 1 is
{} -> 42
"#
),
indoc!(
r#"
The 1st pattern in this `when` is causing a mismatch:
2 {} -> 42
^^
The first pattern is trying to match record values of type:
{}a
But the expression between `when` and `is` has the type:
Num a
"#
),
)
}
#[test]
fn pattern_when_pattern() {
report_problem_as(
indoc!(
r#"
when 1 is
2 -> 3
{} -> 42
"#
),
indoc!(
r#"
The 2nd pattern in this `when` does not match the previous ones:
3 {} -> 42
^^
The 2nd pattern is trying to match record values of type:
{}a
But all the previous branches match:
Num a
"#
),
)
}
}

View File

@ -250,9 +250,15 @@ fn solve(
state
}
Pattern(_region, _category, typ, expected) => {
Pattern(region, category, typ, expectation) => {
let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
let expected = type_to_var(subs, rank, pools, cached_aliases, expected.get_type_ref());
let expected = type_to_var(
subs,
rank,
pools,
cached_aliases,
expectation.get_type_ref(),
);
match unify(subs, actual, expected) {
Success(vars) => {
@ -260,10 +266,20 @@ fn solve(
state
}
other => todo!(
"the case where unify for Pattern was unsuccessful with {:?}",
other
),
Failure(vars, actual_type, expected_type) => {
introduce(subs, rank, pools, &vars);
let problem = TypeError::BadPattern(
*region,
category.clone(),
actual_type,
expectation.clone().replace(expected_type),
);
problems.push(problem);
state
}
}
}
Let(let_con) => {

View File

@ -598,11 +598,10 @@ pub struct RecordStructure {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PReason {
TypedArg { name: Box<str>, index: usize },
TypedArg { name: Symbol, index: usize },
WhenMatch { index: usize },
CtorArg { name: Box<str>, index: usize },
CtorArg { name: Symbol, index: usize },
ListEntry { index: usize },
Tail,
}
#[derive(Debug, Clone, PartialEq, Eq)]