From d033057a581df4f29338ae22047794aaa128516d Mon Sep 17 00:00:00 2001 From: Agus Zubiaga Date: Tue, 27 Aug 2024 19:06:31 -0300 Subject: [PATCH] Hide params from annotation type mismatches --- crates/cli/tests/cli_run.rs | 51 +++++++++++++++++++ crates/cli/tests/module_params/BadAnn.roc | 9 ++++ crates/cli/tests/module_params/bad_ann.roc | 8 +++ .../compiler/lower_params/src/type_error.rs | 46 +++++++++++++++-- crates/compiler/types/src/types.rs | 14 ++--- 5 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 crates/cli/tests/module_params/BadAnn.roc create mode 100644 crates/cli/tests/module_params/bad_ann.roc diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 1457a168b3..88f99fb417 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -846,6 +846,57 @@ mod cli_run { ) } + #[test] + #[cfg_attr(windows, ignore)] + fn module_params_bad_ann() { + check_compile_error_with( + CMD_DEV, + &cli_testing_dir("/module_params/bad_ann.roc"), + &[], + indoc!( + r#" + ── TYPE MISMATCH in tests/module_params/BadAnn.roc ───────────────────────────── + + Something is off with the body of the fnAnnotatedAsValue definition: + + 3│ fnAnnotatedAsValue : Str + 4│> fnAnnotatedAsValue = /postId, commentId -> + 5│> "/posts/$(postId)/comments/$(Num.toStr commentId)" + + The body is an anonymous function of type: + + Str, Num * -> Str + + But the type annotation on fnAnnotatedAsValue says it should be: + + Str + + + ── TYPE MISMATCH in tests/module_params/BadAnn.roc ───────────────────────────── + + Something is off with the body of the missingArg definition: + + 7│ missingArg : Str -> Str + 8│> missingArg = /postId, _ -> + 9│> "/posts/$(postId)/comments" + + The body is an anonymous function of type: + + (Str, ? -> Str) + + But the type annotation on missingArg says it should be: + + (Str -> Str) + + Tip: It looks like it takes too many arguments. I'm seeing 1 extra. + + ──────────────────────────────────────────────────────────────────────────────── + + 2 errors and 1 warning found in ms."# + ), + ); + } + #[test] #[cfg_attr(windows, ignore)] fn transitive_expects() { diff --git a/crates/cli/tests/module_params/BadAnn.roc b/crates/cli/tests/module_params/BadAnn.roc new file mode 100644 index 0000000000..2ccfce872e --- /dev/null +++ b/crates/cli/tests/module_params/BadAnn.roc @@ -0,0 +1,9 @@ +module { appId } -> [fnAnnotatedAsValue, missingArg] + +fnAnnotatedAsValue : Str +fnAnnotatedAsValue = \postId, commentId -> + "/posts/$(postId)/comments/$(Num.toStr commentId)" + +missingArg : Str -> Str +missingArg = \postId, _ -> + "/posts/$(postId)/comments" diff --git a/crates/cli/tests/module_params/bad_ann.roc b/crates/cli/tests/module_params/bad_ann.roc new file mode 100644 index 0000000000..fc549e362a --- /dev/null +++ b/crates/cli/tests/module_params/bad_ann.roc @@ -0,0 +1,8 @@ +app [main] { + pf: platform "../fixtures/multi-dep-str/platform/main.roc", +} + +import BadAnn { appId: "one" } + +main = + "" diff --git a/crates/compiler/lower_params/src/type_error.rs b/crates/compiler/lower_params/src/type_error.rs index ecc8387a9d..52e56edf85 100644 --- a/crates/compiler/lower_params/src/type_error.rs +++ b/crates/compiler/lower_params/src/type_error.rs @@ -1,4 +1,4 @@ -use roc_can::expected::Expected; +use roc_can::{expected::Expected, pattern::Pattern}; use roc_module::symbol::{ModuleId, Symbol}; use roc_region::all::Loc; use roc_solve_problem::TypeError; @@ -32,9 +32,47 @@ pub fn remove_module_param_arguments( for error in errors { match error { TypeError::BadExpr(_, _, found, Expected::ForReason(reason, expected, _)) => { - remove_with_bad_expr_reason(&env, found, reason, expected); + remove_for_reason(&env, found, reason, expected); } - TypeError::BadExpr(_, _, _, _) => todo!("{:?}", error), + + TypeError::BadExpr( + _, + _, + found, + Expected::FromAnnotation( + Loc { + value: Pattern::Identifier(name), + region: _, + }, + arity, + _, + expected, + ), + ) if env.is_extended(name) => { + if *arity > 1 { + *arity -= 1; + } + + drop_last_argument(found); + drop_last_argument(expected); + + if let ( + ErrorType::Function(found_args, _, _), + ErrorType::Function(expected_args, _, _), + ) = (found, expected) + { + if found_args.len() > expected_args.len() { + // If the found arity doesn't match the annotation's, the params + // record would appear in the error, so we replace it with `?`. + if let Some(last) = found_args.last_mut() { + *last = ErrorType::Error; + } + } + } + } + + TypeError::BadExpr(_, _, _, Expected::FromAnnotation(_, _, _, _)) + | TypeError::BadExpr(_, _, _, Expected::NoExpectation(_)) => {} // Irrelevant TypeError::BadPattern(_, _, _, _) @@ -80,7 +118,7 @@ impl Env { } } -fn remove_with_bad_expr_reason( +fn remove_for_reason( env: &Env, found_type: &mut ErrorType, reason: &mut Reason, diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index f6757247c9..684eaa3a90 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -3594,7 +3594,7 @@ pub enum Mismatch { pub type DoesNotImplementAbility = Vec<(ErrorType, Symbol)>; -#[derive(PartialEq, Eq, Clone, Hash)] +#[derive(PartialEq, Eq, Clone, Hash, Debug)] pub enum ErrorType { Infinite, Type(Symbol, Vec), @@ -3619,12 +3619,12 @@ pub enum ErrorType { Error, } -impl std::fmt::Debug for ErrorType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // TODO remove clone - write!(f, "{:?}", write_debug_error_type(self.clone())) - } -} +// impl std::fmt::Debug for ErrorType { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// // TODO remove clone +// write!(f, "{:?}", write_debug_error_type(self.clone())) +// } +// } impl ErrorType { pub fn unwrap_structural_alias(self) -> ErrorType {