add FnCall error message

This commit is contained in:
Folkert 2020-04-07 13:31:48 +02:00
parent 1a3356ff0c
commit ccd021a755
2 changed files with 227 additions and 23 deletions

View file

@ -129,7 +129,9 @@ fn to_expr_report(
use ReportText::*; use ReportText::*;
match expected { match expected {
Expected::NoExpectation(expected_type) => todo!("hit no expectation with type {:?}", expected_type), Expected::NoExpectation(expected_type) => {
todo!("hit no expectation with type {:?}", expected_type)
}
Expected::FromAnnotation(name, _arity, annotation_source, expected_type) => { Expected::FromAnnotation(name, _arity, annotation_source, expected_type) => {
use roc_types::types::AnnotationSource::*; use roc_types::types::AnnotationSource::*;
@ -137,30 +139,50 @@ fn to_expr_report(
// TODO special-case 2-branch if // TODO special-case 2-branch if
let thing = match annotation_source { let thing = match annotation_source {
TypedIfBranch ( index ) => Concat(vec![ plain_text(&format!("{} branch of this ", int_to_ordinal(index))), keyword_text("if"), plain_text(" expression:") ]), TypedIfBranch(index) => Concat(vec![
TypedWhenBranch ( index ) => Concat(vec![ plain_text(&format!("{} branch of this ", int_to_ordinal(index))), keyword_text("when"), plain_text(" expression:") ]), plain_text(&format!("{} branch of this ", int_to_ordinal(index))),
TypedBody => Concat(vec![ plain_text("body of the "), name_text.clone(), plain_text(" definition:") ]), keyword_text("if"),
plain_text(" expression:"),
]),
TypedWhenBranch(index) => Concat(vec![
plain_text(&format!("{} branch of this ", int_to_ordinal(index))),
keyword_text("when"),
plain_text(" expression:"),
]),
TypedBody => Concat(vec![
plain_text("body of the "),
name_text.clone(),
plain_text(" definition:"),
]),
}; };
let it_is = match annotation_source { let it_is = match annotation_source {
TypedIfBranch ( index ) => format!("The {} branch is", int_to_ordinal(index)), TypedIfBranch(index) => format!("The {} branch is", int_to_ordinal(index)),
TypedWhenBranch ( index ) => format!("The {} branch is", int_to_ordinal(index)), TypedWhenBranch(index) => format!("The {} branch is", int_to_ordinal(index)),
TypedBody => "The body is".into(), TypedBody => "The body is".into(),
}; };
let comparison = let comparison = type_comparison(
type_comparison( found,
found, expected_type,
expected_type, add_category(plain_text(&it_is), &category),
add_category(plain_text(&it_is), &category), Concat(vec![
Concat(vec![ plain_text("But the type annotation on "), name_text, plain_text(" says it should be:")]), plain_text("But the type annotation on "),
Concat(vec![]), name_text,
); plain_text(" says it should be:"),
]),
Concat(vec![]),
);
Report { Report {
title: "TYPE MISMATCH".to_string(), title: "TYPE MISMATCH".to_string(),
filename, filename,
text: Concat(vec![ plain_text("Something is off with the "), thing , Region(expr_region), comparison ]), text: Concat(vec![
plain_text("Something is off with the "),
thing,
Region(expr_region),
comparison,
]),
} }
} }
Expected::ForReason(reason, expected_type, region) => match reason { Expected::ForReason(reason, expected_type, region) => match reason {
@ -337,7 +359,7 @@ fn to_expr_report(
plain_text("I need all elements of a list to have the same type!"), plain_text("I need all elements of a list to have the same type!"),
) )
} }
Reason::RecordUpdateValue(field) => { report_mismatch( Reason::RecordUpdateValue(field) => report_mismatch(
filename, filename,
&category, &category,
found, found,
@ -355,9 +377,105 @@ fn to_expr_report(
plain_text(" to be"), plain_text(" to be"),
]), ]),
plain_text("But it should be:"), plain_text("But it should be:"),
plain_text("Record update syntax does not allow you to change the type of fields. You can achieve that with record literal syntax."), plain_text(
)} r#"Record update syntax does not allow you to change the type of fields. You can achieve that with record literal syntax."#,
Reason::FnArg { name , arg_index } => { ),
),
Reason::FnCall { name, arity } => match count_arguments(&found) {
0 => {
let this_value = match name {
None => plain_text("This value"),
Some(symbol) => Concat(vec![
plain_text("The "),
Value(symbol),
plain_text(" value"),
]),
};
let lines = vec![
Concat(vec![
this_value,
plain_text(&format!(
" is not a function, but it was given {}:",
if arity == 1 {
"1 argument".into()
} else {
format!("{} arguments", arity)
}
)),
]),
ReportText::Region(expr_region),
plain_text("Are there any missing commas? Or missing parentheses?"),
];
Report {
filename,
title: "TOO MANY ARGS".to_string(),
text: Concat(lines),
}
}
n => {
let this_function = match name {
None => plain_text("This function"),
Some(symbol) => Concat(vec![
plain_text("The "),
Value(symbol),
plain_text(" function"),
]),
};
if n < arity as usize {
let lines = vec![
Concat(vec![
this_function,
plain_text(&format!(
" expects {}, but it got {} instead:",
if n == 1 {
"1 argument".into()
} else {
format!("{} arguments", n)
},
arity
)),
]),
ReportText::Region(expr_region),
plain_text("Are there any missing commas? Or missing parentheses?"),
];
Report {
filename,
title: "TOO MANY ARGS".to_string(),
text: Concat(lines),
}
} else {
let lines = vec![
Concat(vec![
this_function,
plain_text(&format!(
" expects {}, but it got only {}:",
if n == 1 {
"1 argument".into()
} else {
format!("{} arguments", n)
},
arity
)),
]),
ReportText::Region(expr_region),
plain_text(
r#"Roc does not allow functions to be partially applied. Use a closure to make partial application explicit."#,
),
];
Report {
filename,
title: "TOO FEW ARGS".to_string(),
text: Concat(lines),
}
}
}
},
Reason::FnArg { name, arg_index } => {
let ith = int_to_ordinal(arg_index as usize + 1); let ith = int_to_ordinal(arg_index as usize + 1);
let this_function = match name { let this_function = match name {
@ -372,13 +490,19 @@ fn to_expr_report(
expected_type, expected_type,
region, region,
Some(expr_region), Some(expr_region),
Concat(vec![ plain_text(&format!("The {} argument to ", ith)) Concat(vec![
, this_function.clone(), plain_text(" is not what I expect:" )]), plain_text(&format!("The {} argument to ", ith)),
this_function.clone(),
plain_text(" is not what I expect:"),
]),
plain_text("This argument is"), plain_text("This argument is"),
Concat(vec![ plain_text("But "), this_function, plain_text(&format!(" needs the {} argument to be:", ith))]), Concat(vec![
plain_text("But "),
this_function,
plain_text(&format!(" needs the {} argument to be:", ith)),
]),
plain_text(""), plain_text(""),
) )
} }
other => { other => {
// NamedFnArg(String /* function name */, u8 /* arg index */), // NamedFnArg(String /* function name */, u8 /* arg index */),
@ -398,6 +522,17 @@ fn to_expr_report(
} }
} }
fn count_arguments(tipe: &ErrorType) -> usize {
use ErrorType::*;
match tipe {
Function(args, _) => args.len(),
Type(Symbol::ATTR_ATTR, args) => count_arguments(&args[1]),
Alias(_, _, actual) => count_arguments(actual),
_ => 0,
}
}
fn type_comparison( fn type_comparison(
actual: ErrorType, actual: ErrorType,
expected: ErrorType, expected: ErrorType,

View file

@ -1357,4 +1357,73 @@ mod test_reporting {
), ),
) )
} }
#[test]
fn fncall_value() {
report_problem_as(
indoc!(
r#"
x : Int
x = 42
x 3
"#
),
indoc!(
r#"
The `x` value is not a function, but it was given 1 argument:
4 x 3
^
Are there any missing commas? Or missing parentheses?"#
),
)
}
#[test]
fn fncall_overapplied() {
report_problem_as(
indoc!(
r#"
f : Int -> Int
f = \_ -> 42
f 1 2
"#
),
indoc!(
r#"
The `f` function expects 1 argument, but it got 2 instead:
4 f 1 2
^
Are there any missing commas? Or missing parentheses?"#
),
)
}
#[test]
fn fncall_underapplied() {
report_problem_as(
indoc!(
r#"
f : Int, Int -> Int
f = \_, _ -> 42
f 1
"#
),
indoc!(
r#"
The `f` function expects 2 arguments, but it got only 1:
4 f 1
^
Roc does not allow functions to be partially applied. Use a closure to make partial application explicit."#
),
)
}
} }