mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 08:34:33 +00:00
improved error messages for function definitions
This commit is contained in:
parent
1d2251b064
commit
e93c04a8ce
4 changed files with 267 additions and 79 deletions
|
@ -953,6 +953,45 @@ fn to_pattern_report<'b>(
|
|||
}
|
||||
|
||||
PExpected::ForReason(reason, expected_type, region) => match reason {
|
||||
PReason::TypedArg { opt_name, index } => {
|
||||
let name = match opt_name {
|
||||
Some(n) => alloc.symbol_unqualified(n),
|
||||
None => alloc.text(" this definition "),
|
||||
};
|
||||
let doc = alloc.stack(vec![
|
||||
alloc
|
||||
.text("The ")
|
||||
.append(alloc.text(index.ordinal()))
|
||||
.append(alloc.text(" argument to "))
|
||||
.append(name.clone())
|
||||
.append(alloc.text(" is weird:")),
|
||||
alloc.region(region),
|
||||
pattern_type_comparision(
|
||||
alloc,
|
||||
found,
|
||||
expected_type,
|
||||
add_pattern_category(
|
||||
alloc,
|
||||
alloc.text("The argument is a pattern that matches"),
|
||||
&category,
|
||||
),
|
||||
alloc.concat(vec![
|
||||
alloc.text("But the annotation on "),
|
||||
name,
|
||||
alloc.text(" says the "),
|
||||
alloc.text(index.ordinal()),
|
||||
alloc.text(" argument should be:"),
|
||||
]),
|
||||
vec![],
|
||||
),
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
title: "TYPE MISMATCH".to_string(),
|
||||
doc,
|
||||
}
|
||||
}
|
||||
PReason::WhenMatch { index } => {
|
||||
if index == Index::FIRST {
|
||||
let doc = alloc.stack(vec![
|
||||
|
@ -1295,10 +1334,17 @@ pub fn to_doc<'b>(
|
|||
alloc,
|
||||
fields
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
.map(|(k, value)| {
|
||||
(
|
||||
alloc.string(k.as_str().to_string()),
|
||||
to_doc(alloc, Parens::Unnecessary, v.into_inner()),
|
||||
match value {
|
||||
RecordField::Optional(v) => {
|
||||
RecordField::Optional(to_doc(alloc, Parens::Unnecessary, v))
|
||||
}
|
||||
RecordField::Required(v) => {
|
||||
RecordField::Required(to_doc(alloc, Parens::Unnecessary, v))
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
|
@ -1594,12 +1640,18 @@ fn diff_record<'b>(
|
|||
left: (
|
||||
field.clone(),
|
||||
alloc.string(field.as_str().to_string()),
|
||||
diff.left,
|
||||
match t1 {
|
||||
RecordField::Optional(_) => RecordField::Optional(diff.left),
|
||||
RecordField::Required(_) => RecordField::Required(diff.left),
|
||||
},
|
||||
),
|
||||
right: (
|
||||
field.clone(),
|
||||
alloc.string(field.as_str().to_string()),
|
||||
diff.right,
|
||||
match t2 {
|
||||
RecordField::Optional(_) => RecordField::Optional(diff.right),
|
||||
RecordField::Required(_) => RecordField::Required(diff.right),
|
||||
},
|
||||
),
|
||||
status: diff.status,
|
||||
}
|
||||
|
@ -1608,7 +1660,7 @@ fn diff_record<'b>(
|
|||
(
|
||||
field.clone(),
|
||||
alloc.string(field.as_str().to_string()),
|
||||
to_doc(alloc, Parens::Unnecessary, tipe.clone().into_inner()),
|
||||
tipe.map(|t| to_doc(alloc, Parens::Unnecessary, t.clone())),
|
||||
)
|
||||
};
|
||||
let shared_keys = fields1
|
||||
|
@ -1661,11 +1713,12 @@ fn diff_record<'b>(
|
|||
|
||||
let ext_diff = ext_to_diff(alloc, ext1, ext2);
|
||||
|
||||
let mut fields_diff: Diff<Vec<(Lowercase, RocDocBuilder<'b>, RocDocBuilder<'b>)>> = Diff {
|
||||
left: vec![],
|
||||
right: vec![],
|
||||
status: Status::Similar,
|
||||
};
|
||||
let mut fields_diff: Diff<Vec<(Lowercase, RocDocBuilder<'b>, RecordField<RocDocBuilder<'b>>)>> =
|
||||
Diff {
|
||||
left: vec![],
|
||||
right: vec![],
|
||||
status: Status::Similar,
|
||||
};
|
||||
|
||||
for diff in both {
|
||||
fields_diff.left.push(diff.left);
|
||||
|
@ -1938,7 +1991,7 @@ mod report_text {
|
|||
|
||||
pub fn record<'b>(
|
||||
alloc: &'b RocDocAllocator<'b>,
|
||||
entries: Vec<(RocDocBuilder<'b>, RocDocBuilder<'b>)>,
|
||||
entries: Vec<(RocDocBuilder<'b>, RecordField<RocDocBuilder<'b>>)>,
|
||||
opt_ext: Option<RocDocBuilder<'b>>,
|
||||
) -> RocDocBuilder<'b> {
|
||||
let ext_doc = if let Some(t) = opt_ext {
|
||||
|
@ -1951,8 +2004,15 @@ mod report_text {
|
|||
alloc.text("{}").append(ext_doc)
|
||||
} else {
|
||||
let entry_to_doc =
|
||||
|(field_name, field_type): (RocDocBuilder<'b>, RocDocBuilder<'b>)| {
|
||||
field_name.append(alloc.text(" : ")).append(field_type)
|
||||
|(field_name, field_type): (RocDocBuilder<'b>, RecordField<RocDocBuilder<'b>>)| {
|
||||
match field_type {
|
||||
RecordField::Required(field) => {
|
||||
field_name.append(alloc.text(" : ")).append(field)
|
||||
}
|
||||
RecordField::Optional(field) => {
|
||||
field_name.append(alloc.text(" ? ")).append(field)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let starts =
|
||||
|
|
|
@ -1630,6 +1630,21 @@ mod test_reporting {
|
|||
|
||||
#[test]
|
||||
fn bad_double_rigid() {
|
||||
// this previously reported the message below, not sure which is better
|
||||
//
|
||||
// Something is off with the body of the `f` definition:
|
||||
//
|
||||
// 1 ┆ f : a, b -> a
|
||||
// 2 ┆ f = \x, y -> if True then x else y
|
||||
// ┆ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
//
|
||||
// The body is an anonymous function of type:
|
||||
//
|
||||
// a, a -> a
|
||||
//
|
||||
// But the type annotation on `f` says it should be:
|
||||
//
|
||||
// a, b -> a
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -1643,21 +1658,22 @@ mod test_reporting {
|
|||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
Something is off with the body of the `f` definition:
|
||||
This `if` has an `else` branch with a different type from its `then` branch:
|
||||
|
||||
1 ┆ f : a, b -> a
|
||||
2 ┆ f = \x, y -> if True then x else y
|
||||
┆ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
┆ ^
|
||||
|
||||
The body is an anonymous function of type:
|
||||
This `y` value is a:
|
||||
|
||||
a, a -> a
|
||||
b
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
but the `then` branch has the type:
|
||||
|
||||
a, b -> a
|
||||
a
|
||||
|
||||
Hint: Your type annotation uses `a` and `b` as separate type variables.
|
||||
I need all branches in an `if` to have the same type!
|
||||
|
||||
Hint: Your type annotation uses `b` and `a` as separate type variables.
|
||||
Your code seems to be saying they are the same though. Maybe they
|
||||
should be the same your type annotation? Maybe your code uses them in
|
||||
a weird way?
|
||||
|
@ -1958,27 +1974,17 @@ mod test_reporting {
|
|||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
Something is off with the body of the `f` definition:
|
||||
The `r` record does not have a `.foo` field:
|
||||
|
||||
1 ┆ f : { fo: Int }ext -> Int
|
||||
2 ┆> f = \r ->
|
||||
3 ┆> r2 = { r & foo: r.fo }
|
||||
4 ┆>
|
||||
5 ┆> r2.fo
|
||||
3 ┆ r2 = { r & foo: r.fo }
|
||||
┆ ^^^^^^^^^
|
||||
|
||||
The body is an anonymous function of type:
|
||||
This is usually a typo. Here are the `r` fields that are most similar:
|
||||
|
||||
{ fo : Int, foo : Int }a -> Int
|
||||
{ fo : Int
|
||||
}ext
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
|
||||
{ fo : Int }ext -> Int
|
||||
|
||||
Hint: Seems like a record field typo. Maybe `foo` should be `fo`?
|
||||
|
||||
Hint: Can more type annotations be added? Type annotations always help
|
||||
me give more specific messages, and I think they could help a lot in
|
||||
this case
|
||||
So maybe `.foo` should be `.fo`?
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -3492,7 +3498,7 @@ mod test_reporting {
|
|||
indoc!(
|
||||
r#"
|
||||
f : { x : Int, y ? Int } -> Int
|
||||
f = \{ x, y ? "foo" } -> x + y
|
||||
f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||||
|
||||
f
|
||||
"#
|
||||
|
@ -3501,18 +3507,18 @@ mod test_reporting {
|
|||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
The 2nd argument to `add` is not what I expect:
|
||||
The 1st argument to `f` is weird:
|
||||
|
||||
2 ┆ f = \{ x, y ? "foo" } -> x + y
|
||||
┆ ^
|
||||
2 ┆ f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||||
┆ ^^^^^^^^^^^^^^^^
|
||||
|
||||
This `y` value is a:
|
||||
The argument is a pattern that matches record values of type:
|
||||
|
||||
Str
|
||||
{ x : Int, y ? Str }
|
||||
|
||||
But `add` needs the 2nd argument to be:
|
||||
But the annotation on `f` says the 1st argument should be:
|
||||
|
||||
Num a
|
||||
{ x : Int, y ? Int }
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -3538,21 +3544,18 @@ mod test_reporting {
|
|||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
Something is off with the body of the `f` definition:
|
||||
The 1st pattern in this `when` is causing a mismatch:
|
||||
|
||||
1 ┆ f : { x : Int, y : Int } -> Int
|
||||
2 ┆> f = \r ->
|
||||
3 ┆> when r is
|
||||
4 ┆> { x, y : "foo" } -> x + 0
|
||||
5 ┆> _ -> 0
|
||||
4 ┆ { x, y : "foo" } -> x + 0
|
||||
┆ ^^^^^^^^^^^^^^^^
|
||||
|
||||
The body is an anonymous function of type:
|
||||
The first pattern is trying to match record values of type:
|
||||
|
||||
{ x : Num Integer, y : Str } -> Num Integer
|
||||
{ x : Int, y : Str }
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
But the expression between `when` and `is` has the type:
|
||||
|
||||
{ x : Int, y : Int } -> Int
|
||||
{ x : Int, y : Int }
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -3579,21 +3582,18 @@ mod test_reporting {
|
|||
r#"
|
||||
-- TYPE MISMATCH ---------------------------------------------------------------
|
||||
|
||||
Something is off with the body of the `f` definition:
|
||||
The 1st pattern in this `when` is causing a mismatch:
|
||||
|
||||
1 ┆ f : { x : Int, y ? Int } -> Int
|
||||
2 ┆> f = \r ->
|
||||
3 ┆> when r is
|
||||
4 ┆> { x, y ? "foo" } -> (\g, _ -> g) x y
|
||||
5 ┆> _ -> 0
|
||||
4 ┆ { x, y ? "foo" } -> (\g, _ -> g) x y
|
||||
┆ ^^^^^^^^^^^^^^^^
|
||||
|
||||
The body is an anonymous function of type:
|
||||
The first pattern is trying to match record values of type:
|
||||
|
||||
{ x : Num Integer, y : Str } -> Num Integer
|
||||
{ x : Int, y ? Str }
|
||||
|
||||
But the type annotation on `f` says it should be:
|
||||
But the expression between `when` and `is` has the type:
|
||||
|
||||
{ x : Int, y : Int } -> Int
|
||||
{ x : Int, y ? Int }
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue