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(
|
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 },
|
||||||
|
@ -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]
|
||||||
|
@ -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()
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) => {
|
||||||
|
@ -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)]
|
||||||
|
Loading…
Reference in New Issue
Block a user