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

View File

@ -38,6 +38,25 @@ impl Region {
end_col: end.end_col, 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] #[test]

View File

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

View File

@ -558,14 +558,17 @@ fn type_comparison(
) -> ReportText { ) -> ReportText {
let comparison = to_comparison(actual, expected); let comparison = to_comparison(actual, expected);
ReportText::Stack(vec![ let mut lines = vec![
i_am_seeing, i_am_seeing,
comparison.actual, comparison.actual,
instead_of, instead_of,
comparison.expected, comparison.expected,
context_hints, context_hints,
problems_to_hint(comparison.problems), ];
])
lines.extend(problems_to_hint(comparison.problems));
ReportText::Stack(lines)
} }
fn lone_type( fn lone_type(
@ -576,12 +579,11 @@ fn lone_type(
) -> ReportText { ) -> ReportText {
let comparison = to_comparison(actual, expected); let comparison = to_comparison(actual, expected);
ReportText::Stack(vec![ let mut lines = vec![i_am_seeing, comparison.actual, further_details];
i_am_seeing,
comparison.actual, lines.extend(problems_to_hint(comparison.problems));
further_details,
problems_to_hint(comparison.problems), ReportText::Stack(lines)
])
} }
fn add_category(this_is: ReportText, category: &Category) -> ReportText { 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( fn to_pattern_report(
_filename: PathBuf, filename: PathBuf,
_expr_region: roc_region::all::Region, expr_region: roc_region::all::Region,
_category: PatternCategory, category: PatternCategory,
_found: ErrorType, found: ErrorType,
_expected: PExpected<ErrorType>, expected: PExpected<ErrorType>,
) -> Report { ) -> 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( fn to_circular_report(
@ -703,9 +847,9 @@ pub enum Problem {
BadRigidVar(Lowercase, ErrorType), BadRigidVar(Lowercase, ErrorType),
} }
fn problems_to_hint(_problems: Vec<Problem>) -> ReportText { fn problems_to_hint(_problems: Vec<Problem>) -> Vec<ReportText> {
// TODO // TODO
ReportText::Concat(vec![]) vec![]
} }
pub struct Comparison { 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. 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 -> 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. 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 List -> a
"# "#
), ),
) )
@ -1298,7 +1300,7 @@ mod test_reporting {
indoc!( indoc!(
r#" r#"
x : Int x : Int
x = x =
when True is when True is
_ -> 3.14 _ -> 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 state
} }
Pattern(_region, _category, typ, expected) => { Pattern(region, category, typ, expectation) => {
let actual = type_to_var(subs, rank, pools, cached_aliases, typ); 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) { match unify(subs, actual, expected) {
Success(vars) => { Success(vars) => {
@ -260,10 +266,20 @@ fn solve(
state state
} }
other => todo!( Failure(vars, actual_type, expected_type) => {
"the case where unify for Pattern was unsuccessful with {:?}", introduce(subs, rank, pools, &vars);
other
), let problem = TypeError::BadPattern(
*region,
category.clone(),
actual_type,
expectation.clone().replace(expected_type),
);
problems.push(problem);
state
}
} }
} }
Let(let_con) => { Let(let_con) => {

View File

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