#[macro_use] extern crate pretty_assertions; extern crate bumpalo; extern crate indoc; extern crate roc_reporting; mod helpers; #[cfg(test)] mod test_reporting { use crate::helpers::{can_expr, infer_expr, test_home, CanExprOut, ParseErrOut}; use bumpalo::Bump; use indoc::indoc; use roc_can::abilities::AbilitiesStore; use roc_can::expr::PendingDerives; use roc_load::{self, ExecutionMode, LoadConfig, LoadedModule, LoadingProblem, Threading}; use roc_module::symbol::{Interns, ModuleId}; use roc_packaging::cache::RocCacheDir; use roc_parse::module::parse_header; use roc_parse::state::State; use roc_parse::test_helpers::parse_expr_with; use roc_problem::Severity; use roc_region::all::LineInfo; use roc_reporting::report::{ can_problem, parse_problem, type_problem, RenderTarget, Report, ANSI_STYLE_CODES, DEFAULT_PALETTE, }; use roc_reporting::report::{RocDocAllocator, RocDocBuilder}; use roc_solve::FunctionKind; use roc_solve_problem::TypeError; use roc_types::subs::Subs; use std::path::PathBuf; fn filename_from_string(str: &str) -> PathBuf { let mut filename = PathBuf::new(); filename.push(str); filename } fn to_simple_report(doc: RocDocBuilder) -> Report { Report { title: "".to_string(), doc, filename: filename_from_string(r"/code/proj/Main.roc"), severity: Severity::RuntimeError, } } fn promote_expr_to_module(src: &str) -> String { let mut buffer = String::from("app \"test\" provides [main] to \"./platform\"\n\nmain =\n"); for line in src.lines() { // indent the body! buffer.push_str(" "); buffer.push_str(line); buffer.push('\n'); } buffer } fn maybe_save_parse_test_case(test_name: &str, src: &str, is_expr: bool) { // First check if the env var indicates we should migrate tests if std::env::var("ROC_MIGRATE_REPORTING_TESTS").is_err() { return; } // Check if we have parse errors let arena = Bump::new(); let has_error = if is_expr { parse_expr_with(&arena, src).is_err() } else { parse_header(&arena, State::new(src.trim().as_bytes())).is_err() // TODO: also parse the module defs }; if !has_error { return; } let mut path = PathBuf::from(std::env!("ROC_WORKSPACE_DIR")); path.push("crates"); path.push("compiler"); path.push("parse"); path.push("tests"); path.push("snapshots"); path.push("fail"); let kind = if is_expr { "expr" } else { "header" }; path.push(format!("{test_name}.{kind}.roc")); std::fs::write(path, src).unwrap(); } fn run_load_and_infer<'a>( subdir: &str, arena: &'a Bump, src: &'a str, ) -> (String, Result>) { use std::fs::File; use std::io::Write; let module_src = if src.starts_with("app") { maybe_save_parse_test_case(subdir, src, false); // this is already a module src.to_string() } else { maybe_save_parse_test_case(subdir, src, true); // this is an expression, promote it to a module promote_expr_to_module(src) }; let loaded = { // Use a deterministic temporary directory. // We can't have all tests use "tmp" because tests run in parallel, // so append the test name to the tmp path. let tmp = format!("tmp/{subdir}"); let dir = roc_test_utils::TmpDir::new(&tmp); let filename = PathBuf::from("Test.roc"); let file_path = dir.path().join(filename); let full_file_path = file_path.clone(); let mut file = File::create(file_path).unwrap(); writeln!(file, "{module_src}").unwrap(); let load_config = LoadConfig { target_info: roc_target::TargetInfo::default_x86_64(), render: RenderTarget::Generic, palette: DEFAULT_PALETTE, threading: Threading::Single, exec_mode: ExecutionMode::Check, function_kind: FunctionKind::LambdaSet, }; let result = roc_load::load_and_typecheck( arena, full_file_path, RocCacheDir::Disallowed, load_config, ); drop(file); result }; (module_src, loaded) } #[allow(clippy::type_complexity)] fn infer_expr_help_new<'a>( subdir: &str, arena: &'a Bump, expr_src: &'a str, ) -> Result< ( String, Vec, Vec, ModuleId, Interns, ), LoadingProblem<'a>, > { let (module_src, result) = run_load_and_infer(subdir, arena, expr_src); let LoadedModule { module_id: home, mut can_problems, mut type_problems, interns, .. } = result?; let can_problems = can_problems.remove(&home).unwrap_or_default(); let type_problems = type_problems.remove(&home).unwrap_or_default(); Ok((module_src, type_problems, can_problems, home, interns)) } fn list_reports_new(subdir: &str, arena: &Bump, src: &str, finalize_render: F) -> String where F: FnOnce(RocDocBuilder<'_>, &mut String), { use ven_pretty::DocAllocator; let filename = filename_from_string(r"/code/proj/Main.roc"); let mut buf = String::new(); match infer_expr_help_new(subdir, arena, src) { Err(LoadingProblem::FormattedReport(fail)) => fail, Ok((module_src, type_problems, can_problems, home, interns)) => { let lines = LineInfo::new(&module_src); let src_lines: Vec<&str> = module_src.split('\n').collect(); let mut reports = Vec::new(); let alloc = RocDocAllocator::new(&src_lines, home, &interns); for problem in can_problems { let report = can_problem(&alloc, &lines, filename.clone(), problem.clone()); reports.push(report); } for problem in type_problems { if let Some(report) = type_problem(&alloc, &lines, filename.clone(), problem.clone()) { reports.push(report); } } let has_reports = !reports.is_empty(); let doc = alloc .stack(reports.into_iter().map(|v| v.pretty(&alloc))) .append(if has_reports { alloc.line() } else { alloc.nil() }); finalize_render(doc, &mut buf); buf } Err(other) => { panic!("failed to load: {other:?}"); } } } fn infer_expr_help<'a>( arena: &'a Bump, expr_src: &'a str, ) -> Result< ( Vec, Vec, ModuleId, Interns, ), ParseErrOut<'a>, > { let CanExprOut { loc_expr: _, output, var_store, var, constraints, constraint, home, interns, problems: can_problems, mut types, .. } = can_expr(arena, expr_src)?; let mut subs = Subs::new_from_varstore(var_store); for named in output.introduced_variables.named { subs.rigid_var(named.variable, named.name); } for var in output.introduced_variables.wildcards { subs.rigid_var(var.value, "*".into()); } let mut solve_aliases = roc_solve::Aliases::default(); for (name, alias) in output.aliases { solve_aliases.insert(&mut types, name, alias); } let mut unify_problems = Vec::new(); let mut abilities_store = AbilitiesStore::default(); let (_content, _subs) = infer_expr( subs, &mut unify_problems, types, &constraints, constraint, // Use `new_report_problem_as` in order to get proper derives. // TODO: remove the non-new reporting test infra. PendingDerives::default(), &mut solve_aliases, &mut abilities_store, Default::default(), var, ); Ok((unify_problems, can_problems, home, interns)) } fn list_reports(arena: &Bump, src: &str, buf: &mut String, callback: F) where F: FnOnce(RocDocBuilder<'_>, &mut String), { use ven_pretty::DocAllocator; let src_lines: Vec<&str> = src.split('\n').collect(); let lines = LineInfo::new(src); let filename = filename_from_string(r"/code/proj/Main.roc"); match infer_expr_help(arena, src) { Err(parse_err) => { let ParseErrOut { fail, home, interns, } = parse_err; let alloc = RocDocAllocator::new(&src_lines, home, &interns); let problem = fail.into_file_error(filename.clone()); let doc = parse_problem(&alloc, &lines, filename, 0, problem); callback(doc.pretty(&alloc).append(alloc.line()), buf) } Ok((type_problems, can_problems, home, interns)) => { let mut reports = Vec::new(); let alloc = RocDocAllocator::new(&src_lines, home, &interns); for problem in can_problems { let report = can_problem(&alloc, &lines, filename.clone(), problem.clone()); reports.push(report); } for problem in type_problems { if let Some(report) = type_problem(&alloc, &lines, filename.clone(), problem.clone()) { reports.push(report); } } let has_reports = !reports.is_empty(); let doc = alloc .stack(reports.into_iter().map(|v| v.pretty(&alloc))) .append(if has_reports { alloc.line() } else { alloc.nil() }); callback(doc, buf) } } } fn list_header_reports(arena: &Bump, src: &str, buf: &mut String, callback: F) where F: FnOnce(RocDocBuilder<'_>, &mut String), { use ven_pretty::DocAllocator; let state = State::new(src.as_bytes()); let filename = filename_from_string(r"/code/proj/Main.roc"); let src_lines: Vec<&str> = src.split('\n').collect(); let lines = LineInfo::new(src); match roc_parse::module::parse_header(arena, state) { Err(fail) => { let interns = Interns::default(); let home = crate::helpers::test_home(); let alloc = RocDocAllocator::new(&src_lines, home, &interns); use roc_parse::parser::SyntaxError; let problem = fail .map_problem(SyntaxError::Header) .into_file_error(filename.clone()); let doc = parse_problem(&alloc, &lines, filename, 0, problem); callback(doc.pretty(&alloc).append(alloc.line()), buf) } Ok(_) => todo!(), } } fn report_header_problem_as(src: &str, expected_rendering: &str) { let mut buf: String = String::new(); let arena = Bump::new(); let callback = |doc: RocDocBuilder<'_>, buf: &mut String| { doc.1 .render_raw(70, &mut roc_reporting::report::CiWrite::new(buf)) .expect("list_reports") }; list_header_reports(&arena, src, &mut buf, callback); // convenient to copy-paste the generated message if buf != expected_rendering { for line in buf.split('\n') { println!(" {line}"); } } assert_eq!(buf, expected_rendering); } fn color_report_problem_as(src: &str, expected_rendering: &str) { let mut buf: String = String::new(); let arena = Bump::new(); let callback = |doc: RocDocBuilder<'_>, buf: &mut String| { doc.1 .render_raw( 70, &mut roc_reporting::report::ColorWrite::new( &roc_reporting::report::DEFAULT_PALETTE, buf, ), ) .expect("list_reports") }; list_reports(&arena, src, &mut buf, callback); let readable = human_readable(&buf); assert_eq!(readable, expected_rendering); } /// Do not call this directly! Use the test_report macro below! fn __new_report_problem_as(test_name: &str, src: &str, check_render: impl FnOnce(&str)) { let arena = Bump::new(); let finalize_render = |doc: RocDocBuilder<'_>, buf: &mut String| { doc.1 .render_raw(70, &mut roc_reporting::report::CiWrite::new(buf)) .expect("list_reports") }; let buf = list_reports_new(test_name, &arena, src, finalize_render); check_render(buf.as_str()); } macro_rules! test_report { ($(#[$meta:meta])* $test_name:ident, $program:expr, @$output:literal) => { test_report!($(#[$meta])* $test_name, $program, |golden| insta::assert_snapshot!(golden, @$output) ); }; ($(#[$meta:meta])* $test_name: ident, $program:expr, $expecting:expr) => { #[test] $(#[$meta])* fn $test_name() { __new_report_problem_as(std::stringify!($test_name), $program, $expecting) } } } macro_rules! test_no_problem { ($(#[$meta:meta])* $test_name: ident, $program:expr) => { #[test] $(#[$meta])* fn $test_name() { __new_report_problem_as(std::stringify!($test_name), $program, |golden| pretty_assertions::assert_eq!(golden, "")) } } } fn human_readable(str: &str) -> String { str.replace(ANSI_STYLE_CODES.red, "") .replace(ANSI_STYLE_CODES.white, "") .replace(ANSI_STYLE_CODES.blue, "") .replace(ANSI_STYLE_CODES.yellow, "") .replace(ANSI_STYLE_CODES.green, "") .replace(ANSI_STYLE_CODES.cyan, "") .replace(ANSI_STYLE_CODES.magenta, "") .replace(ANSI_STYLE_CODES.reset, "") .replace(ANSI_STYLE_CODES.bold, "") .replace(ANSI_STYLE_CODES.underline, "") } test_report!( value_not_exposed, indoc!( r#" List.isempty 1 2 "# ), @r###" ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ The List module does not expose `isempty`: 4│ List.isempty 1 2 ^^^^^^^^^^^^ Did you mean one of these? List.isEmpty List.set List.get List.keepIf "### ); test_report!( report_unused_def, indoc!( r#" x = 1 y = 2 x "# ), @r###" ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `y` is not used anywhere in your code. 5│ y = 2 ^ If you didn't intend on using `y` then remove it so future readers of your code don't wonder why it is there. "### ); test_report!( report_shadowing, indoc!( r#" i = 1 s = \i -> i + 1 s i "# ), @r###" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ The `i` name is first defined here: 4│ i = 1 ^ But then it's defined a second time here: 6│ s = \i -> ^ Since these variables have the same name, it's easy to use the wrong one by accident. Give one of them a new name. "### ); test_report!( report_shadowing_in_annotation, indoc!( r#" Booly : [Yes, No] Booly : [Yes, No, Maybe] x : List Booly x = [] x "# ), @r###" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ The `Booly` name is first defined here: 4│ Booly : [Yes, No] ^^^^^^^^^^^^^^^^^ But then it's defined a second time here: 6│ Booly : [Yes, No, Maybe] ^^^^^^^^^^^^^^^^^^^^^^^^ Since these aliases have the same name, it's easy to use the wrong one by accident. Give one of them a new name. "### ); test_report!( report_precedence_problem_single_line, indoc!( r#"x = 1 y = if selectedId != thisId == adminsId then 4 else 5 { x, y } "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ Using != and == together requires parentheses, to clarify how they should be grouped. 6│ if selectedId != thisId == adminsId then ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "### ); test_report!( #[ignore = "Blocked on https://github.com/roc-lang/roc/issues/3385"] unrecognized_name, indoc!( r#" foo = { x: 1 == 1, y: 0x4 } baz = 3 main : Str main = when foo.y is 4 -> bar baz "yay" _ -> "nay" main "# ), @r#" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `bar` in this scope. 8│ 4 -> bar baz "yay" ^^^ Did you mean one of these? baz Str Err main "# ); test_report!( lowercase_primitive_tag_bool, indoc!( r#" if true then 1 else 2 "# ), @r###" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `true` in this scope. 4│ if true then 1 else 2 ^^^^ Did you mean one of these? Frac Num Str Err "### ); test_report!( report_precedence_problem_multiline, indoc!( r#" if 1 == 2 == 3 then 2 else 3 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ Using more than one == like this requires parentheses, to clarify how things should be grouped. 5│> 1 6│> == 2 7│> == 3 "### ); test_report!( unused_arg_and_unused_def, indoc!( r#" y = 9 box = \class, htmlChildren -> div [class] [] div = \_, _ -> 4 box "wizard" [] "# ), @r###" ── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─ `box` doesn't use `htmlChildren`. 6│ box = \class, htmlChildren -> ^^^^^^^^^^^^ If you don't need `htmlChildren`, then you can just remove it. However, if you really do need `htmlChildren` as an argument of `box`, prefix it with an underscore, like this: "_`htmlChildren`". Adding an underscore at the start of a variable name is a way of saying that the variable is not used. ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `y` is not used anywhere in your code. 4│ y = 9 ^ If you didn't intend on using `y` then remove it so future readers of your code don't wonder why it is there. "### ); #[test] fn report_value_color() { let src: &str = indoc!( r#" activityIndicatorLarge = div view activityIndicatorLarge "# ); let arena = Bump::new(); let (_type_problems, _can_problems, home, interns) = infer_expr_help(&arena, src).expect("parse error"); let mut buf = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); let alloc = RocDocAllocator::new(&src_lines, home, &interns); let symbol = interns.symbol(test_home(), "activityIndicatorLarge".into()); to_simple_report(alloc.symbol_unqualified(symbol)).render_color_terminal( &mut buf, &alloc, &DEFAULT_PALETTE, ); assert_eq!(human_readable(&buf), "activityIndicatorLarge"); } #[test] fn report_module_color() { let src: &str = indoc!( r#" x = 1 y = 2 x "# ); let arena = Bump::new(); let (_type_problems, _can_problems, home, mut interns) = infer_expr_help(&arena, src).expect("parse error"); let mut buf = String::new(); let src_lines: Vec<&str> = src.split('\n').collect(); let module_id = interns.module_id(&"Util.Int".into()); let alloc = RocDocAllocator::new(&src_lines, home, &interns); to_simple_report(alloc.module(module_id)).render_color_terminal( &mut buf, &alloc, &DEFAULT_PALETTE, ); assert_eq!(human_readable(&buf), "Util.Int"); } #[test] fn report_region_in_color() { color_report_problem_as( indoc!( r#" isDisabled = \user -> user.isAdmin theAdmin |> isDisabled "# ), indoc!( r#" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `theAdmin` in this scope. 3 theAdmin ^^^^^^^^ Did you mean one of these? Ok List Err Box "# ), ); } test_report!( if_condition_not_bool, indoc!( r#" if "foo" then 2 else 3 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` condition needs to be a Bool: 4│ if "foo" then 2 else 3 ^^^^^ Right now it’s a string of type: Str But I need every `if` condition to evaluate to a Bool—either `Bool.true` or `Bool.false`. "### ); test_report!( when_if_guard, indoc!( r#" when 1 is 2 if 1 -> 0x0 _ -> 0x1 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` guard condition needs to be a Bool: 4│ when 1 is 5│> 2 if 1 -> 0x0 6│ _ -> 0x1 Right now it’s a number of type: Num * But I need every `if` guard condition to evaluate to a Bool—either `Bool.true` or `Bool.false`. "### ); test_report!( if_2_branch_mismatch, indoc!( r#" if Bool.true then 2 else "foo" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` has an `else` branch with a different type from its `then` branch: 4│ if Bool.true then 2 else "foo" ^^^^^ The `else` branch is a string of type: Str but the `then` branch has the type: Num * All branches in an `if` must have the same type! "### ); test_report!( if_3_branch_mismatch, indoc!( r#" if Bool.true then 2 else if Bool.false then 2 else "foo" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 3rd branch of this `if` does not match all the previous branches: 4│ if Bool.true then 2 else if Bool.false then 2 else "foo" ^^^^^ The 3rd branch is a string of type: Str But all the previous branches have type: Num * All branches in an `if` must have the same type! "### ); test_report!( when_branch_mismatch, indoc!( r#" when 1 is 2 -> "foo" 3 -> {} _ -> "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 2nd branch of this `when` does not match all the previous branches: 4│ when 1 is 5│ 2 -> "foo" 6│> 3 -> {} 7│ _ -> "" The 2nd branch is a record of type: {} But all the previous branches have type: Str All branches of a `when` must have the same type! "### ); test_report!( tuple_exhaustiveness_bad, indoc!( r#" Color : [Red, Blue] value : (Color, Color) value = (Red, Red) when value is (Blue, Blue) -> "foo" (Red, Blue) -> "foo" (Blue, Red) -> "foo" #(Red, Red) -> "foo" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 9│> when value is 10│> (Blue, Blue) -> "foo" 11│> (Red, Blue) -> "foo" 12│> (Blue, Red) -> "foo" Other possibilities include: ( Red, Red ) I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( tuple_exhaustiveness_good, indoc!( r#" Color : [Red, Blue] value : (Color, Color) value = (Red, Red) when value is (Blue, Blue) -> "foo" (Red, Blue) -> "foo" (Blue, Red) -> "foo" (Red, Red) -> "foo" "# ), @"" // No error ); test_report!( elem_in_list, indoc!( r#" [1, 3, "foo"] "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This list contains elements with different types: 4│ [1, 3, "foo"] ^^^^^ Its 3rd element is a string of type: Str However, the preceding elements in the list all have the type: Num * Every element in a list must have the same type! "### ); test_report!( unwrap_num_elem_in_list, indoc!( r#" [1, 2.2, 0x3] "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This list contains elements with different types: 4│ [1, 2.2, 0x3] ^^^ Its 3rd element is an integer of type: Int * However, the preceding elements in the list all have the type: Frac * Every element in a list must have the same type! Tip: You can convert between integers and fractions using functions like `Num.toFrac` and `Num.round`. "### ); test_report!( record_update_value, indoc!( r#" x : { foo : {} } x = { foo: {} } { x & foo: "bar" } "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ I cannot update the `.foo` field like this: 7│ { x & foo: "bar" } ^^^^^ You are trying to update `.foo` to be a string of type: Str But it should be: {} Record update syntax does not allow you to change the type of fields. You can achieve that with record literal syntax. "### ); test_report!( circular_type, indoc!( r#" f = \g -> g g f "# ), @r###" ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `f`: 4│ f = \g -> g g ^ 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 "### ); test_report!( polymorphic_recursion, indoc!( r#" f = \x -> f [x] f "# ), @r###" ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `f`: 4│ f = \x -> f [x] ^ 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 ∞ -> * "### ); test_report!( polymorphic_mutual_recursion, indoc!( r#" f = \x -> g x g = \x -> f [x] f "# ), @r###" ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `f`: 4│ f = \x -> g x ^ 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 ∞ -> * ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `g`: 5│ g = \x -> f [x] ^ 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 ∞ -> * "### ); test_report!( polymorphic_mutual_recursion_annotated, indoc!( r#" f : a -> List a f = \x -> g x g = \x -> f [x] f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 5│ f = \x -> g x ^^^ This `g` call produces: List List a But you are trying to use it as: List a Tip: The type annotation uses the type variable `a` to say that this definition can produce any type of value. But in the body I see that it will only produce a `List` value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? "### ); test_report!( polymorphic_mutual_recursion_dually_annotated_lie, indoc!( r#" f : a -> List a f = \x -> g x g : b -> List b g = \x -> f [x] f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 7│ g = \x -> f [x] ^^^^^ This `f` call produces: List List b But you are trying to use it as: List b Tip: The type annotation uses the type variable `b` to say that this definition can produce any type of value. But in the body I see that it will only produce a `List` value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? "### ); test_report!( polymorphic_recursion_inference_var, indoc!( r#" f : _ f = \x -> f [x] f "# ), @r###" ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `f`: 5│ f = \x -> f [x] ^ 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 ∞ -> * "### ); test_report!( polymorphic_recursion_with_deep_inference_var, indoc!( r#" f : _ -> List _ f = \x -> f [x] f "# ), @r###" ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `f`: 5│ f = \x -> f [x] ^ 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 ∞ -> List * "### ); test_report!( mutual_polymorphic_recursion_with_inference_var, indoc!( r#" f : _ -> List _ f = \x -> g x g = \x -> f [x] f "# ), // TODO: the second error is duplicated because when solving `f : _ -> List _`, we // introduce the variable for `f` twice: once to solve `f` without generalization, // and then a second time to properly generalize it. When a def is unannotated // (like in `g`) the same variable gets used both times, because the type of `g` is // only an unbound type variable. However, for `f`, we run `type_to_var` twice, // receiving two separate variables, and the second variable doesn't have the cycle // error already recorded for the first. // The way to resolve this is to always give type annotation signatures an extra // variables they can put themselves in, and to run the constraint algorithm // against that extra variable, rather than possibly having to translate a `Type` // again. @r###" ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `f`: 5│ f = \x -> g x ^ 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 ∞ -> List * ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `g`: 6│ g = \x -> f [x] ^ 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 ∞ -> List * "### ); test_report!( mutual_polymorphic_recursion_with_inference_var_second, indoc!( r#" f = \x -> g x g : _ -> List _ g = \x -> f [x] f "# ), @r###" ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `f`: 4│ f = \x -> g x ^ 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 ∞ -> List * ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `g`: 6│ g = \x -> f [x] ^ 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 ∞ -> List * "### ); test_report!( record_field_mismatch, indoc!( r#" bar = { bar : 0x3 } f : { foo : Num.Int * } -> [Yes, No] f = \_ -> Yes f bar "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `f` has an unexpected type: 9│ f bar ^^^ This `bar` value is a: { bar : Int * } But `f` needs its 1st argument to be: { foo : Int * } Tip: Seems like a record field typo. Maybe `bar` should be `foo`? Tip: 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 "### ); test_report!( tag_mismatch, indoc!( r#" f : [Red, Green] -> [Yes, No] f = \_ -> Yes f Blue "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `f` has an unexpected type: 7│ f Blue ^^^^ This `Blue` tag has the type: [Blue] But `f` needs its 1st argument to be: [ Green, Red, ] Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? Tip: 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 "### ); test_report!( tag_with_arguments_mismatch, indoc!( r#" f : [Red (Num.Int *), Green Str] -> Str f = \_ -> "yes" f (Blue 3.14) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `f` has an unexpected type: 7│ f (Blue 3.14) ^^^^^^^^^ This `Blue` tag application has the type: [Blue (Frac *)] But `f` needs its 1st argument to be: [ Green Str, Red (Int *), ] Tip: Seems like a tag typo. Maybe `Blue` should be `Red`? Tip: 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 "### ); test_report!( from_annotation_if, indoc!( r#" x : Num.Int * x = if Bool.true then 3.14 else 4 x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the `then` branch of this `if` expression: 4│ x : Num.Int * 5│ x = if Bool.true then 3.14 else 4 ^^^^ This branch is a fraction of type: Frac * But the type annotation on `x` says it should be: Int * Tip: You can convert between integers and fractions using functions like `Num.toFrac` and `Num.round`. "### ); test_report!( from_annotation_when, indoc!( r#" x : Num.Int * x = when True is _ -> 3.14 x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `x` definition: 4│ x : Num.Int * 5│ x = 6│> when True is 7│> _ -> 3.14 This `when` expression produces: Frac * But the type annotation on `x` says it should be: Int * Tip: You can convert between integers and fractions using functions like `Num.toFrac` and `Num.round`. "### ); test_report!( from_annotation_function, indoc!( r#" x : Num.Int * -> Num.Int * x = \_ -> 3.14 x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `x` definition: 4│ x : Num.Int * -> Num.Int * 5│ x = \_ -> 3.14 ^^^^ The body is a fraction of type: Frac * But the type annotation on `x` says it should be: Int * Tip: You can convert between integers and fractions using functions like `Num.toFrac` and `Num.round`. "### ); test_report!( fncall_value, indoc!( r#" x : Num.I64 x = 42 x 3 "# ), @r###" ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ The `x` value is not a function, but it was given 1 argument: 7│ x 3 ^ Are there any missing commas? Or missing parentheses? "### ); test_report!( fncall_overapplied, indoc!( r#" f : Num.I64 -> Num.I64 f = \_ -> 42 f 1 2 "# ), @r###" ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ The `f` function expects 1 argument, but it got 2 instead: 7│ f 1 2 ^ Are there any missing commas? Or missing parentheses? "### ); test_report!( fncall_underapplied, indoc!( r#" f : Num.I64, Num.I64 -> Num.I64 f = \_, _ -> 42 f 1 "# ), @r###" ── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─ The `f` function expects 2 arguments, but it got only 1: 7│ f 1 ^ Roc does not allow functions to be partially applied. Use a closure to make partial application explicit. "### ); test_report!( pattern_when_condition, indoc!( r#" when 1 is {} -> 42 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 4│> when 1 is 5│ {} -> 42 The `when` condition is a number of type: Num * But the branch patterns have type: {}a The branches must be cases of the `when` condition's type! "### ); test_report!( pattern_when_pattern, indoc!( r#" when 1 is 2 -> 3 {} -> 42 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern in this `when` does not match the previous ones: 6│ {} -> 42 ^^ The 2nd pattern is trying to match record values of type: {}a But all the previous branches match: Num * "### ); test_report!( pattern_guard_mismatch_alias, indoc!( r#" when { foo: 1 } is { foo: True } -> 42 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 4│> when { foo: 1 } is 5│ { foo: True } -> 42 The `when` condition is a record of type: { foo : Num * } But the branch patterns have type: { foo : [True] } The branches must be cases of the `when` condition's type! "### ); test_report!( pattern_guard_mismatch, indoc!( r#" when { foo: "" } is { foo: True } -> 42 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 4│> when { foo: "" } is 5│ { foo: True } -> 42 The `when` condition is a record of type: { foo : Str } But the branch patterns have type: { foo : [True] } The branches must be cases of the `when` condition's type! "### ); // needs some improvement, but the principle works test_report!( pattern_guard_does_not_bind_label, indoc!( r#" when { foo: 1 } is { foo: _ } -> foo "# ), @r###" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `foo` in this scope. 5│ { foo: _ } -> foo ^^^ Did you mean one of these? Box Bool U8 F64 "### ); test_report! { pattern_guard_can_be_shadowed_above, indoc!( r#" foo = 3 when { foo: 1 } is { foo: 2 } -> foo _ -> foo "# ), @"" // should give no error } test_report! { pattern_guard_can_be_shadowed_below, indoc!( r#" when { foo: 1 } is { foo: 2 } -> foo = 3 foo _ -> 3 "# ), // should give no error @"" } test_report!( pattern_or_pattern_mismatch, indoc!( r#" when { foo: 1 } is {} | 1 -> 3 "# ), // Just putting this here. We should probably handle or-patterns better @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern in this branch does not match the previous ones: 5│ {} | 1 -> 3 ^ The 2nd pattern is trying to match numbers: Num * But all the previous branches match: {}a "### ); test_report!( pattern_let_mismatch, indoc!( r#" (Foo x) = 42 x "# ), // Maybe this should specifically say the pattern doesn't work? @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 4│ (Foo x) = 42 ^^ It is a number of type: Num * But you are trying to use it as: [Foo *] "### ); test_report!( from_annotation_complex_pattern, indoc!( r#" { x } : { x : Num.Int * } { x } = { x: 4.0 } x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of this definition: 4│ { x } : { x : Num.Int * } 5│ { x } = { x: 4.0 } ^^^^^^^^^^ The body is a record of type: { x : Frac * } But the type annotation says it should be: { x : Int * } Tip: You can convert between integers and fractions using functions like `Num.toFrac` and `Num.round`. "### ); test_report!( malformed_int_pattern, indoc!( r#" when 1 is 100A -> 3 _ -> 4 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer pattern is malformed: 5│ 100A -> 3 ^^^^ Tip: Learn more about number literals at TODO "### ); test_report!( malformed_float_pattern, indoc!( r#" when 1 is 2.X -> 3 _ -> 4 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This float pattern is malformed: 5│ 2.X -> 3 ^^^ Tip: Learn more about number literals at TODO "### ); test_report!( malformed_hex_pattern, indoc!( r#" when 1 is 0xZ -> 3 _ -> 4 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This hex integer pattern is malformed: 5│ 0xZ -> 3 ^^^ Tip: Learn more about number literals at TODO "### ); test_report!( malformed_oct_pattern, indoc!( r#" when 1 is 0o9 -> 3 _ -> 4 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This octal integer pattern is malformed: 5│ 0o9 -> 3 ^^^ Tip: Learn more about number literals at TODO "### ); test_report!( malformed_bin_pattern, indoc!( r#" when 1 is 0b4 -> 3 _ -> 4 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This binary integer pattern is malformed: 5│ 0b4 -> 3 ^^^ Tip: Learn more about number literals at TODO "### ); test_report!( missing_fields, indoc!( r#" x : { a : Num.Int *, b : Num.Frac *, c : Str } x = { b: 4.0 } x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `x` definition: 4│ x : { a : Num.Int *, b : Num.Frac *, c : Str } 5│ x = { b: 4.0 } ^^^^^^^^^^ The body is a record of type: { b : Frac * } But the type annotation on `x` says it should be: { a : Int *, b : Frac *, c : Str, } Tip: Looks like the c and a fields are missing. "### ); // 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 Bool.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 test_report!( bad_double_rigid, indoc!( r#" f : a, b -> a f = \x, y -> if Bool.true then x else y f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the `else` branch of this `if` expression: 4│ f : a, b -> a 5│ f = \x, y -> if Bool.true then x else y ^ This `y` value is a: b But the type annotation on `f` says it should be: a Tip: 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 in your type annotation? Maybe your code uses them in a weird way? "### ); test_report!( bad_rigid_function, indoc!( r#" f : Str -> msg f = \_ -> Foo f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : Str -> msg 5│ f = \_ -> Foo ^^^ This `Foo` tag has the type: [Foo] But the type annotation on `f` says it should be: msg Tip: The type annotation uses the type variable `msg` to say that this definition can produce any type of value. But in the body I see that it will only produce a tag value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? "### ); test_report!( bad_rigid_value, indoc!( r#" f : msg f = 0x3 f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : msg 5│ f = 0x3 ^^^ The body is an integer of type: Int * But the type annotation on `f` says it should be: msg Tip: The type annotation uses the type variable `msg` to say that this definition can produce any type of value. But in the body I see that it will only produce a `Int` value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? "### ); // TODO improve tag suggestions test_report!( typo_lowercase_ok, indoc!( r#" f : Str -> [Ok Num.I64, InvalidFoo] f = \_ -> ok 4 f "# ), @r###" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `ok` in this scope. 5│ f = \_ -> ok 4 ^^ Did you mean one of these? Ok U8 Box Eq "### ); // these error messages seem pretty helpful test_report!( typo_uppercase_ok, indoc!( r#" f : Str -> Num.I64 f = \_ -> ok = 3 Ok f "# ), @r###" ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `ok` is not used anywhere in your code. 6│ ok = 3 ^^ If you didn't intend on using `ok` then remove it so future readers of your code don't wonder why it is there. ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : Str -> Num.I64 5│ f = \_ -> 6│ ok = 3 7│ 8│ Ok ^^ This `Ok` tag has the type: [Ok] But the type annotation on `f` says it should be: I64 "### ); // invalid recursion test_report!( circular_definition_self, indoc!( r#" f = f f "# ), @r###" ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ `f` is defined directly in terms of itself: 4│ f = f ^^^^^ Roc evaluates values strictly, so running this program would enter an infinite loop! Hint: Did you mean to define `f` as a function? "### ); // invalid mutual recursion test_report!( circular_definition, indoc!( r#" foo = bar bar = foo foo "# ), @r###" ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ The `foo` definition is causing a very tricky infinite loop: 4│ foo = bar ^^^ The `foo` value depends on itself through the following chain of definitions: ┌─────┐ │ foo │ ↓ │ bar └─────┘ "### ); test_report!( update_empty_record, indoc!( r#" x = {} { x & foo: 3 } "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `x` record doesn’t have a `foo` field: 6│ { x & foo: 3 } ^^^^^^ In fact, `x` is a record with no fields at all! "### ); test_report!( update_record, indoc!( r#" x = { fo: 3, bar: 4 } { x & foo: 3 } "# ), // TODO also suggest fields with the correct type @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `x` record doesn’t have a `foo` field: 6│ { x & foo: 3 } ^^^^^^ There may be a typo. These `x` fields are the most similar: { fo : Num *, bar : Num *, } Maybe `foo:` should be `fo:` instead? "### ); test_report!( update_record_ext, indoc!( r#" f : { fo: Num.I64 }ext -> Num.I64 f = \r -> r2 = { r & foo: r.fo } r2.fo f "# ), // TODO also suggest fields with the correct type @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `r` record doesn’t have a `foo` field: 6│ r2 = { r & foo: r.fo } ^^^^^^^^^ There may be a typo. These `r` fields are the most similar: { fo : I64, }ext Maybe `foo:` should be `fo:` instead? "### ); test_report!( update_record_snippet, indoc!( r#" x = { fo: 3, bar: 4, baz: 3, spam: 42, foobar: 3 } { x & foo: 3 } "# ), // TODO also suggest fields with the correct type @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `x` record doesn’t have a `foo` field: 6│ { x & foo: 3 } ^^^^^^ There may be a typo. These `x` fields are the most similar: { fo : Num *, foobar : Num *, bar : Num *, baz : Num *, … } Maybe `foo:` should be `fo:` instead? "### ); test_report!( plus_on_str, indoc!( r#" 0x4 + "foo" "# ), // TODO also suggest fields with the correct type @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to + has an unexpected type: 4│ 0x4 + "foo" ^^^^^ The argument is a string of type: Str But + needs its 2nd argument to be: Int * "### ); test_report!( int_frac, indoc!( r#" 0x4 + 3.14 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to + has an unexpected type: 4│ 0x4 + 3.14 ^^^^ The argument is a fraction of type: Frac * But + needs its 2nd argument to be: Int * Tip: You can convert between integers and fractions using functions like `Num.toFrac` and `Num.round`. "### ); test_report!( boolean_tag, indoc!( r#" 42 + True "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to + has an unexpected type: 4│ 42 + True ^^^^ This `True` tag has the type: [True] But + needs its 2nd argument to be: Num * "### ); test_report!( tag_missing, indoc!( r#" f : [A] -> [A, B] f = \a -> a f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : [A] -> [A, B] 5│ f = \a -> a ^ This `a` value is a: […] But the type annotation on `f` says it should be: [B, …] Tip: Looks like a closed tag union does not have the `B` tag. Tip: Closed tag unions can't grow, because that might change the size in memory. Can you use an open tag union? "### ); test_report!( tags_missing, indoc!( r#" f : [A] -> [A, B, C] f = \a -> a f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : [A] -> [A, B, C] 5│ f = \a -> a ^ This `a` value is a: […] But the type annotation on `f` says it should be: [ B, C, … ] Tip: Looks like a closed tag union does not have the `B` and `C` tags. Tip: Closed tag unions can't grow, because that might change the size in memory. Can you use an open tag union? "### ); test_report!( patterns_fn_not_exhaustive, indoc!( r#" Either : [Left {}, Right Str] x : Either x = Left {} f : Either -> {} f = \Left v -> v f x "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This pattern does not cover all the possibilities: 10│ f = \Left v -> v ^^^^^^ Other possibilities include: Right _ I would have to crash if I saw one of those! So rather than pattern matching in function arguments, put a `when` in the function body to account for all possibilities. ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 9│ f : Either -> {} 10│ f = \Left v -> v ^^^^^^^^^^^^ The body is an anonymous function of type: […] -> {} But the type annotation on `f` says it should be: [Right Str, …] -> {} Tip: Looks like a closed tag union does not have the `Right` tag. Tip: Closed tag unions can't grow, because that might change the size in memory. Can you use an open tag union? "### ); test_report!( patterns_let_not_exhaustive, indoc!( r#" x : [Left {}, Right Str] x = Left {} (Left y) = x y "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 8│ (Left y) = x ^ This `x` value is a: [Right Str, …] But you are trying to use it as: […] Tip: Looks like a closed tag union does not have the `Right` tag. Tip: Closed tag unions can't grow, because that might change the size in memory. Can you use an open tag union? "### ); test_report!( patterns_when_not_exhaustive, indoc!( r#" when 0x1 is 2 -> 0x3 "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 4│> when 0x1 is 5│> 2 -> 0x3 Other possibilities include: _ I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( patterns_bool_not_exhaustive, indoc!( r#" x : [Red, Green] x = Green when x is Red -> 3 "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 7│> when x is 8│> Red -> 3 Other possibilities include: Green I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( patterns_enum_not_exhaustive, indoc!( r#" x : [Red, Green, Blue] x = Red when x is Red -> 0 Green -> 1 "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 7│> when x is 8│> Red -> 0 9│> Green -> 1 Other possibilities include: Blue I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( patterns_remote_data_not_exhaustive, indoc!( r#" RemoteData e a : [NotAsked, Loading, Failure e, Success a] x : RemoteData Num.I64 Str when x is NotAsked -> 3 "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 8│> when x is 9│> NotAsked -> 3 Other possibilities include: Failure _ Loading Success _ I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( patterns_record_not_exhaustive, indoc!( r#" x = { a: 3 } when x is { a: 4 } -> 4 "# ), // Tip: Looks like a record field guard is not exhaustive. Learn more about record pattern matches at TODO. @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when x is 7│> { a: 4 } -> 4 Other possibilities include: { a } I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( patterns_record_guard_not_exhaustive, indoc!( r#" y : [Nothing, Just Num.I64] y = Just 4 x = { a: y, b: 42} when x is { a: Nothing } -> 4 { a: Just 3 } -> 4 "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 8│> when x is 9│> { a: Nothing } -> 4 10│> { a: Just 3 } -> 4 Other possibilities include: { a: Just _ } I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( patterns_nested_tag_not_exhaustive, indoc!( r#" when Record Nothing 1 is Record (Nothing) b -> b Record (Just 3) b -> b "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 4│> when Record Nothing 1 is 5│> Record (Nothing) b -> b 6│> Record (Just 3) b -> b Other possibilities include: Record (Just _) _ I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( patterns_int_redundant, indoc!( r#" when 0x1 is 2 -> 3 2 -> 4 _ -> 5 "# ), @r###" ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern is redundant: 4│ when 0x1 is 5│ 2 -> 3 6│> 2 -> 4 7│ _ -> 5 Any value of this shape will be handled by a previous pattern, so this one should be removed. "### ); test_report!( unify_alias_other, indoc!( r#" Foo a : { x : Num.Int a } f : Foo a -> Num.Int a f = \r -> r.x f { y: 3.14 } "# ), // de-aliases the alias to give a better error message @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `f` has an unexpected type: 9│ f { y: 3.14 } ^^^^^^^^^^^ The argument is a record of type: { y : Frac * } But `f` needs its 1st argument to be: { x : Int a } Tip: Seems like a record field typo. Maybe `y` should be `x`? Tip: 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 "### ); test_report!( #[ignore] cyclic_alias, indoc!( r#" Foo : { x : Bar } Bar : { y : Foo } f : Foo f "# ), // should not report Bar as unused! @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `Foo` alias is self-recursive in an invalid way: 4│ Foo : { x : Bar } ^^^ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( self_recursive_alias, indoc!( r#" Foo : { x : Foo } f : Foo f = 3 f "# ), // should not report Bar as unused! @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `Foo` alias is self-recursive in an invalid way: 4│ Foo : { x : Foo } ^^^ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( record_duplicate_field_same_type, indoc!( r#" { x: 4, y: 3, x: 4 } "# ), @r###" ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ This record defines the `.x` field twice! 4│ { x: 4, y: 3, x: 4 } ^^^^ ^^^^ In the rest of the program, I will only use the latter definition: 4│ { x: 4, y: 3, x: 4 } ^^^^ For clarity, remove the previous `.x` definitions from this record. "### ); test_report!( record_duplicate_field_different_types, indoc!( r#" { x: 4, y: 3, x: "foo" } "# ), @r###" ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ This record defines the `.x` field twice! 4│ { x: 4, y: 3, x: "foo" } ^^^^ ^^^^^^^^ In the rest of the program, I will only use the latter definition: 4│ { x: 4, y: 3, x: "foo" } ^^^^^^^^ For clarity, remove the previous `.x` definitions from this record. "### ); test_report!( record_duplicate_field_multiline, indoc!( r#" { x: 4, y: 3, x: "foo" } "# ), @r###" ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ This record defines the `.x` field twice! 4│ { 5│> x: 4, 6│ y: 3, 7│> x: "foo" 8│ } In the rest of the program, I will only use the latter definition: 4│ { 5│ x: 4, 6│ y: 3, 7│> x: "foo" 8│ } For clarity, remove the previous `.x` definitions from this record. "### ); test_report!( record_update_duplicate_field_multiline, indoc!( r#" \r -> { r & x: 4, y: 3, x: "foo" } "# ), @r###" ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ This record defines the `.x` field twice! 5│ { r & 6│> x: 4, 7│ y: 3, 8│> x: "foo" 9│ } In the rest of the program, I will only use the latter definition: 5│ { r & 6│ x: 4, 7│ y: 3, 8│> x: "foo" 9│ } For clarity, remove the previous `.x` definitions from this record. "### ); test_report!( record_type_duplicate_field, indoc!( r#" a : { foo : Num.I64, bar : {}, foo : Str } a = { bar: {}, foo: "foo" } a "# ), @r###" ── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─ This record type defines the `.foo` field twice! 4│ a : { foo : Num.I64, bar : {}, foo : Str } ^^^^^^^^^^^^^ ^^^^^^^^^ In the rest of the program, I will only use the latter definition: 4│ a : { foo : Num.I64, bar : {}, foo : Str } ^^^^^^^^^ For clarity, remove the previous `.foo` definitions from this record type. "### ); test_report!( tag_union_duplicate_tag, indoc!( r#" a : [Foo Num.I64, Bar {}, Foo Str] a = Foo "foo" a "# ), @r###" ── DUPLICATE TAG NAME ──────────────────────────────────── /code/proj/Main.roc ─ This tag union type defines the `Foo` tag twice! 4│ a : [Foo Num.I64, Bar {}, Foo Str] ^^^^^^^^^^^ ^^^^^^^ In the rest of the program, I will only use the latter definition: 4│ a : [Foo Num.I64, Bar {}, Foo Str] ^^^^^^^ For clarity, remove the previous `Foo` definitions from this tag union type. "### ); test_report!( annotation_definition_mismatch, indoc!( r#" bar : Num.I64 foo = \x -> x # NOTE: neither bar or foo are defined at this point 4 "# ), @r###" ── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This annotation does not match the definition immediately following it: 4│> bar : Num.I64 5│> foo = \x -> x Is it a typo? If not, put either a newline or comment between them. "### ); test_report!( annotation_newline_body_is_fine, indoc!( r#" bar : Num.I64 foo = \x -> x foo bar "# ), @"" ); test_report!( invalid_alias_rigid_var_pattern, indoc!( r#" MyAlias 1 : Num.I64 4 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This definition of `MyAlias` has an unexpected pattern: 4│ MyAlias 1 : Num.I64 ^ Only type variables like `a` or `value` can occur in this position. ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `MyAlias` is not used anywhere in your code. 4│ MyAlias 1 : Num.I64 ^^^^^^^^^^^^^^^^^^^ If you didn't intend on using `MyAlias` then remove it so future readers of your code don't wonder why it is there. "### ); test_report!( invalid_opaque_rigid_var_pattern, indoc!( r#" Age 1 := Num.I64 a : Age a "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This definition of `Age` has an unexpected pattern: 4│ Age 1 := Num.I64 ^ Only type variables like `a` or `value` can occur in this position. "### ); test_report!( invalid_num, indoc!( r#" a : Num.Num Num.I64 Num.F64 a = 3 a "# ), @r###" ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ The `Num` opaque expects 1 type argument, but it got 2 instead: 4│ a : Num.Num Num.I64 Num.F64 ^^^^^^^^^^^^^^^^^^^^^^^ Are there missing parentheses? "### ); test_report!( invalid_num_fn, indoc!( r#" f : Str -> Num.Num Num.I64 Num.F64 f = \_ -> 3 f "# ), @r###" ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ The `Num` opaque expects 1 type argument, but it got 2 instead: 4│ f : Str -> Num.Num Num.I64 Num.F64 ^^^^^^^^^^^^^^^^^^^^^^^ Are there missing parentheses? "### ); test_report!( too_few_type_arguments, indoc!( r#" Pair a b : [Pair a b] x : Pair Num.I64 x = Pair 2 3 x "# ), @r###" ── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─ The `Pair` alias expects 2 type arguments, but it got 1 instead: 6│ x : Pair Num.I64 ^^^^^^^^^^^^ Are there missing parentheses? "### ); test_report!( too_many_type_arguments, indoc!( r#" Pair a b : [Pair a b] x : Pair Num.I64 Num.I64 Num.I64 x = 3 x "# ), @r###" ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ The `Pair` alias expects 2 type arguments, but it got 3 instead: 6│ x : Pair Num.I64 Num.I64 Num.I64 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Are there missing parentheses? "### ); test_report!( phantom_type_variable, indoc!( r#" Foo a : [Foo] f : Foo Num.I64 f "# ), @r###" ── UNUSED TYPE ALIAS PARAMETER ─────────────────────────── /code/proj/Main.roc ─ The `a` type parameter is not used in the `Foo` alias definition: 4│ Foo a : [Foo] ^ Roc does not allow unused type parameters! Tip: If you want an unused type parameter (a so-called "phantom type"), read the guide section on phantom values. "### ); test_report!( elm_function_syntax, indoc!( r#" f x y = x "# ), @r###" ── ARGUMENTS BEFORE EQUALS ────────────────── tmp/elm_function_syntax/Test.roc ─ I am partway through parsing a definition, but I got stuck here: 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ f x y = x ^^^ Looks like you are trying to define a function. In roc, functions are always written as a lambda, like increment = \n -> n + 1. "### ); test_report!( two_different_cons, indoc!( r#" ConsList a : [Cons a (ConsList a), Nil] x : ConsList {} x = Cons {} (Cons "foo" Nil) x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `x` definition: 6│ x : ConsList {} 7│ x = Cons {} (Cons "foo" Nil) ^^^^^^^^^^^^^^^^^^^^^^^^ This `Cons` tag application has the type: [ Cons {} [ Cons Str [ Cons {} a, Nil, ]b as a, Nil, ]b, Nil, ]b But the type annotation on `x` says it should be: [ Cons {} a, Nil, ] as a "### ); test_report!( mutually_recursive_types_with_type_error, indoc!( r#" AList a b : [ACons a (BList a b), ANil] BList a b : [BCons a (AList a b), BNil] x : AList Num.I64 Num.I64 x = ACons 0 (BCons 1 (ACons "foo" BNil )) y : BList a a y = BNil { x, y } "# ), // TODO render tag unions across multiple lines // TODO do not show recursion var if the recursion var does not render on the surface of a type @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `x` definition: 7│ x : AList Num.I64 Num.I64 8│ x = ACons 0 (BCons 1 (ACons "foo" BNil )) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This `ACons` tag application has the type: [ ACons (Int Signed64) [ BCons (Int Signed64) [ ACons Str [ BCons I64 [ ACons I64 (BList I64 I64), ANil, ]b as ∞, BNil, ]c, ANil, ]b, BNil, ]c, ANil, ]b But the type annotation on `x` says it should be: [ ACons I64 (BList I64 I64), ANil, ] as a "### ); test_report!( integer_out_of_range, indoc!( r#" x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000 y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000 h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728 maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455 x + y + h + l + minlit + maxlit "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer literal is too big: 4│ x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The largest number representable in Roc is the maximum U128 value, 340_282_366_920_938_463_463_374_607_431_768_211_455. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer literal is too small: 6│ y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The smallest number representable in Roc is the minimum I128 value, -170_141_183_460_469_231_731_687_303_715_884_105_728. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer literal is too big: 8│ h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The largest number representable in Roc is the maximum U128 value, 340_282_366_920_938_463_463_374_607_431_768_211_455. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer literal is too small: 9│ l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The smallest number representable in Roc is the minimum I128 value, -170_141_183_460_469_231_731_687_303_715_884_105_728. Tip: Learn more about number literals at TODO ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to + has an unexpected type: 14│ x + y + h + l + minlit + maxlit ^^^^^^ This `maxlit` value is a: U128 But + needs its 2nd argument to be: I128 or Dec "### ); // have to deal with some whitespace issues because of the format! macro test_report!( float_out_of_range, indoc!( r#" overflow = 11.7976931348623157e308 underflow = -11.7976931348623157e308 overflow + underflow "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This float literal is too big: 4│ overflow = 11.7976931348623157e308 ^^^^^^^^^^^^^^^^^^^^^^^ Roc uses signed 64-bit floating points, allowing values between -1.7976931348623157e308 and 1.7976931348623157e308 Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This float literal is too small: 5│ underflow = -11.7976931348623157e308 ^^^^^^^^^^^^^^^^^^^^^^^^ Roc uses signed 64-bit floating points, allowing values between -1.7976931348623157e308 and 1.7976931348623157e308 Tip: Learn more about number literals at TODO "### ); // the generated messages here are incorrect. Waiting for a rust nightly feature to land, // see https://github.com/rust-lang/rust/issues/22639 // this test is here to spot regressions in error reporting test_report!( integer_malformed, indoc!( r#" dec = 100A hex = 0xZZZ oct = 0o9 bin = 0b2 dec + hex + oct + bin "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer literal contains an invalid digit: 4│ dec = 100A ^^^^ Integer literals can only contain the digits 0-9, or have an integer suffix. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This hex integer literal contains an invalid digit: 6│ hex = 0xZZZ ^^^^^ Hexadecimal (base-16) integer literals can only contain the digits 0-9, a-f and A-F, or have an integer suffix. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This octal integer literal contains an invalid digit: 8│ oct = 0o9 ^^^ Octal (base-8) integer literals can only contain the digits 0-7, or have an integer suffix. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This binary integer literal contains an invalid digit: 10│ bin = 0b2 ^^^ Binary (base-2) integer literals can only contain the digits 0 and 1, or have an integer suffix. Tip: Learn more about number literals at TODO "### ); test_report!( integer_empty, indoc!( r#" dec = 20 hex = 0x oct = 0o bin = 0b dec + hex + oct + bin "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This hex integer literal contains no digits: 6│ hex = 0x ^^ Hexadecimal (base-16) integer literals must contain at least one of the digits 0-9, a-f and A-F, or have an integer suffix. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This octal integer literal contains no digits: 8│ oct = 0o ^^ Octal (base-8) integer literals must contain at least one of the digits 0-7, or have an integer suffix. Tip: Learn more about number literals at TODO ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This binary integer literal contains no digits: 10│ bin = 0b ^^ Binary (base-2) integer literals must contain at least one of the digits 0 and 1, or have an integer suffix. Tip: Learn more about number literals at TODO "### ); test_report!( float_malformed, indoc!( r#" x = 3.0A x "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This float literal contains an invalid digit: 4│ x = 3.0A ^^^^ Floating point literals can only contain the digits 0-9, or use scientific notation 10e4, or have a float suffix. Tip: Learn more about number literals at TODO "### ); test_report!( invalid_record_update, indoc!( r#" foo = { bar: 3 } updateNestedRecord = { foo.bar & x: 4 } example = { age: 42 } # these should work y = { Test.example & age: 3 } x = { example & age: 4 } { updateNestedRecord, foo, x, y } "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This expression cannot be updated: 5│ updateNestedRecord = { foo.bar & x: 4 } ^^^^^^^ Only variables can be updated with record update syntax. ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ The `Test` module is not imported: 10│ y = { Test.example & age: 3 } ^^^^^^^^^^^^ Is there an import missing? Perhaps there is a typo. Did you mean one of these? Set List Dict Hash ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This expression cannot be updated: 10│ y = { Test.example & age: 3 } ^^^^^^^^^^^^ Only variables can be updated with record update syntax. "### ); test_report!( module_not_imported, indoc!( r#" Foo.test "# ), @r###" ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ The `Foo` module is not imported: 4│ Foo.test ^^^^^^^^ Is there an import missing? Perhaps there is a typo. Did you mean one of these? Box Bool Num Set "### ); test_report!( optional_record_default_type_error, indoc!( r#" \{ x, y ? True } -> x + y "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to + has an unexpected type: 4│ \{ x, y ? True } -> x + y ^ This `y` value is a: [True] But + needs its 2nd argument to be: Num a "### ); test_report!( optional_record_default_with_signature, indoc!( r#" f : { x : Num.I64, y ? Num.I64 } -> Num.I64 f = \{ x, y ? "foo" } -> (\g, _ -> g) x y f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 1st argument to `f` is weird: 5│ f = \{ x, y ? "foo" } -> (\g, _ -> g) x y ^^^^^^^^^^^^^^^^ The argument is a pattern that matches record values of type: { y ? Str, … } But the annotation on `f` says the 1st argument should be: { y ? I64, … } "### ); test_report!( optional_record_invalid_let_binding, indoc!( r#" \rec -> { x, y } : { x : Num.I64, y ? Str } { x, y } = rec { x, y } "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of this definition: 5│> { x, y } : { x : Num.I64, y ? Str } 6│> { x, y } = rec The body is a value of type: { y : Str, … } But the type annotation says it should be: { y ? Str, … } Tip: To extract the `.y` field it must be non-optional, but the type says this field is optional. Learn more about optional fields at TODO. "### ); test_report!( optional_record_invalid_function, indoc!( r#" f : { x : Num.I64, y ? Num.I64 } -> Num.I64 f = \{ x, y } -> x + y f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 1st argument to `f` is weird: 5│ f = \{ x, y } -> x + y ^^^^^^^^ The argument is a pattern that matches record values of type: { y : I64, … } But the annotation on `f` says the 1st argument should be: { y ? I64, … } Tip: To extract the `.y` field it must be non-optional, but the type says this field is optional. Learn more about optional fields at TODO. "### ); test_report!( optional_record_invalid_when, indoc!( r#" f : { x : Num.I64, y ? Num.I64 } -> Num.I64 f = \r -> when r is { x, y } -> x + y f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 6│> when r is 7│ { x, y } -> x + y This `r` value is a: { y ? I64, … } But the branch patterns have type: { y : I64, … } The branches must be cases of the `when` condition's type! Tip: To extract the `.y` field it must be non-optional, but the type says this field is optional. Learn more about optional fields at TODO. "### ); test_report!( optional_record_invalid_access, indoc!( r#" f : { x : Num.I64, y ? Num.I64 } -> Num.I64 f = \r -> r.y f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 5│ f = \r -> r.y ^^^ This `r` value is a: { y ? I64, … } But you are trying to use it as: { y : I64, … } Tip: To extract the `.y` field it must be non-optional, but the type says this field is optional. Learn more about optional fields at TODO. "### ); test_report!( optional_record_invalid_accessor, indoc!( r#" f : { x : Num.I64, y ? Num.I64 } -> Num.I64 f = \r -> .y r f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to this function has an unexpected type: 5│ f = \r -> .y r ^ This `r` value is a: { y ? I64, … } But this function needs its 1st argument to be: { y : I64, … } Tip: To extract the `.y` field it must be non-optional, but the type says this field is optional. Learn more about optional fields at TODO. "### ); test_report!( guard_mismatch_with_annotation, indoc!( r#" f : { x : Num.I64, y : Num.I64 } -> Num.I64 f = \r -> when r is { x, y : "foo" } -> x + 0 _ -> 0 f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 6│> when r is 7│ { x, y : "foo" } -> x + 0 8│ _ -> 0 This `r` value is a: { y : I64, … } But the branch patterns have type: { y : Str, … } The branches must be cases of the `when` condition's type! "### ); test_report!( optional_field_mismatch_with_annotation, indoc!( r#" f : { x : Num.I64, y ? Num.I64 } -> Num.I64 f = \r -> when r is { x, y ? "foo" } -> (\g, _ -> g) x y _ -> 0 f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 6│> when r is 7│ { x, y ? "foo" } -> (\g, _ -> g) x y 8│ _ -> 0 This `r` value is a: { y ? I64, … } But the branch patterns have type: { y ? Str, … } The branches must be cases of the `when` condition's type! "### ); test_report!( incorrect_optional_field, indoc!( r#" { x: 5, y ? 42 } "# ), @r###" ── BAD OPTIONAL VALUE ──────────────────────────────────── /code/proj/Main.roc ─ This record uses an optional value for the `.y` field in an incorrect context! 4│ { x: 5, y ? 42 } ^^^^^^ You can only use optional values in record destructuring, like: { answer ? 42, otherField } = myRecord "### ); test_report!( first_wildcard_is_required, indoc!( r#" when Foo 1 2 3 is Foo _ 1 _ -> 1 _ -> 2 "# ), @"" ); test_report!( second_wildcard_is_redundant, indoc!( r#" when Foo 1 2 3 is Foo _ 1 _ -> 1 _ -> 2 _ -> 3 "# ), @r###" ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ The 3rd pattern is redundant: 4│ when Foo 1 2 3 is 5│ Foo _ 1 _ -> 1 6│ _ -> 2 7│ _ -> 3 ^ Any value of this shape will be handled by a previous pattern, so this one should be removed. "### ); test_report!( alias_using_alias, indoc!( r#" # The color of a node. Leaves are considered Black. NodeColor : [Red, Black] RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty] # Create an empty dictionary. empty : RBTree k v empty = Empty empty "# ), @"" ); test_report!( unused_argument, indoc!( r#" f = \foo -> 1 f "# ), @r###" ── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─ `f` doesn't use `foo`. 4│ f = \foo -> 1 ^^^ If you don't need `foo`, then you can just remove it. However, if you really do need `foo` as an argument of `f`, prefix it with an underscore, like this: "_`foo`". Adding an underscore at the start of a variable name is a way of saying that the variable is not used. "### ); test_report!( qualified_tag, indoc!( r#" Foo.Bar "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am trying to parse a qualified name here: 4│ Foo.Bar ^ This looks like a qualified tag name to me, but tags cannot be qualified! Maybe you wanted a qualified name, something like Json.Decode.string? "### ); test_report!( module_ident_ends_with_dot, indoc!( r#" Foo.Bar. "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am trying to parse a qualified name here: 4│ Foo.Bar. ^ I was expecting to see an identifier next, like height. A complete qualified name looks something like Json.Decode.string. "### ); test_report!( record_access_ends_with_dot, indoc!( r#" foo.bar. "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am trying to parse a record field access here: 4│ foo.bar. ^ So I expect to see a lowercase letter next, like .name or .height. "### ); test_report!( type_annotation_double_colon, indoc!( r#" f :: I64 f = 42 f "# ), @r###" ── UNKNOWN OPERATOR ──────────────── tmp/type_annotation_double_colon/Test.roc ─ This looks like an operator, but it's not one I recognize! 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ f :: I64 ^^ I have no specific suggestion for this operator, see TODO for the full list of operators in Roc. "### ); // NOTE: VERY BAD ERROR MESSAGE // // looks like `x y` are considered argument to the add, even though they are // on a lower indentation level test_report!( double_equals_in_def, indoc!( r#" x = 3 y = x == 5 Num.add 1 2 { x, y } "# ), @r###" ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ This value is not a function, but it was given 3 arguments: 6│ x == 5 ^ Are there any missing commas? Or missing parentheses? "### ); test_report!( tag_union_open, indoc!( r#" f : [ "# ), @r###" ── UNFINISHED TAG UNION TYPE ───────────────────── tmp/tag_union_open/Test.roc ─ I am partway through parsing a tag union type, but I got stuck here: 4│ f : [ 5│ 6│ ^ I was expecting to see a closing square bracket before this, so try adding a ] and see if that helps? "### ); test_report!( tag_union_end, indoc!( r#" f : [Yes, "# ), @r###" ── UNFINISHED TAG UNION TYPE ────────────────────── tmp/tag_union_end/Test.roc ─ I am partway through parsing a tag union type, but I got stuck here: 4│ f : [Yes, 5│ 6│ ^ I was expecting to see a closing square bracket before this, so try adding a ] and see if that helps? "### ); test_report!( tag_union_lowercase_tag_name, indoc!( r#" f : [lowercase] "# ), @r###" ── WEIRD TAG NAME ────────────────── tmp/tag_union_lowercase_tag_name/Test.roc ─ I am partway through parsing a tag union type, but I got stuck here: 4│ f : [lowercase] ^ I was expecting to see a tag name. Hint: Tag names start with an uppercase letter, like Err or Green. "### ); test_report!( tag_union_second_lowercase_tag_name, indoc!( r#" f : [Good, bad] "# ), @r###" ── WEIRD TAG NAME ─────────── tmp/tag_union_second_lowercase_tag_name/Test.roc ─ I am partway through parsing a tag union type, but I got stuck here: 4│ f : [Good, bad] ^ I was expecting to see a tag name. Hint: Tag names start with an uppercase letter, like Err or Green. "### ); test_report!( record_type_open, indoc!( r#" f : { "# ), @r###" ── UNFINISHED RECORD TYPE ────────────────────── tmp/record_type_open/Test.roc ─ I am partway through parsing a record type, but I got stuck here: 4│ f : { 5│ 6│ ^ I was expecting to see a closing curly brace before this, so try adding a } and see if that helps? "### ); test_report!( record_type_open_indent, indoc!( r#" f : { foo : I64, "# ), @r###" ── UNFINISHED RECORD TYPE ─────────────── tmp/record_type_open_indent/Test.roc ─ I am partway through parsing a record type, but I got stuck here: 4│ f : { 5│ foo : I64, 6│ 7│ ^ I was expecting to see a closing curly brace before this, so try adding a } and see if that helps? "### ); test_report!( record_type_end, indoc!( r#" f : { a: Int, "# ), @r###" ── UNFINISHED RECORD TYPE ─────────────────────── tmp/record_type_end/Test.roc ─ I am partway through parsing a record type, but I got stuck here: 4│ f : { a: Int, 5│ 6│ ^ I was expecting to see a closing curly brace before this, so try adding a } and see if that helps? "### ); test_report!( record_type_keyword_field_name, indoc!( r#" f : { if : I64 } "# ), @r###" ── UNFINISHED RECORD TYPE ──────── tmp/record_type_keyword_field_name/Test.roc ─ I just started parsing a record type, but I got stuck on this field name: 4│ f : { if : I64 } ^^ Looks like you are trying to use `if` as a field name, but that is a reserved word. Try using a different name! "### ); // a case where the message cannot be as good as elm's test_report!( record_type_missing_comma, indoc!( r#" f : { foo bar } "# ), @r###" ── UNFINISHED RECORD TYPE ───────────── tmp/record_type_missing_comma/Test.roc ─ I am partway through parsing a record type, but I got stuck here: 4│ f : { foo bar } ^ I was expecting to see a colon, question mark, comma or closing curly brace. "### ); // a case where the message cannot be as good as elm's test_report!( record_type_tab, "f : { foo \t }", @r###" ── TAB CHARACTER ──────────────────────────────── tmp/record_type_tab/Test.roc ─ I encountered a tab character: 4│ f : { foo } ^ Tab characters are not allowed, use spaces instead. "### ); test_report!( comment_with_tab, "# comment with a \t\n4", @r###" ── TAB CHARACTER ─────────────────────────────── tmp/comment_with_tab/Test.roc ─ I encountered a tab character: 4│ # comment with a ^ Tab characters are not allowed, use spaces instead. "### ); test_report!( comment_with_control_character, "# comment with a \x07\n", @r###" ── ASCII CONTROL CHARACTER ─────── tmp/comment_with_control_character/Test.roc ─ I encountered an ASCII control character: 4│ # comment with a ^ ASCII control characters are not allowed. "### ); test_report!( record_type_carriage_return, "f : { \r foo }", @r###" ── MISPLACED CARRIAGE RETURN ──────── tmp/record_type_carriage_return/Test.roc ─ I encountered a stray carriage return (\r): 4│ f : { foo } ^ A carriage return (\r) has to be followed by a newline (\n). "### ); // TODO bad error message test_report!( type_in_parens_start, indoc!( r#" f : ( "# ), @r###" ── UNFINISHED PARENTHESES ────────────────── tmp/type_in_parens_start/Test.roc ─ I am partway through parsing a type in parentheses, but I got stuck here: 4│ f : ( 5│ 6│ ^ I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "### ); test_report!( type_in_parens_end, indoc!( r#" f : ( I64 "# ), @r###" ── UNFINISHED PARENTHESES ──────────────────── tmp/type_in_parens_end/Test.roc ─ I am partway through parsing a type in parentheses, but I got stuck here: 4│ f : ( I64 5│ 6│ ^ I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "### ); test_report!( type_apply_double_dot, indoc!( r#" f : Foo..Bar f "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am confused by this type name: 4│ f : Foo..Bar ^^^^^^^^ Type names start with an uppercase letter, and can optionally be qualified by a module name, like Bool or Http.Request.Request. "### ); // ── DOUBLE DOT ────────────────────────────────────────────────────────────────── // // I encountered two dots in a row: // // 1│ f : Foo..Bar // ^ // // Try removing one of them. test_report!( type_apply_trailing_dot, indoc!( r#" f : Foo.Bar. f "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am confused by this type name: 4│ f : Foo.Bar. ^^^^^^^^ Type names start with an uppercase letter, and can optionally be qualified by a module name, like Bool or Http.Request.Request. "### ); // ── TRAILING DOT ──────────────────────────────────────────────────────────────── // // I encountered a dot with nothing after it: // // 1│ f : Foo.Bar. // ^ // // Dots are used to refer to a type in a qualified way, like // Num.I64 or List.List a. Try adding a type name next. test_report!( type_apply_stray_dot, indoc!( r#" f : . "# ), @r###" ── UNFINISHED TYPE ───────────────────────── tmp/type_apply_stray_dot/Test.roc ─ I just started parsing a type, but I got stuck here: 4│ f : . ^ I am expecting a type next, like Bool or List a. "### ); test_report!( type_apply_start_with_number, indoc!( r#" f : Foo.1 f "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am confused by this type name: 4│ f : Foo.1 ^^^^^ Type names start with an uppercase letter, and can optionally be qualified by a module name, like Bool or Http.Request.Request. "### ); // ── WEIRD QUALIFIED NAME ──────────────────────────────────────────────────────── // // I encountered a number at the start of a qualified name segment: // // 1│ f : Foo.1 // ^ // // All parts of a qualified type name must start with an uppercase // letter, like Num.I64 or List.List a. test_report!( type_apply_start_with_lowercase, indoc!( r#" f : Foo.foo f "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am confused by this type name: 4│ f : Foo.foo ^^^^^^^ Type names start with an uppercase letter, and can optionally be qualified by a module name, like Bool or Http.Request.Request. "### ); test_report!( def_missing_final_expression, indoc!( r#" f : Foo.foo "# ), @r###" ── MISSING FINAL EXPRESSION ──────── tmp/def_missing_final_expression/Test.roc ─ I am partway through parsing a definition, but I got stuck here: 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ f : Foo.foo ^ This definition is missing a final expression. A nested definition must be followed by either another definition, or an expression x = 4 y = 2 x + y "### ); test_report!( expression_indentation_end, indoc!( r#" f <- Foo.foo "# ), @r###" ── INDENT ENDS AFTER EXPRESSION ────── tmp/expression_indentation_end/Test.roc ─ I am partway through parsing an expression, but I got stuck here: 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ f <- Foo.foo ^ Looks like the indentation ends prematurely here. Did you mean to have another expression after this line? "### ); test_report!( type_inline_alias, indoc!( r#" f : I64 as f = 0 f "# ), @r###" ── UNFINISHED INLINE ALIAS ──────────────────── tmp/type_inline_alias/Test.roc ─ I just started parsing an inline type alias, but I got stuck here: 4│ f : I64 as ^ Note: I may be confused by indentation "### ); test_report!( type_double_comma, indoc!( r#" f : I64,,I64 -> I64 f = 0 f "# ), @r###" ── DOUBLE COMMA ─────────────────────────────── tmp/type_double_comma/Test.roc ─ I just started parsing a function argument type, but I encountered two commas in a row: 4│ f : I64,,I64 -> I64 ^ Try removing one of them. "### ); test_report!( type_argument_no_arrow, indoc!( r#" f : I64, I64 f = 0 f "# ), @r###" ── UNFINISHED TYPE ─────────────────────── tmp/type_argument_no_arrow/Test.roc ─ I am partway through parsing a type, but I got stuck here: 4│ f : I64, I64 ^ Note: I may be confused by indentation "### ); // TODO could do better by pointing out we're parsing a function type test_report!( type_argument_arrow_then_nothing, indoc!( r#" f : I64, I64 -> f = 0 f "# ), @r###" ── UNFINISHED TYPE ───────────── tmp/type_argument_arrow_then_nothing/Test.roc ─ I just started parsing a type, but I got stuck here: 4│ f : I64, I64 -> ^ Note: I may be confused by indentation "### ); // TODO could do better by pointing out we're parsing a function type test_report!( dict_type_formatting, indoc!( r#" app "dict" imports [ Dict ] provides [main] to "./platform" myDict : Dict.Dict Num.I64 Str myDict = Dict.insert (Dict.empty {}) "foo" 42 main = myDict "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `myDict` definition: 3│ myDict : Dict.Dict Num.I64 Str 4│ myDict = Dict.insert (Dict.empty {}) "foo" 42 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This `insert` call produces: Dict Str (Num *) But the type annotation on `myDict` says it should be: Dict I64 Str "### ); test_report!( alias_type_diff, indoc!( r#" app "test" imports [Set.{ Set }] provides [main] to "./platform" HSet a : Set a foo : Str -> HSet {} myDict : HSet Str myDict = foo "bar" main = myDict "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `myDict` definition: 7│ myDict : HSet Str 8│ myDict = foo "bar" ^^^^^^^^^ This `foo` call produces: HSet {} But the type annotation on `myDict` says it should be: HSet Str "### ); // this should get better with time test_report!( if_guard_without_condition, indoc!( r#" when Just 4 is Just if -> 4 _ -> 2 "# ), @r###" ── IF GUARD NO CONDITION ───────────── tmp/if_guard_without_condition/Test.roc ─ I just started parsing an if guard, but there is no guard condition: 4│ when Just 4 is 5│ Just if -> ^ Try adding an expression before the arrow! "### ); test_report!( empty_or_pattern, indoc!( r#" when Just 4 is Just 4 | -> 4 _ -> 2 "# ), @r###" ── UNFINISHED PATTERN ────────────────────────── tmp/empty_or_pattern/Test.roc ─ I just started parsing a pattern, but I got stuck here: 5│ Just 4 | -> ^ Note: I may be confused by indentation "### ); // TODO check if "what_is_next" is a keyword test_report!( pattern_binds_keyword, indoc!( r#" when Just 4 is Just when -> 4 _ -> 2 "# ), @r###" ── MISSING ARROW ────────────────────────── tmp/pattern_binds_keyword/Test.roc ─ I am partway through parsing a `when` expression, but got stuck here: 4│ when Just 4 is 5│ Just when -> ^ I was expecting to see an arrow next. Note: Sometimes I get confused by indentation, so try to make your `when` look something like this: when List.first plants is Ok n -> n Err _ -> 200 Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! "### ); // this should get better with time test_report!( when_missing_arrow, indoc!( r#" when 5 is 1 -> 2 _ "# ), @r###" ── UNFINISHED WHEN ─────────────────────────── tmp/when_missing_arrow/Test.roc ─ I was partway through parsing a `when` expression, but I got stuck here: 4│ when 5 is 5│ 1 -> 2 6│ _ ^ I was expecting to see a pattern next Note: Here is an example of a valid `when` expression for reference. when List.first plants is Ok n -> n Err _ -> 200 Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! "### ); test_report!( lambda_double_comma, indoc!( r#" \a,,b -> 1 "# ), @r###" ── UNFINISHED ARGUMENT LIST ───────────────── tmp/lambda_double_comma/Test.roc ─ I am partway through parsing a function argument list, but I got stuck at this comma: 4│ \a,,b -> 1 ^ I was expecting an argument pattern before this, so try adding an argument before the comma and see if that helps? "### ); test_report!( lambda_leading_comma, indoc!( r#" \,b -> 1 "# ), @r###" ── UNFINISHED ARGUMENT LIST ──────────────── tmp/lambda_leading_comma/Test.roc ─ I am partway through parsing a function argument list, but I got stuck at this comma: 4│ \,b -> 1 ^ I was expecting an argument pattern before this, so try adding an argument before the comma and see if that helps? "### ); // this should get better with time // TODO this formerly gave // // ── UNFINISHED WHEN ───────────────────────────────────────────────────────────── // // I was partway through parsing a `when` expression, but I got stuck here: // // 3│ _ -> 2 // ^ // // I suspect this is a pattern that is not indented enough? (by 2 spaces) // // but that requires parsing the next pattern blindly, irrespective of indentation. Can // we find an efficient solution that doesn't require parsing an extra pattern for // every `when`, i.e. we want a good error message for the test case above, but for // a valid `when`, we don't want to do extra work, e.g. here // // x // when x is // n -> n // // 4 // // We don't want to parse the `4` and say it's an outdented pattern! test_report!( when_outdented_branch, indoc!( r#" when 4 is 5 -> 2 2 -> 2 "# ), @r###" ── NOT END OF FILE ──────────────────────── tmp/when_outdented_branch/Test.roc ─ I expected to reach the end of the file, but got stuck here: 6│ 2 -> 2 ^ "### ); test_report!( when_over_indented_underscore, indoc!( r#" when 4 is 5 -> 2 _ -> 2 "# ), @r###" ── UNEXPECTED ARROW ─────────────── tmp/when_over_indented_underscore/Test.roc ─ I am parsing a `when` expression right now, but this arrow is confusing me: 5│ 5 -> 2 6│ _ -> 2 ^^ It makes sense to see arrows around here, so I suspect it is something earlier. Maybe this pattern is indented a bit farther from the previous patterns? Note: Here is an example of a valid `when` expression for reference. when List.first plants is Ok n -> n Err _ -> 200 Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! "### ); test_report!( when_over_indented_int, indoc!( r#" when 4 is 5 -> Num.neg 2 -> 2 "# ), @r###" ── UNEXPECTED ARROW ────────────────────── tmp/when_over_indented_int/Test.roc ─ I am parsing a `when` expression right now, but this arrow is confusing me: 5│ 5 -> Num.neg 6│ 2 -> 2 ^^ It makes sense to see arrows around here, so I suspect it is something earlier. Maybe this pattern is indented a bit farther from the previous patterns? Note: Here is an example of a valid `when` expression for reference. when List.first plants is Ok n -> n Err _ -> 200 Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! "### ); // TODO I think we can do better here test_report!( if_outdented_then, indoc!( r#" x = if 5 == 5 then 2 else 3 x "# ), @r###" ── UNFINISHED IF ────────────────────────────── tmp/if_outdented_then/Test.roc ─ I was partway through parsing an `if` expression, but I got stuck here: 5│ if 5 == 5 ^ I was expecting to see the `then` keyword next. "### ); // this should get better with time test_report!( if_missing_else, indoc!( r#" if 5 == 5 then 2 "# ), @r###" ── UNFINISHED IF ──────────────────────────────── tmp/if_missing_else/Test.roc ─ I was partway through parsing an `if` expression, but I got stuck here: 4│ if 5 == 5 then 2 ^ I was expecting to see the `else` keyword next. "### ); test_report!( list_double_comma, indoc!( r#" [1, 2, , 3] "# ), @r###" ── UNFINISHED LIST ──────────────────────────── tmp/list_double_comma/Test.roc ─ I am partway through started parsing a list, but I got stuck here: 4│ [1, 2, , 3] ^ I was expecting to see a list entry before this comma, so try adding a list entry and see if that helps? "### ); test_report!( list_without_end, indoc!( r#" [1, 2, "# ), @r###" ── UNFINISHED LIST ───────────────────────────── tmp/list_without_end/Test.roc ─ I am partway through started parsing a list, but I got stuck here: 4│ [1, 2, 5│ 6│ ^ I was expecting to see a closing square bracket before this, so try adding a ] and see if that helps? Note: When I get stuck like this, it usually means that there is a missing parenthesis or bracket somewhere earlier. It could also be a stray keyword or operator. "### ); test_report!( number_double_dot, indoc!( r#" 1.1.1 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This float literal contains an invalid digit: 4│ 1.1.1 ^^^^^ Floating point literals can only contain the digits 0-9, or use scientific notation 10e4, or have a float suffix. Tip: Learn more about number literals at TODO "### ); test_report!( unicode_not_hex, r#""abc\u(zzzz)def""#, @r###" ── WEIRD CODE POINT ───────────────────────────── tmp/unicode_not_hex/Test.roc ─ I am partway through parsing a unicode code point, but I got stuck here: 4│ "abc\u(zzzz)def" ^ I was expecting a hexadecimal number, like \u(1100) or \u(00FF). Learn more about working with unicode in roc at TODO "### ); test_report!( unicode_too_large, r#""abc\u(110000)def""#, @r###" ── INVALID UNICODE ─────────────────────────────────────── /code/proj/Main.roc ─ This unicode code point is invalid: 4│ "abc\u(110000)def" ^^^^^^ Learn more about working with unicode in roc at TODO "### ); test_report!( weird_escape, r#""abc\qdef""#, @r###" ── WEIRD ESCAPE ──────────────────────────────────── tmp/weird_escape/Test.roc ─ I was partway through parsing a string literal, but I got stuck here: 4│ "abc\qdef" ^^ This is not an escape sequence I recognize. After a backslash, I am looking for one of these: - A newline: \n - A caret return: \r - A tab: \t - An escaped quote: \" - An escaped backslash: \\ - A unicode code point: \u(00FF) - An interpolated string: \(myVariable) "### ); test_report!( single_quote_too_long, r#"'abcdef'"#, @r###" ── INVALID SCALAR ───────────────────────── tmp/single_quote_too_long/Test.roc ─ I am part way through parsing this scalar literal (character literal), but it's too long to fit in a U32 so it's not a valid scalar. 4│ 'abcdef' ^ You could change it to something like 'a' or '\n'. Note, roc strings use double quotes, like "hello". "### ); test_report!( single_no_end, r#""there is no end"#, @r###" ── ENDLESS STRING ───────────────────────────────── tmp/single_no_end/Test.roc ─ I cannot find the end of this string: 4│ "there is no end ^ You could change it to something like "to be or not to be" or even just "". "### ); test_report!( multi_no_end, r#""""there is no end"#, @r###" ── ENDLESS STRING ────────────────────────────────── tmp/multi_no_end/Test.roc ─ I cannot find the end of this block string: 4│ """there is no end ^ You could change it to something like """to be or not to be""" or even just """""". "### ); test_report!( multi_insufficient_indent, " \"\"\"\n testing\n \"\"\"", // 4 space indent on the start, 2 space on the `testing` line @r###" ── INSUFFICIENT INDENT IN MULTI-LINE STRING ─ ..._insufficient_indent/Test.roc ─ This multiline string is not sufficiently indented: 4│ """ 5│ testing ^ Lines in a multi-line string must be indented at least as much as the beginning """. This extra indentation is automatically removed from the string during compilation. "### ); test_report!( dbg_without_final_expression, indoc!( r#" dbg 42 "# ), @r###" ── INDENT ENDS AFTER EXPRESSION ──── tmp/dbg_without_final_expression/Test.roc ─ I am partway through parsing a dbg statement, but I got stuck here: 4│ dbg 42 ^ I was expecting a final expression, like so dbg 42 "done" "### ); test_report!( expect_without_final_expression, indoc!( r#" expect 1 + 1 == 2 "# ), @r###" ── INDENT ENDS AFTER EXPRESSION ─ tmp/expect_without_final_expression/Test.roc ─ I am partway through parsing an expect statement, but I got stuck here: 4│ expect 1 + 1 == 2 ^ I was expecting a final expression, like so expect 1 + 1 == 2 "done" "### ); // https://github.com/roc-lang/roc/issues/1714 test_report!( interpolate_concat_is_transparent_1714, indoc!( r#" greeting = "Privet" if Bool.true then 1 else "\(greeting), World!" "#, ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` has an `else` branch with a different type from its `then` branch: 6│ if Bool.true then 1 else "\(greeting), World!" ^^^^^^^^^^^^^^^^^^^^^ The `else` branch is a string of type: Str but the `then` branch has the type: Num * All branches in an `if` must have the same type! "### ); macro_rules! comparison_binop_transparency_tests { ($($op:expr, $name:ident),* $(,)?) => { $( test_report!( $name, &format!(r#"if Bool.true then "abc" else 1 {} 2"#, $op), |golden| assert_eq!(golden, format!( r#"── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` has an `else` branch with a different type from its `then` branch: 4│ if Bool.true then "abc" else 1 {} 2 ^^{}^^ This comparison produces: Bool but the `then` branch has the type: Str All branches in an `if` must have the same type! "#, $op, "^".repeat($op.len()) )) ); )* } } comparison_binop_transparency_tests! { "<", lt_binop_is_transparent, ">", gt_binop_is_transparent, "==", eq_binop_is_transparent, "!=", neq_binop_is_transparent, "<=", leq_binop_is_transparent, ">=", geq_binop_is_transparent, } test_report!( keyword_record_field_access, indoc!( r#" foo = {} foo.if "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `foo` record doesn’t have a `if` field: 6│ foo.if ^^^^^^ In fact, `foo` is a record with no fields at all! "### ); test_report!( keyword_qualified_import, indoc!( r#" Num.if "# ), @r###" ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ The Num module does not expose `if`: 4│ Num.if ^^^^^^ Did you mean one of these? Num.sin Num.div Num.min Num.e "### ); test_report!( stray_dot_expr, indoc!( r#" Num.add . 23 "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am trying to parse a record field access here: 4│ Num.add . 23 ^ So I expect to see a lowercase letter next, like .name or .height. "### ); test_report!( opaque_ref_field_access, indoc!( r#" @UUID.bar "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am very confused by this field access: 4│ @UUID.bar ^^^^ It looks like a record field access on an opaque reference. "### ); test_report!( weird_accessor, indoc!( r#" .foo.bar "# ), @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am very confused by this field access 4│ .foo.bar ^^^^^^^^ It looks like a field access on an accessor. I parse.client.name as (.client).name. Maybe use an anonymous function like (\r -> r.client.name) instead? "### ); test_report!( closure_underscore_ident, indoc!( r#" \the_answer -> 100 "# ), @r###" ── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am trying to parse an identifier here: 4│ \the_answer -> 100 ^ Underscores are not allowed in identifiers. Use camelCase instead! "### ); test_report!( #[ignore] double_binop, indoc!( r#" key >= 97 && <= 122 "# ), @r#" "# ); test_report!( #[ignore] case_of, indoc!( r#" case 1 of 1 -> True _ -> False "# ), @r###" ── UNKNOWN OPERATOR ───────────────────────────────────── tmp/case_of/Test.roc ─ This looks like an operator, but it's not one I recognize! 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ case 1 of 5│ 1 -> True ^^ The arrow -> is used to define cases in a `when` expression: when color is Red -> "stop!" Green -> "go!" And to define a function: increment : I64 -> I64 increment = \n -> n + 1 "### ); test_report!( argument_without_space, indoc!( r#" ["foo", bar("")] "# ), @r###" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `bar` in this scope. 4│ ["foo", bar("")] ^^^ Did you mean one of these? Nat Str Err U8 "### ); test_report!( invalid_operator, indoc!( r#" main = 5 ** 3 "# ), @r###" ── UNKNOWN OPERATOR ──────────────────────────── tmp/invalid_operator/Test.roc ─ This looks like an operator, but it's not one I recognize! 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ main = 5│ 5 ** 3 ^^ I have no specific suggestion for this operator, see TODO for the full list of operators in Roc. "### ); test_report!( double_plus, indoc!( r#" main = [] ++ [] "# ), @r###" ── UNKNOWN OPERATOR ───────────────────────────────── tmp/double_plus/Test.roc ─ This looks like an operator, but it's not one I recognize! 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ main = 5│ [] ++ [] ^^ To concatenate two lists or strings, try using List.concat or Str.concat instead. "### ); test_report!( inline_hastype, indoc!( r#" main = (\x -> x) : I64 3 "# ), @r###" ── UNKNOWN OPERATOR ────────────────────────────── tmp/inline_hastype/Test.roc ─ This looks like an operator, but it's not one I recognize! 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ main = 5│ (\x -> x) : I64 ^ The has-type operator : can only occur in a definition's type signature, like increment : I64 -> I64 increment = \x -> x + 1 "### ); // this is still bad, but changing the order and progress of other parsers should improve it // down the line test_report!( wild_case_arrow, indoc!( r#" main = 5 -> 3 "# ), |golden| pretty_assertions::assert_eq!( golden, &format!( r###"── UNKNOWN OPERATOR ───────────────────────────── tmp/wild_case_arrow/Test.roc ─ This looks like an operator, but it's not one I recognize! 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ main = 5 -> 3 ^^ Looks like you are trying to define a function.{} In roc, functions are always written as a lambda, like{} increment = \n -> n + 1"###, ' ', ' ' ) ) ); #[test] fn provides_to_identifier() { report_header_problem_as( indoc!( r#" app "test-base64" packages { pf: "platform/main.roc" } imports [pf.Task, Base64] provides [main, @Foo] to pf "# ), indoc!( r#" ── WEIRD PROVIDES ──────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a provides list, but I got stuck here: 3│ imports [pf.Task, Base64] 4│ provides [main, @Foo] to pf ^ I was expecting a type name, value name or function name next, like provides [Animal, default, tame] "# ), ) } #[test] fn missing_provides_in_app_header() { report_header_problem_as( indoc!( r#" app "broken" packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br", } imports [ pf.Stdout, ] main = Stdout.line "answer" "# ), indoc!( r#" ── WEIRD PROVIDES ──────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a header, but I got stuck here: 7│ ] ^ I am expecting the `provides` keyword next, like provides [Animal, default, tame] "# ), ) } #[test] fn provides_missing_to_in_app_header() { report_header_problem_as( indoc!( r#" app "broken" provides [main] "# ), indoc!( r#" ── WEIRD PROVIDES ──────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a header, but I got stuck here: 1│ app "broken" 2│ provides [main] ^ I am expecting the `to` keyword next, like: to pf "# ), ) } #[test] fn provides_to_missing_platform_in_app_header() { report_header_problem_as( indoc!( r#" app "broken" provides [main] to "# ), indoc!( r#" ── WEIRD PROVIDES ──────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a header, but I got stuck here: 1│ app "broken" 2│ provides [main] to ^ I am expecting the platform name next, like: to pf "# ), ) } #[test] fn platform_requires_rigids() { report_header_problem_as( indoc!( r#" platform "folkertdev/foo" requires { main : Effect {} } exposes [] packages {} imports [Task] provides [mainForHost] effects fx.Effect { putChar : I64 -> Effect {}, putLine : Str -> Effect {}, getLine : Effect Str } "# ), indoc!( r#" ── BAD REQUIRES ────────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a header, but I got stuck here: 1│ platform "folkertdev/foo" 2│ requires { main : Effect {} } ^ I am expecting a list of type names like `{}` or `{ Model }` next. A full `requires` definition looks like requires { Model, Msg } {main : Effect {}} "# ), ) } #[test] fn missing_imports() { report_header_problem_as( indoc!( r#" interface Foobar exposes [main, Foo] "# ), indoc!( r#" ── WEIRD IMPORTS ───────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a header, but I got stuck here: 2│ exposes [main, Foo] ^ I am expecting the `imports` keyword next, like imports [Animal, default, tame] "# ), ) } #[test] fn exposes_identifier() { report_header_problem_as( indoc!( r#" interface Foobar exposes [main, @Foo] imports [pf.Task, Base64] "# ), indoc!( r#" ── WEIRD EXPOSES ───────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing an `exposes` list, but I got stuck here: 1│ interface Foobar 2│ exposes [main, @Foo] ^ I was expecting a type name, value name or function name next, like exposes [Animal, default, tame] "# ), ) } #[test] fn invalid_module_name() { report_header_problem_as( indoc!( r#" interface foobar exposes [main, @Foo] imports [pf.Task, Base64] "# ), indoc!( r#" ── WEIRD MODULE NAME ───────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a header, but got stuck here: 1│ interface foobar ^ I am expecting a module name next, like BigNum or Main. Module names must start with an uppercase letter. "# ), ) } #[test] fn invalid_app_name() { report_header_problem_as( indoc!( r#" app foobar exposes [main, @Foo] imports [pf.Task, Base64] "# ), indoc!( r#" ── WEIRD APP NAME ──────────────────────────────────────── /code/proj/Main.roc ─ I am partway through parsing a header, but got stuck here: 1│ app foobar ^ I am expecting an application name next, like app "main" or app "editor". App names are surrounded by quotation marks. "# ), ) } test_report!( apply_unary_negative, indoc!( r#" foo = 3 -foo 1 2 "# ), @r###" ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ This value is not a function, but it was given 2 arguments: 6│ -foo 1 2 ^^^^ Are there any missing commas? Or missing parentheses? "### ); test_report!( apply_unary_not, indoc!( r#" foo = Bool.true !foo 1 2 "# ), @r###" ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ This value is not a function, but it was given 2 arguments: 6│ !foo 1 2 ^^^^ Are there any missing commas? Or missing parentheses? "### ); test_report!( applied_tag_function, indoc!( r#" x : List [Foo Str] x = List.map [1, 2] Foo x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `x` definition: 4│ x : List [Foo Str] 5│ x = List.map [1, 2] Foo ^^^^^^^^^^^^^^^^^^^ This `map` call produces: List [Foo (Num *)] But the type annotation on `x` says it should be: List [Foo Str] "### ); test_report!( pattern_in_parens_open, indoc!( r#" \( a "# ), @r###" ── UNFINISHED PARENTHESES ──────────────── tmp/pattern_in_parens_open/Test.roc ─ I am partway through parsing a pattern in parentheses, but I got stuck here: 4│ \( a 5│ 6│ ^ I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "### ); test_report!( pattern_in_parens_end_comma, indoc!( r#" \( a, "# ), @r###" ── UNFINISHED PARENTHESES ─────────── tmp/pattern_in_parens_end_comma/Test.roc ─ I am partway through parsing a pattern in parentheses, but I got stuck here: 4│ \( a, 5│ 6│ ^ I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "### ); test_report!( pattern_in_parens_end, indoc!( r#" \( a "# ), @r###" ── UNFINISHED PARENTHESES ───────────────── tmp/pattern_in_parens_end/Test.roc ─ I am partway through parsing a pattern in parentheses, but I got stuck here: 4│ \( a 5│ 6│ ^ I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "### ); test_report!( unfinished_closure_pattern_in_parens, indoc!( r#" x = \( a ) "# ), @r###" ── UNFINISHED FUNCTION ───── tmp/unfinished_closure_pattern_in_parens/Test.roc ─ I was partway through parsing a function, but I got stuck here: 4│ x = \( a 5│ ) ^ I just saw a pattern, so I was expecting to see a -> next. "### ); test_report!( pattern_in_parens_indent_open, indoc!( r#" \( "# ), @r###" ── UNFINISHED PARENTHESES ───────── tmp/pattern_in_parens_indent_open/Test.roc ─ I am partway through parsing a pattern in parentheses, but I got stuck here: 4│ \( 5│ 6│ ^ I was expecting to see a closing parenthesis before this, so try adding a ) and see if that helps? "### ); test_report!( backpassing_type_error, indoc!( r#" x <- List.map ["a", "b"] x + 1 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `map` has an unexpected type: 4│> x <- List.map ["a", "b"] 5│> 6│> x + 1 The argument is an anonymous function of type: Num * -> Num * But `map` needs its 2nd argument to be: Str -> Num * "### ); test_report!( expect_expr_type_error, indoc!( r#" expect "foobar" 4 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `expect` condition needs to be a Bool: 4│ expect "foobar" ^^^^^^^^ Right now it’s a string of type: Str But I need every `expect` condition to evaluate to a Bool—either `Bool.true` or `Bool.false`. "### ); test_report!( num_too_general_wildcard, indoc!( r#" mult : Num.Num *, Num.F64 -> Num.F64 mult = \a, b -> a * b mult 0 0 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to * has an unexpected type: 5│ mult = \a, b -> a * b ^ This `b` value is a: F64 But * needs its 2nd argument to be: Num * ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `mult` definition: 4│ mult : Num.Num *, Num.F64 -> Num.F64 5│ mult = \a, b -> a * b ^^^^^ This `mul` call produces: Num * But the type annotation on `mult` says it should be: F64 "### ); test_report!( num_too_general_named, indoc!( r#" mult : Num.Num a, Num.F64 -> Num.F64 mult = \a, b -> a * b mult 0 0 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to * has an unexpected type: 5│ mult = \a, b -> a * b ^ This `b` value is a: F64 But * needs its 2nd argument to be: Num a ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `mult` definition: 4│ mult : Num.Num a, Num.F64 -> Num.F64 5│ mult = \a, b -> a * b ^^^^^ This `mul` call produces: Num a But the type annotation on `mult` says it should be: F64 "### ); test_report!( inference_var_not_enough_in_alias, indoc!( r#" Result a b : [Ok a, Err b] canIGo : _ -> Result _ canIGo = \color -> when color is "green" -> Ok "go!" "yellow" -> Err (SlowIt "whoa, let's slow down!") "red" -> Err (StopIt "absolutely not") _ -> Err (UnknownColor "this is a weird stoplight") canIGo "# ), @r###" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ This alias has the same name as a builtin: 4│ Result a b : [Ok a, Err b] ^^^^^^^^^^^^^^^^^^^^^^^^^^ All builtin aliases are in scope by default, so I need this alias to have a different name! ── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─ The `Result` alias expects 2 type arguments, but it got 1 instead: 6│ canIGo : _ -> Result _ ^^^^^^^^ Are there missing parentheses? "### ); test_report!( inference_var_too_many_in_alias, indoc!( r#" Result a b : [Ok a, Err b] canIGo : _ -> Result _ _ _ canIGo = \color -> when color is "green" -> Ok "go!" "yellow" -> Err (SlowIt "whoa, let's slow down!") "red" -> Err (StopIt "absolutely not") _ -> Err (UnknownColor "this is a weird stoplight") canIGo "# ), @r###" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ This alias has the same name as a builtin: 4│ Result a b : [Ok a, Err b] ^^^^^^^^^^^^^^^^^^^^^^^^^^ All builtin aliases are in scope by default, so I need this alias to have a different name! ── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─ The `Result` alias expects 2 type arguments, but it got 3 instead: 6│ canIGo : _ -> Result _ _ _ ^^^^^^^^^^^^ Are there missing parentheses? "### ); test_report!( inference_var_conflict_in_rigid_links, indoc!( r#" f : a -> (_ -> b) where a implements Eq f = \x -> \y -> if x == y then x else y f "# ), // TODO: We should tell the user that we inferred `_` as `a` @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : a -> (_ -> b) where a implements Eq 5│ f = \x -> \y -> if x == y then x else y ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The body is an anonymous function of type: a -> a where a implements Eq, a implements Eq But the type annotation on `f` says it should be: a -> b where a implements Eq Tip: 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 in your type annotation? Maybe your code uses them in a weird way? "### ); test_report!( error_wildcards_are_related, indoc!( r#" f : * -> * f = \x -> x f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : * -> * 5│ f = \x -> x ^ The type annotation on `f` says this `x` value should have the type: * However, the type of this `x` value is connected to another type in a way that isn't reflected in this annotation. Tip: Any connection between types must use a named type variable, not a `*`! Maybe the annotation on `f` should have a named type variable in place of the `*`? "### ); test_report!( error_nested_wildcards_are_related, indoc!( r#" f : a, b, * -> {x: a, y: b, z: *} f = \x, y, z -> {x, y, z} f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : a, b, * -> {x: a, y: b, z: *} 5│ f = \x, y, z -> {x, y, z} ^^^^^^^^^ The type annotation on `f` says the body is a record should have the type: { x : a, y : b, z : *, } However, the type of the body is a record is connected to another type in a way that isn't reflected in this annotation. Tip: Any connection between types must use a named type variable, not a `*`! Maybe the annotation on `f` should have a named type variable in place of the `*`? "### ); test_report!( error_wildcards_are_related_in_nested_defs, indoc!( r#" f : a, b, * -> * f = \_, _, x2 -> inner : * -> * inner = \y -> y inner x2 f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `inner` definition: 6│ inner : * -> * 7│ inner = \y -> y ^ The type annotation on `inner` says this `y` value should have the type: * However, the type of this `y` value is connected to another type in a way that isn't reflected in this annotation. Tip: Any connection between types must use a named type variable, not a `*`! Maybe the annotation on `inner` should have a named type variable in place of the `*`? "### ); test_report!( error_inline_alias_not_an_alias, indoc!( r#" f : List elem -> [Nil, Cons elem a] as a "# ), @r###" ── NOT AN INLINE ALIAS ────────── tmp/error_inline_alias_not_an_alias/Test.roc ─ The inline type after this `as` is not a type alias: 4│ f : List elem -> [Nil, Cons elem a] as a ^ Inline alias types must start with an uppercase identifier and be followed by zero or more type arguments, like Point or List a. "### ); test_report!( error_inline_alias_qualified, indoc!( r#" f : List elem -> [Nil, Cons elem a] as Module.LinkedList a "# ), @r###" ── QUALIFIED ALIAS NAME ──────────── tmp/error_inline_alias_qualified/Test.roc ─ This type alias has a qualified name: 4│ f : List elem -> [Nil, Cons elem a] as Module.LinkedList a ^ An alias introduces a new name to the current scope, so it must be unqualified. "### ); test_report!( error_inline_alias_argument_uppercase, indoc!( r#" f : List elem -> [Nil, Cons elem a] as LinkedList U "# ), @r###" ── TYPE ARGUMENT NOT LOWERCASE ─ ...r_inline_alias_argument_uppercase/Test.roc ─ This alias type argument is not lowercase: 4│ f : List elem -> [Nil, Cons elem a] as LinkedList U ^ All type arguments must be lowercase. "### ); test_report!( mismatched_single_tag_arg, indoc!( r#" isEmpty = \email -> Email str = email Str.isEmpty str isEmpty (Name "boo") "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `isEmpty` has an unexpected type: 9│ isEmpty (Name "boo") ^^^^^^^^^^ This `Name` tag application has the type: [Name Str] But `isEmpty` needs its 1st argument to be: [Email Str] Tip: Seems like a tag typo. Maybe `Name` should be `Email`? Tip: 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 "### ); test_report!( issue_2326, indoc!( r#" C a b : a -> D a b D a b : { a, b } f : C a Num.Nat -> D a Num.Nat f = \c -> c 6 f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `c` has an unexpected type: 8│ f = \c -> c 6 ^ The argument is a number of type: Num * But `c` needs its 1st argument to be: a Tip: The type annotation uses the type variable `a` to say that this definition can produce any type of value. But in the body I see that it will only produce a `Num` value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? "### ); test_report!( issue_2380_annotations_only, indoc!( r#" F : F a : F a "# ), @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `F` alias is self-recursive in an invalid way: 4│ F : F ^ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( issue_2380_typed_body, indoc!( r#" F : F a : F a = 1 a "# ), @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `F` alias is self-recursive in an invalid way: 4│ F : F ^ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( issue_2380_alias_with_vars, indoc!( r#" F a b : F a b a : F Str Str a "# ), @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `F` alias is self-recursive in an invalid way: 4│ F a b : F a b ^ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( issue_2167_record_field_optional_and_required_mismatch, indoc!( r#" Job : [Job { inputs : List Str }] job : { inputs ? List Str } -> Job job = \{ inputs } -> Job { inputs } job { inputs: ["build", "test"] } "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 1st argument to `job` is weird: 6│ job = \{ inputs } -> ^^^^^^^^^^ The argument is a pattern that matches record values of type: { inputs : List Str } But the annotation on `job` says the 1st argument should be: { inputs ? List Str } Tip: To extract the `.inputs` field it must be non-optional, but the type says this field is optional. Learn more about optional fields at TODO. "### ); test_report!( unify_recursive_with_nonrecursive, indoc!( r#" Job : [Job { inputs : List Job }] job : { inputs : List Str } -> Job job = \{ inputs } -> Job { inputs } job { inputs: ["build", "test"] } "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `job` definition: 6│ job : { inputs : List Str } -> Job 7│ job = \{ inputs } -> 8│ Job { inputs } ^^^^^^^^^^^^^^ This `Job` tag application has the type: [Job { inputs : List Str }] But the type annotation on `job` says it should be: [Job { inputs : List a }]a as a "### ); test_report!( nested_datatype, indoc!( r#" Nested a : [Chain a (Nested (List a)), Term] s : Nested Str s "# ), @r###" ── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─ `Nested` is a nested datatype. Here is one recursive usage of it: 4│ Nested a : [Chain a (Nested (List a)), Term] ^^^^^^^^^^^^^^^ But recursive usages of `Nested` must match its definition: 4│ Nested a : [Chain a (Nested (List a)), Term] ^^^^^^^^ Nested datatypes are not supported in Roc. Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments. "### ); test_report!( nested_datatype_inline, indoc!( r#" f : {} -> [Chain a (Nested (List a)), Term] as Nested a f "# ), @r###" ── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─ `Nested` is a nested datatype. Here is one recursive usage of it: 4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a ^^^^^^^^^^^^^^^ But recursive usages of `Nested` must match its definition: 4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a ^^^^^^^^ Nested datatypes are not supported in Roc. Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments. "### ); macro_rules! mismatched_suffix_tests { ($($number:expr, $suffix:expr, $name:ident)*) => {$( test_report!( $name, &{ let number = $number.to_string(); let mut typ = $suffix.to_string(); typ.get_mut(0..1).unwrap().make_ascii_uppercase(); let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; format!(indoc!( r#" use : Num.{} -> Num.U8 use {}{} "# ), bad_type, number, $suffix) }, |golden| { let number = $number.to_string(); let mut typ = $suffix.to_string(); typ.get_mut(0..1).unwrap().make_ascii_uppercase(); let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; let carets = "^".repeat(number.len() + $suffix.len()); let kind = match $suffix { "dec"|"f32"|"f64" => "a fraction", _ => "an integer", }; let real = format!(indoc!( r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `use` has an unexpected type: 5│ use {}{} {} The argument is {} of type: {} But `use` needs its 1st argument to be: {} "# ), number, $suffix, carets, kind, typ, bad_type); assert_eq!(golden, real); } ); )*} } mismatched_suffix_tests! { 1, "u8", mismatched_suffix_u8 1, "u16", mismatched_suffix_u16 1, "u32", mismatched_suffix_u32 1, "u64", mismatched_suffix_u64 1, "u128", mismatched_suffix_u128 1, "i8", mismatched_suffix_i8 1, "i16", mismatched_suffix_i16 1, "i32", mismatched_suffix_i32 1, "i64", mismatched_suffix_i64 1, "i128", mismatched_suffix_i128 1, "nat", mismatched_suffix_nat 1, "dec", mismatched_suffix_dec 1, "f32", mismatched_suffix_f32 1, "f64", mismatched_suffix_f64 } macro_rules! mismatched_suffix_tests_in_pattern { ($($number:expr, $suffix:expr, $name:ident)*) => {$( test_report!( $name, &{ let number = $number.to_string(); let mut typ = $suffix.to_string(); typ.get_mut(0..1).unwrap().make_ascii_uppercase(); let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" }; format!(indoc!( r#" when {}{} is {}{} -> 1 _ -> 1 "# ), number, bad_suffix, number, $suffix) }, |golden| { let number = $number.to_string(); let mut typ = $suffix.to_string(); typ.get_mut(0..1).unwrap().make_ascii_uppercase(); let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" }; let bad_type = if $suffix == "u8" { "I8" } else { "U8" }; let real = format!(indoc!( r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 4│> when {}{} is 5│ {}{} -> 1 6│ _ -> 1 The `when` condition is an integer of type: {} But the branch patterns have type: {} The branches must be cases of the `when` condition's type! "# ), number, bad_suffix, number, $suffix, bad_type, typ); assert_eq!(golden, real); } ); )*} } mismatched_suffix_tests_in_pattern! { 1, "u8", mismatched_suffix_u8_pattern 1, "u16", mismatched_suffix_u16_pattern 1, "u32", mismatched_suffix_u32_pattern 1, "u64", mismatched_suffix_u64_pattern 1, "u128", mismatched_suffix_u128_pattern 1, "i8", mismatched_suffix_i8_pattern 1, "i16", mismatched_suffix_i16_pattern 1, "i32", mismatched_suffix_i32_pattern 1, "i64", mismatched_suffix_i64_pattern 1, "i128", mismatched_suffix_i128_pattern 1, "nat", mismatched_suffix_nat_pattern 1, "dec", mismatched_suffix_dec_pattern 1, "f32", mismatched_suffix_f32_pattern 1, "f64", mismatched_suffix_f64_pattern } test_report!( bad_numeric_literal_suffix, indoc!( r#" 1u256 "# ), // TODO: link to number suffixes @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer literal contains an invalid digit: 4│ 1u256 ^^^^^ Integer literals can only contain the digits 0-9, or have an integer suffix. Tip: Learn more about number literals at TODO "### ); test_report!( numer_literal_multi_suffix, indoc!( r#" 1u8u8 "# ), // TODO: link to number suffixes @r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This integer literal contains an invalid digit: 4│ 1u8u8 ^^^^^ Integer literals can only contain the digits 0-9, or have an integer suffix. Tip: Learn more about number literals at TODO "### ); test_report!( int_literal_has_float_suffix, indoc!( r#" 0b1f32 "# ), @r###" ── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─ This number literal is an integer, but it has a float suffix: 4│ 0b1f32 ^^^^^^ "### ); test_report!( float_literal_has_int_suffix, indoc!( r#" 1.0u8 "# ), @r###" ── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─ This number literal is a float, but it has an integer suffix: 4│ 1.0u8 ^^^^^ "### ); test_report!( u8_overflow, "256u8", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 256u8 ^^^^^ Tip: The suffix indicates this integer is a U8, whose maximum value is 255. "### ); test_report!( negative_u8, "-1u8", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -1u8 ^^^^ Tip: The suffix indicates this integer is a U8, whose minimum value is 0. "### ); test_report!( u16_overflow, "65536u16", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 65536u16 ^^^^^^^^ Tip: The suffix indicates this integer is a U16, whose maximum value is 65535. "### ); test_report!( negative_u16, "-1u16", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -1u16 ^^^^^ Tip: The suffix indicates this integer is a U16, whose minimum value is 0. "### ); test_report!( u32_overflow, "4_294_967_296u32", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 4_294_967_296u32 ^^^^^^^^^^^^^^^^ Tip: The suffix indicates this integer is a U32, whose maximum value is 4_294_967_295. "### ); test_report!( negative_u32, "-1u32", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -1u32 ^^^^^ Tip: The suffix indicates this integer is a U32, whose minimum value is 0. "### ); test_report!( u64_overflow, "18_446_744_073_709_551_616u64", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 18_446_744_073_709_551_616u64 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tip: The suffix indicates this integer is a U64, whose maximum value is 18_446_744_073_709_551_615. "### ); test_report!( negative_u64, "-1u64", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -1u64 ^^^^^ Tip: The suffix indicates this integer is a U64, whose minimum value is 0. "### ); test_report!( negative_u128, "-1u128", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -1u128 ^^^^^^ Tip: The suffix indicates this integer is a U128, whose minimum value is 0. "### ); test_report!( i8_overflow, "128i8", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 128i8 ^^^^^ Tip: The suffix indicates this integer is a I8, whose maximum value is 127. "### ); test_report!( i8_underflow, "-129i8", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -129i8 ^^^^^^ Tip: The suffix indicates this integer is a I8, whose minimum value is -128. "### ); test_report!( i16_overflow, "32768i16", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 32768i16 ^^^^^^^^ Tip: The suffix indicates this integer is a I16, whose maximum value is 32767. "### ); test_report!( i16_underflow, "-32769i16", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -32769i16 ^^^^^^^^^ Tip: The suffix indicates this integer is a I16, whose minimum value is -32768. "### ); test_report!( i32_overflow, "2_147_483_648i32", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 2_147_483_648i32 ^^^^^^^^^^^^^^^^ Tip: The suffix indicates this integer is a I32, whose maximum value is 2_147_483_647. "### ); test_report!( i32_underflow, "-2_147_483_649i32", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -2_147_483_649i32 ^^^^^^^^^^^^^^^^^ Tip: The suffix indicates this integer is a I32, whose minimum value is -2_147_483_648. "### ); test_report!( i64_overflow, "9_223_372_036_854_775_808i64", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 9_223_372_036_854_775_808i64 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tip: The suffix indicates this integer is a I64, whose maximum value is 9_223_372_036_854_775_807. "### ); test_report!( i64_underflow, "-9_223_372_036_854_775_809i64", @r###" ── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─ This integer literal underflows the type indicated by its suffix: 4│ -9_223_372_036_854_775_809i64 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tip: The suffix indicates this integer is a I64, whose minimum value is -9_223_372_036_854_775_808. "### ); test_report!( i128_overflow, "170_141_183_460_469_231_731_687_303_715_884_105_728i128", @r###" ── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─ This integer literal overflows the type indicated by its suffix: 4│ 170_141_183_460_469_231_731_687_303_715_884_105_728i128 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Tip: The suffix indicates this integer is a I128, whose maximum value is 170_141_183_460_469_231_731_687_303_715_884_105_727. "### ); test_report!( list_get_negative_number, indoc!( r#" List.get [1,2,3] -1 "# ), // TODO: this error message could be improved, e.g. something like "This argument can // be used as ... because of its literal value" @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `get` has an unexpected type: 4│ List.get [1,2,3] -1 ^^ The argument is a number of type: I8, I16, F32, I32, F64, I64, I128, or Dec But `get` needs its 2nd argument to be: Nat "### ); test_report!( list_get_negative_number_indirect, indoc!( r#" a = -9_223_372_036_854 List.get [1,2,3] a "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `get` has an unexpected type: 5│ List.get [1,2,3] a ^ This `a` value is a: F64, I64, I128, or Dec But `get` needs its 2nd argument to be: Nat "### ); test_report!( list_get_negative_number_double_indirect, indoc!( r#" a = -9_223_372_036_854 b = a List.get [1,2,3] b "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `get` has an unexpected type: 6│ List.get [1,2,3] b ^ This `b` value is a: F64, I64, I128, or Dec But `get` needs its 2nd argument to be: Nat "### ); test_report!( compare_unsigned_to_signed, indoc!( r#" when -1 is 1u8 -> 1 _ -> 1 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 4│> when -1 is 5│ 1u8 -> 1 6│ _ -> 1 The `when` condition is a number of type: I8, I16, F32, I32, F64, I64, I128, or Dec But the branch patterns have type: U8 The branches must be cases of the `when` condition's type! "### ); test_report!( recursive_type_alias_is_newtype, indoc!( r#" R a : [Only (R a)] v : R Str v "# ), @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `R` alias is self-recursive in an invalid way: 4│ R a : [Only (R a)] ^ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( recursive_type_alias_is_newtype_deep, indoc!( r#" R a : [Only { very: [Deep (R a)] }] v : R Str v "# ), @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `R` alias is self-recursive in an invalid way: 4│ R a : [Only { very: [Deep (R a)] }] ^ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( recursive_type_alias_is_newtype_mutual, indoc!( r#" Foo a : [Thing (Bar a)] Bar a : [Stuff (Foo a)] v : Bar Str v "# ), @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `Foo` alias is recursive in an invalid way: 4│ Foo a : [Thing (Bar a)] ^^^ The `Foo` alias depends on itself through the following chain of definitions: ┌─────┐ │ Foo │ ↓ │ Bar └─────┘ Recursion in aliases is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( issue_2458, indoc!( r#" Result a b : [Ok a, Err b] Foo a : [Blah (Result (Bar a) [])] Bar a : Foo a v : Bar Str v "# ), @r###" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ This alias has the same name as a builtin: 4│ Result a b : [Ok a, Err b] ^^^^^^^^^^^^^^^^^^^^^^^^^^ All builtin aliases are in scope by default, so I need this alias to have a different name! "### ); test_report!( opaque_type_not_in_scope, indoc!( r#" @Age 21 "# ), @r###" ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ The opaque type Age referenced here is not defined: 4│ @Age 21 ^^^^ Note: It looks like there are no opaque types declared in this scope yet! "### ); test_report!( opaque_reference_not_opaque_type, indoc!( r#" Age : Num.U8 @Age 21 "# ), @r###" ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ The opaque type Age referenced here is not defined: 6│ @Age 21 ^^^^ Note: There is an alias of the same name: 4│ Age : Num.U8 ^^^ Note: It looks like there are no opaque types declared in this scope yet! ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `Age` is not used anywhere in your code. 4│ Age : Num.U8 ^^^^^^^^^^^^ If you didn't intend on using `Age` then remove it so future readers of your code don't wonder why it is there. "### ); test_report!( qualified_opaque_reference, indoc!( r#" OtherModule.@Age 21 "# ), // TODO: get rid of the first error. Consider parsing OtherModule.@Age to completion // and checking it during can. The reason the error appears is because it is parsed as // Apply(Error(OtherModule), [@Age, 21]) @r###" ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ The opaque type Age referenced here is not defined: 4│ OtherModule.@Age 21 ^^^^ Note: It looks like there are no opaque types declared in this scope yet! ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ I am trying to parse a qualified name here: 4│ OtherModule.@Age 21 ^ I was expecting to see an identifier next, like height. A complete qualified name looks something like Json.Decode.string. "### ); test_report!( opaque_used_outside_declaration_scope, indoc!( r#" age = Age := Num.U8 21u8 @Age age "# ), // TODO(opaques): there is a potential for a better error message here, if the usage of // `@Age` can be linked to the declaration of `Age` inside `age`, and a suggestion to // raise that declaration to the outer scope. @r###" ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `Age` is not used anywhere in your code. 5│ Age := Num.U8 ^^^^^^^^^^^^^ If you didn't intend on using `Age` then remove it so future readers of your code don't wonder why it is there. ── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─ The opaque type Age referenced here is not defined: 8│ @Age age ^^^^ Note: It looks like there are no opaque types declared in this scope yet! "### ); test_report!( unimported_modules_reported, indoc!( r#" alt : Task.Task {} [] alt = "whatever man you don't even know my type" alt "# ), @r###" ── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─ The `Task` module is not imported: 4│ alt : Task.Task {} [] ^^^^^^^^^^^^^^^ Is there an import missing? Perhaps there is a typo. Did you mean one of these? Hash List Num Box "### ); test_report!( opaque_mismatch_check, indoc!( r#" Age := Num.U8 n : Age n = @Age "" n "# ), // TODO(opaques): error could be improved by saying that the opaque definition demands // that the argument be a U8, and linking to the definitin! @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 7│ n = @Age "" ^^ This argument to an opaque type has type: Str But you are trying to use it as: U8 "### ); test_report!( opaque_mismatch_infer, indoc!( r#" F n := n if Bool.true then @F "" else @F {} "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 8│ else @F {} ^^ This argument to an opaque type has type: {} But you are trying to use it as: Str "### ); test_report!( opaque_creation_is_not_wrapped, indoc!( r#" F n := n v : F Str v = "" v "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `v` definition: 6│ v : F Str 7│ v = "" ^^ The body is a string of type: Str But the type annotation on `v` says it should be: F Str Tip: Type comparisons between an opaque type are only ever equal if both types are the same opaque type. Did you mean to create an opaque type by wrapping it? If I have an opaque type Age := U32 I can create an instance of this opaque type by doing @Age 23. "### ); test_report!( opaque_mismatch_pattern_check, indoc!( r#" Age := Num.U8 f : Age -> Num.U8 f = \Age n -> n f "# ), // TODO(opaques): error could be improved by saying that the user-provided pattern // probably wants to change "Age" to "@Age"! @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 1st argument to `f` is weird: 7│ f = \Age n -> n ^^^^^ The argument is a pattern that matches a `Age` tag of type: [Age *] But the annotation on `f` says the 1st argument should be: Age Tip: Type comparisons between an opaque type are only ever equal if both types are the same opaque type. Did you mean to create an opaque type by wrapping it? If I have an opaque type Age := U32 I can create an instance of this opaque type by doing @Age 23. "### ); test_report!( opaque_mismatch_pattern_infer, indoc!( r#" F n := n \x -> when x is @F A -> "" @F {} -> "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern in this `when` does not match the previous ones: 9│ @F {} -> "" ^^^^^ The 2nd pattern is trying to matchF unwrappings of type: F {}a But all the previous branches match: F [A] "### ); test_report!( opaque_pattern_match_not_exhaustive_tag, indoc!( r#" F n := n v : F [A, B, C] when v is @F A -> "" @F B -> "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 8│> when v is 9│ @F A -> "" 10│ @F B -> "" This `v` value is a: F [C, …] But the branch patterns have type: F […] The branches must be cases of the `when` condition's type! Tip: Looks like the branches are missing coverage of the `C` tag. Tip: Maybe you need to add a catch-all branch, like `_`? "### ); test_report!( opaque_pattern_match_not_exhaustive_int, indoc!( r#" F n := n v : F Num.U8 when v is @F 1 -> "" @F 2 -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 8│> when v is 9│> @F 1 -> "" 10│> @F 2 -> "" Other possibilities include: @F _ I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( let_polymorphism_with_scoped_type_variables, indoc!( r#" f : a -> a f = \x -> y : a -> a y = \z -> z n = y 1u8 x1 = y x (\_ -> x1) n f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `y` has an unexpected type: 9│ n = y 1u8 ^^^ The argument is an integer of type: U8 But `y` needs its 1st argument to be: a Tip: The type annotation uses the type variable `a` to say that this definition can produce any type of value. But in the body I see that it will only produce a `U8` value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? "### ); test_report!( non_exhaustive_with_guard, indoc!( r#" x : [A] when x is A if Bool.true -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 5│> when x is 6│> A if Bool.true -> "" Other possibilities include: A (note the lack of an if clause) I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( invalid_record_extension_type, indoc!( r#" f : { x : Num.Nat }[] f "# ), @r###" ── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─ This record extension type is invalid: 4│ f : { x : Num.Nat }[] ^^ Note: A record extension variable can only contain a type variable or another record. "### ); test_report!( invalid_tag_extension_type, indoc!( r#" f : [A]Str f "# ), @r###" ── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─ This tag union extension type is invalid: 4│ f : [A]Str ^^^ Note: A tag union extension variable can only contain a type variable or another tag union. "### ); test_report!( unknown_type, indoc!( r#" Type : [Constructor UnknownType] insertHelper : UnknownType, Type -> Type insertHelper = \h, m -> when m is Constructor _ -> Constructor h insertHelper "# ), @r###" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `UnknownType` in this scope. 4│ Type : [Constructor UnknownType] ^^^^^^^^^^^ Did you mean one of these? Type Unsigned8 Unsigned16 Unsigned64 ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `UnknownType` in this scope. 6│ insertHelper : UnknownType, Type -> Type ^^^^^^^^^^^ Did you mean one of these? Type Unsigned8 Unsigned16 Unsigned64 "### ); test_report!( ability_first_demand_not_indented_enough, indoc!( r#" MEq implements eq : a, a -> U64 where a implements MEq 1 "# ), @r###" ── UNFINISHED ABILITY ── tmp/ability_first_demand_not_indented_enough/Test.roc ─ I was partway through parsing an ability definition, but I got stuck here: 4│ MEq implements 5│ eq : a, a -> U64 where a implements MEq ^ I suspect this line is not indented enough (by 1 spaces) "### ); test_report!( ability_demands_not_indented_with_first, indoc!( r#" MEq implements eq : a, a -> U64 where a implements MEq neq : a, a -> U64 where a implements MEq 1 "# ), @r#" ── UNFINISHED ABILITY ─── tmp/ability_demands_not_indented_with_first/Test.roc ─ I was partway through parsing an ability definition, but I got stuck here: 5│ eq : a, a -> U64 where a implements MEq 6│ neq : a, a -> U64 where a implements MEq ^ I suspect this line is indented too much (by 4 spaces)"# ); test_report!( ability_demand_value_has_args, indoc!( r#" MEq implements eq b c : a, a -> U64 where a implements MEq 1 "# ), @r#" ── UNFINISHED ABILITY ───────────── tmp/ability_demand_value_has_args/Test.roc ─ I was partway through parsing an ability definition, but I got stuck here: 4│ MEq implements 5│ eq b c : a, a -> U64 where a implements MEq ^ I was expecting to see a : annotating the signature of this value next."# ); test_report!( ability_non_signature_expression, indoc!( r#" MEq implements 123 1 "# ), @r###" ── UNFINISHED ABILITY ────────── tmp/ability_non_signature_expression/Test.roc ─ I was partway through parsing an ability definition, but I got stuck here: 4│ MEq implements 5│ 123 ^ I was expecting to see a value signature next. "### ); test_report!( wildcard_in_alias, indoc!( r#" I : Num.Int * a : I a "# ), @r###" ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ The definition of `I` has an unbound type variable: 4│ I : Num.Int * ^ Tip: Type variables must be bound before the `:`. Perhaps you intended to add a type parameter to this type? "### ); test_report!( wildcard_in_opaque, indoc!( r#" I := Num.Int * a : I a "# ), @r###" ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ The definition of `I` has an unbound type variable: 4│ I := Num.Int * ^ Tip: Type variables must be bound before the `:=`. Perhaps you intended to add a type parameter to this type? "### ); test_report!( multiple_wildcards_in_alias, indoc!( r#" I : [A (Num.Int *), B (Num.Int *)] a : I a "# ), @r###" ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ The definition of `I` has 2 unbound type variables. Here is one occurrence: 4│ I : [A (Num.Int *), B (Num.Int *)] ^ Tip: Type variables must be bound before the `:`. Perhaps you intended to add a type parameter to this type? "### ); test_report!( inference_var_in_alias, indoc!( r#" I : Num.Int _ a : I a "# ), @r###" ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ The definition of `I` has an unbound type variable: 4│ I : Num.Int _ ^ Tip: Type variables must be bound before the `:`. Perhaps you intended to add a type parameter to this type? "### ); test_report!( unbound_var_in_alias, indoc!( r#" I : Num.Int a a : I a "# ), @r###" ── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─ The definition of `I` has an unbound type variable: 4│ I : Num.Int a ^ Tip: Type variables must be bound before the `:`. Perhaps you intended to add a type parameter to this type? "### ); test_report!( ability_bad_type_parameter, indoc!( r#" app "test" provides [] to "./platform" MHash a b c implements hash : a -> U64 where a implements MHash "# ), @r###" ── ABILITY HAS TYPE VARIABLES ──────────────────────────── /code/proj/Main.roc ─ The definition of the `MHash` ability includes type variables: 3│ MHash a b c implements ^^^^^ Abilities cannot depend on type variables, but their member values can! ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `MHash` is not used anywhere in your code. 3│ MHash a b c implements ^^^^^ If you didn't intend on using `MHash` then remove it so future readers of your code don't wonder why it is there. "### ); test_report!( alias_in_implements_clause, indoc!( r#" app "test" provides [hash] to "./platform" MHash implements hash : a, b -> Num.U64 where a implements MHash, b implements Bool.Bool "# ), @r###" ── IMPLEMENTS CLAUSE IS NOT AN ABILITY ─────────────────── /code/proj/Main.roc ─ The type referenced in this "implements" clause is not an ability: 3│ MHash implements hash : a, b -> Num.U64 where a implements MHash, b implements Bool.Bool ^^^^^^^^^ "### ); test_report!( shadowed_type_variable_in_has_clause, indoc!( r#" app "test" provides [ab1] to "./platform" Ab1 implements ab1 : a -> {} where a implements Ab1, a implements Ab1 "# ), @r#" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ The `a` name is first defined here: 3│ Ab1 implements ab1 : a -> {} where a implements Ab1, a implements Ab1 ^^^^^^^^^^^^^^^^ But then it's defined a second time here: 3│ Ab1 implements ab1 : a -> {} where a implements Ab1, a implements Ab1 ^^^^^^^^^^^^^^^^ Since these variables have the same name, it's easy to use the wrong one by accident. Give one of them a new name. "# ); test_report!( ability_shadows_ability, indoc!( r#" app "test" provides [ab] to "./platform" Ability implements ab : a -> U64 where a implements Ability Ability implements ab1 : a -> U64 where a implements Ability "# ), @r#" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ The `Ability` name is first defined here: 3│ Ability implements ab : a -> U64 where a implements Ability ^^^^^^^ But then it's defined a second time here: 5│ Ability implements ab1 : a -> U64 where a implements Ability ^^^^^^^ Since these abilities have the same name, it's easy to use the wrong one by accident. Give one of them a new name. "# ); test_report!( ability_member_does_not_bind_ability, indoc!( r#" app "test" provides [] to "./platform" Ability implements ab : {} -> {} "# ), @r#" ── ABILITY MEMBER MISSING IMPLEMENTS CLAUSE ────────────── /code/proj/Main.roc ─ The definition of the ability member `ab` does not include an `implements` clause binding a type variable to the ability `Ability`: 3│ Ability implements ab : {} -> {} ^^ Ability members must include an `implements` clause binding a type variable to an ability, like a implements Ability Otherwise, the function does not need to be part of the ability! ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `Ability` is not used anywhere in your code. 3│ Ability implements ab : {} -> {} ^^^^^^^ If you didn't intend on using `Ability` then remove it so future readers of your code don't wonder why it is there. "# ); test_report!( ability_member_binds_parent_twice, indoc!( r#" app "test" provides [] to "./platform" MEq implements eq : a, b -> Bool.Bool where a implements MEq, b implements MEq "# ), @r#" ── ABILITY MEMBER BINDS MULTIPLE VARIABLES ─────────────── /code/proj/Main.roc ─ The definition of the ability member `eq` includes multiple variables bound to the `MEq`` ability:` 3│ MEq implements eq : a, b -> Bool.Bool where a implements MEq, b implements MEq ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ability members can only bind one type variable to their parent ability. Otherwise, I wouldn't know what type implements an ability by looking at specializations! Hint: Did you mean to only bind `a` to `MEq`? "# ); test_report!( has_clause_not_on_toplevel, indoc!( r#" app "test" provides [f] to "./platform" MHash implements hash : (a where a implements MHash) -> Num.U64 f : a -> Num.U64 where a implements MHash "# ), @r###" ── ILLEGAL IMPLEMENTS CLAUSE ───────────────────────────── /code/proj/Main.roc ─ An `implements` clause is not allowed here: 3│ MHash implements hash : (a where a implements MHash) -> Num.U64 ^^^^^^^^^^^^^^^^^^ `implements` clauses can only be specified on the top-level type annotations. ── ABILITY MEMBER MISSING IMPLEMENTS CLAUSE ────────────── /code/proj/Main.roc ─ The definition of the ability member `hash` does not include an `implements` clause binding a type variable to the ability `MHash`: 3│ MHash implements hash : (a where a implements MHash) -> Num.U64 ^^^^ Ability members must include an `implements` clause binding a type variable to an ability, like a implements MHash Otherwise, the function does not need to be part of the ability! "### ); test_report!( ability_specialization_does_not_match_type, indoc!( r#" app "test" provides [hash] to "./platform" MHash implements hash : a -> U64 where a implements MHash Id := U32 implements [MHash {hash}] hash = \@Id n -> n "# ), @r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with this specialization of `hash`: 7│ hash = \@Id n -> n ^^^^ This value is a declared specialization of type: Id -> U32 But the type annotation on `hash` says it must match: Id -> U64 "# ); test_report!( ability_specialization_is_incomplete, indoc!( r#" app "test" provides [eq, le] to "./platform" MEq implements eq : a, a -> Bool where a implements MEq le : a, a -> Bool where a implements MEq Id := U64 implements [MEq {eq}] eq = \@Id m, @Id n -> m == n "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ This type does not fully implement the `MEq` ability: 7│ Id := U64 implements [MEq {eq}] ^^^^^^^^ The following necessary members are missing implementations: le "### ); test_report!( ability_specialization_is_unused, indoc!( r#" app "test" provides [hash] to "./platform" MHash implements hash : a -> U64 where a implements MHash hash = \_ -> 0u64 "# ), @r###" ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `hash` is not used anywhere in your code. 6│ hash = \_ -> 0u64 ^^^^ If you didn't intend on using `hash` then remove it so future readers of your code don't wonder why it is there. "### ); test_report!( ability_specialization_is_duplicated, indoc!( r#" app "test" provides [hash, One, Two] to "./platform" MHash implements hash : a -> U64 where a implements MHash One := {} implements [MHash {hash}] Two := {} implements [MHash {hash}] hash = \_ -> 0u64 "# ), // TODO: the error message here could be seriously improved! @r###" ── OVERLOADED SPECIALIZATION ───────────────────────────── /code/proj/Main.roc ─ This ability member specialization is already claimed to specialize another opaque type: 7│ Two := {} implements [MHash {hash}] ^^^^ Previously, we found it to specialize `hash` for `One`. Ability specializations can only provide implementations for one opaque type, since all opaque types are different! ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This specialization of `hash` is overly general: 9│ hash = \_ -> 0u64 ^^^^ This value is a declared specialization of type: * -> U64 But the type annotation on `hash` says it must match: a -> U64 where a implements MHash Note: The specialized type is too general, and does not provide a concrete type where a type variable is bound to an ability. Specializations can only be made for concrete types. If you have a generic implementation for this value, perhaps you don't need an ability? "### ); test_report!( ability_specialization_is_duplicated_with_type_mismatch, indoc!( r#" app "test" provides [hash, One, Two] to "./platform" MHash implements hash : a -> U64 where a implements MHash One := {} implements [MHash {hash}] Two := {} implements [MHash {hash}] hash = \@One _ -> 0u64 "# ), @r###" ── OVERLOADED SPECIALIZATION ───────────────────────────── /code/proj/Main.roc ─ This ability member specialization is already claimed to specialize another opaque type: 7│ Two := {} implements [MHash {hash}] ^^^^ Previously, we found it to specialize `hash` for `One`. Ability specializations can only provide implementations for one opaque type, since all opaque types are different! "### ); test_report!( ability_specialization_conflicting_specialization_types, indoc!( r#" app "test" provides [eq] to "./platform" MEq implements eq : a, a -> Bool where a implements MEq You := {} implements [MEq {eq}] AndI := {} eq = \@You {}, @AndI {} -> False "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with this specialization of `eq`: 9│ eq = \@You {}, @AndI {} -> False ^^ This value is a declared specialization of type: You, AndI -> [False] But the type annotation on `eq` says it must match: You, You -> Bool Tip: Did you mean to use `Bool.false` rather than `False`? "### ); test_report!( ability_specialization_checked_against_annotation, indoc!( r#" app "test" provides [hash] to "./platform" MHash implements hash : a -> U64 where a implements MHash Id := U64 implements [MHash {hash}] hash : Id -> U32 hash = \@Id n -> n "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `hash` definition: 8│ hash : Id -> U32 9│ hash = \@Id n -> n ^ This `n` value is a: U64 But the type annotation on `hash` says it should be: U32 "### ); test_report!( ability_specialization_called_with_non_specializing, indoc!( r#" app "test" provides [noGoodVeryBadTerrible] to "./platform" MHash implements hash : a -> U64 where a implements MHash Id := U64 implements [MHash {hash}] hash = \@Id n -> n User := {} noGoodVeryBadTerrible = { nope: hash (@User {}), notYet: hash (A 1), } "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 15│ notYet: hash (A 1), ^^^ I can't generate an implementation of the `MHash` ability for [A (Num *)] Only builtin abilities can have generated implementations! ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 14│ nope: hash (@User {}), ^^^^^^^^ The type `User` does not fully implement the ability `MHash`. "### ); test_report!( ability_not_on_toplevel, indoc!( r#" app "test" provides [main] to "./platform" main = MHash implements hash : a -> U64 where a implements MHash 123 "# ), @r#" ── ABILITY NOT ON TOP-LEVEL ────────────────────────────── /code/proj/Main.roc ─ This ability definition is not on the top-level of a module: 4│> MHash implements 5│> hash : a -> U64 where a implements MHash Abilities can only be defined on the top-level of a Roc module. "# ); test_report!( expression_generalization_to_ability_is_an_error, indoc!( r#" app "test" provides [hash, hashable] to "./platform" MHash implements hash : a -> U64 where a implements MHash Id := U64 implements [MHash {hash}] hash = \@Id n -> n hashable : a where a implements MHash hashable = @Id 15 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `hashable` definition: 9│ hashable : a where a implements MHash 10│ hashable = @Id 15 ^^^^^^ This Id opaque wrapping has the type: Id But the type annotation on `hashable` says it should be: a where a implements MHash Note: The type variable `a` says it can take on any value that implements the ability `MHash`. But, I see that the type is only ever used as a a `Id` value. Can you replace `a` with a more specific type? "### ); test_report!( ability_value_annotations_are_an_error, indoc!( r#" app "test" provides [result] to "./platform" MHash implements hash : a -> U64 where a implements MHash mulMHashes : MHash, MHash -> U64 mulMHashes = \x, y -> hash x * hash y Id := U64 implements [MHash {hash: hashId}] hashId = \@Id n -> n Three := {} implements [MHash {hash: hashThree}] hashThree = \@Three _ -> 3 result = mulMHashes (@Id 100) (@Three {}) "# ), @r###" ── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─ You are attempting to use the ability `MHash` as a type directly: 6│ mulMHashes : MHash, MHash -> U64 ^^^^^ Abilities can only be used in type annotations to constrain type variables. Hint: Perhaps you meant to include an `implements` annotation, like a implements MHash ── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─ You are attempting to use the ability `MHash` as a type directly: 6│ mulMHashes : MHash, MHash -> U64 ^^^^^ Abilities can only be used in type annotations to constrain type variables. Hint: Perhaps you meant to include an `implements` annotation, like b implements MHash "### ); test_report!( branches_have_more_cases_than_condition, indoc!( r#" foo : Bool -> Str foo = \bool -> when bool is True -> "true" False -> "false" Wat -> "surprise!" foo "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 6│> when bool is 7│ True -> "true" 8│ False -> "false" 9│ Wat -> "surprise!" This `bool` value is a: Bool But the branch patterns have type: [ False, True, Wat, ] The branches must be cases of the `when` condition's type! "### ); // from https://github.com/roc-lang/roc/commit/1372737f5e53ee5bb96d7e1b9593985e5537023a // There was a bug where this reported UnusedArgument("val") // since it was used only in the returned function only. // // we want this to not give any warnings/errors! test_report!( always_function, indoc!( r#" always = \val -> \_ -> val always "# ), @"" ); test_report!( imports_missing_comma, indoc!( r#" app "test-missing-comma" packages { pf: "platform/main.roc" } imports [pf.Task Base64] provides [main, @Foo] to pf "# ), @r#" ── WEIRD IMPORTS ────────────────────────── tmp/imports_missing_comma/Test.roc ─ I am partway through parsing a imports list, but I got stuck here: 2│ packages { pf: "platform/main.roc" } 3│ imports [pf.Task Base64] ^ I am expecting a comma or end of list, like imports [Shape, Vector]"# ); test_report!( not_enough_cases_for_open_union, indoc!( r#" foo : [A, B]a -> Str foo = \it -> when it is A -> "" foo "# ), @r#" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when it is 7│> A -> "" Other possibilities include: B _ I would have to crash if I saw one of those! Add branches for them! "# ); test_report!( issue_2778_specialization_is_not_a_redundant_pattern, indoc!( r#" formatColor = \color -> when color is Red -> "red" Yellow -> "yellow" _ -> "unknown" Red |> formatColor |> Str.concat (formatColor Orange) "# ), @"" // no problem ); test_report!( nested_specialization, indoc!( r#" app "test" provides [main] to "./platform" Default implements default : {} -> a where a implements Default main = A := {} implements [Default {default}] default = \{} -> @A {} default {} "# ), @r#" ── SPECIALIZATION NOT ON TOP-LEVEL ─────────────────────── /code/proj/Main.roc ─ This specialization of the `default` ability member is in a nested scope: 7│ default = \{} -> @A {} ^^^^^^^ Specializations can only be defined on the top-level of a module. "# ); test_report!( recursion_var_specialization_error, indoc!( r#" Job a : [Job (List (Job a))] job : Job Str when job is Job lst -> lst == "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to == has an unexpected type: 9│ Job lst -> lst == "" ^^ The argument is a string of type: Str But == needs its 2nd argument to be: List [Job ∞] as ∞ "### ); test_report!( #[ignore] type_error_in_apply_is_circular, indoc!( r#" app "test" imports [Set] provides [go] to "./platform" S a : { set : Set.Set a } go : a, S a -> Result (List a) * go = \goal, model -> if goal == goal then Ok [] else new = { model & set : Set.remove goal model.set } go goal new "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `remove` has an unexpected type: 10│ new = { model & set : Set.remove goal model.set } ^^^^ This `goal` value is a: a But `remove` needs the 1st argument to be: Set k Tip: The type annotation uses the type variable `a` to say that this definition can produce any type of value. But in the body I see that it will only produce a `Set` value of a single specific type. Maybe change the type annotation to be more specific? Maybe change the code to be more general? ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `new`: 10│ new = { model & set : Set.remove goal model.set } ^^^ 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. { set : Set ∞ } ── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─ I'm inferring a weird self-referential type for `goal`: 6│ go = \goal, model -> ^^^^ 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. Set ∞ "### ); test_report!( cycle_through_non_function, indoc!( r#" force : ({} -> I64) -> I64 force = \eval -> eval {} t1 = \_ -> force (\_ -> t2) t2 = t1 {} t2 "# ), @r#" ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ The `t1` definition is causing a very tricky infinite loop: 7│ t1 = \_ -> force (\_ -> t2) ^^ The `t1` value depends on itself through the following chain of definitions: ┌─────┐ │ t1 │ ↓ │ t2 └─────┘ "# ); test_report!( function_does_not_implement_encoding, indoc!( r#" app "test" imports [] provides [main] to "./platform" main = Encode.toEncoder \x -> x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 3│ main = Encode.toEncoder \x -> x ^^^^^^^ I can't generate an implementation of the `Encoding` ability for a -> a Note: `Encoding` cannot be generated for functions. "### ); test_report!( nested_opaque_does_not_implement_encoding, indoc!( r#" app "test" imports [] provides [main] to "./platform" A := {} main = Encode.toEncoder { x: @A {} } "# ), // TODO: this error message is quite unfortunate. We should remove the duplication, and // also support regions that point to things in other modules. See also https://github.com/roc-lang/roc/issues/3056. @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 4│ main = Encode.toEncoder { x: @A {} } ^^^^^^^^^^^^ I can't generate an implementation of the `Encoding` ability for { x : A } In particular, an implementation for A cannot be generated. Tip: `A` does not implement `Encoding`. Consider adding a custom implementation or `implements Encode.Encoding` to the definition of `A`. "### ); test_report!( cycle_through_non_function_top_level, indoc!( r#" app "test" provides [t2] to "./platform" force : ({} -> I64) -> I64 force = \eval -> eval {} t1 = \_ -> force (\_ -> t2) t2 = t1 {} "# ), @r#" ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ The `t1` definition is causing a very tricky infinite loop: 6│ t1 = \_ -> force (\_ -> t2) ^^ The `t1` value depends on itself through the following chain of definitions: ┌─────┐ │ t1 │ ↓ │ t2 └─────┘ "# ); test_report!( opaque_ability_impl_not_found_shorthand_syntax, indoc!( r#" app "test" provides [A] to "./platform" MEq implements eq : a, a -> U64 where a implements MEq A := U8 implements [MEq {eq}] "# ), @r###" ── IMPLEMENTATION NOT FOUND ────────────────────────────── /code/proj/Main.roc ─ An implementation of `eq` could not be found in this scope: 5│ A := U8 implements [MEq {eq}] ^^ Tip: consider adding a value of name `eq` in this scope, or using another variable that implements this ability member, like { eq: myeq } ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ This type does not fully implement the `MEq` ability: 5│ A := U8 implements [MEq {eq}] ^^^^^^^^ The following necessary members are missing implementations: eq "### ); test_report!( opaque_ability_impl_not_found, indoc!( r#" app "test" provides [A, myMEq] to "./platform" MEq implements eq : a, a -> Bool where a implements MEq A := U8 implements [ MEq {eq: aMEq} ] myMEq = \m, n -> m == n "# ), @r###" ── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `aMEq` in this scope. 5│ A := U8 implements [ MEq {eq: aMEq} ] ^^^^ Did you mean one of these? MEq Eq myMEq eq ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ This type does not fully implement the `MEq` ability: 5│ A := U8 implements [ MEq {eq: aMEq} ] ^^^^^^^^^^^^^^ The following necessary members are missing implementations: eq "### ); test_report!( opaque_ability_impl_optional, indoc!( r#" app "test" provides [A, myMEq] to "./platform" MEq implements eq : a, a -> Bool where a implements MEq A := U8 implements [ MEq {eq ? aMEq} ] myMEq = \m, n -> m == n "# ), @r###" ── OPTIONAL ABILITY IMPLEMENTATION ─────────────────────── /code/proj/Main.roc ─ Ability implementations cannot be optional: 5│ A := U8 implements [ MEq {eq ? aMEq} ] ^^^^^^^^^ Custom implementations must be supplied fully. ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ This type does not fully implement the `MEq` ability: 5│ A := U8 implements [ MEq {eq ? aMEq} ] ^^^^^^^^^^^^^^^ The following necessary members are missing implementations: eq "### ); test_report!( opaque_builtin_ability_impl_optional, indoc!( r#" app "test" imports [] provides [A, myEncoder] to "./platform" A := U8 implements [ Encoding {toEncoder ? myEncoder} ] myEncoder = 1 "# ), @r###" ── OPTIONAL ABILITY IMPLEMENTATION ─────────────────────── /code/proj/Main.roc ─ Ability implementations cannot be optional: 5│ A := U8 implements [ Encoding {toEncoder ? myEncoder} ] ^^^^^^^^^^^^^^^^^^^^^ Custom implementations must be supplied fully. Hint: if you want this implementation to be derived, don't include a record of implementations. For example, implements [Encoding] will attempt to derive `Encoding` ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ This type does not fully implement the `Encoding` ability: 5│ A := U8 implements [ Encoding {toEncoder ? myEncoder} ] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The following necessary members are missing implementations: toEncoder "### ); test_report!( opaque_ability_impl_qualified, indoc!( r#" app "test" provides [A] to "./platform" MEq implements eq : a, a -> Bool where a implements MEq A := U8 implements [ MEq {eq : Bool.eq} ] "# ), @r###" ── QUALIFIED ABILITY IMPLEMENTATION ────────────────────── /code/proj/Main.roc ─ This ability implementation is qualified: 5│ A := U8 implements [ MEq {eq : Bool.eq} ] ^^^^^^^ Custom implementations must be defined in the local scope, and unqualified. ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ This type does not fully implement the `MEq` ability: 5│ A := U8 implements [ MEq {eq : Bool.eq} ] ^^^^^^^^^^^^^^^^^^ The following necessary members are missing implementations: eq "### ); test_report!( opaque_ability_impl_not_identifier, indoc!( r#" app "test" provides [A] to "./platform" MEq implements eq : a, a -> Bool where a implements MEq A := U8 implements [ MEq {eq : \m, n -> m == n} ] "# ), @r###" ── ABILITY IMPLEMENTATION NOT IDENTIFIER ───────────────── /code/proj/Main.roc ─ This ability implementation is not an identifier: 5│ A := U8 implements [ MEq {eq : \m, n -> m == n} ] ^^^^^^^^^^^^^^^ Custom ability implementations defined in this position can only be unqualified identifiers, not arbitrary expressions. Tip: consider defining this expression as a variable. ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ This type does not fully implement the `MEq` ability: 5│ A := U8 implements [ MEq {eq : \m, n -> m == n} ] ^^^^^^^^^^^^^^^^^^^^^^^^^^ The following necessary members are missing implementations: eq "### ); test_report!( opaque_ability_impl_duplicate, indoc!( r#" app "test" provides [A] to "./platform" MEq implements eq : a, a -> Bool where a implements MEq A := U8 implements [ MEq {eq: eqA, eq: eqA} ] eqA = \@A m, @A n -> m == n "# ), @r###" ── DUPLICATE IMPLEMENTATION ────────────────────────────── /code/proj/Main.roc ─ This ability member implementation is duplicate: 5│ A := U8 implements [ MEq {eq: eqA, eq: eqA} ] ^^^^^^^ The first implementation was defined here: 5│ A := U8 implements [ MEq {eq: eqA, eq: eqA} ] ^^^^^^^ Only one custom implementation can be defined for an ability member. "### ); test_report!( implements_type_not_ability, indoc!( r#" app "test" provides [A, Foo] to "./platform" Foo := {} A := U8 implements [ Foo {} ] "# ), @r###" ── NOT AN ABILITY ──────────────────────────────────────── /code/proj/Main.roc ─ This identifier is not an ability in scope: 5│ A := U8 implements [ Foo {} ] ^^^ Only abilities can be implemented. "### ); test_report!( derive_non_builtin_ability, indoc!( r#" app "test" provides [A] to "./platform" Ab implements ab : a -> a where a implements Ab A := {} implements [Ab] "# ), @r###" ── ILLEGAL DERIVE ──────────────────────────────────────── /code/proj/Main.roc ─ This ability cannot be derived: 5│ A := {} implements [Ab] ^^ Only builtin abilities can be derived. Note: The builtin abilities are `Encoding`, `Decoding`, `Hash`, `Eq`, `Inspect` "### ); test_report!( has_encoding_for_function, indoc!( r#" app "test" imports [] provides [A] to "./platform" A a := a -> a implements [Encode.Encoding] "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Encoding` ability for `A`: 3│ A a := a -> a implements [Encode.Encoding] ^^^^^^^^^^^^^^^ Note: `Encoding` cannot be generated for functions. Tip: You can define a custom implementation of `Encoding` for `A`. "### ); test_report!( has_encoding_for_non_encoding_alias, indoc!( r#" app "test" imports [] provides [A] to "./platform" A := B implements [Encode.Encoding] B := {} "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Encoding` ability for `A`: 3│ A := B implements [Encode.Encoding] ^^^^^^^^^^^^^^^ Tip: `B` does not implement `Encoding`. Consider adding a custom implementation or `implements Encode.Encoding` to the definition of `B`. Tip: You can define a custom implementation of `Encoding` for `A`. "### ); test_report!( has_encoding_for_other_has_encoding, indoc!( r#" app "test" imports [] provides [A] to "./platform" A := B implements [Encode.Encoding] B := {} implements [Encode.Encoding] "# ), @"" // no error ); test_report!( has_encoding_for_recursive_deriving, indoc!( r#" app "test" imports [] provides [MyNat] to "./platform" MyNat := [S MyNat, Z] implements [Encode.Encoding] "# ), @"" // no error ); test_report!( shadowing_top_level_scope, indoc!( r#" app "test" provides [ main ] to "./platform" main = 1 main = \n -> n + 2 "# ), @r###" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ The `main` name is first defined here: 3│ main = 1 ^^^^ But then it's defined a second time here: 5│ main = \n -> n + 2 ^^^^ Since these variables have the same name, it's easy to use the wrong one by accident. Give one of them a new name. ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 5│ main = \n -> n + 2 ^^^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! "### ); test_report!( issue_1755, indoc!( r#" Handle := {} await : Result a err, (a -> Result b err) -> Result b err open : {} -> Result Handle * close : Handle -> Result {} * withOpen : (Handle -> Result {} *) -> Result {} * withOpen = \callback -> handle <- await (open {}) {} <- await (callback handle) close handle withOpen "# ), @r#" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `withOpen` definition: 10│ withOpen : (Handle -> Result {} *) -> Result {} * 11│ withOpen = \callback -> 12│> handle <- await (open {}) 13│> {} <- await (callback handle) 14│> close handle The type annotation on `withOpen` says this `await` call should have the type: Result {} * However, the type of this `await` call is connected to another type in a way that isn't reflected in this annotation. Tip: Any connection between types must use a named type variable, not a `*`! Maybe the annotation on `withOpen` should have a named type variable in place of the `*`? "# ); test_report!( recursive_body_and_annotation_with_inference_disagree, indoc!( r#" f : _ -> (_ -> Str) f = \_ -> if Bool.true then {} else f {} f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression is used in an unexpected way: 5│ f = \_ -> if Bool.true then {} else f {} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It is a value of type: {} But you are trying to use it as: * -> Str "### ); test_report!( same_phantom_types_unify, indoc!( r#" F a b := b foo : F Str Str -> {} x : F Str Str foo x "# ), @r"" // okay ); test_report!( different_phantom_types, indoc!( r#" F a b := b foo : F Str Str -> {} x : F U8 Str foo x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `foo` has an unexpected type: 10│ foo x ^ This `x` value is a: F U8 Str But `foo` needs its 1st argument to be: F Str Str "### ); test_report!( #[ignore = "TODO This should be a type error"] phantom_type_bound_to_ability_not_implementing, indoc!( r#" app "test" provides [x] to "./platform" Foo implements foo : a -> a where a implements Foo F a b := b where a implements Foo MHash := {} x : F MHash {} "# ), @r###" "### ); test_report!( int_literals_cannot_fit_in_same_type, indoc!( r#" 0x80000000000000000000000000000000 == -0x80000000000000000000000000000000 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to == has an unexpected type: 4│ 0x80000000000000000000000000000000 == -0x80000000000000000000000000000000 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The argument is an integer of type: I128 But == needs its 2nd argument to be: U128 "### ); test_report!( num_literals_cannot_fit_in_same_type, indoc!( r#" 170141183460469231731687303715884105728 == -170141183460469231731687303715884105728 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to == has an unexpected type: 4│ 170141183460469231731687303715884105728 == -170141183460469231731687303715884105728 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The argument is a number of type: I128 or Dec But == needs its 2nd argument to be: U128 "### ); test_report!( recursive_alias_cannot_leak_into_recursive_opaque, indoc!( r#" OList := [Nil, Cons {} OList] AList : [Nil, Cons {} AList] alist : AList olist : OList olist = when alist is Nil -> @OList Nil Cons _ lst -> lst olist "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the 2nd branch of this `when` expression: 10│ olist : OList 11│ olist = 12│> when alist is 13│> Nil -> @OList Nil 14│> Cons _ lst -> lst This `lst` value is a: [ Cons {} ∞, Nil, ] as ∞ But the type annotation on `olist` says it should be: OList Tip: Type comparisons between an opaque type are only ever equal if both types are the same opaque type. Did you mean to create an opaque type by wrapping it? If I have an opaque type Age := U32 I can create an instance of this opaque type by doing @Age 23. "### ); test_report!( opaque_wrap_function_mismatch, indoc!( r#" A := U8 List.map [1u16, 2u16, 3u16] @A "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `map` has an unexpected type: 5│ List.map [1u16, 2u16, 3u16] @A ^^ This A opaque wrapping has the type: U8 -> A But `map` needs its 2nd argument to be: U16 -> A "### ); test_report!( symbols_not_bound_in_all_patterns, indoc!( r#" when A "" is A x | B y -> x "# ), @r###" ── NAME NOT BOUND IN ALL PATTERNS ──────────────────────── /code/proj/Main.roc ─ `x` is not bound in all patterns of this `when` branch 5│ A x | B y -> x ^ Identifiers introduced in a `when` branch must be bound in all patterns of the branch. Otherwise, the program would crash when it tries to use an identifier that wasn't bound! ── NAME NOT BOUND IN ALL PATTERNS ──────────────────────── /code/proj/Main.roc ─ `y` is not bound in all patterns of this `when` branch 5│ A x | B y -> x ^ Identifiers introduced in a `when` branch must be bound in all patterns of the branch. Otherwise, the program would crash when it tries to use an identifier that wasn't bound! ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `y` is not used in this `when` branch. 5│ A x | B y -> x ^ If you don't need to use `y`, prefix it with an underscore, like "_y", or replace it with just an "_". "### ); test_report!( flip_flop_catch_all_branches_not_exhaustive, indoc!( r#" \x -> when x is A B _ -> "" A _ C -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 4│> \x -> when x is 5│> A B _ -> "" 6│> A _ C -> "" Other possibilities include: A _ _ I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( forgot_to_remove_underscore, indoc!( r#" \_foo -> foo "# ), |golden| pretty_assertions::assert_eq!( golden, indoc!( r###"── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─ Nothing is named `foo` in this scope. 4│ \_foo -> foo ^^^ There is an ignored identifier of a similar name here: 4│ \_foo -> foo ^^^^ Did you mean to remove the leading underscore? If not, did you mean one of these? Box Bool U8 F64 "### ), ) ); test_report!( call_with_underscore_identifier, indoc!( r#" f = \x, y, z -> x + y + z f 1 _ 1 "# ), |golden| pretty_assertions::assert_eq!( golden, indoc!( r###"── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ An underscore is being used as a variable here: 6│ f 1 _ 1 ^ An underscore can be used to ignore a value when pattern matching, but it cannot be used as a variable. "### ), ) ); test_report!( call_with_declared_identifier_starting_with_underscore, indoc!( r#" f = \x, y, z -> x + y + z \a, _b -> f a _b 1 "# ), |golden| pretty_assertions::assert_eq!( golden, indoc!( r###"── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This variable's name starts with an underscore: 6│ \a, _b -> f a _b 1 ^^ But then it is used here: 6│ \a, _b -> f a _b 1 ^^ A variable's name can only start with an underscore if the variable is unused. Since you are using this variable, you could remove the underscore from its name in both places. "### ), ) ); test_report!( call_with_undeclared_identifier_starting_with_underscore, indoc!( r#" f = \x, y, z -> x + y + z \a, _b -> f a _r 1 "# ), |golden| pretty_assertions::assert_eq!( golden, indoc!( r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ This variable's name starts with an underscore: 6│ \a, _b -> f a _r 1 ^^ A variable's name can only start with an underscore if the variable is unused. But it looks like the variable is being used here! "### ), ) ); test_report!( underscore_in_middle_of_identifier, indoc!( r#" f = \x, y, z -> x + y + z \a, _b -> f a var_name 1 "# ), |golden| pretty_assertions::assert_eq!( golden, indoc!( r###" ── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─ Underscores are not allowed in identifier names: 6│ \a, _b -> f a var_name 1 ^^^^^^^^ I recommend using camelCase. It's the standard style in Roc code! "### ), ) ); // Record Builders test_report!( optional_field_in_record_builder, indoc!( r#" { a: <- apply "a", b, c ? "optional" } "# ), @r###" ── BAD RECORD BUILDER ────────── tmp/optional_field_in_record_builder/Test.roc ─ I am partway through parsing a record builder, and I found an optional field: 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ { 5│ a: <- apply "a", 6│ b, 7│ c ? "optional" ^^^^^^^^^^^^^^ Optional fields can only appear when you destructure a record. "### ); test_report!( record_update_builder, indoc!( r#" { rec & a: <- apply "a", b: 3 } "# ), @r###" ── BAD RECORD UPDATE ────────────────────── tmp/record_update_builder/Test.roc ─ I am partway through parsing a record update, and I found a record builder field: 1│ app "test" provides [main] to "./platform" 2│ 3│ main = 4│ { rec & 5│ a: <- apply "a", ^^^^^^^^^^^^^^^ Record builders cannot be updated like records. "### ); test_report!( multiple_record_builders, indoc!( r#" succeed { a: <- apply "a" } { b: <- apply "b" } "# ), @r###" ── MULTIPLE RECORD BUILDERS ────────────────────────────── /code/proj/Main.roc ─ This function is applied to multiple record builders: 4│> succeed 5│> { a: <- apply "a" } 6│> { b: <- apply "b" } Note: Functions can only take at most one record builder! Tip: You can combine them or apply them separately. "### ); test_report!( unapplied_record_builder, indoc!( r#" { a: <- apply "a" } "# ), @r###" ── UNAPPLIED RECORD BUILDER ────────────────────────────── /code/proj/Main.roc ─ This record builder was not applied to a function: 4│ { a: <- apply "a" } ^^^^^^^^^^^^^^^^^^^ However, we need a function to construct the record. Note: Functions must be applied directly. The pipe operator (|>) cannot be used. "### ); test_report!( record_builder_apply_non_function, indoc!( r#" succeed = \_ -> crash "" succeed { a: <- "a", } "# ), @r###" ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ This value is not a function, but it was given 1 argument: 7│ a: <- "a", ^^^ Tip: Remove `<-` to assign the field directly. "### ); // Skipping test because opaque types defined in the same module // do not fail with the special opaque type error // // test_report!( // record_builder_apply_opaque, // indoc!( // r#" // succeed = \_ -> crash "" // Decode := {} // get : Str -> Decode // get = \_ -> @Decode {} // succeed { // a: <- get "a", // # missing |> apply ^ // } // "# // ), // @r###" // ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ // This value is an opaque type, so it cannot be called with an argument: // 12│ a: <- get "a", // ^^^^^^^ // Hint: Did you mean to apply it to a function first? // "### // ); test_report!( destructure_assignment_introduces_no_variables_nested, indoc!( r#" Pair _ _ = Pair 0 1 _ = Pair 0 1 {} = {} Foo = Foo 0 "# ), @r###" ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 4│ Pair _ _ = Pair 0 1 ^^^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 6│ _ = Pair 0 1 ^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 8│ {} = {} ^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 10│ Foo = Foo ^^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! "### ); test_report!( destructure_assignment_introduces_no_variables_nested_toplevel, indoc!( r#" app "test" provides [] to "./platform" Pair _ _ = Pair 0 1 _ = Pair 0 1 {} = {} Foo = Foo "# ), @r###" ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 3│ Pair _ _ = Pair 0 1 ^^^^^^^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 5│ _ = Pair 0 1 ^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 7│ {} = {} ^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! ── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─ This destructure assignment doesn't introduce any new variables: 9│ Foo = Foo ^^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! "### ); test_report!( unused_shadow_specialization, indoc!( r#" app "test" provides [hash, Id] to "./platform" MHash implements hash : a -> U64 where a implements MHash Id := {} hash = \@Id _ -> 0 "# ), @r###" ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `hash` is not used anywhere in your code. 7│ hash = \@Id _ -> 0 ^^^^ If you didn't intend on using `hash` then remove it so future readers of your code don't wonder why it is there. "### ); test_report!( specialization_for_wrong_type, indoc!( r#" app "test" provides [hash, Id, Id2] to "./platform" MHash implements hash : a -> U64 where a implements MHash Id := {} implements [MHash {hash}] Id2 := {} hash = \@Id2 _ -> 0 "# ), @r###" ── WRONG SPECIALIZATION TYPE ───────────────────────────── /code/proj/Main.roc ─ This specialization of `hash` is not for the expected type: 8│ hash = \@Id2 _ -> 0 ^^^^ It was previously claimed to be a specialization for `Id`, but was determined to actually specialize `Id2`! "### ); test_report!( mismatched_record_annotation, indoc!( r#" x : { y : Str } x = {} x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `x` definition: 4│ x : { y : Str } 5│ x = {} ^^ The body is a record of type: {} But the type annotation on `x` says it should be: { y : Str } Tip: Looks like the y field is missing. "### ); test_report!( cyclic_opaque, indoc!( r#" Recursive := [Infinitely Recursive] 0 "# ), @r###" ── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─ The `Recursive` opaque is self-recursive in an invalid way: 4│ Recursive := [Infinitely Recursive] ^^^^^^^^^ Recursion in opaquees is only allowed if recursion happens behind a tagged union, at least one variant of which is not recursive. "### ); test_report!( derive_decoding_for_function, indoc!( r#" app "test" imports [] provides [A] to "./platform" A a := a -> a implements [Decode.Decoding] "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Decoding` ability for `A`: 3│ A a := a -> a implements [Decode.Decoding] ^^^^^^^^^^^^^^^ Note: `Decoding` cannot be generated for functions. Tip: You can define a custom implementation of `Decoding` for `A`. "### ); test_report!( derive_decoding_for_non_decoding_opaque, indoc!( r#" app "test" imports [] provides [A] to "./platform" A := B implements [Decode.Decoding] B := {} "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Decoding` ability for `A`: 3│ A := B implements [Decode.Decoding] ^^^^^^^^^^^^^^^ Tip: `B` does not implement `Decoding`. Consider adding a custom implementation or `implements Decode.Decoding` to the definition of `B`. Tip: You can define a custom implementation of `Decoding` for `A`. "### ); test_report!( derive_decoding_for_other_has_decoding, indoc!( r#" app "test" imports [] provides [A] to "./platform" A := B implements [Decode.Decoding] B := {} implements [Decode.Decoding] "# ), @"" // no error ); test_report!( derive_decoding_for_recursive_deriving, indoc!( r#" app "test" imports [] provides [MyNat] to "./platform" MyNat := [S MyNat, Z] implements [Decode.Decoding] "# ), @"" // no error ); test_report!( function_cannot_derive_encoding, indoc!( r#" app "test" imports [Decode.{decoder}] provides [main] to "./platform" main = myDecoder : Decoder (a -> a) fmt where fmt implements DecoderFormatting myDecoder = decoder myDecoder "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ myDecoder = decoder ^^^^^^^ I can't generate an implementation of the `Decoding` ability for a -> a Note: `Decoding` cannot be generated for functions. "### ); test_report!( nested_opaque_cannot_derive_encoding, indoc!( r#" app "test" imports [Decode.{decoder}] provides [main] to "./platform" A := {} main = myDecoder : Decoder {x : A} fmt where fmt implements DecoderFormatting myDecoder = decoder myDecoder "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 7│ myDecoder = decoder ^^^^^^^ I can't generate an implementation of the `Decoding` ability for { x : A } In particular, an implementation for A cannot be generated. Tip: `A` does not implement `Decoding`. Consider adding a custom implementation or `implements Decode.Decoding` to the definition of `A`. "### ); test_report!( anonymous_function_does_not_use_param, indoc!( r#" (\x -> 5) 1 "# ), @r###" ── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─ This function doesn't use `x`. 4│ (\x -> 5) 1 ^ If you don't need `x`, then you can just remove it. However, if you really do need `x` as an argument of this function, prefix it with an underscore, like this: "_`x`". Adding an underscore at the start of a variable name is a way of saying that the variable is not used. "### ); test_report!( expected_tag_has_too_many_args, indoc!( r#" app "test" provides [fromBytes] to "./platform" u8 : [Good (List U8), Bad [DecodeProblem]] fromBytes = when u8 is Good _ _ -> Ok "foo" Bad _ -> Ok "foo" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 6│> when u8 is 7│ Good _ _ -> 8│ Ok "foo" 9│ 10│ Bad _ -> 11│ Ok "foo" This `u8` value is a: [Good …, …] But the branch patterns have type: [Good … *, …] The branches must be cases of the `when` condition's type! "### ); test_report!( create_value_with_optional_record_field_type, indoc!( r#" f : {a: Str, b ? Str} f = {a: "b", b: ""} f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : {a: Str, b ? Str} 5│ f = {a: "b", b: ""} ^^^^^^^^^^^^^^^ The body is a record of type: { a : Str, b : Str, } But the type annotation on `f` says it should be: { a : Str, b ? Str, } Tip: To extract the `.b` field it must be non-optional, but the type says this field is optional. Learn more about optional fields at TODO. "### ); test_report!( create_value_with_conditionally_optional_record_field_type, indoc!( r#" f : {a: Str, b ? Str} f = if Bool.true then {a: ""} else {a: "b", b: ""} f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the `then` branch of this `if` expression: 4│ f : {a: Str, b ? Str} 5│ f = if Bool.true then {a: ""} else {a: "b", b: ""} ^^^^^^^ This branch is a record of type: { a : Str } But the type annotation on `f` says it should be: { a : Str, b ? Str, } Tip: Looks like the b field is missing. "### ); test_report!( unused_def_in_branch_pattern, indoc!( r#" when A "" is A foo -> "" "# ), @r###" ── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─ `foo` is not used in this `when` branch. 5│ A foo -> "" ^^^ If you don't need to use `foo`, prefix it with an underscore, like "_foo", or replace it with just an "_". "### ); test_report!( infer_decoded_record_error_with_function_field, indoc!( r#" app "test" imports [TotallyNotJson] provides [main] to "./platform" main = decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes TotallyNotJson.json when decoded is Ok rcd -> rcd.first rcd.second _ -> "something went wrong" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 6│ Ok rcd -> rcd.first rcd.second ^^^^^^^^^ I can't generate an implementation of the `Decoding` ability for * -> * Note: `Decoding` cannot be generated for functions. "### ); test_report!( record_with_optional_field_types_cannot_derive_decoding, indoc!( r#" app "test" imports [Decode.{decoder}] provides [main] to "./platform" main = myDecoder : Decoder {x : Str, y ? Str} fmt where fmt implements DecoderFormatting myDecoder = decoder myDecoder "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ myDecoder = decoder ^^^^^^^ I can't generate an implementation of the `Decoding` ability for { x : Str, y ? Str, } Note: I can't derive decoding for a record with an optional field, which in this case is `.y`. Optional record fields are polymorphic over records that may or may not contain them at compile time, but are not a concept that extends to runtime! Maybe you wanted to use a `Result`? "### ); test_report!( uninhabited_type_is_trivially_exhaustive, indoc!( r#" x : Result {} [] when x is Ok {} -> "" "# ), // no problem! @r###" "### ); test_report!( uninhabited_type_is_trivially_exhaustive_nested, indoc!( r#" x : Result (Result [A, B] []) [] when x is Ok (Ok A) -> "" Ok (Ok B) -> "" "# ), // no problem! @r###" "### ); test_report!( branch_patterns_missing_nested_case, indoc!( r#" x : Result (Result [A, B] {}) {} when x is Ok (Ok A) -> "" Ok (Err _) -> "" Err _ -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when x is 7│> Ok (Ok A) -> "" 8│> Ok (Err _) -> "" 9│> Err _ -> "" Other possibilities include: Ok (Ok B) I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( branch_patterns_missing_nested_case_with_trivially_exhausted_variant, indoc!( r#" x : Result (Result [A, B] []) [] when x is Ok (Ok A) -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when x is 7│> Ok (Ok A) -> "" Other possibilities include: Ok (Ok B) I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( uninhabited_err_branch_is_redundant_when_err_is_matched, indoc!( r#" x : Result {} [] when x is Ok {} -> "" Err _ -> "" "# ), @r###" ── UNMATCHABLE PATTERN ─────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern will never be matched: 6│ when x is 7│ Ok {} -> "" 8│ Err _ -> "" ^^^^^ It's impossible to create a value of this shape, so this pattern can be safely removed! "### ); test_report!( uninhabited_err_branch_is_redundant_when_err_is_matched_nested, indoc!( r#" x : Result (Result {} []) [] when x is Ok (Ok {}) -> "" Ok (Err _) -> "" Err _ -> "" "# ), @r###" ── UNMATCHABLE PATTERN ─────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern will never be matched: 6│ when x is 7│ Ok (Ok {}) -> "" 8│> Ok (Err _) -> "" 9│ Err _ -> "" It's impossible to create a value of this shape, so this pattern can be safely removed! ── UNMATCHABLE PATTERN ─────────────────────────────────── /code/proj/Main.roc ─ The 3rd pattern will never be matched: 6│ when x is 7│ Ok (Ok {}) -> "" 8│ Ok (Err _) -> "" 9│ Err _ -> "" ^^^^^ It's impossible to create a value of this shape, so this pattern can be safely removed! "### ); test_report!( custom_type_conflicts_with_builtin, indoc!( r#" Nat := [ S Nat, Z ] "" "# ), @r###" ── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─ This opaque type has the same name as a builtin: 4│ Nat := [ S Nat, Z ] ^^^^^^^^^^^^^^^^^^^ All builtin opaque types are in scope by default, so I need this opaque type to have a different name! "### ); test_report!( unused_value_import, indoc!( r#" app "test" imports [List.{ concat }] provides [main] to "./platform" main = "" "# ), @r###" ── UNUSED IMPORT ───────────────────────────────────────── /code/proj/Main.roc ─ `List.concat` is not used in this module. 1│ app "test" imports [List.{ concat }] provides [main] to "./platform" ^^^^^^ Since `List.concat` isn't used, you don't need to import it. "### ); test_report!( #[ignore = "https://github.com/roc-lang/roc/issues/4096"] unnecessary_builtin_module_import, indoc!( r#" app "test" imports [Str] provides [main] to "./platform" main = Str.concat "" "" "# ), @r###" "### ); test_report!( #[ignore = "https://github.com/roc-lang/roc/issues/4096"] unnecessary_builtin_type_import, indoc!( r#" app "test" imports [Decode.{ DecodeError }] provides [main, E] to "./platform" E : DecodeError main = "" "# ), @r###" "### ); test_report!( invalid_toplevel_cycle, indoc!( r#" app "test" imports [] provides [main] to "./platform" main = if Bool.true then {} else main "# ), @r###" ── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─ `main` is defined directly in terms of itself: 3│> main = 4│> if Bool.true then {} else main Roc evaluates values strictly, so running this program would enter an infinite loop! Hint: Did you mean to define `main` as a function? "### ); test_report!( bool_vs_true_tag, indoc!( r#" if True then "" else "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` condition needs to be a Bool: 4│ if True then "" else "" ^^^^ This `True` tag has the type: [True] But I need every `if` condition to evaluate to a Bool—either `Bool.true` or `Bool.false`. Tip: Did you mean to use `Bool.true` rather than `True`? "### ); test_report!( bool_vs_false_tag, indoc!( r#" if False then "" else "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This `if` condition needs to be a Bool: 4│ if False then "" else "" ^^^^^ This `False` tag has the type: [False] But I need every `if` condition to evaluate to a Bool—either `Bool.true` or `Bool.false`. Tip: Did you mean to use `Bool.false` rather than `False`? "### ); test_report!( derive_hash_for_function, indoc!( r#" app "test" provides [A] to "./platform" A a := a -> a implements [Hash] "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Hash` ability for `A`: 3│ A a := a -> a implements [Hash] ^^^^ Note: `Hash` cannot be generated for functions. Tip: You can define a custom implementation of `Hash` for `A`. "### ); test_report!( derive_hash_for_non_hash_opaque, indoc!( r#" app "test" provides [A] to "./platform" A := B implements [Hash] B := {} "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Hash` ability for `A`: 3│ A := B implements [Hash] ^^^^ Tip: `B` does not implement `Hash`. Consider adding a custom implementation or `implements Hash.Hash` to the definition of `B`. Tip: You can define a custom implementation of `Hash` for `A`. "### ); test_report!( derive_hash_for_other_has_hash, indoc!( r#" app "test" provides [A] to "./platform" A := B implements [Hash] B := {} implements [Hash] "# ), @"" // no error ); test_report!( derive_hash_for_recursive_deriving, indoc!( r#" app "test" provides [MyNat] to "./platform" MyNat := [S MyNat, Z] implements [Hash] "# ), @"" // no error ); test_report!( derive_hash_for_record, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Hash main = foo {a: "", b: 1} "# ), @"" // no error ); test_report!( derive_hash_for_tag, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Hash t : [A {}, B U8 U64, C Str] main = foo t "# ), @"" // no error ); test_report!( cannot_derive_hash_for_function, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Hash main = foo (\x -> x) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = foo (\x -> x) ^^^^^^^ I can't generate an implementation of the `Hash` ability for a -> a Note: `Hash` cannot be generated for functions. "### ); test_report!( cannot_derive_hash_for_structure_containing_function, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Hash main = foo (A (\x -> x) B) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = foo (A (\x -> x) B) ^^^^^^^^^^^^^ I can't generate an implementation of the `Hash` ability for [A (a -> a) [B]a] In particular, an implementation for a -> a cannot be generated. Note: `Hash` cannot be generated for functions. "### ); test_no_problem!( derive_hash_for_tuple, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Hash main = foo ("", 1) "# ) ); test_report!( cannot_hash_tuple_with_non_hash_element, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Hash main = foo ("", \{} -> {}) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = foo ("", \{} -> {}) ^^^^^^^^^^^^^^^ I can't generate an implementation of the `Hash` ability for ( Str, {}a -> {}, )a In particular, an implementation for {}a -> {} cannot be generated. Note: `Hash` cannot be generated for functions. "### ); test_report!( shift_by_negative, indoc!( r#" { a: Num.shiftLeftBy 1 -1, b: Num.shiftRightBy 1 -1, c: Num.shiftRightZfBy 1 -1, } "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `shiftRightZfBy` has an unexpected type: 7│ c: Num.shiftRightZfBy 1 -1, ^^ The argument is a number of type: I8, I16, F32, I32, F64, I64, I128, or Dec But `shiftRightZfBy` needs its 2nd argument to be: U8 ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `shiftRightBy` has an unexpected type: 6│ b: Num.shiftRightBy 1 -1, ^^ The argument is a number of type: I8, I16, F32, I32, F64, I64, I128, or Dec But `shiftRightBy` needs its 2nd argument to be: U8 ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `shiftLeftBy` has an unexpected type: 5│ a: Num.shiftLeftBy 1 -1, ^^ The argument is a number of type: I8, I16, F32, I32, F64, I64, I128, or Dec But `shiftLeftBy` needs its 2nd argument to be: U8 "### ); test_report!( big_char_does_not_fit_in_u8, indoc!( r#" digits : List U8 digits = List.range { start: At '0', end: At '9' } List.contains digits '☃' "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `contains` has an unexpected type: 7│ List.contains digits '☃' ^^^^^ The argument is a Unicode scalar value of type: U16, I32, U32, I64, Nat, U64, I128, or U128 But `contains` needs its 2nd argument to be: Int Unsigned8 "### ); test_report!( derive_eq_for_function, indoc!( r#" app "test" provides [A] to "./platform" A a := a -> a implements [Eq] "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Eq` ability for `A`: 3│ A a := a -> a implements [Eq] ^^ Note: `Eq` cannot be generated for functions. Tip: You can define a custom implementation of `Eq` for `A`. "### ); test_report!( big_char_does_not_fit_in_u8_pattern, indoc!( r#" x : U8 when x is '☃' -> "" _ -> "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 6│> when x is 7│ '☃' -> "" 8│ _ -> "" This `x` value is a: U8 But the branch patterns have type: U16, I32, U32, I64, Nat, U64, I128, or U128 The branches must be cases of the `when` condition's type! "### ); test_report!( derive_eq_for_f32, indoc!( r#" app "test" provides [A] to "./platform" A := F32 implements [Eq] "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Eq` ability for `A`: 3│ A := F32 implements [Eq] ^^ Note: I can't derive `Bool.isEq` for floating-point types. That's because Roc's floating-point numbers cannot be compared for total equality - in Roc, `NaN` is never comparable to `NaN`. If a type doesn't support total equality, it cannot support the `Eq` ability! Tip: You can define a custom implementation of `Eq` for `A`. "### ); test_report!( derive_eq_for_f64, indoc!( r#" app "test" provides [A] to "./platform" A := F64 implements [Eq] "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Eq` ability for `A`: 3│ A := F64 implements [Eq] ^^ Note: I can't derive `Bool.isEq` for floating-point types. That's because Roc's floating-point numbers cannot be compared for total equality - in Roc, `NaN` is never comparable to `NaN`. If a type doesn't support total equality, it cannot support the `Eq` ability! Tip: You can define a custom implementation of `Eq` for `A`. "### ); test_report!( derive_eq_for_non_eq_opaque, indoc!( r#" app "test" provides [A] to "./platform" A := B implements [Eq] B := {} "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Eq` ability for `A`: 3│ A := B implements [Eq] ^^ Tip: `B` does not implement `Eq`. Consider adding a custom implementation or `implements Bool.Eq` to the definition of `B`. Tip: You can define a custom implementation of `Eq` for `A`. "### ); test_report!( derive_eq_for_other_has_eq, indoc!( r#" app "test" provides [A] to "./platform" A := B implements [Eq] B := {} implements [Eq] "# ), @"" // no error ); test_report!( derive_eq_for_recursive_deriving, indoc!( r#" app "test" provides [MyNat] to "./platform" MyNat := [S MyNat, Z] implements [Eq] "# ), @"" // no error ); test_report!( derive_eq_for_record, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Eq main = foo {a: "", b: 1} "# ), @"" // no error ); test_report!( derive_eq_for_tag, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Eq t : [A {}, B U8 U64, C Str] main = foo t "# ), @"" // no error ); test_report!( cannot_derive_eq_for_function, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Eq main = foo (\x -> x) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = foo (\x -> x) ^^^^^^^ I can't generate an implementation of the `Eq` ability for a -> a Note: `Eq` cannot be generated for functions. "### ); test_report!( cannot_derive_eq_for_structure_containing_function, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Eq main = foo (A (\x -> x) B) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = foo (A (\x -> x) B) ^^^^^^^^^^^^^ I can't generate an implementation of the `Eq` ability for [A (a -> a) [B]a] In particular, an implementation for a -> a cannot be generated. Note: `Eq` cannot be generated for functions. "### ); test_report!( cannot_eq_functions, indoc!( r#" (\x -> x) == (\x -> x) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 4│ (\x -> x) == (\x -> x) ^^^^^^^ I can't generate an implementation of the `Eq` ability for a -> a Note: `Eq` cannot be generated for functions. "### ); test_report!( cannot_not_eq_functions, indoc!( r#" (\x -> x) == (\x -> x) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 4│ (\x -> x) == (\x -> x) ^^^^^^^ I can't generate an implementation of the `Eq` ability for a -> a Note: `Eq` cannot be generated for functions. "### ); test_no_problem!( derive_eq_for_tuple, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Eq main = foo ("", 1) "# ) ); test_report!( cannot_eq_tuple_with_non_eq_element, indoc!( r#" app "test" provides [main] to "./platform" foo : a -> {} where a implements Eq main = foo ("", 1.0f64) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = foo ("", 1.0f64) ^^^^^^^^^^^^ I can't generate an implementation of the `Eq` ability for ( Str, F64, )a In particular, an implementation for F64 cannot be generated. Note: I can't derive `Bool.isEq` for floating-point types. That's because Roc's floating-point numbers cannot be compared for total equality - in Roc, `NaN` is never comparable to `NaN`. If a type doesn't support total equality, it cannot support the `Eq` ability! "### ); test_report!( cannot_import_structural_eq_not_eq, indoc!( r#" { a: Bool.structuralEq, b: Bool.structuralNotEq, } "# ), @r###" ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ The Bool module does not expose `structuralEq`: 5│ a: Bool.structuralEq, ^^^^^^^^^^^^^^^^^ Did you mean one of these? Bool.true Bool.isNotEq Bool.false Bool.isEq ── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─ The Bool module does not expose `structuralNotEq`: 6│ b: Bool.structuralNotEq, ^^^^^^^^^^^^^^^^^^^^ Did you mean one of these? Bool.isNotEq Bool.true Bool.boolIsEq Bool.false "### ); test_report!( expand_ability_from_type_alias_mismatch, indoc!( r#" app "test" provides [f] to "./platform" F a : a where a implements Hash f : F ({} -> {}) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ f : F ({} -> {}) ^^^^^^^^ I can't generate an implementation of the `Hash` ability for {} -> {} Note: `Hash` cannot be generated for functions. "### ); test_report!( demanded_vs_optional_record_field, indoc!( r#" foo : { a : Str } -> Str foo = \{ a ? "" } -> a foo "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The 1st argument to `foo` is weird: 5│ foo = \{ a ? "" } -> a ^^^^^^^^^^ The argument is a pattern that matches record values of type: { a ? Str } But the annotation on `foo` says the 1st argument should be: { a : Str } "### ); test_report!( underivable_opaque_doesnt_error_for_derived_bodies, indoc!( r#" app "test" provides [main] to "./platform" F := U8 -> U8 implements [Hash, Eq, Encoding] main = "" "# ), @r###" ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Hash` ability for `F`: 3│ F := U8 -> U8 implements [Hash, Eq, Encoding] ^^^^ Note: `Hash` cannot be generated for functions. Tip: You can define a custom implementation of `Hash` for `F`. ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Eq` ability for `F`: 3│ F := U8 -> U8 implements [Hash, Eq, Encoding] ^^ Note: `Eq` cannot be generated for functions. Tip: You can define a custom implementation of `Eq` for `F`. ── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─ I can't derive an implementation of the `Encoding` ability for `F`: 3│ F := U8 -> U8 implements [Hash, Eq, Encoding] ^^^^^^^^ Note: `Encoding` cannot be generated for functions. Tip: You can define a custom implementation of `Encoding` for `F`. "### ); test_report!( duplicate_ability_in_has_clause, indoc!( r#" f : a -> {} where a implements Hash & Hash f "# ), @r###" ── DUPLICATE BOUND ABILITY ─────────────────────────────── /code/proj/Main.roc ─ I already saw that this type variable is bound to the `Hash` ability once before: 4│ f : a -> {} where a implements Hash & Hash ^^^^ Abilities only need to bound to a type variable once in an `implements` clause! "### ); test_report!( rigid_able_bounds_must_be_a_superset_of_flex_bounds, indoc!( r#" app "test" provides [main] to "./platform" g : x -> x where x implements Decoding & Encoding main : x -> x where x implements Encoding main = \x -> g x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `g` has an unexpected type: 6│ main = \x -> g x ^ This `x` value is a: x where x implements Encoding But `g` needs its 1st argument to be: x where x implements Encoding & Decoding Note: The type variable `x` says it can take on any value that implements only the ability `Encoding`. But, I see that it's also used as if it implements the ability `Decoding`. Can you use `x` without that ability? If not, consider adding it to the `implements` clause of `x`. "### ); test_report!( rigid_able_bounds_must_be_a_superset_of_flex_bounds_multiple, indoc!( r#" app "test" provides [main] to "./platform" g : x -> x where x implements Decoding & Encoding & Hash main : x -> x where x implements Encoding main = \x -> g x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `g` has an unexpected type: 6│ main = \x -> g x ^ This `x` value is a: x where x implements Encoding But `g` needs its 1st argument to be: x where x implements Hash & Encoding & Decoding Note: The type variable `x` says it can take on any value that implements only the ability `Encoding`. But, I see that it's also used as if it implements the abilities `Hash` and `Decoding`. Can you use `x` without those abilities? If not, consider adding them to the `implements` clause of `x`. "### ); test_report!( rigid_able_bounds_must_be_a_superset_of_flex_bounds_with_indirection, indoc!( r#" app "test" provides [main] to "./platform" f : x -> x where x implements Hash g : x -> x where x implements Decoding & Encoding main : x -> x where x implements Hash & Encoding main = \x -> g (f x) "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 1st argument to `g` has an unexpected type: 7│ main = \x -> g (f x) ^^^ This `f` call produces: x where x implements Hash & Encoding But `g` needs its 1st argument to be: x where x implements Encoding & Decoding Note: The type variable `x` says it can take on any value that implements only the abilities `Hash` and `Encoding`. But, I see that it's also used as if it implements the ability `Decoding`. Can you use `x` without that ability? If not, consider adding it to the `implements` clause of `x`. "### ); test_report!( list_pattern_not_terminated, indoc!( r#" when [] is [1, 2, -> "" "# ), @r###" ── UNFINISHED LIST PATTERN ────────── tmp/list_pattern_not_terminated/Test.roc ─ I am partway through parsing a list pattern, but I got stuck here: 5│ [1, 2, -> "" ^ I was expecting to see a closing square brace before this, so try adding a ] and see if that helps? "### ); test_report!( list_pattern_weird_rest_pattern, indoc!( r#" when [] is [...] -> "" "# ), @r###" ── INCORRECT REST PATTERN ─────── tmp/list_pattern_weird_rest_pattern/Test.roc ─ It looks like you may trying to write a list rest pattern, but it's not the form I expect: 5│ [...] -> "" ^ List rest patterns, which match zero or more elements in a list, are denoted with .. - is that what you meant? "### ); test_report!( unnecessary_extension_variable, indoc!( r#" f : {} -> [A, B]* f "# ), @r###" ── UNNECESSARY WILDCARD ────────────────────────────────── /code/proj/Main.roc ─ This type annotation has a wildcard type variable (`*`) that isn't needed. 4│ f : {} -> [A, B]* ^ Annotations for tag unions which are constants, or which are returned from functions, work the same way with or without a `*` at the end. (The `*` means something different when the tag union is an argument to a function, though!) You can safely remove this to make the code more concise without changing what it means. "### ); test_report!( multiple_list_patterns_start_and_end, indoc!( r#" when [] is [.., A, ..] -> "" "# ), @r###" ── MULTIPLE LIST REST PATTERNS ─────────────────────────── /code/proj/Main.roc ─ This list pattern match has multiple rest patterns: 5│ [.., A, ..] -> "" ^^ I only support compiling list patterns with one .. pattern! Can you remove this additional one? ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 4│> when [] is 5│> [.., A, ..] -> "" Other possibilities include: _ I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( multiple_list_patterns_in_a_row, indoc!( r#" when [] is [A, .., .., B] -> "" "# ), @r###" ── MULTIPLE LIST REST PATTERNS ─────────────────────────── /code/proj/Main.roc ─ This list pattern match has multiple rest patterns: 5│ [A, .., .., B] -> "" ^^ I only support compiling list patterns with one .. pattern! Can you remove this additional one? ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 4│> when [] is 5│> [A, .., .., B] -> "" Other possibilities include: _ I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( mismatch_within_list_pattern, indoc!( r#" when [] is [A, 1u8] -> "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This list element doesn't match the types of other elements in the pattern: 5│ [A, 1u8] -> "" ^^^ It matches integers: U8 But the other elements in this list pattern match [A] "### ); test_report!( mismatch_list_pattern_vs_condition, indoc!( r#" when [A, B] is ["foo", "bar"] -> "" "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ The branches of this `when` expression don't match the condition: 4│> when [A, B] is 5│ ["foo", "bar"] -> "" The `when` condition is a list of type: List [ A, B, ] But the branch patterns have type: List Str The branches must be cases of the `when` condition's type! "### ); test_report!( list_match_non_exhaustive_only_empty, indoc!( r#" l : List [A] when l is [] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [] -> "" Other possibilities include: [_, ..] I would have to crash if I saw one of those! Add branches for them! "### ); test_no_problem!( list_match_spread_exhaustive, indoc!( r#" l : List [A] when l is [..] -> "" "# ) ); test_report!( list_match_non_exhaustive_infinite, indoc!( r#" l : List [A] when l is [] -> "" [A] -> "" [A, A] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [] -> "" 8│> [A] -> "" 9│> [A, A] -> "" Other possibilities include: [_, _, _, ..] I would have to crash if I saw one of those! Add branches for them! "### ); test_no_problem!( list_match_spread_required_front_back, indoc!( r#" l : List [A, B] when l is [A, ..] -> "" [.., A] -> "" [..] -> "" "# ) ); test_report!( list_match_spread_redundant_front_back, indoc!( r#" l : List [A] when l is [A, ..] -> "" [.., A] -> "" [..] -> "" "# ), @r###" ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern is redundant: 6│ when l is 7│ [A, ..] -> "" 8│> [.., A] -> "" 9│ [..] -> "" Any value of this shape will be handled by a previous pattern, so this one should be removed. "### ); test_no_problem!( list_match_spread_as, indoc!( r#" l : List [A, B] when l is [A, .. as rest] | [.. as rest, A] -> rest [.. as rest] -> rest "# ) ); test_no_problem!( list_match_exhaustive_empty_and_rest_with_unary_head, indoc!( r#" l : List [A] when l is [] -> "" [_, ..] -> "" "# ) ); test_no_problem!( list_match_exhaustive_empty_and_rest_with_exhausted_head, indoc!( r#" l : List [A, B] when l is [] -> "" [A, ..] -> "" [B, ..] -> "" "# ) ); test_report!( list_match_exhaustive_empty_and_rest_with_nonexhaustive_head, indoc!( r#" l : List [A, B] when l is [] -> "" [A, ..] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [] -> "" 8│> [A, ..] -> "" Other possibilities include: [B, ..] I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( list_match_no_small_sizes_and_non_exhaustive_head, indoc!( r#" l : List [A, B] when l is [A, B, ..] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [A, B, ..] -> "" Other possibilities include: [] [_] [_, A, ..] I would have to crash if I saw one of those! Add branches for them! "### ); test_no_problem!( list_match_exhaustive_empty_and_rest_with_exhausted_tail, indoc!( r#" l : List [A, B] when l is [] -> "" [.., A] -> "" [.., B] -> "" "# ) ); test_report!( list_match_exhaustive_empty_and_rest_with_nonexhaustive_tail, indoc!( r#" l : List [A, B] when l is [] -> "" [.., A] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [] -> "" 8│> [.., A] -> "" Other possibilities include: [.., B] I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( list_match_no_small_sizes_and_non_exhaustive_tail, indoc!( r#" l : List [A, B] when l is [.., B, A] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [.., B, A] -> "" Other possibilities include: [] [_] [.., _, B] I would have to crash if I saw one of those! Add branches for them! "### ); test_no_problem!( list_match_exhaustive_empty_and_rest_with_exhausted_head_and_tail, indoc!( r#" l : List [A, B] when l is [] -> "" [A] -> "" [B] -> "" [A, .., A] -> "" [A, .., B] -> "" [B, .., A] -> "" [B, .., B] -> "" "# ) ); test_report!( list_match_exhaustive_empty_and_rest_with_nonexhaustive_head_and_tail, indoc!( r#" l : List [A, B] when l is [] -> "" [_] -> "" [A, .., B] -> "" [B, .., A] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [] -> "" 8│> [_] -> "" 9│> [A, .., B] -> "" 10│> [B, .., A] -> "" Other possibilities include: [_, .., _] I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( list_match_no_small_sizes_and_non_exhaustive_head_and_tail, indoc!( r#" l : List [A, B] when l is [A, .., B] -> "" [B, .., A] -> "" [B, .., B] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [A, .., B] -> "" 8│> [B, .., A] -> "" 9│> [B, .., B] -> "" Other possibilities include: [] [_] [A, .., A] I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( list_match_exhaustive_big_sizes_but_not_small_sizes, indoc!( r#" l : List [A] when l is [A, A, A, .., A, A, A] -> "" [A, A, A, .., A, A] -> "" [A, A, .., A, A] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [A, A, A, .., A, A, A] -> "" 8│> [A, A, A, .., A, A] -> "" 9│> [A, A, .., A, A] -> "" Other possibilities include: [] [_] [_, _] [_, _, _] I would have to crash if I saw one of those! Add branches for them! "### ); test_no_problem!( list_match_nested_list_exhaustive, indoc!( r#" l : List (List [A]) when l is [] -> "" [[]] -> "" [[A, ..]] -> "" [[..], .., [..]] -> "" "# ) ); test_report!( list_match_nested_list_not_exhaustive, indoc!( r#" l : List (List [A, B]) when l is [] -> "" [[]] -> "" [[A, ..]] -> "" [[..], .., [.., B]] -> "" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 6│> when l is 7│> [] -> "" 8│> [[]] -> "" 9│> [[A, ..]] -> "" 10│> [[..], .., [.., B]] -> "" Other possibilities include: [[B, ..]] [_, .., []] [_, .., [.., A]] I would have to crash if I saw one of those! Add branches for them! "### ); test_report!( list_match_redundant_exact_size, indoc!( r#" l : List [A] when l is [] -> "" [_] -> "" [_] -> "" [..] -> "" "# ), @r###" ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ The 3rd pattern is redundant: 6│ when l is 7│ [] -> "" 8│ [_] -> "" 9│> [_] -> "" 10│ [..] -> "" Any value of this shape will be handled by a previous pattern, so this one should be removed. "### ); test_report!( list_match_redundant_any_slice, indoc!( r#" l : List [A] when l is [] -> "" [_, ..] -> "" [..] -> "" "# ), @r###" ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ The 3rd pattern is redundant: 6│ when l is 7│ [] -> "" 8│ [_, ..] -> "" 9│ [..] -> "" ^^^^ Any value of this shape will be handled by a previous pattern, so this one should be removed. "### ); test_report!( list_match_redundant_suffix_slice_with_sized_prefix, indoc!( r#" l : List [A] when l is [] -> "" [_, ..] -> "" [.., _] -> "" "# ), @r###" ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ The 3rd pattern is redundant: 6│ when l is 7│ [] -> "" 8│ [_, ..] -> "" 9│ [.., _] -> "" ^^^^^^^ Any value of this shape will be handled by a previous pattern, so this one should be removed. "### ); test_report!( list_match_redundant_based_on_ctors, indoc!( r#" l : List {} when l is [{}, .., _] -> "" [_, .., {}] -> "" [..] -> "" "# ), @r###" ── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─ The 2nd pattern is redundant: 6│ when l is 7│ [{}, .., _] -> "" 8│> [_, .., {}] -> "" 9│ [..] -> "" Any value of this shape will be handled by a previous pattern, so this one should be removed. "### ); test_no_problem!( list_match_with_guard, indoc!( r#" l : List [A] when l is [ A, .. ] if Bool.true -> "" [ A, .. ] -> "" _ -> "" "# ) ); test_report!( suggest_binding_rigid_var_to_ability, indoc!( r#" app "test" provides [f] to "./p" f : List e -> List e f = \l -> if l == l then l else l "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 4│ f = \l -> if l == l then l else l ^ I can't generate an implementation of the `Eq` ability for List e In particular, an implementation for e cannot be generated. Tip: This type variable is not bound to `Eq`. Consider adding an `implements` clause to bind the type variable, like `where e implements Bool.Eq` "### ); test_report!( crash_given_non_string, indoc!( r#" crash {} "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This value passed to `crash` is not a string: 4│ crash {} ^^ The value is a record of type: {} But I can only `crash` with messages of type Str "### ); test_report!( crash_unapplied, indoc!( r#" crash "# ), @r###" ── UNAPPLIED CRASH ─────────────────────────────────────── /code/proj/Main.roc ─ This `crash` doesn't have a message given to it: 4│ crash ^^^^^ `crash` must be passed a message to crash with at the exact place it's used. `crash` can't be used as a value that's passed around, like functions can be - it must be applied immediately! "### ); test_report!( crash_overapplied, indoc!( r#" crash "" "" "# ), @r###" ── OVERAPPLIED CRASH ───────────────────────────────────── /code/proj/Main.roc ─ This `crash` has too many values given to it: 4│ crash "" "" ^^^^^ `crash` must be given exacly one message to crash with. "### ); test_no_problem!( resolve_eq_for_unbound_num, indoc!( r#" app "test" provides [main] to "./platform" n : Num * main = n == 1 "# ) ); test_report!( resolve_eq_for_unbound_num_float, indoc!( r#" app "test" provides [main] to "./platform" n : Num * main = n == 1f64 "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = n == 1f64 ^^^^ I can't generate an implementation of the `Eq` ability for FloatingPoint ? Note: I can't derive `Bool.isEq` for floating-point types. That's because Roc's floating-point numbers cannot be compared for total equality - in Roc, `NaN` is never comparable to `NaN`. If a type doesn't support total equality, it cannot support the `Eq` ability! "### ); test_no_problem!( resolve_hash_for_unbound_num, indoc!( r#" app "test" provides [main] to "./platform" n : Num * main = \hasher -> Hash.hash hasher n "# ) ); test_report!( self_recursive_not_reached, indoc!( r#" app "test" provides [f] to "./platform" f = h {} h = \{} -> 1 g = \{} -> if Bool.true then "" else g {} "# ), @r###" ── DEFINITION ONLY USED IN RECURSION ───────────────────── /code/proj/Main.roc ─ This definition is only used in recursion with itself: 4│ g = \{} -> if Bool.true then "" else g {} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you don't intend to use or export this definition, it should be removed! "### ); test_no_problem!( self_recursive_not_reached_but_exposed, indoc!( r#" app "test" provides [g] to "./platform" g = \{} -> if Bool.true then "" else g {} "# ) ); test_report!( mutual_recursion_not_reached, indoc!( r#" app "test" provides [h] to "./platform" h = "" f = \{} -> if Bool.true then "" else g {} g = \{} -> if Bool.true then "" else f {} "# ), @r###" ── DEFINITIONs ONLY USED IN RECURSION ──────────────────── /code/proj/Main.roc ─ These 2 definitions are only used in mutual recursion with themselves: 3│> f = \{} -> if Bool.true then "" else g {} 4│> g = \{} -> if Bool.true then "" else f {} If you don't intend to use or export any of them, they should all be removed! "### ); test_report!( mutual_recursion_not_reached_but_exposed, indoc!( r#" app "test" provides [f] to "./platform" f = \{} -> if Bool.true then "" else g {} g = \{} -> if Bool.true then "" else f {} "# ), @r###" "### ); test_report!( self_recursive_not_reached_nested, indoc!( r#" app "test" provides [main] to "./platform" main = g = \{} -> if Bool.true then "" else g {} "" "# ), @r###" ── DEFINITION ONLY USED IN RECURSION ───────────────────── /code/proj/Main.roc ─ This definition is only used in recursion with itself: 3│ g = \{} -> if Bool.true then "" else g {} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you don't intend to use or export this definition, it should be removed! "### ); test_no_problem!( self_recursive_not_reached_but_exposed_nested, indoc!( r#" app "test" provides [main] to "./platform" main = g = \{} -> if Bool.true then "" else g {} g "# ) ); test_report!( mutual_recursion_not_reached_nested, indoc!( r#" app "test" provides [main] to "./platform" main = f = \{} -> if Bool.true then "" else g {} g = \{} -> if Bool.true then "" else f {} "" "# ), @r###" ── DEFINITIONs ONLY USED IN RECURSION ──────────────────── /code/proj/Main.roc ─ These 2 definitions are only used in mutual recursion with themselves: 3│> f = \{} -> if Bool.true then "" else g {} 4│> g = \{} -> if Bool.true then "" else f {} If you don't intend to use or export any of them, they should all be removed! "### ); test_report!( mutual_recursion_not_reached_but_exposed_nested, indoc!( r#" app "test" provides [main] to "./platform" main = f = \{} -> if Bool.true then "" else g {} g = \{} -> if Bool.true then "" else f {} f "# ), @r###" "### ); // TODO(weakening-reports) test_report!( concat_different_types, indoc!( r#" empty = [] one = List.concat [1] empty str = List.concat ["blah"] empty {one, str} "#), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This 2nd argument to `concat` has an unexpected type: 6│ str = List.concat ["blah"] empty ^^^^^ This `empty` value is a: List (Num *) But `concat` needs its 2nd argument to be: List Str "### ); test_report!( implicit_inferred_open_in_output_position_cannot_grow, indoc!( r#" app "test" provides [main] to "./platform" main : {} -> [One] main = \{} -> if Bool.true then One else Two "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the `else` branch of this `if` expression: 3│ main : {} -> [One] 4│ main = \{} -> 5│ if Bool.true 6│ then One 7│ else Two ^^^ This `Two` tag has the type: [Two] But the type annotation on `main` says it should be: [One] "### ); test_report!( implicit_inferred_open_in_output_position_cannot_grow_alias, indoc!( r#" app "test" provides [main] to "./platform" R : [One] main : {} -> R main = \{} -> if Bool.true then One else Two "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the `else` branch of this `if` expression: 5│ main : {} -> R 6│ main = \{} -> 7│ if Bool.true 8│ then One 9│ else Two ^^^ This `Two` tag has the type: [Two] But the type annotation on `main` says it should be: [One] "### ); test_report!( implicit_inferred_open_in_output_position_cannot_grow_nested, indoc!( r#" app "test" provides [main] to "./platform" main : List [One, Two] -> List [One] main = \tags -> List.map tags \tag -> when tag is One -> One Two -> Two "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `main` definition: 3│ main : List [One, Two] -> List [One] 4│ main = \tags -> 5│> List.map tags \tag -> 6│> when tag is 7│> One -> One 8│> Two -> Two This `map` call produces: List [Two, …] But the type annotation on `main` says it should be: List […] "### ); test_report!( implicit_inferred_open_in_output_position_cannot_grow_nested_alias, indoc!( r#" app "test" provides [main] to "./platform" R : [One] main : List [One, Two] -> List R main = \tags -> List.map tags \tag -> when tag is One -> One Two -> Two "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `main` definition: 5│ main : List [One, Two] -> List R 6│ main = \tags -> 7│> List.map tags \tag -> 8│> when tag is 9│> One -> One 10│> Two -> Two This `map` call produces: List [Two, …] But the type annotation on `main` says it should be: List […] "### ); test_no_problem!( explicit_inferred_open_in_output_position_can_grow, indoc!( r#" app "test" provides [main] to "./platform" main : List [One, Two] -> List [One]_ main = \tags -> List.map tags \tag -> when tag is One -> One Two -> Two "# ) ); test_report!( derive_decoding_for_nat, indoc!( r#" app "test" imports [Decode.{decoder}] provides [main] to "./platform" main = myDecoder : Decoder Nat fmt where fmt implements DecoderFormatting myDecoder = decoder myDecoder "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ myDecoder = decoder ^^^^^^^ I can't generate an implementation of the `Decoding` ability for Nat Note: Decoding to a Nat is not supported. Consider decoding to a fixed-sized unsigned integer, like U64, then converting to a Nat if needed. "### ); test_report!( derive_encoding_for_nat, indoc!( r#" app "test" imports [] provides [main] to "./platform" x : Nat main = Encode.toEncoder x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = Encode.toEncoder x ^ I can't generate an implementation of the `Encoding` ability for Int Natural In particular, an implementation for Natural cannot be generated. Tip: `Natural` does not implement `Encoding`. "### ); test_no_problem!( derive_decoding_for_tuple, indoc!( r#" app "test" imports [Decode.{decoder}] provides [main] to "./platform" main = myDecoder : Decoder (U32, Str) fmt where fmt implements DecoderFormatting myDecoder = decoder myDecoder "# ) ); test_report!( cannot_decode_tuple_with_non_decode_element, indoc!( r#" app "test" imports [Decode.{decoder}] provides [main] to "./platform" main = myDecoder : Decoder (U32, {} -> {}) fmt where fmt implements DecoderFormatting myDecoder = decoder myDecoder "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ myDecoder = decoder ^^^^^^^ I can't generate an implementation of the `Decoding` ability for U32, {} -> {} Note: `Decoding` cannot be generated for functions. "### ); test_no_problem!( derive_encoding_for_tuple, indoc!( r#" app "test" imports [] provides [main] to "./platform" x : (U32, Str) main = Encode.toEncoder x "# ) ); test_report!( cannot_encode_tuple_with_non_encode_element, indoc!( r#" app "test" imports [] provides [main] to "./platform" x : (U32, {} -> {}) main = Encode.toEncoder x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ This expression has a type that does not implement the abilities it's expected to: 5│ main = Encode.toEncoder x ^ I can't generate an implementation of the `Encoding` ability for U32, {} -> {} Note: `Encoding` cannot be generated for functions. "### ); test_report!( exhaustiveness_check_function_or_tag_union_issue_4994, indoc!( r#" app "test" provides [main] to "./platform" x : U8 ifThenCase = when x is 0 -> Red 1 -> Yellow 2 -> Purple 3 -> Zulip _ -> Green main = when ifThenCase is Red -> "red" Green -> "green" Yellow -> "yellow" Zulip -> "zulip" "# ), @r###" ── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─ This `when` does not cover all the possibilities: 14│> when ifThenCase is 15│> Red -> "red" 16│> Green -> "green" 17│> Yellow -> "yellow" 18│> Zulip -> "zulip" Other possibilities include: Purple _ I would have to crash if I saw one of those! Add branches for them! "### ); test_no_problem!( openness_constraint_opens_under_tuple, indoc!( r#" x : [A, B, C] when (x, 1u8) is (A, _) -> Bool.true (B, _) -> Bool.true _ -> Bool.true "# ) ); test_report!( apply_opaque_as_function, indoc!( r#" app "test" provides [main] to "./platform" Parser a := Str -> a parser : Parser Str parser = @Parser \s -> Str.concat s "asd" main : Str main = parser "hi" "# ), @r###" ── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─ The `parser` value is an opaque type, so it cannot be called with an argument: 9│ main = parser "hi" ^^^^^^ I can't call an opaque type because I don't know what it is! Maybe you meant to unwrap it first? "### ); test_report!( function_arity_mismatch_too_few, indoc!( r#" app "test" provides [f] to "./platform" f : U8, U8 -> U8 f = \x -> x "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 3│ f : U8, U8 -> U8 4│ f = \x -> x ^^^^^^^ The body is an anonymous function of type: (U8 -> U8) But the type annotation on `f` says it should be: (U8, U8 -> U8) Tip: It looks like it takes too few arguments. I was expecting 1 more. "### ); test_report!( function_arity_mismatch_too_many, indoc!( r#" app "test" provides [f] to "./platform" f : U8, U8 -> U8 f = \x, y, z -> x + y + z "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 3│ f : U8, U8 -> U8 4│ f = \x, y, z -> x + y + z ^^^^^^^^^^^^^^^^^^^^^ The body is an anonymous function of type: (U8, U8, Int Unsigned8 -> U8) But the type annotation on `f` says it should be: (U8, U8 -> U8) Tip: It looks like it takes too many arguments. I'm seeing 1 extra. "### ); test_report!( function_arity_mismatch_nested_too_few, indoc!( r#" app "test" provides [main] to "./platform" main = f : U8, U8 -> U8 f = \x -> x f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : U8, U8 -> U8 5│ f = \x -> x ^^^^^^^ The body is an anonymous function of type: (U8 -> U8) But the type annotation on `f` says it should be: (U8, U8 -> U8) Tip: It looks like it takes too few arguments. I was expecting 1 more. "### ); test_report!( function_arity_mismatch_nested_too_many, indoc!( r#" app "test" provides [main] to "./platform" main = f : U8, U8 -> U8 f = \x, y, z -> x + y + z f "# ), @r###" ── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─ Something is off with the body of the `f` definition: 4│ f : U8, U8 -> U8 5│ f = \x, y, z -> x + y + z ^^^^^^^^^^^^^^^^^^^^^ The body is an anonymous function of type: (U8, U8, Int Unsigned8 -> U8) But the type annotation on `f` says it should be: (U8, U8 -> U8) Tip: It looks like it takes too many arguments. I'm seeing 1 extra. "### ); test_report!( pizza_parens_right, indoc!( r#" 2 |> (Num.sub 3) "# ), @r###" ── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─ The `sub` function expects 2 arguments, but it got only 1: 4│ 2 |> (Num.sub 3) ^^^^^^^ Roc does not allow functions to be partially applied. Use a closure to make partial application explicit. "### ); test_report!( pizza_parens_middle, indoc!( r#" 2 |> (Num.sub 3) |> Num.sub 3 "# ), @r###" ── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─ The `sub` function expects 2 arguments, but it got only 1: 4│ 2 |> (Num.sub 3) |> Num.sub 3 ^^^^^^^ Roc does not allow functions to be partially applied. Use a closure to make partial application explicit. "### ); }