mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 23:37:56 +03:00
add When pattern errors
This commit is contained in:
parent
2d1a1621c3
commit
7f999a06f3
@ -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 },
|
||||
|
@ -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]
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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)]
|
||||
|
Loading…
Reference in New Issue
Block a user