mirror of
				https://github.com/roc-lang/roc.git
				synced 2025-10-31 05:04:32 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			15936 lines
		
	
	
	
		
			426 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			15936 lines
		
	
	
	
		
			426 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| #[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::header::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_test_utils_dir::TmpDir;
 | ||
|     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<LoadedModule, LoadingProblem<'a>>) {
 | ||
|         use std::fs::File;
 | ||
|         use std::io::Write;
 | ||
| 
 | ||
|         let module_src = if src.starts_with("app") || src.starts_with("module") {
 | ||
|             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 = 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: roc_target::Target::LinuxX64,
 | ||
|                 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,
 | ||
|                 None,
 | ||
|                 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<TypeError>,
 | ||
|             Vec<roc_problem::can::Problem>,
 | ||
|             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<F>(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<TypeError>,
 | ||
|             Vec<roc_problem::can::Problem>,
 | ||
|             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<F>(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<F>(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::header::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, "<red>")
 | ||
|             .replace(ANSI_STYLE_CODES.white, "<white>")
 | ||
|             .replace(ANSI_STYLE_CODES.yellow, "<yellow>")
 | ||
|             .replace(ANSI_STYLE_CODES.green, "<green>")
 | ||
|             .replace(ANSI_STYLE_CODES.cyan, "<cyan>")
 | ||
|             .replace(ANSI_STYLE_CODES.reset, "<reset>")
 | ||
|             .replace(ANSI_STYLE_CODES.bold, "<bold>")
 | ||
|             .replace(ANSI_STYLE_CODES.underline, "<underline>")
 | ||
|     }
 | ||
| 
 | ||
|     test_report!(
 | ||
|         value_not_exposed,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             List.isempty 1 2
 | ||
|         "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── NOT EXPOSED in /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 in /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 in /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 in /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 in /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 in /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 in /code/proj/Main.roc ────────────────────────────────────
 | ||
| 
 | ||
|     Nothing is named `true` in this scope.
 | ||
| 
 | ||
|     4│      if true then 1 else 2
 | ||
|                ^^^^
 | ||
| 
 | ||
|     Did you mean one of these?
 | ||
| 
 | ||
|         Str
 | ||
|         Frac
 | ||
|         Num
 | ||
|         U8
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         report_precedence_problem_multiline,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             if
 | ||
|                 1
 | ||
|                     == 2
 | ||
|                     == 3
 | ||
|             then
 | ||
|                 2
 | ||
| 
 | ||
|             else
 | ||
|                 3
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── SYNTAX PROBLEM in /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 in /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 in /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), "<cyan>activityIndicatorLarge<reset>");
 | ||
|     }
 | ||
| 
 | ||
|     #[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), "<green>Util.Int<reset>");
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn report_region_in_color() {
 | ||
|         color_report_problem_as(
 | ||
|             indoc!(
 | ||
|                 r"
 | ||
|                     isDisabled = \user -> user.isAdmin
 | ||
| 
 | ||
|                     theAdmin
 | ||
|                         |> isDisabled
 | ||
|                 "
 | ||
|             ),
 | ||
|             indoc!(
 | ||
|                 r"
 | ||
|                 <cyan>── UNRECOGNIZED NAME in /code/proj/Main.roc ────────────────────────────────────<reset>
 | ||
| 
 | ||
|                 Nothing is named `theAdmin` in this scope.
 | ||
| 
 | ||
|                 <cyan>3<reset><cyan>│<reset>  <white>theAdmin<reset>
 | ||
|                     <red>^^^^^^^^<reset>
 | ||
| 
 | ||
|                 Did you mean one of these?
 | ||
| 
 | ||
|                     List
 | ||
|                     Box
 | ||
|                     Str
 | ||
|                     isDisabled
 | ||
|                 "
 | ||
|             ),
 | ||
|         );
 | ||
|     }
 | ||
| 
 | ||
|     test_report!(
 | ||
|         if_condition_not_bool,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             if "foo" then 2 else 3
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This returns something that's incompatible with the return type of the
 | ||
|     enclosing function:
 | ||
| 
 | ||
|     5│      f = \x -> g x
 | ||
|                       ^^^
 | ||
| 
 | ||
|     This `g` call produces:
 | ||
| 
 | ||
|         List List a
 | ||
| 
 | ||
|     But I expected the function to have return type:
 | ||
| 
 | ||
|         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 in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This returns something that's incompatible with the return type of the
 | ||
|     enclosing function:
 | ||
| 
 | ||
|     7│      g = \x -> f [x]
 | ||
|                       ^^^^^
 | ||
| 
 | ||
|     This `f` call produces:
 | ||
| 
 | ||
|         List List b
 | ||
| 
 | ||
|     But I expected the function to have return type:
 | ||
| 
 | ||
|         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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /code/proj/Main.roc ────────────────────────────────────
 | ||
| 
 | ||
|     Nothing is named `ok` in this scope.
 | ||
| 
 | ||
|     5│      f = \_ -> ok 4
 | ||
|                       ^^
 | ||
| 
 | ||
|     Did you mean one of these?
 | ||
| 
 | ||
|         U8
 | ||
|         Box
 | ||
|         Eq
 | ||
|         f
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     // 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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in 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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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
 | ||
|         Task
 | ||
|         List
 | ||
|         Dict
 | ||
| 
 | ||
|     ── SYNTAX PROBLEM in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in 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
 | ||
|     https://www.roc-lang.org/tutorial#operator-desugaring-table 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###"
 | ||
|     ── STATEMENT AFTER EXPRESSION in /code/proj/Main.roc ───────────────────────────
 | ||
| 
 | ||
|     I just finished parsing an expression with a series of definitions,
 | ||
| 
 | ||
|     and this line is indented as if it's intended to be part of that
 | ||
|     expression:
 | ||
| 
 | ||
|     6│          x == 5
 | ||
|                 ^^^^^^
 | ||
| 
 | ||
|     However, I already saw the final expression in that series of
 | ||
|     definitions.
 | ||
| 
 | ||
|     Tip: An expression like `4`, `"hello"`, or `functionCall MyThing` is
 | ||
|     like `return 4` in other programming languages. To me, it seems like
 | ||
|     you did `return 4` followed by more code in the lines after, that code
 | ||
|     would never be executed!
 | ||
| 
 | ||
|     Tip: If you are working with `Task`, this error can happen if you
 | ||
|     forgot a `!` somewhere.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         tag_union_open,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             f : [
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── UNFINISHED TAG UNION TYPE in 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 in 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 in 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 in 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 in 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 in 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 in 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 in 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 in 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 in tmp/record_type_tab/Test.roc ───────────────────────────────
 | ||
| 
 | ||
|     I encountered a tab character:
 | ||
| 
 | ||
|     4│      f : { foo 	 }
 | ||
|                       ^
 | ||
| 
 | ||
|     Tab characters are not allowed in Roc code. Please use spaces instead!
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         comment_with_tab,
 | ||
|         "# comment with a \t char\n4",
 | ||
|         @r###"
 | ||
|     ── TAB CHARACTER in tmp/comment_with_tab/Test.roc ──────────────────────────────
 | ||
| 
 | ||
|     I encountered a tab character:
 | ||
| 
 | ||
|     4│      # comment with a 	 char
 | ||
|                              ^
 | ||
| 
 | ||
|     Tab characters are not allowed in Roc code. Please use spaces instead!
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         comment_with_control_character,
 | ||
|         "# comment with a \x07 char\n",
 | ||
|         @r###"
 | ||
|     ── ASCII CONTROL CHARACTER in tmp/comment_with_control_character/Test.roc ──────
 | ||
| 
 | ||
|     I encountered an ASCII control character:
 | ||
| 
 | ||
|     4│      # comment with a  char
 | ||
|                              ^
 | ||
| 
 | ||
|     ASCII control characters are not allowed.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         record_type_carriage_return,
 | ||
|         "f : { \r foo }",
 | ||
|         @r"
 | ||
|     ── MISPLACED CARRIAGE RETURN in 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 in 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 in 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 in /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 in /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 in 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 in /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 in /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.
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     // TODO investigate this test. It was disabled in https://github.com/roc-lang/roc/pull/6634
 | ||
|     // as the way Defs without final expressions are handled. The changes probably shouldn't have
 | ||
|     // changed this error report. The exact same test_syntax test for this has not changed, so
 | ||
|     // we know the parser is parsing the same thing. Therefore the way the AST is desugared must be
 | ||
|     // the cause of the change in error report.
 | ||
|     // test_report!(
 | ||
|     //     def_missing_final_expression,
 | ||
|     //     indoc!(
 | ||
|     //         r"
 | ||
|     //         f : Foo.foo
 | ||
|     //         "
 | ||
|     //     ),
 | ||
|     //     @r#"
 | ||
|     // ── MISSING FINAL EXPRESSION in 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 in 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 in 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 in 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 in 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 in 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
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unfinished_import,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import [
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNFINISHED IMPORT in tmp/unfinished_import/Test.roc ─────────────────────────
 | ||
| 
 | ||
|     I was partway through parsing an `import`, but I got stuck here:
 | ||
| 
 | ||
|     4│      import [
 | ||
|                    ^
 | ||
| 
 | ||
|     I was expecting to see a module name, like:
 | ||
| 
 | ||
|         import BigNum
 | ||
| 
 | ||
|     Or a package module name, like:
 | ||
| 
 | ||
|         import pf.Stdout
 | ||
| 
 | ||
|     Or a file path to ingest, like:
 | ||
| 
 | ||
|         import "users.json" as users : Str
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         weird_import_params_record,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import Menu { x = 4 }
 | ||
|             "
 | ||
|         ),@r###"
 | ||
|     ── RECORD PARSE PROBLEM in tmp/weird_import_params_record/Test.roc ─────────────
 | ||
| 
 | ||
|     I am partway through parsing a record, but I got stuck here:
 | ||
| 
 | ||
|     4│      import Menu { x = 4 }
 | ||
|                         ^
 | ||
| 
 | ||
|     TODO provide more context.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         record_update_in_module_params,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import Menu { myParams & echo: echoFn }
 | ||
|             "
 | ||
|         ),@r###"
 | ||
|     ── RECORD UPDATE IN MODULE PARAMS in ...ecord_update_in_module_params/Test.roc ─
 | ||
| 
 | ||
|     I was partway through parsing module params, but I got stuck here:
 | ||
| 
 | ||
|     4│      import Menu { myParams & echo: echoFn }
 | ||
|                           ^^^^^^^^
 | ||
| 
 | ||
|     It looks like you're trying to update a record, but module params
 | ||
|     require a standalone record literal.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unfinished_import_as_or_exposing,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import svg.Path a
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNFINISHED IMPORT in tmp/unfinished_import_as_or_exposing/Test.roc ──────────
 | ||
| 
 | ||
|     I was partway through parsing an `import`, but I got stuck here:
 | ||
| 
 | ||
|     4│      import svg.Path a
 | ||
|                            ^
 | ||
| 
 | ||
|     I was expecting to see the `as` keyword next, like:
 | ||
| 
 | ||
|         import svg.Path as SvgPath
 | ||
| 
 | ||
|     Or the `exposing` keyword, like:
 | ||
| 
 | ||
|         import svg.Path exposing [arc, rx]
 | ||
| 
 | ||
|     Or module params, like:
 | ||
| 
 | ||
|         import Menu { echo, read }
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unfinished_import_alias,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import svg.Path as
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNFINISHED IMPORT in tmp/unfinished_import_alias/Test.roc ───────────────────
 | ||
| 
 | ||
|     I was partway through parsing an `import`, but I got stuck here:
 | ||
| 
 | ||
|     4│      import svg.Path as
 | ||
|                               ^
 | ||
| 
 | ||
|     I just saw the `as` keyword, so I was expecting to see an alias next.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         lowercase_import_alias,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import svg.Path as path
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── LOWERCASE ALIAS in tmp/lowercase_import_alias/Test.roc ──────────────────────
 | ||
| 
 | ||
|     This import is using a lowercase alias:
 | ||
| 
 | ||
|     4│      import svg.Path as path
 | ||
|                                ^^^^
 | ||
| 
 | ||
|     Module names and aliases must start with an uppercase letter.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unfinished_import_exposing,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import svg.Path exposing
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNFINISHED IMPORT in tmp/unfinished_import_exposing/Test.roc ────────────────
 | ||
| 
 | ||
|     I was partway through parsing an `import`, but I got stuck here:
 | ||
| 
 | ||
|     4│      import svg.Path exposing
 | ||
|                                     ^
 | ||
| 
 | ||
|     I just saw the `exposing` keyword, so I was expecting to see `[` next.
 | ||
|     "###);
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unfinished_import_exposing_name,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             import svg.Path exposing [3
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── WEIRD EXPOSING in tmp/unfinished_import_exposing_name/Test.roc ──────────────
 | ||
| 
 | ||
|     I'm partway through parsing an exposing list, but I got stuck here:
 | ||
| 
 | ||
|     4│      import svg.Path exposing [3
 | ||
|                                       ^
 | ||
| 
 | ||
|     I was expecting a type, value, or function name next, like:
 | ||
| 
 | ||
|         import Svg exposing [Path, arc, rx]
 | ||
|     "###);
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unfinished_ingested_file_name,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             import "example.json" as
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNFINISHED IMPORT in tmp/unfinished_ingested_file_name/Test.roc ─────────────
 | ||
| 
 | ||
|     I was partway through parsing an `import`, but I got stuck here:
 | ||
| 
 | ||
|     4│      import "example.json" as
 | ||
|                                     ^
 | ||
| 
 | ||
|     I was expecting to see a name next, like:
 | ||
| 
 | ||
|         import "users.json" as users : Str
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         ingested_file_import_ann_syntax_err,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             import "example.json" as example : List U8, U32
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNFINISHED TYPE in tmp/ingested_file_import_ann_syntax_err/Test.roc ─────────
 | ||
| 
 | ||
|     I am partway through parsing a type, but I got stuck here:
 | ||
| 
 | ||
|     4│      import "example.json" as example : List U8, U32
 | ||
|                                                            ^
 | ||
| 
 | ||
|     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 [] provides [main] to "./platform"
 | ||
| 
 | ||
|             myDict : Dict Num.I64 Str
 | ||
|             myDict = Dict.insert (Dict.empty {}) "foo" 42
 | ||
| 
 | ||
|             main = myDict
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     Something is off with the body of the `myDict` definition:
 | ||
| 
 | ||
|     3│  myDict : 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 [] provides [main] to "./platform"
 | ||
| 
 | ||
|             HSet a : Set a
 | ||
| 
 | ||
|             foo : Str -> HSet {}
 | ||
| 
 | ||
|             myDict : HSet Str
 | ||
|             myDict = foo "bar"
 | ||
| 
 | ||
|             main = myDict
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /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 in 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 in 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 in 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 in 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 in 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 in 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###"
 | ||
|     ── UNKNOWN OPERATOR in tmp/when_outdented_branch/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│      when 4 is
 | ||
|     5│          5 -> 2
 | ||
|     6│       2 -> 2
 | ||
|                ^^
 | ||
| 
 | ||
|     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!(
 | ||
|         when_over_indented_underscore,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             when 4 is
 | ||
|                 5 -> 2
 | ||
|                  _ -> 2
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNEXPECTED ARROW in tmp/when_over_indented_underscore/Test.roc ──────────────
 | ||
| 
 | ||
|     I am parsing a `when` expression right now, but this arrow is confusing
 | ||
|     me:
 | ||
| 
 | ||
|     4│      when 4 is
 | ||
|     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 in tmp/when_over_indented_int/Test.roc ─────────────────────
 | ||
| 
 | ||
|     I am parsing a `when` expression right now, but this arrow is confusing
 | ||
|     me:
 | ||
| 
 | ||
|     4│      when 4 is
 | ||
|     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 in 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 in 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 in 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 in 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!(
 | ||
|         missing_return_expression,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             return
 | ||
|             "
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── MISSING EXPRESSION in tmp/missing_return_expression/Test.roc ────────────────
 | ||
| 
 | ||
|     I am partway through parsing a return statement, but I got stuck here:
 | ||
| 
 | ||
|     4│      return
 | ||
|                   ^
 | ||
| 
 | ||
|     I was expecting to see an expression like 42 or "hello".
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         return_as_def_name,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             return = \a -> a
 | ||
|             return
 | ||
|             "
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── MISSING EXPRESSION in tmp/return_as_def_name/Test.roc ───────────────────────
 | ||
| 
 | ||
|     I am partway through parsing a return statement, but I got stuck here:
 | ||
| 
 | ||
|     4│      return = \a -> a
 | ||
|                    ^
 | ||
| 
 | ||
|     I was expecting to see an expression like 42 or "hello".
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         return_space_problem,
 | ||
|         "return \t",
 | ||
|         @r###"
 | ||
|     ── TAB CHARACTER in tmp/return_space_problem/Test.roc ──────────────────────────
 | ||
| 
 | ||
|     I encountered a tab character:
 | ||
| 
 | ||
|     4│      return 	
 | ||
|                    ^
 | ||
| 
 | ||
|     Tab characters are not allowed in Roc code. Please use spaces instead!
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         number_double_dot,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             1.1.1
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── SYNTAX PROBLEM in /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 in 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 in /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 in 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)
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         single_quote_too_long,
 | ||
|         r"'abcdef'",
 | ||
|         @r#"
 | ||
|     ── INVALID SCALAR in 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 in 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 in 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 in ...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!(
 | ||
|         expect_without_final_expression,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             expect 1 + 1 == 2
 | ||
|             "
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── INDENT ENDS AFTER EXPRESSION in ...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"
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unhandled_parse_error,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app "test" provides [main] to "./platform"
 | ||
| 
 | ||
|             42
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── UNHANDLED PARSE ERROR in tmp/unhandled_parse_error/Test.roc ─────────────────
 | ||
| 
 | ||
|     I got stuck while parsing this:
 | ||
| 
 | ||
|     1│  app "test" provides [main] to "./platform"
 | ||
|     2│
 | ||
|     3│  42
 | ||
|         ^
 | ||
| 
 | ||
|     Here's the internal parse problem:
 | ||
| 
 | ||
|         UnexpectedTopLevelExpr(@44)
 | ||
| 
 | ||
|     Unfortunately, I'm not able to provide a more insightful error message
 | ||
|     for this syntax problem yet. This is considered a bug in the compiler.
 | ||
| 
 | ||
|     Note: If you'd like to contribute to Roc, this would be a good first issue!
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     // 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 in /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 in /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 in /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 in /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.e
 | ||
|         Num.pi
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         stray_dot_expr,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             Num.add . 23
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── SYNTAX PROBLEM in /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 in /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 in /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!(
 | ||
|         #[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 in 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 in /code/proj/Main.roc ────────────────────────────────────
 | ||
| 
 | ||
|     Nothing is named `bar` in this scope.
 | ||
| 
 | ||
|     4│      ["foo", bar("")]
 | ||
|                     ^^^
 | ||
| 
 | ||
|     Did you mean one of these?
 | ||
| 
 | ||
|         Str
 | ||
|         U8
 | ||
|         F64
 | ||
|         Box
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         invalid_operator,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             main =
 | ||
|                 5 ** 3
 | ||
|             "
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── UNKNOWN OPERATOR in 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
 | ||
|     https://www.roc-lang.org/tutorial#operator-desugaring-table for the
 | ||
|     full list of operators in Roc.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         double_plus,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             main =
 | ||
|                 [] ++ []
 | ||
|             "
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── UNKNOWN OPERATOR in 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 in 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
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── SYNTAX PROBLEM in tmp/wild_case_arrow/Test.roc ──────────────────────────────
 | ||
| 
 | ||
|     I got stuck here:
 | ||
| 
 | ||
|     1│  app "test" provides [main] to "./platform"
 | ||
|     2│
 | ||
|     3│  main =
 | ||
|     4│      main = 5 -> 3
 | ||
|                     ^
 | ||
| 
 | ||
|     Whatever I am running into is confusing me a lot! Normally I can give
 | ||
|     fairly specific hints, but something is really tripping me up this
 | ||
|     time.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     #[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 in /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: "generic-test-platform/main.roc",
 | ||
|                     }
 | ||
|                     imports [
 | ||
|                         pf.Stdout,
 | ||
|                     ]
 | ||
| 
 | ||
|                 main =
 | ||
|                     Stdout.line "answer"
 | ||
|                 "#
 | ||
|             ),
 | ||
|             indoc!(
 | ||
|                 r"
 | ||
|                 ── WEIRD PROVIDES in /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 in /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 in /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 module_params_with_missing_arrow() {
 | ||
|         report_header_problem_as(
 | ||
|             indoc!(
 | ||
|                 r#"
 | ||
|                 module {echo, read} [menu]
 | ||
|                 "#
 | ||
|             ),
 | ||
|             indoc!(
 | ||
|                 r#"
 | ||
|                 ── WEIRD MODULE PARAMS in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|                 I am partway through parsing a module header, but I got stuck here:
 | ||
| 
 | ||
|                 1│  module {echo, read} [menu]
 | ||
|                                         ^
 | ||
| 
 | ||
|                 I am expecting `->` next, like:
 | ||
| 
 | ||
|                     module { echo, read } -> [menu]
 | ||
|                 "#
 | ||
|             ),
 | ||
|         )
 | ||
|     }
 | ||
| 
 | ||
|     // TODO: this test seems out of date (what is the `effects` clause?) and as such should be removed
 | ||
|     #[test]
 | ||
|     fn platform_requires_rigids() {
 | ||
|         report_header_problem_as(
 | ||
|             indoc!(
 | ||
|                 r#"
 | ||
|                 platform "folkertdev/foo"
 | ||
|                     requires { main : Task {} [] }
 | ||
|                     exposes []
 | ||
|                     packages {}
 | ||
|                     imports []
 | ||
|                     provides [mainForHost]
 | ||
|                     effects fx.Effect
 | ||
|                          {
 | ||
|                              putChar : I64 -> Effect {},
 | ||
|                              putLine : Str -> Effect {},
 | ||
|                              getLine : Effect Str
 | ||
|                          }
 | ||
|                 "#
 | ||
|             ),
 | ||
|             indoc!(
 | ||
|                 r#"
 | ||
|                 ── BAD REQUIRES in /code/proj/Main.roc ─────────────────────────────────────────
 | ||
| 
 | ||
|                 I am partway through parsing a header, but I got stuck here:
 | ||
| 
 | ||
|                 1│  platform "folkertdev/foo"
 | ||
|                 2│      requires { main : Task {} [] }
 | ||
|                                    ^
 | ||
| 
 | ||
|                 I am expecting a list of type names like `{}` or `{ Model }` next. A full
 | ||
|                 `requires` definition looks like
 | ||
| 
 | ||
|                     requires { Model, Msg } {main : Task {} []}
 | ||
|             "#
 | ||
|             ),
 | ||
|         )
 | ||
|     }
 | ||
| 
 | ||
|     #[test]
 | ||
|     fn missing_imports() {
 | ||
|         report_header_problem_as(
 | ||
|             indoc!(
 | ||
|                 r"
 | ||
|                 interface Foobar
 | ||
|                     exposes [main, Foo]
 | ||
|                 "
 | ||
|             ),
 | ||
|             indoc!(
 | ||
|                 r"
 | ||
|                 ── WEIRD IMPORTS in /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"
 | ||
|                 module [main, @Foo]
 | ||
|                 "
 | ||
|             ),
 | ||
|             indoc!(
 | ||
|                 r"
 | ||
|                 ── WEIRD EXPOSES in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|                 I am partway through parsing an `exposes` list, but I got stuck here:
 | ||
| 
 | ||
|                 1│  module [main, @Foo]
 | ||
|                                   ^
 | ||
| 
 | ||
|                 I was expecting a type name, value name or function name next, like
 | ||
| 
 | ||
|                     [Animal, default, tame]
 | ||
|             "
 | ||
|             ),
 | ||
|         )
 | ||
|     }
 | ||
| 
 | ||
|     #[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 in /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 in /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 in /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 in /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 in 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 in 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 in 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 in 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 in 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!(
 | ||
|         expect_expr_type_error,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             expect "foobar"
 | ||
| 
 | ||
|             4
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in 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 in 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 in ..._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 in /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 U64 -> D a U64
 | ||
|             f = \c -> c 6
 | ||
|             f
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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, "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 in /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, "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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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:
 | ||
| 
 | ||
|         U64
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         list_get_negative_number_indirect,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|              a = -9_223_372_036_854
 | ||
|              List.get [1,2,3] a
 | ||
|              "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /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:
 | ||
| 
 | ||
|         U64
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     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 in /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:
 | ||
| 
 | ||
|         U64
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         compare_unsigned_to_signed,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             when -1 is
 | ||
|                1u8 -> 1
 | ||
|                _ -> 1
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 : Unimported.CustomType
 | ||
|             alt = "whatever man you don't even know my type"
 | ||
|             alt
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── MODULE NOT IMPORTED in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     The `Unimported` module is not imported:
 | ||
| 
 | ||
|     4│      alt : Unimported.CustomType
 | ||
|                   ^^^^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Is there an import missing? Perhaps there is a typo. Did you mean one
 | ||
|     of these?
 | ||
| 
 | ||
|         Encode
 | ||
|         Inspect
 | ||
|         Dict
 | ||
|         List
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     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 definition!
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /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 in /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 in /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: *Add type annotations* to functions or values to help you figure
 | ||
|     this out.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     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 in /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: *Add type annotations* to functions or values to help you figure
 | ||
|     this out.
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         opaque_mismatch_pattern_infer,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             F n := n
 | ||
| 
 | ||
|             \x ->
 | ||
|                 when x is
 | ||
|                     @F A -> ""
 | ||
|                     @F {} -> ""
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /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 in /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 in /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 in /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 in /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 : U64 }[]
 | ||
|             f
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── INVALID_EXTENSION_TYPE in /code/proj/Main.roc ───────────────────────────────
 | ||
| 
 | ||
|     This record extension type is invalid:
 | ||
| 
 | ||
|     4│      f : { x : U64 }[]
 | ||
|                            ^^
 | ||
| 
 | ||
|     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 in /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 in /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 in /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 in 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 in 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 in 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 in 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###"
 | ||
|     ── WILDCARD NOT ALLOWED HERE in /code/proj/Main.roc ────────────────────────────
 | ||
| 
 | ||
|     The definition of `I` includes a wildcard (`*`) type variable:
 | ||
| 
 | ||
|     4│      I : Num.Int *
 | ||
|                         ^
 | ||
| 
 | ||
|     Type alias definitions may not use wildcard (`*`) type variables. Only
 | ||
|     named type variables are allowed.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         underscore_in_alias,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             I : Num.Int _
 | ||
|             a : I
 | ||
|             a
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNDERSCORE NOT ALLOWED HERE in /code/proj/Main.roc ──────────────────────────
 | ||
| 
 | ||
|     The definition of `I` includes an inferred (`_`) type:
 | ||
| 
 | ||
|     4│      I : Num.Int _
 | ||
|                         ^
 | ||
| 
 | ||
|     Type alias definitions may not use inferred types (`_`).
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         wildcard_in_opaque,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             I := Num.Int *
 | ||
|             a : I
 | ||
|             a
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── WILDCARD NOT ALLOWED HERE in /code/proj/Main.roc ────────────────────────────
 | ||
| 
 | ||
|     The definition of `I` includes a wildcard (`*`) type variable:
 | ||
| 
 | ||
|     4│      I := Num.Int *
 | ||
|                          ^
 | ||
| 
 | ||
|     Opaque type definitions may not use wildcard (`*`) type variables. Only
 | ||
|     named type variables are allowed.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         multiple_wildcards_in_alias,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             I : [A (Num.Int *), B (Num.Int *)]
 | ||
|             a : I
 | ||
|             a
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── WILDCARD NOT ALLOWED HERE in /code/proj/Main.roc ────────────────────────────
 | ||
| 
 | ||
|     The definition of `I` includes 2 wildcard (`*`) type variables. Here is
 | ||
|     one of them:
 | ||
| 
 | ||
|     4│      I : [A (Num.Int *), B (Num.Int *)]
 | ||
|                             ^
 | ||
| 
 | ||
|     Type alias definitions may not use wildcard (`*`) type variables. Only
 | ||
|     named type variables are allowed.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         inference_var_in_alias,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             I : Num.Int _
 | ||
|             a : I
 | ||
|             a
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNDERSCORE NOT ALLOWED HERE in /code/proj/Main.roc ──────────────────────────
 | ||
| 
 | ||
|     The definition of `I` includes an inferred (`_`) type:
 | ||
| 
 | ||
|     4│      I : Num.Int _
 | ||
|                         ^
 | ||
| 
 | ||
|     Type alias definitions may not use inferred types (`_`).
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unbound_var_in_alias,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             I : Num.Int a
 | ||
|             a : I
 | ||
|             a
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNDECLARED TYPE VARIABLE in /code/proj/Main.roc ─────────────────────────────
 | ||
| 
 | ||
|     The definition of `I` includes an undeclared type variable:
 | ||
| 
 | ||
|     4│      I : Num.Int a
 | ||
|                         ^
 | ||
| 
 | ||
|     All type variables in type alias definitions must be declared.
 | ||
| 
 | ||
|     Tip: You can declare type variables by putting them right before the `:`
 | ||
|     symbol, separated by spaces.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in 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 in /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 in /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 in /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 [] 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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 effects are not
 | ||
|     allowed at the top-level, 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 ->
 | ||
|                 await (open {}) \handle ->
 | ||
|                     await (callback handle) \_ ->
 | ||
|                         close handle
 | ||
| 
 | ||
|             withOpen
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|         ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|         Something is off with the body of the `withOpen` definition:
 | ||
| 
 | ||
|         10│       withOpen : (Handle -> Result {} *) -> Result {} *
 | ||
|         11│       withOpen = \callback ->
 | ||
|         12│>          await (open {}) \handle ->
 | ||
|         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 in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This returns something that's incompatible with the return type of the
 | ||
|     enclosing function:
 | ||
| 
 | ||
|     5│      f = \_ -> if Bool.true then {} else f {}
 | ||
|                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     It a value of type:
 | ||
| 
 | ||
|         {}
 | ||
| 
 | ||
|     But I expected the function to have return type:
 | ||
| 
 | ||
|         * -> 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 in /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 in /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 in /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 in /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: *Add type annotations* to functions or values to help you figure
 | ||
|     this out.
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         opaque_wrap_function_mismatch,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             A := U8
 | ||
|             List.map [1u16, 2u16, 3u16] @A
 | ||
|             "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 2nd argument to `map` has an unexpected type:
 | ||
| 
 | ||
|     4│      A := U8
 | ||
|     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 in /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 in /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 in /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!(
 | ||
|         issue_6279,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             when A "" is
 | ||
|                 A x | B x | C -> x
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── NAME NOT BOUND IN ALL PATTERNS in /code/proj/Main.roc ───────────────────────
 | ||
| 
 | ||
|         `x` is not bound in all patterns of this `when` branch
 | ||
| 
 | ||
|         5│          A x | B x | C -> 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!
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         issue_6825,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             when [] is
 | ||
|                 [] | [_, .. as rest] if List.isEmpty rest -> []
 | ||
|                 _ -> []
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── NAME NOT BOUND IN ALL PATTERNS in /code/proj/Main.roc ───────────────────────
 | ||
| 
 | ||
|         `rest` is not bound in all patterns of this `when` branch
 | ||
| 
 | ||
|         5│          [] | [_, .. as rest] if List.isEmpty rest -> []
 | ||
|                                    ^^^^
 | ||
| 
 | ||
|         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!
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         flip_flop_catch_all_branches_not_exhaustive,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             \x -> when x is
 | ||
|                     A B _ -> ""
 | ||
|                     A _ C -> ""
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── UNSAFE PATTERN in /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 in /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 in /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 in /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 in /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!(
 | ||
|         call_with_declared_identifier_with_more_than_one_underscore,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             f__arg = \x, y, z -> x + y + z
 | ||
| 
 | ||
|             \a, b -> f__arg a b 1
 | ||
|             "
 | ||
|         ),
 | ||
|         |golden| pretty_assertions::assert_eq!(
 | ||
|             golden,
 | ||
|             indoc!(
 | ||
|                 r"── NAMING PROBLEM in /code/proj/Main.roc ───────────────────────────────────────
 | ||
| 
 | ||
|                 I am trying to parse an identifier here:
 | ||
| 
 | ||
|                 4│      f__arg = \x, y, z -> x + y + z
 | ||
|                         ^^^^^^
 | ||
| 
 | ||
|                 Snake case is allowed here, but only a single consecutive underscore
 | ||
|                 should be used.
 | ||
|                 "
 | ||
|             ),
 | ||
|         )
 | ||
|     );
 | ||
| 
 | ||
|     // Record Builders
 | ||
| 
 | ||
|     test_report!(
 | ||
|         empty_record_builder,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             { a <- }
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── EMPTY RECORD BUILDER in /code/proj/Main.roc ─────────────────────────────────
 | ||
| 
 | ||
|     This record builder has no fields:
 | ||
| 
 | ||
|     4│      { a <- }
 | ||
|             ^^^^^^^^
 | ||
| 
 | ||
|     I need at least two fields to combine their values into a record.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         single_field_record_builder,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             { a <-
 | ||
|                 b: 123
 | ||
|             }
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── NOT ENOUGH FIELDS IN RECORD BUILDER in /code/proj/Main.roc ──────────────────
 | ||
| 
 | ||
|     This record builder only has one field:
 | ||
| 
 | ||
|     4│>      { a <-
 | ||
|     5│>          b: 123
 | ||
|     6│>      }
 | ||
| 
 | ||
|     I need at least two fields to combine their values into a record.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         optional_field_in_record_builder,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             { a <-
 | ||
|                 b: 123,
 | ||
|                 c? 456
 | ||
|             }
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── OPTIONAL FIELD IN RECORD BUILDER in /code/proj/Main.roc ─────────────────────
 | ||
| 
 | ||
|     Optional fields are not allowed to be used in record builders.
 | ||
| 
 | ||
|     4│       { a <-
 | ||
|     5│           b: 123,
 | ||
|     6│>          c? 456
 | ||
|     7│       }
 | ||
| 
 | ||
|     Record builders can only have required values for their fields.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     // CalledVia::RecordBuilder => {
 | ||
|     //     alloc.concat([
 | ||
|     //         alloc.note(""),
 | ||
|     //         alloc.reflow("Record builders need a mapper function before the "),
 | ||
|     //         alloc.keyword("<-"),
 | ||
|     //         alloc.reflow(" to combine fields together with.")
 | ||
|     //     ])
 | ||
|     // }
 | ||
|     // _ => {
 | ||
|     //     alloc.reflow("Are there any missing commas? Or missing parentheses?")
 | ||
| 
 | ||
|     test_report!(
 | ||
|         record_builder_with_non_function_mapper,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             xyz = "abc"
 | ||
| 
 | ||
|             { xyz <-
 | ||
|                 b: 123,
 | ||
|                 c: 456
 | ||
|             }
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TOO MANY ARGS in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     The `xyz` value is not a function, but it was given 3 arguments:
 | ||
| 
 | ||
|     6│      { xyz <-
 | ||
|               ^^^
 | ||
| 
 | ||
|     Note: Record builders need a mapper function before the <- to combine
 | ||
|     fields together with.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         destructure_assignment_introduces_no_variables_nested,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             Pair _ _ = Pair 0 1
 | ||
| 
 | ||
|             _ = Pair 0 1
 | ||
| 
 | ||
|             {} = {}
 | ||
| 
 | ||
|             Foo = Foo
 | ||
| 
 | ||
|             0
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
 | ||
| 
 | ||
|     This assignment doesn't introduce any new variables:
 | ||
| 
 | ||
|     4│      Pair _ _ = Pair 0 1
 | ||
|             ^^^^^^^^
 | ||
| 
 | ||
|     Since it doesn't call any effectful functions, this assignment cannot
 | ||
|     affect the program's behavior. If you don't need to use the value on
 | ||
|     the right-hand side, consider removing the assignment.
 | ||
| 
 | ||
|     ── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
 | ||
| 
 | ||
|     This assignment doesn't introduce any new variables:
 | ||
| 
 | ||
|     6│      _ = Pair 0 1
 | ||
|             ^
 | ||
| 
 | ||
|     Since it doesn't call any effectful functions, this assignment cannot
 | ||
|     affect the program's behavior. If you don't need to use the value on
 | ||
|     the right-hand side, consider removing the assignment.
 | ||
| 
 | ||
|     ── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
 | ||
| 
 | ||
|     This assignment doesn't introduce any new variables:
 | ||
| 
 | ||
|     8│      {} = {}
 | ||
|             ^^
 | ||
| 
 | ||
|     Since it doesn't call any effectful functions, this assignment cannot
 | ||
|     affect the program's behavior. If you don't need to use the value on
 | ||
|     the right-hand side, consider removing the assignment.
 | ||
| 
 | ||
|     ── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
 | ||
| 
 | ||
|     This assignment doesn't introduce any new variables:
 | ||
| 
 | ||
|     10│      Foo = Foo
 | ||
|              ^^^
 | ||
| 
 | ||
|     Since it doesn't call any effectful functions, this assignment cannot
 | ||
|     affect the program's behavior. If you don't need to use the value on
 | ||
|     the right-hand side, consider removing the assignment.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     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 in /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 effects are not
 | ||
|     allowed at the top-level, assignments that don't introduce variables
 | ||
|     cannot affect a program's behavior
 | ||
| 
 | ||
|     ── UNNECESSARY DEFINITION in /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 effects are not
 | ||
|     allowed at the top-level, assignments that don't introduce variables
 | ||
|     cannot affect a program's behavior
 | ||
| 
 | ||
|     ── UNNECESSARY DEFINITION in /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 effects are not
 | ||
|     allowed at the top-level, assignments that don't introduce variables
 | ||
|     cannot affect a program's behavior
 | ||
| 
 | ||
|     ── UNNECESSARY DEFINITION in /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 effects are not
 | ||
|     allowed at the top-level, 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 in /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 in /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 in /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 in /code/proj/Main.roc ─────────────────────────────────────────
 | ||
| 
 | ||
|     The `Recursive` opaque is self-recursive in an invalid way:
 | ||
| 
 | ||
|     4│      Recursive := [Infinitely Recursive]
 | ||
|             ^^^^^^^^^
 | ||
| 
 | ||
|     Recursion in opaque types 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 in /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 in /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 [] provides [main] to "./platform"
 | ||
| 
 | ||
|             import Decode exposing [decoder]
 | ||
| 
 | ||
|             main =
 | ||
|                 myDecoder : Decoder (a -> a) fmt where fmt implements DecoderFormatting
 | ||
|                 myDecoder = decoder
 | ||
| 
 | ||
|                 myDecoder
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── TYPE MISMATCH in /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
 | ||
| 
 | ||
|         a -> a
 | ||
| 
 | ||
|     Note: `Decoding` cannot be generated for functions.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         nested_opaque_cannot_derive_encoding,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app "test" imports [] provides [main] to "./platform"
 | ||
| 
 | ||
|             import Decode exposing [decoder]
 | ||
| 
 | ||
|             A := {}
 | ||
| 
 | ||
|             main =
 | ||
|                 myDecoder : Decoder {x : A} fmt where fmt implements DecoderFormatting
 | ||
|                 myDecoder = decoder
 | ||
| 
 | ||
|                 myDecoder
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This expression has a type that does not implement the abilities it's expected to:
 | ||
| 
 | ||
|     9│      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 in /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 in /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 in /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 in /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 in /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 [] provides [main] to "./platform"
 | ||
| 
 | ||
|             ErrDecoder := {} implements [DecoderFormatting {
 | ||
|                 u8: decodeU8,
 | ||
|                 u16: decodeU16,
 | ||
|                 u32: decodeU32,
 | ||
|                 u64: decodeU64,
 | ||
|                 u128: decodeU128,
 | ||
|                 i8: decodeI8,
 | ||
|                 i16: decodeI16,
 | ||
|                 i32: decodeI32,
 | ||
|                 i64: decodeI64,
 | ||
|                 i128: decodeI128,
 | ||
|                 f32: decodeF32,
 | ||
|                 f64: decodeF64,
 | ||
|                 dec: decodeDec,
 | ||
|                 bool: decodeBool,
 | ||
|                 string: decodeString,
 | ||
|                 list: decodeList,
 | ||
|                 record: decodeRecord,
 | ||
|                 tuple: decodeTuple,
 | ||
|             }]
 | ||
|             decodeU8 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeU16 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeU32 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeU64 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeU128 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeI8 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeI16 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeI32 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeI64 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeI128 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeF32 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeF64 = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeDec = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeBool = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeString = Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeList : Decoder elem (ErrDecoder) -> Decoder (List elem) (ErrDecoder)
 | ||
|             decodeList = \_ -> Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeRecord : state, (state, Str -> [Keep (Decoder state (ErrDecoder)), Skip]), (state, (ErrDecoder) -> Result val DecodeError) -> Decoder val (ErrDecoder)
 | ||
|             decodeRecord =\_, _, _ ->  Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
|             decodeTuple : state, (state, U64 -> [Next (Decoder state (ErrDecoder)), TooLong]), (state -> Result val DecodeError) -> Decoder val (ErrDecoder)
 | ||
|             decodeTuple = \_, _, _ -> Decode.custom \rest, @ErrDecoder {} -> {result: Err TooShort, rest}
 | ||
| 
 | ||
|             main =
 | ||
|                 decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes (@ErrDecoder {})
 | ||
|                 when decoded is
 | ||
|                     Ok rcd -> rcd.first rcd.second
 | ||
|                     _ -> "something went wrong"
 | ||
|             "#
 | ||
|         ),
 | ||
|     @r###"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This expression has a type that does not implement the abilities it's expected to:
 | ||
| 
 | ||
|     48│          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 [] provides [main] to "./platform"
 | ||
| 
 | ||
|              import Decode exposing [decoder]
 | ||
| 
 | ||
|              main =
 | ||
|                  myDecoder : Decoder {x : Str, y ? Str} fmt where fmt implements DecoderFormatting
 | ||
|                  myDecoder = decoder
 | ||
| 
 | ||
|                  myDecoder
 | ||
|              "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── TYPE MISMATCH in /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 : Str,
 | ||
|             y ? Str,
 | ||
|         }
 | ||
| 
 | ||
|     Note: I can't derive decoding for a record with an optional field,
 | ||
|     which in this case is `.y`. Default value 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 in /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 in /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 in /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 in /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 in /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#"
 | ||
|             Dec := [ S Dec, Z ]
 | ||
| 
 | ||
|             ""
 | ||
|             "#
 | ||
|         ),
 | ||
|     @r"
 | ||
|     ── DUPLICATE NAME in /code/proj/Main.roc ───────────────────────────────────────
 | ||
| 
 | ||
|     This opaque type has the same name as a builtin:
 | ||
| 
 | ||
|     4│      Dec := [ S Dec, 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 [] provides [main] to "./platform"
 | ||
| 
 | ||
|             import List exposing [concat]
 | ||
| 
 | ||
|             main = ""
 | ||
|             "#
 | ||
|         ),
 | ||
|     @r###"
 | ||
|     ── UNUSED IMPORT in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     List is imported but not used.
 | ||
| 
 | ||
|     3│  import List exposing [concat]
 | ||
|         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Since List 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 [] provides [main, E] to "./platform"
 | ||
| 
 | ||
|             import Decode exposing [DecodeError]
 | ||
| 
 | ||
|             E : DecodeError
 | ||
| 
 | ||
|             main = ""
 | ||
|             "#
 | ||
|         ),
 | ||
|     @r"
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         deprecated_backpassing,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             foo = \bar ->
 | ||
|                 baz <- Result.try bar
 | ||
| 
 | ||
|                 Ok (baz * 3)
 | ||
| 
 | ||
|             foo (Ok 123)
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── BACKPASSING DEPRECATED in /code/proj/Main.roc ───────────────────────────────
 | ||
| 
 | ||
|     Backpassing (<-) like this will soon be deprecated:
 | ||
| 
 | ||
|     5│          baz <- Result.try bar
 | ||
|                 ^^^^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     You should use a ! for awaiting tasks or a ? for trying results, and
 | ||
|     functions everywhere else.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unknown_shorthand_no_deps,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             import foo.Foo
 | ||
| 
 | ||
|             Foo.foo
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNRECOGNIZED PACKAGE in tmp/unknown_shorthand_no_deps/Test.roc ──────────────
 | ||
| 
 | ||
|     This module is trying to import from `foo`:
 | ||
| 
 | ||
|     4│      import foo.Foo
 | ||
|                    ^^^^^^^
 | ||
| 
 | ||
|     A lowercase name indicates a package shorthand, but no packages have
 | ||
|     been specified.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unknown_shorthand_in_app,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main] { pf: platform "../../tests/platform.roc" }
 | ||
| 
 | ||
|             import foo.Foo
 | ||
| 
 | ||
|             main =
 | ||
|                 Foo.foo
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNRECOGNIZED PACKAGE in tmp/unknown_shorthand_in_app/Test.roc ───────────────
 | ||
| 
 | ||
|     This module is trying to import from `foo`:
 | ||
| 
 | ||
|     3│  import foo.Foo
 | ||
|                ^^^^^^^
 | ||
| 
 | ||
|     A lowercase name indicates a package shorthand, but I don't recognize
 | ||
|     this one. Did you mean one of these?
 | ||
| 
 | ||
|         pf
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         import_qualified_builtin,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main] { pf: platform "../../tests/platform.roc" }
 | ||
| 
 | ||
|             import pf.Bool
 | ||
| 
 | ||
|             main =
 | ||
|                 ""
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     [1;36m── FILE NOT FOUND in tmp/import_qualified_builtin/../../tests/Bool.roc ─────────[0m
 | ||
| 
 | ||
|     I am looking for this file, but it's not there:
 | ||
| 
 | ||
|         [1;33mtmp/import_qualified_builtin/../../tests/Bool.roc[0m
 | ||
| 
 | ||
|     Is the file supposed to be there? Maybe there is a typo in the file
 | ||
|     name?
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         invalid_toplevel_cycle,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app "test" imports [] provides [main] to "./platform"
 | ||
| 
 | ||
|             main =
 | ||
|                 if Bool.true then {} else main
 | ||
|             "#
 | ||
|         ),
 | ||
|     @r"
 | ||
|     ── CIRCULAR DEFINITION in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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, 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 in /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 in /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, 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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in 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 in 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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /code/proj/Main.roc ────────────────────────────────────
 | ||
| 
 | ||
|     This `crash` has too many values given to it:
 | ||
| 
 | ||
|     4│      crash "" ""
 | ||
|                   ^^^^^
 | ||
| 
 | ||
|     `crash` must be given exactly 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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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_no_problem!(
 | ||
|         derive_decoding_for_tuple,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app "test" imports [] provides [main] to "./platform"
 | ||
| 
 | ||
|             import Decode exposing [decoder]
 | ||
| 
 | ||
|             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 [] provides [main] to "./platform"
 | ||
| 
 | ||
|             import Decode exposing [decoder]
 | ||
| 
 | ||
|             main =
 | ||
|                 myDecoder : Decoder (U32, {} -> {}) fmt where fmt implements DecoderFormatting
 | ||
|                 myDecoder = decoder
 | ||
| 
 | ||
|                 myDecoder
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── TYPE MISMATCH in /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
 | ||
| 
 | ||
|         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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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 in /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.
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         dbg_unapplied,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             1 + dbg + 2
 | ||
|             "
 | ||
|         ),
 | ||
|     @r"
 | ||
|     ── UNAPPLIED DBG in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This `dbg` doesn't have a value given to it:
 | ||
| 
 | ||
|     4│      1 + dbg + 2
 | ||
|                 ^^^
 | ||
| 
 | ||
|     `dbg` must be passed a value to print at the exact place it's used. `dbg`
 | ||
|     can't be used as a value that's passed around, like functions can be -
 | ||
|     it must be applied immediately!
 | ||
| 
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 2nd argument to + has an unexpected type:
 | ||
| 
 | ||
|     4│      1 + dbg + 2
 | ||
|                 ^^^
 | ||
| 
 | ||
|     This value is a:
 | ||
| 
 | ||
|         {}
 | ||
| 
 | ||
|     But + needs its 2nd argument to be:
 | ||
| 
 | ||
|         Num *
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         dbg_overapplied,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             1 + dbg "" "" + 2
 | ||
|             "#
 | ||
|         ),
 | ||
|     @r#"
 | ||
|     ── OVERAPPLIED DBG in /code/proj/Main.roc ──────────────────────────────────────
 | ||
| 
 | ||
|     This `dbg` has too many values given to it:
 | ||
| 
 | ||
|     4│      1 + dbg "" "" + 2
 | ||
|                     ^^^^^
 | ||
| 
 | ||
|     `dbg` must be given exactly one value to print.
 | ||
| 
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 2nd argument to + has an unexpected type:
 | ||
| 
 | ||
|     4│      1 + dbg "" "" + 2
 | ||
|                 ^^^^^^^^^
 | ||
| 
 | ||
|     This value is a:
 | ||
| 
 | ||
|         {}
 | ||
| 
 | ||
|     But + needs its 2nd argument to be:
 | ||
| 
 | ||
|         Num *
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         issue_6240_1,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             {}.abcde
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This record doesn’t have a `abcde` field:
 | ||
| 
 | ||
|     4│      {}.abcde
 | ||
|             ^^^^^^^^
 | ||
| 
 | ||
|     In fact, it’s a record with no fields at all!
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         issue_6240_2,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|               ("", "").abcde
 | ||
|               "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|       ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|       This expression is used in an unexpected way:
 | ||
| 
 | ||
|       4│      ("", "").abcde
 | ||
|               ^^^^^^^^^^^^^^
 | ||
| 
 | ||
|       It is a tuple of type:
 | ||
| 
 | ||
|           (
 | ||
|               Str,
 | ||
|               Str,
 | ||
|           )a
 | ||
| 
 | ||
|       But you are trying to use it as:
 | ||
| 
 | ||
|           { abcde : * }b
 | ||
|       "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         issue_6240_3,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|               {}.0
 | ||
|               "
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This expression is used in an unexpected way:
 | ||
| 
 | ||
|     4│      {}.0
 | ||
|             ^^^^
 | ||
| 
 | ||
|     It is a record of type:
 | ||
| 
 | ||
|         {}
 | ||
| 
 | ||
|     But you are trying to use it as:
 | ||
| 
 | ||
|         (*)b
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         return_outside_of_function,
 | ||
|         indoc!(
 | ||
|             r"
 | ||
|             someVal =
 | ||
|                 if 10 > 5 then
 | ||
|                     x = 5
 | ||
|                     return x
 | ||
|                 else
 | ||
|                     6
 | ||
| 
 | ||
|             someVal + 2
 | ||
|             "
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── RETURN OUTSIDE OF FUNCTION in /code/proj/Main.roc ───────────────────────────
 | ||
| 
 | ||
|         This `return` doesn't belong to a function:
 | ||
| 
 | ||
|         7│              return x
 | ||
|                         ^^^^^^^^
 | ||
| 
 | ||
|         I wouldn't know where to return to if I used it!
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         statements_after_return,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             myFunction = \x ->
 | ||
|                 if x == 2 then
 | ||
|                     return x
 | ||
| 
 | ||
|                     log! "someData"
 | ||
|                     useX x 123
 | ||
|                 else
 | ||
|                     x + 5
 | ||
| 
 | ||
|             myFunction 2
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── UNREACHABLE CODE in /code/proj/Main.roc ─────────────────────────────────────
 | ||
| 
 | ||
|         This code won't run because it follows a `return` statement:
 | ||
| 
 | ||
|         6│>              return x
 | ||
|         7│>
 | ||
|         8│>              log! "someData"
 | ||
|         9│>              useX x 123
 | ||
| 
 | ||
|         Hint: you can move the `return` statement below this block to make the
 | ||
|         code that follows it run.
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         return_at_end_of_function,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             myFunction = \x ->
 | ||
|                 y = Num.toStr x
 | ||
| 
 | ||
|                 return y
 | ||
| 
 | ||
|             myFunction 3
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── UNNECESSARY RETURN in /code/proj/Main.roc ───────────────────────────────────
 | ||
| 
 | ||
|         This `return` keyword is redundant:
 | ||
| 
 | ||
|         7│          return y
 | ||
|                     ^^^^^^^^
 | ||
| 
 | ||
|         The last expression in a function is treated like a `return` statement.
 | ||
|         You can safely remove `return` here.
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         mismatch_early_return_with_function_output,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             myFunction = \x ->
 | ||
|                 if x == 5 then
 | ||
|                     return "abc"
 | ||
|                 else
 | ||
|                     x
 | ||
| 
 | ||
|             myFunction 3
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This returns something that's incompatible with the return type of the
 | ||
|     enclosing function:
 | ||
| 
 | ||
|     5│           if x == 5 then
 | ||
|     6│>              return "abc"
 | ||
|     7│           else
 | ||
|     8│               x
 | ||
| 
 | ||
|     This returns a value of type:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     But I expected the function to have return type:
 | ||
| 
 | ||
|         Num *
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         try_in_bare_statement,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             validateNum = \num ->
 | ||
|                 if num > 5 then
 | ||
|                     Ok {}
 | ||
|                 else
 | ||
|                     Err TooBig
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! "hello"
 | ||
| 
 | ||
|                 # this returns {}, so it's ignored
 | ||
|                 try validateNum 10
 | ||
| 
 | ||
|                 # this returns a value, so we are incorrectly
 | ||
|                 # dropping the parsed value
 | ||
|                 try List.get [1, 2, 3] 5
 | ||
| 
 | ||
|                 Ok {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── IGNORED RESULT in /code/proj/Main.roc ───────────────────────────────────────
 | ||
|         
 | ||
|         The result of this expression is ignored:
 | ||
|         
 | ||
|         19│      try List.get [1, 2, 3] 5
 | ||
|                  ^^^^^^^^^^^^^^^^^^^^^^^^
 | ||
|         
 | ||
|         Standalone statements are required to produce an empty record, but the
 | ||
|         type of this one is:
 | ||
|         
 | ||
|             Num *
 | ||
|         
 | ||
|         If you still want to ignore it, assign it to `_`, like this:
 | ||
|         
 | ||
|             _ = File.delete! "data.json"
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         return_in_bare_statement,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! "hello"
 | ||
| 
 | ||
|                 # this outputs {}, so it's ignored
 | ||
|                 if 7 > 5 then
 | ||
|                     {}
 | ||
|                 else
 | ||
|                     return Err TooBig
 | ||
| 
 | ||
|                 # this outputs a value, so we are incorrectly
 | ||
|                 # dropping the parsed value
 | ||
|                 when List.get [1, 2, 3] 5 is
 | ||
|                     Ok item -> item
 | ||
|                     Err err ->
 | ||
|                         return Err err
 | ||
| 
 | ||
|                 Ok {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── IGNORED RESULT in /code/proj/Main.roc ───────────────────────────────────────
 | ||
| 
 | ||
|     The result of this expression is ignored:
 | ||
| 
 | ||
|     16│>      when List.get [1, 2, 3] 5 is
 | ||
|     17│>          Ok item -> item
 | ||
|     18│>          Err err ->
 | ||
|     19│>              return Err err
 | ||
| 
 | ||
|     Standalone statements are required to produce an empty record, but the
 | ||
|     type of this one is:
 | ||
| 
 | ||
|         Num *
 | ||
| 
 | ||
|     If you still want to ignore it, assign it to `_`, like this:
 | ||
| 
 | ||
|         _ = File.delete! "data.json"
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         mismatch_only_early_returns,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             myFunction = \x ->
 | ||
|                 if x == 5 then
 | ||
|                     return "abc"
 | ||
|                 else
 | ||
|                     return 123
 | ||
| 
 | ||
|             myFunction 3
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This returns something that's incompatible with the return type of the
 | ||
|     enclosing function:
 | ||
| 
 | ||
|     5│          if x == 5 then
 | ||
|     6│              return "abc"
 | ||
|     7│          else
 | ||
|     8│              return 123
 | ||
|                     ^^^^^^^^^^
 | ||
| 
 | ||
|     This returns a value of type:
 | ||
| 
 | ||
|         Num *
 | ||
| 
 | ||
|     But I expected the function to have return type:
 | ||
| 
 | ||
|         Str
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         try_with_ignored_output,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! "hello"
 | ||
| 
 | ||
|                 # not ignored, warning
 | ||
|                 try List.get [1, 2, 3] 5
 | ||
| 
 | ||
|                 # ignored, OK
 | ||
|                 _ = try List.get [1, 2, 3] 5
 | ||
|                 _ignored = try List.get [1, 2, 3] 5
 | ||
| 
 | ||
|                 Ok {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── IGNORED RESULT in /code/proj/Main.roc ───────────────────────────────────────
 | ||
|         
 | ||
|         The result of this expression is ignored:
 | ||
|         
 | ||
|         9│      try List.get [1, 2, 3] 5
 | ||
|                 ^^^^^^^^^^^^^^^^^^^^^^^^
 | ||
|         
 | ||
|         Standalone statements are required to produce an empty record, but the
 | ||
|         type of this one is:
 | ||
|         
 | ||
|             Num *
 | ||
|         
 | ||
|         If you still want to ignore it, assign it to `_`, like this:
 | ||
|         
 | ||
|             _ = File.delete! "data.json"
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         return_with_ignored_output,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! "hello"
 | ||
| 
 | ||
|                 # not ignored, warning
 | ||
|                 when List.get [1, 2, 3] 5 is
 | ||
|                     Ok item -> item
 | ||
|                     Err err ->
 | ||
|                         return Err err
 | ||
| 
 | ||
|                 # ignored, OK
 | ||
|                 _ =
 | ||
|                     when List.get [1, 2, 3] 5 is
 | ||
|                         Ok item -> item
 | ||
|                         Err err ->
 | ||
|                             return Err err
 | ||
| 
 | ||
|                 # also ignored, also OK
 | ||
|                 _ignored =
 | ||
|                     when List.get [1, 2, 3] 5 is
 | ||
|                         Ok item -> item
 | ||
|                         Err err ->
 | ||
|                             return Err err
 | ||
| 
 | ||
|                 Ok {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── IGNORED RESULT in /code/proj/Main.roc ───────────────────────────────────────
 | ||
| 
 | ||
|     The result of this expression is ignored:
 | ||
| 
 | ||
|      9│>      when List.get [1, 2, 3] 5 is
 | ||
|     10│>          Ok item -> item
 | ||
|     11│>          Err err ->
 | ||
|     12│>              return Err err
 | ||
| 
 | ||
|     Standalone statements are required to produce an empty record, but the
 | ||
|     type of this one is:
 | ||
| 
 | ||
|         Num *
 | ||
| 
 | ||
|     If you still want to ignore it, assign it to `_`, like this:
 | ||
| 
 | ||
|         _ = File.delete! "data.json"
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         no_early_return_in_bare_statement,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! "hello"
 | ||
| 
 | ||
|                 Num.toStr 123
 | ||
| 
 | ||
|                 Ok {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── IGNORED RESULT in /code/proj/Main.roc ───────────────────────────────────────
 | ||
| 
 | ||
|     The result of this call to `Num.toStr` is ignored:
 | ||
| 
 | ||
|     8│      Num.toStr 123
 | ||
|             ^^^^^^^^^
 | ||
| 
 | ||
|     Standalone statements are required to produce an empty record, but the
 | ||
|     type of this one is:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     If you still want to ignore it, assign it to `_`, like this:
 | ||
| 
 | ||
|         _ = File.delete! "data.json"
 | ||
| 
 | ||
|     ── LEFTOVER STATEMENT in /code/proj/Main.roc ───────────────────────────────────
 | ||
| 
 | ||
|     This statement does not produce any effects:
 | ||
| 
 | ||
|     8│      Num.toStr 123
 | ||
|             ^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Standalone statements are only useful if they call effectful
 | ||
|     functions.
 | ||
| 
 | ||
|     Did you forget to use its result? If not, feel free to remove it.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         no_early_return_in_ignored_statement,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! "hello"
 | ||
| 
 | ||
|                 _ignored = Num.toStr 123
 | ||
| 
 | ||
|                 Ok {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── UNNECESSARY DEFINITION in /code/proj/Main.roc ───────────────────────────────
 | ||
| 
 | ||
|     This assignment doesn't introduce any new variables:
 | ||
| 
 | ||
|     8│      _ignored = Num.toStr 123
 | ||
|             ^^^^^^^^
 | ||
| 
 | ||
|     Since it doesn't call any effectful functions, this assignment cannot
 | ||
|     affect the program's behavior. If you don't need to use the value on
 | ||
|     the right-hand side, consider removing the assignment.
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         mismatch_early_return_annotated_function,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             myFunction : U64 -> Str
 | ||
|             myFunction = \x ->
 | ||
|                 if x == 5 then
 | ||
|                     return 123
 | ||
|                 else
 | ||
|                     "abc"
 | ||
| 
 | ||
|             myFunction 3
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|         ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
|         
 | ||
|         Something is off with the body of the `myFunction` definition:
 | ||
|         
 | ||
|         4│      myFunction : U64 -> Str
 | ||
|         5│      myFunction = \x ->
 | ||
|         6│          if x == 5 then
 | ||
|         7│              return 123
 | ||
|                         ^^^^^^^^^^
 | ||
|         
 | ||
|         This returns a value of type:
 | ||
|         
 | ||
|             Num *
 | ||
|         
 | ||
|         But the type annotation on `myFunction` says it should be:
 | ||
|         
 | ||
|             Str
 | ||
| 
 | ||
|         "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         function_with_early_return_generalizes,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             parseItemsWith = \parser ->
 | ||
|                 when List.mapTry ["123", "456"] parser is
 | ||
|                     Ok ok -> Ok ok
 | ||
|                     Err err ->
 | ||
|                         return Err err
 | ||
| 
 | ||
|             u64Nums = parseItemsWith Str.toU64
 | ||
|             u8Nums = parseItemsWith Str.toU8
 | ||
| 
 | ||
|             "$(Inspect.toStr u64Nums) $(Inspect.toStr u8Nums)"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @"" // no errors
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         keyword_try_with_non_result_target,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             invalidTry = \{} ->
 | ||
|                 nonResult = "abc"
 | ||
|                 x = try nonResult
 | ||
| 
 | ||
|                 Ok (x * 2)
 | ||
| 
 | ||
|             invalidTry {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── INVALID TRY TARGET in /code/proj/Main.roc ───────────────────────────────────
 | ||
| 
 | ||
|     This expression cannot be used as a `try` target:
 | ||
| 
 | ||
|     6│          x = try nonResult
 | ||
|                         ^^^^^^^^^
 | ||
| 
 | ||
|     I expected a Result, but it actually has type:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     Hint: Did you forget to wrap the value with an `Ok` or an `Err` tag?
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         question_try_with_non_result_target,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             invalidTry = \{} ->
 | ||
|                 nonResult = "abc"
 | ||
|                 x = nonResult?
 | ||
| 
 | ||
|                 Ok (x * 2)
 | ||
| 
 | ||
|             invalidTry {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── INVALID TRY TARGET in /code/proj/Main.roc ───────────────────────────────────
 | ||
| 
 | ||
|     This expression cannot be tried with the `?` operator:
 | ||
| 
 | ||
|     6│          x = nonResult?
 | ||
|                     ^^^^^^^^^^
 | ||
| 
 | ||
|     I expected a Result, but it actually has type:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     Hint: Did you forget to wrap the value with an `Ok` or an `Err` tag?
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         incompatible_try_errs,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             incompatibleTrys = \{} ->
 | ||
|                 x = try Err 123
 | ||
| 
 | ||
|                 y = try Err "abc"
 | ||
| 
 | ||
|                 Ok (x + y)
 | ||
| 
 | ||
|             incompatibleTrys {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This returns something that's incompatible with the return type of the
 | ||
|     enclosing function:
 | ||
| 
 | ||
|     5│           x = try Err 123
 | ||
|     6│
 | ||
|     7│>          y = try Err "abc"
 | ||
|     8│
 | ||
|     9│           Ok (x + y)
 | ||
| 
 | ||
|     This returns an `Err` of type:
 | ||
| 
 | ||
|         [Err Str, …]
 | ||
| 
 | ||
|     But I expected the function to have return type:
 | ||
| 
 | ||
|         [Err (Num *), …]a
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         keyword_try_prefix_in_pipe,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             readFile : Str -> Str
 | ||
| 
 | ||
|             getFileContents : Str -> Result Str _
 | ||
|             getFileContents = \filePath ->
 | ||
|                 contents =
 | ||
|                     readFile filePath
 | ||
|                     |> try Result.mapErr ErrWrapper
 | ||
| 
 | ||
|                 contents
 | ||
| 
 | ||
|             getFileContents "file.txt"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 1st argument to this function has an unexpected type:
 | ||
| 
 | ||
|      9│>              readFile filePath
 | ||
|     10│               |> try Result.mapErr ErrWrapper
 | ||
| 
 | ||
|     This `readFile` call produces:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     But this function needs its 1st argument to be:
 | ||
| 
 | ||
|         Result ok a
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         keyword_try_suffix_in_pipe,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             readFile : Str -> Str
 | ||
| 
 | ||
|             getFileContents : Str -> Result Str _
 | ||
|             getFileContents = \filePath ->
 | ||
|                 contents =
 | ||
|                     readFile filePath
 | ||
|                     |> Result.mapErr ErrWrapper
 | ||
|                     |> try
 | ||
| 
 | ||
|                 contents
 | ||
| 
 | ||
|             getFileContents "file.txt"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 1st argument to |> has an unexpected type:
 | ||
| 
 | ||
|      9│>              readFile filePath
 | ||
|     10│               |> Result.mapErr ErrWrapper
 | ||
| 
 | ||
|     This `readFile` call produces:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     But |> needs its 1st argument to be:
 | ||
| 
 | ||
|         Result ok a
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         question_try_in_pipe,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             readFile : Str -> Str
 | ||
| 
 | ||
|             getFileContents : Str -> Result Str _
 | ||
|             getFileContents = \filePath ->
 | ||
|                 contents =
 | ||
|                     readFile filePath
 | ||
|                     |> Result.mapErr? ErrWrapper
 | ||
| 
 | ||
|                 contents
 | ||
| 
 | ||
|             getFileContents "file.txt"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 1st argument to this function has an unexpected type:
 | ||
| 
 | ||
|      9│>              readFile filePath
 | ||
|     10│               |> Result.mapErr? ErrWrapper
 | ||
| 
 | ||
|     This `readFile` call produces:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     But this function needs its 1st argument to be:
 | ||
| 
 | ||
|         Result ok a
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         leftover_statement,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 identity {}
 | ||
| 
 | ||
|                 Effect.putLine! "hello"
 | ||
| 
 | ||
|             identity = \x -> x
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── LEFTOVER STATEMENT in /code/proj/Main.roc ───────────────────────────────────
 | ||
| 
 | ||
|     This statement does not produce any effects:
 | ||
| 
 | ||
|     6│      identity {}
 | ||
|             ^^^^^^^^^^^
 | ||
| 
 | ||
|     Standalone statements are only useful if they call effectful
 | ||
|     functions.
 | ||
| 
 | ||
|     Did you forget to use its result? If not, feel free to remove it.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         fx_fn_annotated_as_pure,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! (getCheer "hello")
 | ||
| 
 | ||
|             getCheer : Str -> Str
 | ||
|             getCheer = \msg ->
 | ||
|                 name = Effect.getLine! {}
 | ||
| 
 | ||
|                 "$(msg), $(name)!"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     This call to `Effect.getLine!` might produce an effect:
 | ||
| 
 | ||
|     10│      name = Effect.getLine! {}
 | ||
|                     ^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     However, the type of the enclosing function requires that it's pure:
 | ||
| 
 | ||
|     8│  getCheer : Str -> Str
 | ||
|                    ^^^^^^^^^^
 | ||
| 
 | ||
|     Tip: Replace `->` with `=>` to annotate it as effectful.
 | ||
| 
 | ||
|     You can still run the program with this error, which can be helpful
 | ||
|     when you're debugging.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         fx_fn_annotated_as_pure_stmt,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 trim "hello "
 | ||
| 
 | ||
|             trim : Str -> Str
 | ||
|             trim = \msg ->
 | ||
|                 Effect.putLine! "Trimming $(msg)"
 | ||
|                 Str.trim msg
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── EFFECT IN PURE FUNCTION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     This call to `Effect.putLine!` might produce an effect:
 | ||
| 
 | ||
|     10│      Effect.putLine! "Trimming $(msg)"
 | ||
|              ^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     However, the type of the enclosing function requires that it's pure:
 | ||
| 
 | ||
|     8│  trim : Str -> Str
 | ||
|                ^^^^^^^^^^
 | ||
| 
 | ||
|     Tip: Replace `->` with `=>` to annotate it as effectful.
 | ||
| 
 | ||
|     You can still run the program with this error, which can be helpful
 | ||
|     when you're debugging.
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         nested_function_def_fx_no_bang,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 printHello = \{} ->
 | ||
|                     Effect.putLine! "hello"
 | ||
| 
 | ||
|                 printHello {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This function is effectful, but its name does not indicate so:
 | ||
| 
 | ||
|     6│      printHello = \{} ->
 | ||
|             ^^^^^^^^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         printHello!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         ignored_result_stmt,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.getLine! {}
 | ||
|                 {}
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── IGNORED RESULT in /code/proj/Main.roc ───────────────────────────────────────
 | ||
| 
 | ||
|     The result of this call to `Effect.getLine!` is ignored:
 | ||
| 
 | ||
|     6│      Effect.getLine! {}
 | ||
|             ^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Standalone statements are required to produce an empty record, but the
 | ||
|     type of this one is:
 | ||
| 
 | ||
|         Str
 | ||
| 
 | ||
|     If you still want to ignore it, assign it to `_`, like this:
 | ||
| 
 | ||
|         _ = File.delete! "data.json"
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         ignored_stmt_forgot_to_call,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.getLine!
 | ||
|                 Effect.putLine! "hi"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── IGNORED RESULT in /code/proj/Main.roc ───────────────────────────────────────
 | ||
| 
 | ||
|     The result of this expression is ignored:
 | ||
| 
 | ||
|     6│      Effect.getLine!
 | ||
|             ^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Standalone statements are required to produce an empty record, but the
 | ||
|     type of this one is:
 | ||
| 
 | ||
|         {} => Str
 | ||
| 
 | ||
|     Hint: Did you forget to call the function?
 | ||
| 
 | ||
|     ── LEFTOVER STATEMENT in /code/proj/Main.roc ───────────────────────────────────
 | ||
| 
 | ||
|     This statement does not produce any effects:
 | ||
| 
 | ||
|     6│      Effect.getLine!
 | ||
|             ^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Standalone statements are only useful if they call effectful
 | ||
|     functions.
 | ||
| 
 | ||
|     Did you forget to use its result? If not, feel free to remove it.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         function_def_leftover_bang,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! (hello! {})
 | ||
| 
 | ||
|             hello! = \{} ->
 | ||
|                 "hello"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     This function is pure, but its name suggests otherwise:
 | ||
| 
 | ||
|     8│  hello! = \{} ->
 | ||
|         ^^^^^^
 | ||
| 
 | ||
|     The exclamation mark at the end is reserved for effectful functions.
 | ||
| 
 | ||
|     Hint: Did you forget to run an effect? Is the type annotation wrong?
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         effect_in_top_level_value_def,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             hello =
 | ||
|                 Effect.putLine! "calling hello!"
 | ||
|                 "hello"
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Effect.putLine! hello
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── EFFECT IN TOP-LEVEL in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This call to `Effect.putLine!` might produce an effect:
 | ||
| 
 | ||
|     6│      Effect.putLine! "calling hello!"
 | ||
|             ^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     However, it appears in a top-level def instead of a function. If we
 | ||
|     allowed this, importing this module would produce a side effect.
 | ||
| 
 | ||
|     Tip: If you don't need any arguments, use an empty record:
 | ||
| 
 | ||
|         askName! : {} => Str
 | ||
|         askName! = \{} ->
 | ||
|             Stdout.line! "What's your name?"
 | ||
|             Stdin.line! {}
 | ||
| 
 | ||
|     This will allow the caller to control when the effects run.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         aliased_fx_fn,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 printLn "Hello"
 | ||
| 
 | ||
|             printLn = Effect.putLine!
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This function is effectful, but its name does not indicate so:
 | ||
| 
 | ||
|     8│  printLn = Effect.putLine!
 | ||
|         ^^^^^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         printLn!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unsuffixed_fx_in_record,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 fx = {
 | ||
|                     putLine: Effect.putLine!
 | ||
|                 }
 | ||
|                 fx.putLine "hello world!"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This field's value is an effectful function, but its name does not
 | ||
|     indicate so:
 | ||
| 
 | ||
|     7│          putLine: Effect.putLine!
 | ||
|                 ^^^^^^^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         { readFile! : File.read! }
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unsuffixed_fx_in_record_annotation,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             module [Fx]
 | ||
| 
 | ||
|             Fx : {
 | ||
|                 getLine: {} => Str
 | ||
|             }
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     The type of this record field is an effectful function, but its name
 | ||
|     does not indicate so:
 | ||
| 
 | ||
|     4│      getLine: {} => Str
 | ||
|             ^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         { readFile!: Str => Str }
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         suffixed_pure_fn_in_record_annotation,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             module [Fx]
 | ||
| 
 | ||
|             Fx : {
 | ||
|                 getLine!: {} -> Str
 | ||
|             }
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r"
 | ||
|     ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     The type of this record field is a pure function, but its name
 | ||
|     suggests otherwise:
 | ||
| 
 | ||
|     4│      getLine!: {} -> Str
 | ||
|             ^^^^^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     The exclamation mark at the end is reserved for effectful functions.
 | ||
| 
 | ||
|     Hint: Did you mean to use `=>` instead of `->`?
 | ||
|     "
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unsuffixed_fx_arg,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 ["Hello", "world!"]
 | ||
|                 |> forEach! Effect.putLine!
 | ||
| 
 | ||
|             forEach! : List a, (a => {}) => {}
 | ||
|             forEach! = \l, f ->
 | ||
|                 when l is
 | ||
|                     [] -> {}
 | ||
|                     [x, .. as xs] ->
 | ||
|                         f x
 | ||
|                         forEach! xs f
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This is an effectful function, but its name does not indicate so:
 | ||
| 
 | ||
|     10│  forEach! = \l, f ->
 | ||
|                         ^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         f!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         suffixed_pure_arg,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Ok " hi "
 | ||
|                 |> mapOk  Str.trim
 | ||
|                 |> Result.withDefault ""
 | ||
|                 |> Effect.putLine!
 | ||
| 
 | ||
|             mapOk : Result a err, (a -> b) -> Result b err
 | ||
|             mapOk = \result, fn! ->
 | ||
|                 when result is
 | ||
|                     Ok x -> Ok (fn! x)
 | ||
|                     Err e -> Err e
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     This is a pure function, but its name suggests otherwise:
 | ||
| 
 | ||
|     12│  mapOk = \result, fn! ->
 | ||
|                           ^^^
 | ||
| 
 | ||
|     The exclamation mark at the end is reserved for effectful functions.
 | ||
| 
 | ||
|     Hint: Did you forget to run an effect? Is the type annotation wrong?
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unsuffixed_tuple_fx_field,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 (get, put) = (Effect.getLine!, Effect.putLine!)
 | ||
| 
 | ||
|                 name = get {}
 | ||
|                 put "Hi, $(name)"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This function is effectful, but its name does not indicate so:
 | ||
| 
 | ||
|     6│      (get, put) = (Effect.getLine!, Effect.putLine!)
 | ||
|              ^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         get!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
| 
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This function is effectful, but its name does not indicate so:
 | ||
| 
 | ||
|     6│      (get, put) = (Effect.getLine!, Effect.putLine!)
 | ||
|                   ^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         put!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         suffixed_tuple_pure_field,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 (msg, trim!) = (" hi ", Str.trim)
 | ||
| 
 | ||
|                 Effect.putLine! (trim! msg)
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     This function is pure, but its name suggests otherwise:
 | ||
| 
 | ||
|     6│      (msg, trim!) = (" hi ", Str.trim)
 | ||
|                   ^^^^^
 | ||
| 
 | ||
|     The exclamation mark at the end is reserved for effectful functions.
 | ||
| 
 | ||
|     Hint: Did you forget to run an effect? Is the type annotation wrong?
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unsuffixed_tag_fx_field,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Tag get put = Tag Effect.getLine! Effect.putLine!
 | ||
| 
 | ||
|                 name = get {}
 | ||
|                 put "Hi, $(name)"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This function is effectful, but its name does not indicate so:
 | ||
| 
 | ||
|     6│      Tag get put = Tag Effect.getLine! Effect.putLine!
 | ||
|                 ^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         get!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
| 
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This function is effectful, but its name does not indicate so:
 | ||
| 
 | ||
|     6│      Tag get put = Tag Effect.getLine! Effect.putLine!
 | ||
|                     ^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         put!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         suffixed_tag_pure_field,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 Tag msg trim! = Tag " hi " Str.trim
 | ||
| 
 | ||
|                 Effect.putLine! (trim! msg)
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     This function is pure, but its name suggests otherwise:
 | ||
| 
 | ||
|     6│      Tag msg trim! = Tag " hi " Str.trim
 | ||
|                     ^^^^^
 | ||
| 
 | ||
|     The exclamation mark at the end is reserved for effectful functions.
 | ||
| 
 | ||
|     Hint: Did you forget to run an effect? Is the type annotation wrong?
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         unsuffixed_opaque_fx_field,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             PutLine := Str => {}
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 @PutLine put = @PutLine Effect.putLine!
 | ||
| 
 | ||
|                 put "Hi!"
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── MISSING EXCLAMATION in /code/proj/Main.roc ──────────────────────────────────
 | ||
| 
 | ||
|     This function is effectful, but its name does not indicate so:
 | ||
| 
 | ||
|     8│      @PutLine put = @PutLine Effect.putLine!
 | ||
|                      ^^^
 | ||
| 
 | ||
|     Add an exclamation mark at the end, like:
 | ||
| 
 | ||
|         put!
 | ||
| 
 | ||
|     This will help readers identify it as a source of effects.
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         suffixed_opaque_pure_field,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             Trim := Str -> Str
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 @Trim trim! = @Trim Str.trim
 | ||
| 
 | ||
|                 Effect.putLine! (trim! " hi ")
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r###"
 | ||
|     ── UNNECESSARY EXCLAMATION in /code/proj/Main.roc ──────────────────────────────
 | ||
| 
 | ||
|     This function is pure, but its name suggests otherwise:
 | ||
| 
 | ||
|     8│      @Trim trim! = @Trim Str.trim
 | ||
|                   ^^^^^
 | ||
| 
 | ||
|     The exclamation mark at the end is reserved for effectful functions.
 | ||
| 
 | ||
|     Hint: Did you forget to run an effect? Is the type annotation wrong?
 | ||
|     "###
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         fx_passed_to_untyped_pure_hof,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 pureHigherOrder Effect.putLine! "hi"
 | ||
| 
 | ||
|             pureHigherOrder = \f, x -> f x
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 1st argument to `pureHigherOrder` has an unexpected type:
 | ||
| 
 | ||
|     6│      pureHigherOrder Effect.putLine! "hi"
 | ||
|                             ^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     This `Effect.putLine!` value is a:
 | ||
| 
 | ||
|         Str => {}
 | ||
| 
 | ||
|     But `pureHigherOrder` needs its 1st argument to be:
 | ||
| 
 | ||
|         Str -> {}
 | ||
|     "#
 | ||
|     );
 | ||
| 
 | ||
|     test_report!(
 | ||
|         fx_passed_to_partially_inferred_pure_hof,
 | ||
|         indoc!(
 | ||
|             r#"
 | ||
|             app [main!] { pf: platform "../../../../../crates/cli/tests/test-projects/test-platform-effects-zig/main.roc" }
 | ||
| 
 | ||
|             import pf.Effect
 | ||
| 
 | ||
|             main! = \{} ->
 | ||
|                 pureHigherOrder Effect.putLine! "hi"
 | ||
| 
 | ||
|             pureHigherOrder : _, _ -> _
 | ||
|             pureHigherOrder = \f, x -> f x
 | ||
|             "#
 | ||
|         ),
 | ||
|         @r#"
 | ||
|     ── TYPE MISMATCH in /code/proj/Main.roc ────────────────────────────────────────
 | ||
| 
 | ||
|     This 1st argument to `pureHigherOrder` has an unexpected type:
 | ||
| 
 | ||
|     6│      pureHigherOrder Effect.putLine! "hi"
 | ||
|                             ^^^^^^^^^^^^^^^
 | ||
| 
 | ||
|     This `Effect.putLine!` value is a:
 | ||
| 
 | ||
|         Str => {}
 | ||
| 
 | ||
|     But `pureHigherOrder` needs its 1st argument to be:
 | ||
| 
 | ||
|         Str -> {}
 | ||
|     "#
 | ||
|     );
 | ||
| }
 | 
