mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
b8712bcb30
This change also means we must update the interface of `Dict.empty` and `Set.empty` from ``` Dict.empty : Dict k v ``` to ``` Dict.empty : {} -> Dict k v ```
12871 lines
346 KiB
Rust
12871 lines
346 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::module::parse_header;
|
||
use roc_parse::state::State;
|
||
use roc_parse::test_helpers::parse_expr_with;
|
||
use roc_problem::Severity;
|
||
use roc_region::all::LineInfo;
|
||
use roc_reporting::report::{
|
||
can_problem, parse_problem, type_problem, RenderTarget, Report, ANSI_STYLE_CODES,
|
||
DEFAULT_PALETTE,
|
||
};
|
||
use roc_reporting::report::{RocDocAllocator, RocDocBuilder};
|
||
use roc_solve_problem::TypeError;
|
||
use roc_types::subs::Subs;
|
||
use std::path::PathBuf;
|
||
|
||
fn filename_from_string(str: &str) -> PathBuf {
|
||
let mut filename = PathBuf::new();
|
||
filename.push(str);
|
||
|
||
filename
|
||
}
|
||
|
||
fn to_simple_report(doc: RocDocBuilder) -> Report {
|
||
Report {
|
||
title: "".to_string(),
|
||
doc,
|
||
filename: filename_from_string(r"/code/proj/Main.roc"),
|
||
severity: Severity::RuntimeError,
|
||
}
|
||
}
|
||
|
||
fn promote_expr_to_module(src: &str) -> String {
|
||
let mut buffer = String::from("app \"test\" provides [main] to \"./platform\"\n\nmain =\n");
|
||
|
||
for line in src.lines() {
|
||
// indent the body!
|
||
buffer.push_str(" ");
|
||
buffer.push_str(line);
|
||
buffer.push('\n');
|
||
}
|
||
|
||
buffer
|
||
}
|
||
|
||
fn maybe_save_parse_test_case(test_name: &str, src: &str, is_expr: bool) {
|
||
// First check if the env var indicates we should migrate tests
|
||
if std::env::var("ROC_MIGRATE_REPORTING_TESTS").is_err() {
|
||
return;
|
||
}
|
||
|
||
// Check if we have parse errors
|
||
let arena = Bump::new();
|
||
let has_error = if is_expr {
|
||
parse_expr_with(&arena, src).is_err()
|
||
} else {
|
||
parse_header(&arena, State::new(src.trim().as_bytes())).is_err()
|
||
// TODO: also parse the module defs
|
||
};
|
||
|
||
if !has_error {
|
||
return;
|
||
}
|
||
|
||
let mut path = PathBuf::from(std::env!("ROC_WORKSPACE_DIR"));
|
||
path.push("crates");
|
||
path.push("compiler");
|
||
path.push("parse");
|
||
path.push("tests");
|
||
path.push("snapshots");
|
||
path.push("fail");
|
||
let kind = if is_expr { "expr" } else { "header" };
|
||
path.push(format!("{}.{}.roc", test_name, kind));
|
||
|
||
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") {
|
||
maybe_save_parse_test_case(subdir, src, false);
|
||
// this is already a module
|
||
src.to_string()
|
||
} else {
|
||
maybe_save_parse_test_case(subdir, src, true);
|
||
// this is an expression, promote it to a module
|
||
promote_expr_to_module(src)
|
||
};
|
||
|
||
let loaded = {
|
||
// Use a deterministic temporary directory.
|
||
// We can't have all tests use "tmp" because tests run in parallel,
|
||
// so append the test name to the tmp path.
|
||
let tmp = format!("tmp/{}", subdir);
|
||
let dir = roc_test_utils::TmpDir::new(&tmp);
|
||
|
||
let filename = PathBuf::from("Test.roc");
|
||
let file_path = dir.path().join(filename);
|
||
let full_file_path = file_path.clone();
|
||
let mut file = File::create(file_path).unwrap();
|
||
writeln!(file, "{}", module_src).unwrap();
|
||
let load_config = LoadConfig {
|
||
target_info: roc_target::TargetInfo::default_x86_64(),
|
||
render: RenderTarget::Generic,
|
||
palette: DEFAULT_PALETTE,
|
||
threading: Threading::Single,
|
||
exec_mode: ExecutionMode::Check,
|
||
};
|
||
let result = roc_load::load_and_typecheck(
|
||
arena,
|
||
full_file_path,
|
||
RocCacheDir::Disallowed,
|
||
load_config,
|
||
);
|
||
drop(file);
|
||
|
||
result
|
||
};
|
||
|
||
(module_src, loaded)
|
||
}
|
||
|
||
#[allow(clippy::type_complexity)]
|
||
fn infer_expr_help_new<'a>(
|
||
subdir: &str,
|
||
arena: &'a Bump,
|
||
expr_src: &'a str,
|
||
) -> Result<
|
||
(
|
||
String,
|
||
Vec<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::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::module::parse_header(arena, state) {
|
||
Err(fail) => {
|
||
let interns = Interns::default();
|
||
let home = crate::helpers::test_home();
|
||
|
||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||
|
||
use roc_parse::parser::SyntaxError;
|
||
let problem = fail
|
||
.map_problem(SyntaxError::Header)
|
||
.into_file_error(filename.clone());
|
||
let doc = parse_problem(&alloc, &lines, filename, 0, problem);
|
||
|
||
callback(doc.pretty(&alloc).append(alloc.line()), buf)
|
||
}
|
||
Ok(_) => todo!(),
|
||
}
|
||
}
|
||
|
||
fn report_header_problem_as(src: &str, expected_rendering: &str) {
|
||
let mut buf: String = String::new();
|
||
let arena = Bump::new();
|
||
|
||
let callback = |doc: RocDocBuilder<'_>, buf: &mut String| {
|
||
doc.1
|
||
.render_raw(70, &mut roc_reporting::report::CiWrite::new(buf))
|
||
.expect("list_reports")
|
||
};
|
||
|
||
list_header_reports(&arena, src, &mut buf, callback);
|
||
|
||
// convenient to copy-paste the generated message
|
||
if buf != expected_rendering {
|
||
for line in buf.split('\n') {
|
||
println!(" {}", line);
|
||
}
|
||
}
|
||
|
||
assert_eq!(buf, expected_rendering);
|
||
}
|
||
|
||
fn color_report_problem_as(src: &str, expected_rendering: &str) {
|
||
let mut buf: String = String::new();
|
||
let arena = Bump::new();
|
||
|
||
let callback = |doc: RocDocBuilder<'_>, buf: &mut String| {
|
||
doc.1
|
||
.render_raw(
|
||
70,
|
||
&mut roc_reporting::report::ColorWrite::new(
|
||
&roc_reporting::report::DEFAULT_PALETTE,
|
||
buf,
|
||
),
|
||
)
|
||
.expect("list_reports")
|
||
};
|
||
|
||
list_reports(&arena, src, &mut buf, callback);
|
||
|
||
let readable = human_readable(&buf);
|
||
|
||
assert_eq!(readable, expected_rendering);
|
||
}
|
||
|
||
/// Do not call this directly! Use the test_report macro below!
|
||
fn __new_report_problem_as(test_name: &str, src: &str, check_render: impl FnOnce(&str)) {
|
||
let arena = Bump::new();
|
||
|
||
let finalize_render = |doc: RocDocBuilder<'_>, buf: &mut String| {
|
||
doc.1
|
||
.render_raw(70, &mut roc_reporting::report::CiWrite::new(buf))
|
||
.expect("list_reports")
|
||
};
|
||
|
||
let buf = list_reports_new(test_name, &arena, src, finalize_render);
|
||
|
||
check_render(buf.as_str());
|
||
}
|
||
|
||
macro_rules! test_report {
|
||
($(#[$meta:meta])* $test_name:ident, $program:expr, @$output:literal) => {
|
||
test_report!($(#[$meta])* $test_name, $program, |golden| insta::assert_snapshot!(golden, @$output) );
|
||
};
|
||
($(#[$meta:meta])* $test_name: ident, $program:expr, $expecting:expr) => {
|
||
#[test]
|
||
$(#[$meta])*
|
||
fn $test_name() {
|
||
__new_report_problem_as(std::stringify!($test_name), $program, $expecting)
|
||
}
|
||
}
|
||
}
|
||
|
||
macro_rules! test_no_problem {
|
||
($(#[$meta:meta])* $test_name: ident, $program:expr) => {
|
||
#[test]
|
||
$(#[$meta])*
|
||
fn $test_name() {
|
||
__new_report_problem_as(std::stringify!($test_name), $program, |golden| pretty_assertions::assert_eq!(golden, ""))
|
||
}
|
||
}
|
||
}
|
||
|
||
fn human_readable(str: &str) -> String {
|
||
str.replace(ANSI_STYLE_CODES.red, "<red>")
|
||
.replace(ANSI_STYLE_CODES.white, "<white>")
|
||
.replace(ANSI_STYLE_CODES.blue, "<blue>")
|
||
.replace(ANSI_STYLE_CODES.yellow, "<yellow>")
|
||
.replace(ANSI_STYLE_CODES.green, "<green>")
|
||
.replace(ANSI_STYLE_CODES.cyan, "<cyan>")
|
||
.replace(ANSI_STYLE_CODES.magenta, "<magenta>")
|
||
.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 ─────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The List module does not expose `isempty`:
|
||
|
||
4│ List.isempty 1 2
|
||
^^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
List.isEmpty
|
||
List.set
|
||
List.get
|
||
List.keepIf
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_unused_def,
|
||
indoc!(
|
||
r#"
|
||
x = 1
|
||
y = 2
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not used anywhere in your code.
|
||
|
||
5│ y = 2
|
||
^
|
||
|
||
If you didn't intend on using `y` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_shadowing,
|
||
indoc!(
|
||
r#"
|
||
i = 1
|
||
|
||
s = \i ->
|
||
i + 1
|
||
|
||
s i
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `i` name is first defined here:
|
||
|
||
4│ i = 1
|
||
^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
6│ s = \i ->
|
||
^
|
||
|
||
Since these variables have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_shadowing_in_annotation,
|
||
indoc!(
|
||
r#"
|
||
Booly : [Yes, No]
|
||
|
||
Booly : [Yes, No, Maybe]
|
||
|
||
x : List Booly
|
||
x = []
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Booly` name is first defined here:
|
||
|
||
4│ Booly : [Yes, No]
|
||
^^^^^^^^^^^^^^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
6│ Booly : [Yes, No, Maybe]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Since these aliases have the same name, it's easy to use the wrong one
|
||
on accident. Give one of them a new name.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_precedence_problem_single_line,
|
||
indoc!(
|
||
r#"x = 1
|
||
y =
|
||
if selectedId != thisId == adminsId then
|
||
4
|
||
|
||
else
|
||
5
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Using != and == together requires parentheses, to clarify how they
|
||
should be grouped.
|
||
|
||
6│ if selectedId != thisId == adminsId then
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore = "Blocked on https://github.com/roc-lang/roc/issues/3385"]
|
||
unrecognized_name,
|
||
indoc!(
|
||
r#"
|
||
foo = { x: 1 == 1, y: 0x4 }
|
||
|
||
baz = 3
|
||
|
||
main : Str
|
||
main =
|
||
when foo.y is
|
||
4 -> bar baz "yay"
|
||
_ -> "nay"
|
||
|
||
main
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `bar` in this scope.
|
||
|
||
8│ 4 -> bar baz "yay"
|
||
^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
baz
|
||
Str
|
||
Err
|
||
main
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
lowercase_primitive_tag_bool,
|
||
indoc!(
|
||
r#"
|
||
if true then 1 else 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `true` in this scope.
|
||
|
||
4│ if true then 1 else 2
|
||
^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Str
|
||
Frac
|
||
Num
|
||
Err
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_precedence_problem_multiline,
|
||
indoc!(
|
||
r#"
|
||
if
|
||
1
|
||
== 2
|
||
== 3
|
||
then
|
||
2
|
||
|
||
else
|
||
3
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Using more than one == like this requires parentheses, to clarify how
|
||
things should be grouped.
|
||
|
||
5│> 1
|
||
6│> == 2
|
||
7│> == 3
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unused_arg_and_unused_def,
|
||
indoc!(
|
||
r#"
|
||
y = 9
|
||
|
||
box = \class, htmlChildren ->
|
||
div [class] []
|
||
|
||
div = \_, _ -> 4
|
||
|
||
box "wizard" []
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`box` doesn't use `htmlChildren`.
|
||
|
||
6│ box = \class, htmlChildren ->
|
||
^^^^^^^^^^^^
|
||
|
||
If you don't need `htmlChildren`, then you can just remove it. However,
|
||
if you really do need `htmlChildren` as an argument of `box`, prefix it
|
||
with an underscore, like this: "_`htmlChildren`". Adding an underscore
|
||
at the start of a variable name is a way of saying that the variable
|
||
is not used.
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not used anywhere in your code.
|
||
|
||
4│ y = 9
|
||
^
|
||
|
||
If you didn't intend on using `y` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
#[test]
|
||
fn report_value_color() {
|
||
let src: &str = indoc!(
|
||
r#"
|
||
activityIndicatorLarge = div
|
||
|
||
view activityIndicatorLarge
|
||
"#
|
||
);
|
||
|
||
let arena = Bump::new();
|
||
let (_type_problems, _can_problems, home, interns) =
|
||
infer_expr_help(&arena, src).expect("parse error");
|
||
|
||
let mut buf = String::new();
|
||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||
|
||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||
|
||
let symbol = interns.symbol(test_home(), "activityIndicatorLarge".into());
|
||
|
||
to_simple_report(alloc.symbol_unqualified(symbol)).render_color_terminal(
|
||
&mut buf,
|
||
&alloc,
|
||
&DEFAULT_PALETTE,
|
||
);
|
||
|
||
assert_eq!(human_readable(&buf), "<blue>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 ───────────────────────────────────── /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?
|
||
|
||
Ok
|
||
List
|
||
Err
|
||
Box
|
||
"#
|
||
),
|
||
);
|
||
}
|
||
|
||
test_report!(
|
||
if_condition_not_bool,
|
||
indoc!(
|
||
r#"
|
||
if "foo" then 2 else 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` condition needs to be a Bool:
|
||
|
||
4│ if "foo" then 2 else 3
|
||
^^^^^
|
||
|
||
Right now it’s a string of type:
|
||
|
||
Str
|
||
|
||
But I need every `if` condition to evaluate to a Bool—either `Bool.true`
|
||
or `Bool.false`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_if_guard,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2 if 1 -> 0x0
|
||
_ -> 0x1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` guard condition needs to be a Bool:
|
||
|
||
4│ when 1 is
|
||
5│> 2 if 1 -> 0x0
|
||
6│ _ -> 0x1
|
||
|
||
Right now it’s a number of type:
|
||
|
||
Num *
|
||
|
||
But I need every `if` guard condition to evaluate to a Bool—either
|
||
`Bool.true` or `Bool.false`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
if_2_branch_mismatch,
|
||
indoc!(
|
||
r#"
|
||
if Bool.true then 2 else "foo"
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` has an `else` branch with a different type from its `then` branch:
|
||
|
||
4│ if Bool.true then 2 else "foo"
|
||
^^^^^
|
||
|
||
The `else` branch is a string of type:
|
||
|
||
Str
|
||
|
||
but the `then` branch has the type:
|
||
|
||
Num *
|
||
|
||
All branches in an `if` must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
if_3_branch_mismatch,
|
||
indoc!(
|
||
r#"
|
||
if Bool.true then 2 else if Bool.false then 2 else "foo"
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd branch of this `if` does not match all the previous branches:
|
||
|
||
4│ if Bool.true then 2 else if Bool.false then 2 else "foo"
|
||
^^^^^
|
||
|
||
The 3rd branch is a string of type:
|
||
|
||
Str
|
||
|
||
But all the previous branches have type:
|
||
|
||
Num *
|
||
|
||
All branches in an `if` must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_branch_mismatch,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2 -> "foo"
|
||
3 -> {}
|
||
_ -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd branch of this `when` does not match all the previous branches:
|
||
|
||
4│ when 1 is
|
||
5│ 2 -> "foo"
|
||
6│> 3 -> {}
|
||
7│ _ -> ""
|
||
|
||
The 2nd branch is a record of type:
|
||
|
||
{}
|
||
|
||
But all the previous branches have type:
|
||
|
||
Str
|
||
|
||
All branches of a `when` must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
elem_in_list,
|
||
indoc!(
|
||
r#"
|
||
[1, 3, "foo"]
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This list contains elements with different types:
|
||
|
||
4│ [1, 3, "foo"]
|
||
^^^^^
|
||
|
||
Its 3rd element is a string of type:
|
||
|
||
Str
|
||
|
||
However, the preceding elements in the list all have the type:
|
||
|
||
Num *
|
||
|
||
Every element in a list must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unwrap_num_elem_in_list,
|
||
indoc!(
|
||
r#"
|
||
[1, 2.2, 0x3]
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This list contains elements with different types:
|
||
|
||
4│ [1, 2.2, 0x3]
|
||
^^^
|
||
|
||
Its 3rd element is an integer of type:
|
||
|
||
Int *
|
||
|
||
However, the preceding elements in the list all have the type:
|
||
|
||
Frac *
|
||
|
||
Every element in a list must have the same type!
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_update_value,
|
||
indoc!(
|
||
r#"
|
||
x : { foo : {} }
|
||
x = { foo: {} }
|
||
|
||
{ x & foo: "bar" }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I cannot update the `.foo` field like this:
|
||
|
||
7│ { x & foo: "bar" }
|
||
^^^^^
|
||
|
||
You are trying to update `.foo` to be a string of type:
|
||
|
||
Str
|
||
|
||
But it should be:
|
||
|
||
{}
|
||
|
||
Record update syntax does not allow you to change the type of fields.
|
||
You can achieve that with record literal syntax.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
circular_type,
|
||
indoc!(
|
||
r#"
|
||
f = \g -> g g
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
4│ f = \g -> g g
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
(∞ -> a) -> a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_recursion,
|
||
indoc!(
|
||
r#"
|
||
f = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
4│ f = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_mutual_recursion,
|
||
indoc!(
|
||
r#"
|
||
f = \x -> g x
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
4│ f = \x -> g x
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> *
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `g`:
|
||
|
||
5│ g = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_mutual_recursion_annotated,
|
||
indoc!(
|
||
r#"
|
||
f : a -> List a
|
||
f = \x -> g x
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
5│ f = \x -> g x
|
||
^^^
|
||
|
||
This `g` call produces:
|
||
|
||
List List a
|
||
|
||
But you are trying to use it as:
|
||
|
||
List a
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `List` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_mutual_recursion_dually_annotated_lie,
|
||
indoc!(
|
||
r#"
|
||
f : a -> List a
|
||
f = \x -> g x
|
||
g : b -> List b
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
7│ g = \x -> f [x]
|
||
^^^
|
||
|
||
The argument is a list of type:
|
||
|
||
List b
|
||
|
||
But `f` 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 `List` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_recursion_inference_var,
|
||
indoc!(
|
||
r#"
|
||
f : _
|
||
f = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
5│ f = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_recursion_with_deep_inference_var,
|
||
indoc!(
|
||
r#"
|
||
f : _ -> List _
|
||
f = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
5│ f = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mutual_polymorphic_recursion_with_inference_var,
|
||
indoc!(
|
||
r#"
|
||
f : _ -> List _
|
||
f = \x -> g x
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
// TODO: the second error is duplicated because when solving `f : _ -> List _`, we
|
||
// introduce the variable for `f` twice: once to solve `f` without generalization,
|
||
// and then a second time to properly generalize it. When a def is unannotated
|
||
// (like in `g`) the same variable gets used both times, because the type of `g` is
|
||
// only an unbound type variable. However, for `f`, we run `type_to_var` twice,
|
||
// receiving two separate variables, and the second variable doesn't have the cycle
|
||
// error already recorded for the first.
|
||
// The way to resolve this is to always give type annotation signatures an extra
|
||
// variables they can put themselves in, and to run the constraint algorithm
|
||
// against that extra variable, rather than possibly having to translate a `Type`
|
||
// again.
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
5│ f = \x -> g x
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List *
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `g`:
|
||
|
||
6│ g = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mutual_polymorphic_recursion_with_inference_var_second,
|
||
indoc!(
|
||
r#"
|
||
f = \x -> g x
|
||
g : _ -> List _
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
4│ f = \x -> g x
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List *
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `g`:
|
||
|
||
6│ g = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_field_mismatch,
|
||
indoc!(
|
||
r#"
|
||
bar = { bar : 0x3 }
|
||
|
||
f : { foo : Num.Int * } -> [Yes, No]
|
||
f = \_ -> Yes
|
||
|
||
f bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
9│ f bar
|
||
^^^
|
||
|
||
This `bar` value is a:
|
||
|
||
{ bar : Int * }
|
||
|
||
But `f` needs its 1st argument to be:
|
||
|
||
{ foo : Int * }
|
||
|
||
Tip: Seems like a record field typo. Maybe `bar` should be `foo`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_mismatch,
|
||
indoc!(
|
||
r#"
|
||
f : [Red, Green] -> [Yes, No]
|
||
f = \_ -> Yes
|
||
|
||
f Blue
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
7│ f Blue
|
||
^^^^
|
||
|
||
This `Blue` tag has the type:
|
||
|
||
[Blue]
|
||
|
||
But `f` needs its 1st argument to be:
|
||
|
||
[Green, Red]
|
||
|
||
Tip: Seems like a tag typo. Maybe `Blue` should be `Red`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_with_arguments_mismatch,
|
||
indoc!(
|
||
r#"
|
||
f : [Red (Num.Int *), Green Str] -> Str
|
||
f = \_ -> "yes"
|
||
|
||
f (Blue 3.14)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
7│ f (Blue 3.14)
|
||
^^^^^^^^^
|
||
|
||
This `Blue` tag application has the type:
|
||
|
||
[Blue (Frac *)]
|
||
|
||
But `f` needs its 1st argument to be:
|
||
|
||
[Green Str, Red (Int *)]
|
||
|
||
Tip: Seems like a tag typo. Maybe `Blue` should be `Red`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
from_annotation_if,
|
||
indoc!(
|
||
r#"
|
||
x : Num.Int *
|
||
x = if Bool.true then 3.14 else 4
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the `then` branch of this `if` expression:
|
||
|
||
4│ x : Num.Int *
|
||
5│ x = if Bool.true then 3.14 else 4
|
||
^^^^
|
||
|
||
The 1st branch is a fraction of type:
|
||
|
||
Frac *
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
Int *
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
from_annotation_when,
|
||
indoc!(
|
||
r#"
|
||
x : Num.Int *
|
||
x =
|
||
when True is
|
||
_ -> 3.14
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : Num.Int *
|
||
5│ x =
|
||
6│> when True is
|
||
7│> _ -> 3.14
|
||
|
||
This `when` expression produces:
|
||
|
||
Frac *
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
Int *
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
from_annotation_function,
|
||
indoc!(
|
||
r#"
|
||
x : Num.Int * -> Num.Int *
|
||
x = \_ -> 3.14
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : Num.Int * -> Num.Int *
|
||
5│ x = \_ -> 3.14
|
||
^^^^
|
||
|
||
The body is a fraction of type:
|
||
|
||
Frac *
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
Int *
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
fncall_value,
|
||
indoc!(
|
||
r#"
|
||
x : Num.I64
|
||
x = 42
|
||
|
||
x 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `x` value is not a function, but it was given 1 argument:
|
||
|
||
7│ x 3
|
||
^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
fncall_overapplied,
|
||
indoc!(
|
||
r#"
|
||
f : Num.I64 -> Num.I64
|
||
f = \_ -> 42
|
||
|
||
f 1 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `f` function expects 1 argument, but it got 2 instead:
|
||
|
||
7│ f 1 2
|
||
^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
fncall_underapplied,
|
||
indoc!(
|
||
r#"
|
||
f : Num.I64, Num.I64 -> Num.I64
|
||
f = \_, _ -> 42
|
||
|
||
f 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `f` function expects 2 arguments, but it got only 1:
|
||
|
||
7│ f 1
|
||
^
|
||
|
||
Roc does not allow functions to be partially applied. Use a closure to
|
||
make partial application explicit.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_when_condition,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
{} -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when 1 is
|
||
5│ {} -> 42
|
||
|
||
The `when` condition is a number of type:
|
||
|
||
Num *
|
||
|
||
But the branch patterns have type:
|
||
|
||
{}a
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_when_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2 -> 3
|
||
{} -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern in this `when` does not match the previous ones:
|
||
|
||
6│ {} -> 42
|
||
^^
|
||
|
||
The 2nd pattern is trying to match record values of type:
|
||
|
||
{}a
|
||
|
||
But all the previous branches match:
|
||
|
||
Num *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_guard_mismatch_alias,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{ foo: True } -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when { foo: 1 } is
|
||
5│ { foo: True } -> 42
|
||
|
||
The `when` condition is a record of type:
|
||
|
||
{ foo : Num * }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ foo : [True] }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_guard_mismatch,
|
||
indoc!(
|
||
r#"
|
||
when { foo: "" } is
|
||
{ foo: True } -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when { foo: "" } is
|
||
5│ { foo: True } -> 42
|
||
|
||
The `when` condition is a record of type:
|
||
|
||
{ foo : Str }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ foo : [True] }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
// needs some improvement, but the principle works
|
||
test_report!(
|
||
pattern_guard_does_not_bind_label,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{ foo: _ } -> foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `foo` in this scope.
|
||
|
||
5│ { foo: _ } -> foo
|
||
^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Box
|
||
Bool
|
||
U8
|
||
F64
|
||
"###
|
||
);
|
||
|
||
test_report! {
|
||
pattern_guard_can_be_shadowed_above,
|
||
indoc!(
|
||
r#"
|
||
foo = 3
|
||
|
||
when { foo: 1 } is
|
||
{ foo: 2 } -> foo
|
||
_ -> foo
|
||
"#
|
||
),
|
||
@"" // should give no error
|
||
}
|
||
|
||
test_report! {
|
||
pattern_guard_can_be_shadowed_below,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{ foo: 2 } ->
|
||
foo = 3
|
||
|
||
foo
|
||
_ -> 3
|
||
"#
|
||
),
|
||
// should give no error
|
||
@""
|
||
}
|
||
|
||
test_report!(
|
||
pattern_or_pattern_mismatch,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{} | 1 -> 3
|
||
"#
|
||
),
|
||
// Just putting this here. We should probably handle or-patterns better
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern in this branch does not match the previous ones:
|
||
|
||
5│ {} | 1 -> 3
|
||
^
|
||
|
||
The 2nd pattern is trying to match numbers:
|
||
|
||
Num *
|
||
|
||
But all the previous branches match:
|
||
|
||
{}a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_let_mismatch,
|
||
indoc!(
|
||
r#"
|
||
(Foo x) = 42
|
||
|
||
x
|
||
"#
|
||
),
|
||
// Maybe this should specifically say the pattern doesn't work?
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
4│ (Foo x) = 42
|
||
^^
|
||
|
||
It is a number of type:
|
||
|
||
Num *
|
||
|
||
But you are trying to use it as:
|
||
|
||
[Foo *]
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
from_annotation_complex_pattern,
|
||
indoc!(
|
||
r#"
|
||
{ x } : { x : Num.Int * }
|
||
{ x } = { x: 4.0 }
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of this definition:
|
||
|
||
4│ { x } : { x : Num.Int * }
|
||
5│ { x } = { x: 4.0 }
|
||
^^^^^^^^^^
|
||
|
||
The body is a record of type:
|
||
|
||
{ x : Frac * }
|
||
|
||
But the type annotation says it should be:
|
||
|
||
{ x : Int * }
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_int_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
100A -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer pattern is malformed:
|
||
|
||
5│ 100A -> 3
|
||
^^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_float_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2.X -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float pattern is malformed:
|
||
|
||
5│ 2.X -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_hex_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
0xZ -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This hex integer pattern is malformed:
|
||
|
||
5│ 0xZ -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_oct_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
0o9 -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This octal integer pattern is malformed:
|
||
|
||
5│ 0o9 -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_bin_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
0b4 -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This binary integer pattern is malformed:
|
||
|
||
5│ 0b4 -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
missing_fields,
|
||
indoc!(
|
||
r#"
|
||
x : { a : Num.Int *, b : Num.Frac *, c : Str }
|
||
x = { b: 4.0 }
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : { a : Num.Int *, b : Num.Frac *, c : Str }
|
||
5│ x = { b: 4.0 }
|
||
^^^^^^^^^^
|
||
|
||
The body is a record of type:
|
||
|
||
{ b : Frac * }
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
{
|
||
a : Int *,
|
||
b : Frac *,
|
||
c : Str,
|
||
}
|
||
|
||
Tip: Looks like the c and a fields are missing.
|
||
"###
|
||
);
|
||
|
||
// this previously reported the message below, not sure which is better
|
||
//
|
||
// Something is off with the body of the `f` definition:
|
||
//
|
||
// 1│ f : a, b -> a
|
||
// 2│ f = \x, y -> if Bool.true then x else y
|
||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
//
|
||
// The body is an anonymous function of type:
|
||
//
|
||
// a, a -> a
|
||
//
|
||
// But the type annotation on `f` says it should be:
|
||
//
|
||
// a, b -> a
|
||
test_report!(
|
||
bad_double_rigid,
|
||
indoc!(
|
||
r#"
|
||
f : a, b -> a
|
||
f = \x, y -> if Bool.true then x else y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the `else` branch of this `if` expression:
|
||
|
||
4│ f : a, b -> a
|
||
5│ f = \x, y -> if Bool.true then x else y
|
||
^
|
||
|
||
This `y` value is a:
|
||
|
||
b
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
a
|
||
|
||
Tip: Your type annotation uses `b` and `a` as separate type variables.
|
||
Your code seems to be saying they are the same though. Maybe they
|
||
should be the same in your type annotation? Maybe your code uses them
|
||
in a weird way?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
bad_rigid_function,
|
||
indoc!(
|
||
r#"
|
||
f : Str -> msg
|
||
f = \_ -> Foo
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : Str -> msg
|
||
5│ f = \_ -> Foo
|
||
^^^
|
||
|
||
This `Foo` tag has the type:
|
||
|
||
[Foo]
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
msg
|
||
|
||
Tip: The type annotation uses the type variable `msg` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a tag value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
bad_rigid_value,
|
||
indoc!(
|
||
r#"
|
||
f : msg
|
||
f = 0x3
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : msg
|
||
5│ f = 0x3
|
||
^^^
|
||
|
||
The body is an integer of type:
|
||
|
||
Int *
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
msg
|
||
|
||
Tip: The type annotation uses the type variable `msg` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `Int` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
// TODO improve tag suggestions
|
||
test_report!(
|
||
typo_lowercase_ok,
|
||
indoc!(
|
||
r#"
|
||
f : Str -> [Ok Num.I64, InvalidFoo]
|
||
f = \_ -> ok 4
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `ok` in this scope.
|
||
|
||
5│ f = \_ -> ok 4
|
||
^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Ok
|
||
U8
|
||
Box
|
||
Eq
|
||
"###
|
||
);
|
||
|
||
// these error messages seem pretty helpful
|
||
test_report!(
|
||
typo_uppercase_ok,
|
||
indoc!(
|
||
r#"
|
||
f : Str -> Num.I64
|
||
f = \_ ->
|
||
ok = 3
|
||
|
||
Ok
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`ok` is not used anywhere in your code.
|
||
|
||
6│ ok = 3
|
||
^^
|
||
|
||
If you didn't intend on using `ok` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : Str -> Num.I64
|
||
5│ f = \_ ->
|
||
6│ ok = 3
|
||
7│
|
||
8│ Ok
|
||
^^
|
||
|
||
This `Ok` tag has the type:
|
||
|
||
[Ok]
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
I64
|
||
"###
|
||
);
|
||
|
||
// invalid recursion
|
||
test_report!(
|
||
circular_definition_self,
|
||
indoc!(
|
||
r#"
|
||
f = f
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`f` is defined directly in terms of itself:
|
||
|
||
4│ f = f
|
||
^^^^^
|
||
|
||
Roc evaluates values strictly, so running this program would enter an
|
||
infinite loop!
|
||
|
||
Hint: Did you mean to define `f` as a function?
|
||
"###
|
||
);
|
||
|
||
// invalid mutual recursion
|
||
test_report!(
|
||
circular_definition,
|
||
indoc!(
|
||
r#"
|
||
foo = bar
|
||
|
||
bar = foo
|
||
|
||
foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `foo` definition is causing a very tricky infinite loop:
|
||
|
||
4│ foo = bar
|
||
^^^
|
||
|
||
The `foo` value depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ foo
|
||
│ ↓
|
||
│ bar
|
||
└─────┘
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_empty_record,
|
||
indoc!(
|
||
r#"
|
||
x = {}
|
||
|
||
{ x & foo: 3 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `x` record doesn’t have a `foo` field:
|
||
|
||
6│ { x & foo: 3 }
|
||
^^^^^^
|
||
|
||
In fact, `x` is a record with no fields at all!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_record,
|
||
indoc!(
|
||
r#"
|
||
x = { fo: 3, bar: 4 }
|
||
|
||
{ x & foo: 3 }
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `x` record doesn’t have a `foo` field:
|
||
|
||
6│ { x & foo: 3 }
|
||
^^^^^^
|
||
|
||
There may be a typo. These `x` fields are the most similar:
|
||
|
||
{
|
||
fo : Num *,
|
||
bar : Num *,
|
||
}
|
||
|
||
Maybe `foo:` should be `fo:` instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_record_ext,
|
||
indoc!(
|
||
r#"
|
||
f : { fo: Num.I64 }ext -> Num.I64
|
||
f = \r ->
|
||
r2 = { r & foo: r.fo }
|
||
|
||
r2.fo
|
||
|
||
f
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `r` record doesn’t have a `foo` field:
|
||
|
||
6│ r2 = { r & foo: r.fo }
|
||
^^^^^^^^^
|
||
|
||
There may be a typo. These `r` fields are the most similar:
|
||
|
||
{
|
||
fo : I64,
|
||
}ext
|
||
|
||
Maybe `foo:` should be `fo:` instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_record_snippet,
|
||
indoc!(
|
||
r#"
|
||
x = { fo: 3, bar: 4, baz: 3, spam: 42, foobar: 3 }
|
||
|
||
{ x & foo: 3 }
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `x` record doesn’t have a `foo` field:
|
||
|
||
6│ { x & foo: 3 }
|
||
^^^^^^
|
||
|
||
There may be a typo. These `x` fields are the most similar:
|
||
|
||
{
|
||
fo : Num *,
|
||
foobar : Num *,
|
||
bar : Num *,
|
||
baz : Num *,
|
||
…
|
||
}
|
||
|
||
Maybe `foo:` should be `fo:` instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
plus_on_str,
|
||
indoc!(
|
||
r#"
|
||
0x4 + "foo"
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ 0x4 + "foo"
|
||
^^^^^
|
||
|
||
The argument is a string of type:
|
||
|
||
Str
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Int *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
int_frac,
|
||
indoc!(
|
||
r#"
|
||
0x4 + 3.14
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ 0x4 + 3.14
|
||
^^^^
|
||
|
||
The argument is a fraction of type:
|
||
|
||
Frac *
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Int *
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
boolean_tag,
|
||
indoc!(
|
||
r#"
|
||
42 + True
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ 42 + True
|
||
^^^^
|
||
|
||
This `True` tag has the type:
|
||
|
||
[True]
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Num *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_missing,
|
||
indoc!(
|
||
r#"
|
||
f : [A] -> [A, B]
|
||
f = \a -> a
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : [A] -> [A, B]
|
||
5│ f = \a -> a
|
||
^
|
||
|
||
This `a` value is a:
|
||
|
||
[A]
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
[A, B]
|
||
|
||
Tip: Looks like a closed tag union does not have the `B` tag.
|
||
|
||
Tip: Closed tag unions can't grow, because that might change the size
|
||
in memory. Can you use an open tag union?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tags_missing,
|
||
indoc!(
|
||
r#"
|
||
f : [A] -> [A, B, C]
|
||
f = \a -> a
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : [A] -> [A, B, C]
|
||
5│ f = \a -> a
|
||
^
|
||
|
||
This `a` value is a:
|
||
|
||
[A]
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
[A, B, C]
|
||
|
||
Tip: Looks like a closed tag union does not have the `B` and `C` tags.
|
||
|
||
Tip: Closed tag unions can't grow, because that might change the size
|
||
in memory. Can you use an open tag union?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_fn_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
Either : [Left {}, Right Str]
|
||
|
||
x : Either
|
||
x = Left {}
|
||
|
||
f : Either -> {}
|
||
f = \Left v -> v
|
||
|
||
f x
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This pattern does not cover all the possibilities:
|
||
|
||
10│ f = \Left v -> v
|
||
^^^^^^
|
||
|
||
Other possibilities include:
|
||
|
||
Right _
|
||
|
||
I would have to crash if I saw one of those! So rather than pattern
|
||
matching in function arguments, put a `when` in the function body to
|
||
account for all possibilities.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_let_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x : [Left {}, Right Str]
|
||
x = Left {}
|
||
|
||
|
||
(Left y) = x
|
||
|
||
y
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
8│ (Left y) = x
|
||
^
|
||
|
||
This `x` value is a:
|
||
|
||
[Left {}, Right Str]
|
||
|
||
But you are trying to use it as:
|
||
|
||
[Left *]
|
||
|
||
Tip: Looks like a closed tag union does not have the `Right` tag.
|
||
|
||
Tip: Closed tag unions can't grow, because that might change the size
|
||
in memory. Can you use an open tag union?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_when_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
when 0x1 is
|
||
2 -> 0x3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> when 0x1 is
|
||
5│> 2 -> 0x3
|
||
|
||
Other possibilities include:
|
||
|
||
_
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_bool_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x : [Red, Green]
|
||
x = Green
|
||
|
||
when x is
|
||
Red -> 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
7│> when x is
|
||
8│> Red -> 3
|
||
|
||
Other possibilities include:
|
||
|
||
Green
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_enum_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x : [Red, Green, Blue]
|
||
x = Red
|
||
|
||
when x is
|
||
Red -> 0
|
||
Green -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
7│> when x is
|
||
8│> Red -> 0
|
||
9│> Green -> 1
|
||
|
||
Other possibilities include:
|
||
|
||
Blue
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_remote_data_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
RemoteData e a : [NotAsked, Loading, Failure e, Success a]
|
||
|
||
x : RemoteData Num.I64 Str
|
||
|
||
when x is
|
||
NotAsked -> 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
8│> when x is
|
||
9│> NotAsked -> 3
|
||
|
||
Other possibilities include:
|
||
|
||
Failure _
|
||
Loading
|
||
Success _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_record_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x = { a: 3 }
|
||
|
||
when x is
|
||
{ a: 4 } -> 4
|
||
"#
|
||
),
|
||
// Tip: Looks like a record field guard is not exhaustive. Learn more about record pattern matches at TODO.
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when x is
|
||
7│> { a: 4 } -> 4
|
||
|
||
Other possibilities include:
|
||
|
||
{ a }
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_record_guard_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
y : [Nothing, Just Num.I64]
|
||
y = Just 4
|
||
x = { a: y, b: 42}
|
||
|
||
when x is
|
||
{ a: Nothing } -> 4
|
||
{ a: Just 3 } -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
8│> when x is
|
||
9│> { a: Nothing } -> 4
|
||
10│> { a: Just 3 } -> 4
|
||
|
||
Other possibilities include:
|
||
|
||
{ a: Just _ }
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_nested_tag_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
when Record Nothing 1 is
|
||
Record (Nothing) b -> b
|
||
Record (Just 3) b -> b
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> when Record Nothing 1 is
|
||
5│> Record (Nothing) b -> b
|
||
6│> Record (Just 3) b -> b
|
||
|
||
Other possibilities include:
|
||
|
||
Record (Just _) _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_int_redundant,
|
||
indoc!(
|
||
r#"
|
||
when 0x1 is
|
||
2 -> 3
|
||
2 -> 4
|
||
_ -> 5
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern is redundant:
|
||
|
||
4│ when 0x1 is
|
||
5│ 2 -> 3
|
||
6│> 2 -> 4
|
||
7│ _ -> 5
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unify_alias_other,
|
||
indoc!(
|
||
r#"
|
||
Foo a : { x : Num.Int a }
|
||
|
||
f : Foo a -> Num.Int a
|
||
f = \r -> r.x
|
||
|
||
f { y: 3.14 }
|
||
"#
|
||
),
|
||
// de-aliases the alias to give a better error message
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
9│ f { y: 3.14 }
|
||
^^^^^^^^^^^
|
||
|
||
The argument is a record of type:
|
||
|
||
{ y : Frac * }
|
||
|
||
But `f` needs its 1st argument to be:
|
||
|
||
{ x : Int a }
|
||
|
||
Tip: Seems like a record field typo. Maybe `y` should be `x`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
cyclic_alias,
|
||
indoc!(
|
||
r#"
|
||
Foo : { x : Bar }
|
||
Bar : { y : Foo }
|
||
|
||
f : Foo
|
||
|
||
f
|
||
"#
|
||
),
|
||
// should not report Bar as unused!
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` alias is self-recursive in an invalid way:
|
||
|
||
4│ Foo : { x : Bar }
|
||
^^^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
self_recursive_alias,
|
||
indoc!(
|
||
r#"
|
||
Foo : { x : Foo }
|
||
|
||
f : Foo
|
||
f = 3
|
||
|
||
f
|
||
"#
|
||
),
|
||
// should not report Bar as unused!
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` alias is self-recursive in an invalid way:
|
||
|
||
4│ Foo : { x : Foo }
|
||
^^^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_duplicate_field_same_type,
|
||
indoc!(
|
||
r#"
|
||
{ x: 4, y: 3, x: 4 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
4│ { x: 4, y: 3, x: 4 }
|
||
^^^^ ^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ { x: 4, y: 3, x: 4 }
|
||
^^^^
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_duplicate_field_different_types,
|
||
indoc!(
|
||
r#"
|
||
{ x: 4, y: 3, x: "foo" }
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
4│ { x: 4, y: 3, x: "foo" }
|
||
^^^^ ^^^^^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ { x: 4, y: 3, x: "foo" }
|
||
^^^^^^^^
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_duplicate_field_multiline,
|
||
indoc!(
|
||
r#"
|
||
{
|
||
x: 4,
|
||
y: 3,
|
||
x: "foo"
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
4│ {
|
||
5│> x: 4,
|
||
6│ y: 3,
|
||
7│> x: "foo"
|
||
8│ }
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ {
|
||
5│ x: 4,
|
||
6│ y: 3,
|
||
7│> x: "foo"
|
||
8│ }
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_update_duplicate_field_multiline,
|
||
indoc!(
|
||
r#"
|
||
\r ->
|
||
{ r &
|
||
x: 4,
|
||
y: 3,
|
||
x: "foo"
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
5│ { r &
|
||
6│> x: 4,
|
||
7│ y: 3,
|
||
8│> x: "foo"
|
||
9│ }
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
5│ { r &
|
||
6│ x: 4,
|
||
7│ y: 3,
|
||
8│> x: "foo"
|
||
9│ }
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_duplicate_field,
|
||
indoc!(
|
||
r#"
|
||
a : { foo : Num.I64, bar : {}, foo : Str }
|
||
a = { bar: {}, foo: "foo" }
|
||
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record type defines the `.foo` field twice!
|
||
|
||
4│ a : { foo : Num.I64, bar : {}, foo : Str }
|
||
^^^^^^^^^^^^^ ^^^^^^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ a : { foo : Num.I64, bar : {}, foo : Str }
|
||
^^^^^^^^^
|
||
|
||
For clarity, remove the previous `.foo` definitions from this record
|
||
type.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_duplicate_tag,
|
||
indoc!(
|
||
r#"
|
||
a : [Foo Num.I64, Bar {}, Foo Str]
|
||
a = Foo "foo"
|
||
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE TAG NAME ──────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This tag union type defines the `Foo` tag twice!
|
||
|
||
4│ a : [Foo Num.I64, Bar {}, Foo Str]
|
||
^^^^^^^^^^^ ^^^^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ a : [Foo Num.I64, Bar {}, Foo Str]
|
||
^^^^^^^
|
||
|
||
For clarity, remove the previous `Foo` definitions from this tag union
|
||
type.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
annotation_definition_mismatch,
|
||
indoc!(
|
||
r#"
|
||
bar : Num.I64
|
||
foo = \x -> x
|
||
|
||
# NOTE: neither bar or foo are defined at this point
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This annotation does not match the definition immediately following
|
||
it:
|
||
|
||
4│> bar : Num.I64
|
||
5│> foo = \x -> x
|
||
|
||
Is it a typo? If not, put either a newline or comment between them.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
annotation_newline_body_is_fine,
|
||
indoc!(
|
||
r#"
|
||
bar : Num.I64
|
||
|
||
foo = \x -> x
|
||
|
||
foo bar
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
invalid_alias_rigid_var_pattern,
|
||
indoc!(
|
||
r#"
|
||
MyAlias 1 : Num.I64
|
||
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This definition of `MyAlias` has an unexpected pattern:
|
||
|
||
4│ MyAlias 1 : Num.I64
|
||
^
|
||
|
||
Only type variables like `a` or `value` can occur in this position.
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`MyAlias` is not used anywhere in your code.
|
||
|
||
4│ MyAlias 1 : Num.I64
|
||
^^^^^^^^^^^^^^^^^^^
|
||
|
||
If you didn't intend on using `MyAlias` then remove it so future readers
|
||
of your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_opaque_rigid_var_pattern,
|
||
indoc!(
|
||
r#"
|
||
Age 1 := Num.I64
|
||
|
||
a : Age
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This definition of `Age` has an unexpected pattern:
|
||
|
||
4│ Age 1 := Num.I64
|
||
^
|
||
|
||
Only type variables like `a` or `value` can occur in this position.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_num,
|
||
indoc!(
|
||
r#"
|
||
a : Num.Num Num.I64 Num.F64
|
||
a = 3
|
||
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Num` opaque expects 1 type argument, but it got 2 instead:
|
||
|
||
4│ a : Num.Num Num.I64 Num.F64
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_num_fn,
|
||
indoc!(
|
||
r#"
|
||
f : Str -> Num.Num Num.I64 Num.F64
|
||
f = \_ -> 3
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Num` opaque expects 1 type argument, but it got 2 instead:
|
||
|
||
4│ f : Str -> Num.Num Num.I64 Num.F64
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
too_few_type_arguments,
|
||
indoc!(
|
||
r#"
|
||
Pair a b : [Pair a b]
|
||
|
||
x : Pair Num.I64
|
||
x = Pair 2 3
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Pair` alias expects 2 type arguments, but it got 1 instead:
|
||
|
||
6│ x : Pair Num.I64
|
||
^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
too_many_type_arguments,
|
||
indoc!(
|
||
r#"
|
||
Pair a b : [Pair a b]
|
||
|
||
x : Pair Num.I64 Num.I64 Num.I64
|
||
x = 3
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Pair` alias expects 2 type arguments, but it got 3 instead:
|
||
|
||
6│ x : Pair Num.I64 Num.I64 Num.I64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
phantom_type_variable,
|
||
indoc!(
|
||
r#"
|
||
Foo a : [Foo]
|
||
|
||
f : Foo Num.I64
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED TYPE ALIAS PARAMETER ─────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `a` type parameter is not used in the `Foo` alias definition:
|
||
|
||
4│ Foo a : [Foo]
|
||
^
|
||
|
||
Roc does not allow unused type parameters!
|
||
|
||
Tip: If you want an unused type parameter (a so-called "phantom
|
||
type"), read the guide section on phantom values.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
elm_function_syntax,
|
||
indoc!(
|
||
r#"
|
||
f x y = x
|
||
"#
|
||
),
|
||
@r###"
|
||
── ARGUMENTS BEFORE EQUALS ────────────────── tmp/elm_function_syntax/Test.roc ─
|
||
|
||
I am partway through parsing a definition, but I got stuck here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ f x y = x
|
||
^^^
|
||
|
||
Looks like you are trying to define a function. In roc, functions are
|
||
always written as a lambda, like increment = \n -> n + 1.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
two_different_cons,
|
||
indoc!(
|
||
r#"
|
||
ConsList a : [Cons a (ConsList a), Nil]
|
||
|
||
x : ConsList {}
|
||
x = Cons {} (Cons "foo" Nil)
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
6│ x : ConsList {}
|
||
7│ x = Cons {} (Cons "foo" Nil)
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `Cons` tag application has the type:
|
||
|
||
[Cons {} [Cons Str [Cons {} a, Nil]b as a, Nil]b, Nil]b
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
[Cons {} a, Nil] as a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mutually_recursive_types_with_type_error,
|
||
indoc!(
|
||
r#"
|
||
AList a b : [ACons a (BList a b), ANil]
|
||
BList a b : [BCons a (AList a b), BNil]
|
||
|
||
x : AList Num.I64 Num.I64
|
||
x = ACons 0 (BCons 1 (ACons "foo" BNil ))
|
||
|
||
y : BList a a
|
||
y = BNil
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
// TODO render tag unions across multiple lines
|
||
// TODO do not show recursion var if the recursion var does not render on the surface of a type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
7│ x : AList Num.I64 Num.I64
|
||
8│ x = ACons 0 (BCons 1 (ACons "foo" BNil ))
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `ACons` tag application has the type:
|
||
|
||
[ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons I64 [ACons I64 (BList I64 I64),
|
||
ANil]b as ∞, BNil]c, ANil]b, BNil]c, ANil]b
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
[ACons I64 (BList I64 I64), ANil] as a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
integer_out_of_range,
|
||
indoc!(
|
||
r#"
|
||
x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
|
||
y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
|
||
h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
|
||
minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728
|
||
maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455
|
||
|
||
x + y + h + l + minlit + maxlit
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too big:
|
||
|
||
4│ x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The largest number representable in Roc is the maximum U128 value,
|
||
340_282_366_920_938_463_463_374_607_431_768_211_455.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too small:
|
||
|
||
6│ y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The smallest number representable in Roc is the minimum I128 value,
|
||
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too big:
|
||
|
||
8│ h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The largest number representable in Roc is the maximum U128 value,
|
||
340_282_366_920_938_463_463_374_607_431_768_211_455.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too small:
|
||
|
||
9│ l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The smallest number representable in Roc is the minimum I128 value,
|
||
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
14│ x + y + h + l + minlit + maxlit
|
||
^^^^^^
|
||
|
||
This `maxlit` value is a:
|
||
|
||
U128
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
I128 or Dec
|
||
"###
|
||
);
|
||
|
||
// have to deal with some whitespace issues because of the format! macro
|
||
test_report!(
|
||
float_out_of_range,
|
||
indoc!(
|
||
r#"
|
||
overflow = 11.7976931348623157e308
|
||
underflow = -11.7976931348623157e308
|
||
|
||
overflow + underflow
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal is too big:
|
||
|
||
4│ overflow = 11.7976931348623157e308
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Roc uses signed 64-bit floating points, allowing values between
|
||
-1.7976931348623157e308 and 1.7976931348623157e308
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal is too small:
|
||
|
||
5│ underflow = -11.7976931348623157e308
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Roc uses signed 64-bit floating points, allowing values between
|
||
-1.7976931348623157e308 and 1.7976931348623157e308
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
// the generated messages here are incorrect. Waiting for a rust nightly feature to land,
|
||
// see https://github.com/rust-lang/rust/issues/22639
|
||
// this test is here to spot regressions in error reporting
|
||
test_report!(
|
||
integer_malformed,
|
||
indoc!(
|
||
r#"
|
||
dec = 100A
|
||
|
||
hex = 0xZZZ
|
||
|
||
oct = 0o9
|
||
|
||
bin = 0b2
|
||
|
||
dec + hex + oct + bin
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal contains an invalid digit:
|
||
|
||
4│ dec = 100A
|
||
^^^^
|
||
|
||
Integer literals can only contain the digits
|
||
0-9, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This hex integer literal contains an invalid digit:
|
||
|
||
6│ hex = 0xZZZ
|
||
^^^^^
|
||
|
||
Hexadecimal (base-16) integer literals can only contain the digits
|
||
0-9, a-f and A-F, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This octal integer literal contains an invalid digit:
|
||
|
||
8│ oct = 0o9
|
||
^^^
|
||
|
||
Octal (base-8) integer literals can only contain the digits
|
||
0-7, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This binary integer literal contains an invalid digit:
|
||
|
||
10│ bin = 0b2
|
||
^^^
|
||
|
||
Binary (base-2) integer literals can only contain the digits
|
||
0 and 1, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
integer_empty,
|
||
indoc!(
|
||
r#"
|
||
dec = 20
|
||
|
||
hex = 0x
|
||
|
||
oct = 0o
|
||
|
||
bin = 0b
|
||
|
||
dec + hex + oct + bin
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This hex integer literal contains no digits:
|
||
|
||
6│ hex = 0x
|
||
^^
|
||
|
||
Hexadecimal (base-16) integer literals must contain at least one of
|
||
the digits 0-9, a-f and A-F, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This octal integer literal contains no digits:
|
||
|
||
8│ oct = 0o
|
||
^^
|
||
|
||
Octal (base-8) integer literals must contain at least one of the
|
||
digits 0-7, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This binary integer literal contains no digits:
|
||
|
||
10│ bin = 0b
|
||
^^
|
||
|
||
Binary (base-2) integer literals must contain at least one of the
|
||
digits 0 and 1, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
float_malformed,
|
||
indoc!(
|
||
r#"
|
||
x = 3.0A
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal contains an invalid digit:
|
||
|
||
4│ x = 3.0A
|
||
^^^^
|
||
|
||
Floating point literals can only contain the digits 0-9, or use
|
||
scientific notation 10e4, or have a float suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_record_update,
|
||
indoc!(
|
||
r#"
|
||
foo = { bar: 3 }
|
||
updateNestedRecord = { foo.bar & x: 4 }
|
||
|
||
example = { age: 42 }
|
||
|
||
# these should work
|
||
y = { Test.example & age: 3 }
|
||
x = { example & age: 4 }
|
||
|
||
{ updateNestedRecord, foo, x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression cannot be updated:
|
||
|
||
5│ updateNestedRecord = { foo.bar & x: 4 }
|
||
^^^^^^^
|
||
|
||
Only variables can be updated with record update syntax.
|
||
|
||
── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Test` module is not imported:
|
||
|
||
10│ y = { Test.example & age: 3 }
|
||
^^^^^^^^^^^^
|
||
|
||
Is there an import missing? Perhaps there is a typo. Did you mean one
|
||
of these?
|
||
|
||
Set
|
||
List
|
||
Dict
|
||
Hash
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression cannot be updated:
|
||
|
||
10│ y = { Test.example & age: 3 }
|
||
^^^^^^^^^^^^
|
||
|
||
Only variables can be updated with record update syntax.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
module_not_imported,
|
||
indoc!(
|
||
r#"
|
||
Foo.test
|
||
"#
|
||
),
|
||
@r###"
|
||
── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` module is not imported:
|
||
|
||
4│ Foo.test
|
||
^^^^^^^^
|
||
|
||
Is there an import missing? Perhaps there is a typo. Did you mean one
|
||
of these?
|
||
|
||
Box
|
||
Bool
|
||
Num
|
||
Set
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_default_type_error,
|
||
indoc!(
|
||
r#"
|
||
\{ x, y ? True } -> x + y
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ \{ x, y ? True } -> x + y
|
||
^
|
||
|
||
This `y` value is a:
|
||
|
||
[True]
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Num a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_default_with_signature,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `f` is weird:
|
||
|
||
5│ f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||
^^^^^^^^^^^^^^^^
|
||
|
||
The argument is a pattern that matches record values of type:
|
||
|
||
{ y ? Str, … }
|
||
|
||
But the annotation on `f` says the 1st argument should be:
|
||
|
||
{ y ? I64, … }
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_let_binding,
|
||
indoc!(
|
||
r#"
|
||
\rec ->
|
||
{ x, y } : { x : Num.I64, y ? Str }
|
||
{ x, y } = rec
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of this definition:
|
||
|
||
5│> { x, y } : { x : Num.I64, y ? Str }
|
||
6│> { x, y } = rec
|
||
|
||
The body is a value of type:
|
||
|
||
{ y : Str, … }
|
||
|
||
But the type annotation says it should be:
|
||
|
||
{ y ? Str, … }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_function,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \{ x, y } -> x + y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `f` is weird:
|
||
|
||
5│ f = \{ x, y } -> x + y
|
||
^^^^^^^^
|
||
|
||
The argument is a pattern that matches record values of type:
|
||
|
||
{ y : I64, … }
|
||
|
||
But the annotation on `f` says the 1st argument should be:
|
||
|
||
{ y ? I64, … }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_when,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r ->
|
||
when r is
|
||
{ x, y } -> x + y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when r is
|
||
7│ { x, y } -> x + y
|
||
|
||
This `r` value is a:
|
||
|
||
{ y ? I64, … }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ y : I64, … }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_access,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r -> r.y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
5│ f = \r -> r.y
|
||
^^^
|
||
|
||
This `r` value is a:
|
||
|
||
{ y ? I64, … }
|
||
|
||
But you are trying to use it as:
|
||
|
||
{ y : I64, … }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_accessor,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r -> .y r
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to this function has an unexpected type:
|
||
|
||
5│ f = \r -> .y r
|
||
^
|
||
|
||
This `r` value is a:
|
||
|
||
{ y ? I64, … }
|
||
|
||
But this function needs its 1st argument to be:
|
||
|
||
{ y : I64, … }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
guard_mismatch_with_annotation,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y : Num.I64 } -> Num.I64
|
||
f = \r ->
|
||
when r is
|
||
{ x, y : "foo" } -> x + 0
|
||
_ -> 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when r is
|
||
7│ { x, y : "foo" } -> x + 0
|
||
8│ _ -> 0
|
||
|
||
This `r` value is a:
|
||
|
||
{ y : I64, … }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ y : Str, … }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_field_mismatch_with_annotation,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r ->
|
||
when r is
|
||
{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||
_ -> 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when r is
|
||
7│ { x, y ? "foo" } -> (\g, _ -> g) x y
|
||
8│ _ -> 0
|
||
|
||
This `r` value is a:
|
||
|
||
{ y ? I64, … }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ y ? Str, … }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
incorrect_optional_field,
|
||
indoc!(
|
||
r#"
|
||
{ x: 5, y ? 42 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── BAD OPTIONAL VALUE ──────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record uses an optional value for the `.y` field in an incorrect
|
||
context!
|
||
|
||
4│ { x: 5, y ? 42 }
|
||
^^^^^^
|
||
|
||
You can only use optional values in record destructuring, like:
|
||
|
||
{ answer ? 42, otherField } = myRecord
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
first_wildcard_is_required,
|
||
indoc!(
|
||
r#"
|
||
when Foo 1 2 3 is
|
||
Foo _ 1 _ -> 1
|
||
_ -> 2
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
second_wildcard_is_redundant,
|
||
indoc!(
|
||
r#"
|
||
when Foo 1 2 3 is
|
||
Foo _ 1 _ -> 1
|
||
_ -> 2
|
||
_ -> 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd pattern is redundant:
|
||
|
||
4│ when Foo 1 2 3 is
|
||
5│ Foo _ 1 _ -> 1
|
||
6│ _ -> 2
|
||
7│ _ -> 3
|
||
^
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
alias_using_alias,
|
||
indoc!(
|
||
r#"
|
||
# The color of a node. Leaves are considered Black.
|
||
NodeColor : [Red, Black]
|
||
|
||
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty]
|
||
|
||
# Create an empty dictionary.
|
||
empty : RBTree k v
|
||
empty =
|
||
Empty
|
||
|
||
empty
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
unused_argument,
|
||
indoc!(
|
||
r#"
|
||
f = \foo -> 1
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`f` doesn't use `foo`.
|
||
|
||
4│ f = \foo -> 1
|
||
^^^
|
||
|
||
If you don't need `foo`, then you can just remove it. However, if you
|
||
really do need `foo` as an argument of `f`, prefix it with an underscore,
|
||
like this: "_`foo`". Adding an underscore at the start of a variable
|
||
name is a way of saying that the variable is not used.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
qualified_tag,
|
||
indoc!(
|
||
r#"
|
||
Foo.Bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse a qualified name here:
|
||
|
||
4│ Foo.Bar
|
||
^
|
||
|
||
This looks like a qualified tag name to me, but tags cannot be
|
||
qualified! Maybe you wanted a qualified name, something like
|
||
Json.Decode.string?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
module_ident_ends_with_dot,
|
||
indoc!(
|
||
r#"
|
||
Foo.Bar.
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse a qualified name here:
|
||
|
||
4│ Foo.Bar.
|
||
^
|
||
|
||
I was expecting to see an identifier next, like height. A complete
|
||
qualified name looks something like Json.Decode.string.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_access_ends_with_dot,
|
||
indoc!(
|
||
r#"
|
||
foo.bar.
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I trying to parse a record field access here:
|
||
|
||
4│ foo.bar.
|
||
^
|
||
|
||
So I expect to see a lowercase letter next, like .name or .height.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_annotation_double_colon,
|
||
indoc!(
|
||
r#"
|
||
f :: I64
|
||
f = 42
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ──────────────── tmp/type_annotation_double_colon/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ f :: I64
|
||
^^
|
||
|
||
I have no specific suggestion for this operator, see TODO for the full
|
||
list of operators in Roc.
|
||
"###
|
||
);
|
||
|
||
// NOTE: VERY BAD ERROR MESSAGE
|
||
//
|
||
// looks like `x y` are considered argument to the add, even though they are
|
||
// on a lower indentation level
|
||
test_report!(
|
||
double_equals_in_def,
|
||
indoc!(
|
||
r#"
|
||
x = 3
|
||
y =
|
||
x == 5
|
||
Num.add 1 2
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This value is not a function, but it was given 3 arguments:
|
||
|
||
6│ x == 5
|
||
^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_open,
|
||
indoc!(
|
||
r#"
|
||
f : [
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TAG UNION TYPE ───────────────────── tmp/tag_union_open/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing square bracket before this, so try
|
||
adding a ] and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_end,
|
||
indoc!(
|
||
r#"
|
||
f : [Yes,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TAG UNION TYPE ────────────────────── tmp/tag_union_end/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [Yes,
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing square bracket before this, so try
|
||
adding a ] and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_lowercase_tag_name,
|
||
indoc!(
|
||
r#"
|
||
f : [lowercase]
|
||
"#
|
||
),
|
||
@r###"
|
||
── WEIRD TAG NAME ────────────────── tmp/tag_union_lowercase_tag_name/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [lowercase]
|
||
^
|
||
|
||
I was expecting to see a tag name.
|
||
|
||
Hint: Tag names start with an uppercase letter, like Err or Green.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_second_lowercase_tag_name,
|
||
indoc!(
|
||
r#"
|
||
f : [Good, bad]
|
||
"#
|
||
),
|
||
@r###"
|
||
── WEIRD TAG NAME ─────────── tmp/tag_union_second_lowercase_tag_name/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [Good, bad]
|
||
^
|
||
|
||
I was expecting to see a tag name.
|
||
|
||
Hint: Tag names start with an uppercase letter, like Err or Green.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_open,
|
||
indoc!(
|
||
r#"
|
||
f : {
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ────────────────────── tmp/record_type_open/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : {
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing curly brace before this, so try
|
||
adding a } and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_open_indent,
|
||
indoc!(
|
||
r#"
|
||
f : {
|
||
foo : I64,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ─────────────── tmp/record_type_open_indent/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : {
|
||
5│ foo : I64,
|
||
6│
|
||
7│
|
||
^
|
||
|
||
I was expecting to see a closing curly brace before this, so try
|
||
adding a } and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_end,
|
||
indoc!(
|
||
r#"
|
||
f : { a: Int,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ─────────────────────── tmp/record_type_end/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : { a: Int,
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing curly brace before this, so try
|
||
adding a } and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_keyword_field_name,
|
||
indoc!(
|
||
r#"
|
||
f : { if : I64 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ──────── tmp/record_type_keyword_field_name/Test.roc ─
|
||
|
||
I just started parsing a record type, but I got stuck on this field
|
||
name:
|
||
|
||
4│ f : { if : I64 }
|
||
^^
|
||
|
||
Looks like you are trying to use `if` as a field name, but that is a
|
||
reserved word. Try using a different name!
|
||
"###
|
||
);
|
||
|
||
// a case where the message cannot be as good as elm's
|
||
test_report!(
|
||
record_type_missing_comma,
|
||
indoc!(
|
||
r#"
|
||
f : { foo bar }
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ───────────── tmp/record_type_missing_comma/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : { foo bar }
|
||
^
|
||
|
||
I was expecting to see a colon, question mark, comma or closing curly
|
||
brace.
|
||
"###
|
||
);
|
||
|
||
// a case where the message cannot be as good as elm's
|
||
test_report!(
|
||
record_type_tab,
|
||
"f : { foo \t }",
|
||
@r###"
|
||
── TAB CHARACTER ──────────────────────────────── tmp/record_type_tab/Test.roc ─
|
||
|
||
I encountered a tab character
|
||
|
||
4│ f : { foo }
|
||
^
|
||
|
||
Tab characters are not allowed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
comment_with_tab,
|
||
"# comment with a \t\n4",
|
||
|golden| pretty_assertions::assert_eq!(
|
||
golden,
|
||
&format!(
|
||
r###"── TAB CHARACTER ─────────────────────────────── tmp/comment_with_tab/Test.roc ─
|
||
|
||
I encountered a tab character
|
||
|
||
4│ # comment with a {}
|
||
^
|
||
|
||
Tab characters are not allowed."###,
|
||
"\t"
|
||
)
|
||
)
|
||
);
|
||
|
||
// TODO bad error message
|
||
test_report!(
|
||
type_in_parens_start,
|
||
indoc!(
|
||
r#"
|
||
f : (
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ────────────────── tmp/type_in_parens_start/Test.roc ─
|
||
|
||
I am partway through parsing a type in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ f : (
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_in_parens_end,
|
||
indoc!(
|
||
r#"
|
||
f : ( I64
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ──────────────────── tmp/type_in_parens_end/Test.roc ─
|
||
|
||
I am partway through parsing a type in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ f : ( I64
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_apply_double_dot,
|
||
indoc!(
|
||
r#"
|
||
f : Foo..Bar
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo..Bar
|
||
^^^^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
// ── DOUBLE DOT ──────────────────────────────────────────────────────────────────
|
||
//
|
||
// I encountered two dots in a row:
|
||
//
|
||
// 1│ f : Foo..Bar
|
||
// ^
|
||
//
|
||
// Try removing one of them.
|
||
|
||
test_report!(
|
||
type_apply_trailing_dot,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.Bar.
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo.Bar.
|
||
^^^^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
// ── TRAILING DOT ────────────────────────────────────────────────────────────────
|
||
//
|
||
// I encountered a dot with nothing after it:
|
||
//
|
||
// 1│ f : Foo.Bar.
|
||
// ^
|
||
//
|
||
// Dots are used to refer to a type in a qualified way, like
|
||
// Num.I64 or List.List a. Try adding a type name next.
|
||
|
||
test_report!(
|
||
type_apply_stray_dot,
|
||
indoc!(
|
||
r#"
|
||
f : .
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TYPE ───────────────────────── tmp/type_apply_stray_dot/Test.roc ─
|
||
|
||
I just started parsing a type, but I got stuck here:
|
||
|
||
4│ f : .
|
||
^
|
||
|
||
I am expecting a type next, like Bool or List a.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_apply_start_with_number,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.1
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo.1
|
||
^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
// ── WEIRD QUALIFIED NAME ────────────────────────────────────────────────────────
|
||
//
|
||
// I encountered a number at the start of a qualified name segment:
|
||
//
|
||
// 1│ f : Foo.1
|
||
// ^
|
||
//
|
||
// All parts of a qualified type name must start with an uppercase
|
||
// letter, like Num.I64 or List.List a.
|
||
|
||
test_report!(
|
||
type_apply_start_with_lowercase,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.foo
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo.foo
|
||
^^^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
def_missing_final_expression,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── MISSING FINAL EXPRESSION ──────── tmp/def_missing_final_expression/Test.roc ─
|
||
|
||
I am partway through parsing a definition, but I got stuck here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ f : Foo.foo
|
||
^
|
||
|
||
This definition is missing a final expression. A nested definition
|
||
must be followed by either another definition, or an expression
|
||
|
||
x = 4
|
||
y = 2
|
||
|
||
x + y
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
expression_indentation_end,
|
||
indoc!(
|
||
r#"
|
||
f <- Foo.foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── INDENT ENDS AFTER EXPRESSION ────── tmp/expression_indentation_end/Test.roc ─
|
||
|
||
I am partway through parsing an expression, but I got stuck here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ f <- Foo.foo
|
||
^
|
||
|
||
Looks like the indentation ends prematurely here. Did you mean to have
|
||
another expression after this line?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_inline_alias,
|
||
indoc!(
|
||
r#"
|
||
f : I64 as
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED INLINE ALIAS ──────────────────── tmp/type_inline_alias/Test.roc ─
|
||
|
||
I just started parsing an inline type alias, but I got stuck here:
|
||
|
||
4│ f : I64 as
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_double_comma,
|
||
indoc!(
|
||
r#"
|
||
f : I64,,I64 -> I64
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── DOUBLE COMMA ─────────────────────────────── tmp/type_double_comma/Test.roc ─
|
||
|
||
I just started parsing a function argument type, but I encountered two
|
||
commas in a row:
|
||
|
||
4│ f : I64,,I64 -> I64
|
||
^
|
||
|
||
Try removing one of them.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_argument_no_arrow,
|
||
indoc!(
|
||
r#"
|
||
f : I64, I64
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TYPE ─────────────────────── tmp/type_argument_no_arrow/Test.roc ─
|
||
|
||
I am partway through parsing a type, but I got stuck here:
|
||
|
||
4│ f : I64, I64
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
// TODO could do better by pointing out we're parsing a function type
|
||
test_report!(
|
||
type_argument_arrow_then_nothing,
|
||
indoc!(
|
||
r#"
|
||
f : I64, I64 ->
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TYPE ───────────── tmp/type_argument_arrow_then_nothing/Test.roc ─
|
||
|
||
I just started parsing a type, but I got stuck here:
|
||
|
||
4│ f : I64, I64 ->
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
// TODO could do better by pointing out we're parsing a function type
|
||
test_report!(
|
||
dict_type_formatting,
|
||
indoc!(
|
||
r#"
|
||
app "dict" imports [ Dict ] provides [main] to "./platform"
|
||
|
||
myDict : Dict.Dict Num.I64 Str
|
||
myDict = Dict.insert (Dict.empty {}) "foo" 42
|
||
|
||
main = myDict
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `myDict` definition:
|
||
|
||
3│ myDict : Dict.Dict Num.I64 Str
|
||
4│ myDict = Dict.insert (Dict.empty {}) "foo" 42
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `insert` call produces:
|
||
|
||
Dict Str (Num *)
|
||
|
||
But the type annotation on `myDict` says it should be:
|
||
|
||
Dict I64 Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
alias_type_diff,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Set.{ Set }] provides [main] to "./platform"
|
||
|
||
HSet a : Set a
|
||
|
||
foo : Str -> HSet {}
|
||
|
||
myDict : HSet Str
|
||
myDict = foo "bar"
|
||
|
||
main = myDict
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `myDict` definition:
|
||
|
||
7│ myDict : HSet Str
|
||
8│ myDict = foo "bar"
|
||
^^^^^^^^^
|
||
|
||
This `foo` call produces:
|
||
|
||
HSet {}
|
||
|
||
But the type annotation on `myDict` says it should be:
|
||
|
||
HSet Str
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
test_report!(
|
||
if_guard_without_condition,
|
||
indoc!(
|
||
r#"
|
||
when Just 4 is
|
||
Just if ->
|
||
4
|
||
|
||
_ ->
|
||
2
|
||
"#
|
||
),
|
||
@r###"
|
||
── IF GUARD NO CONDITION ───────────── tmp/if_guard_without_condition/Test.roc ─
|
||
|
||
I just started parsing an if guard, but there is no guard condition:
|
||
|
||
4│ when Just 4 is
|
||
5│ Just if ->
|
||
^
|
||
|
||
Try adding an expression before the arrow!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
empty_or_pattern,
|
||
indoc!(
|
||
r#"
|
||
when Just 4 is
|
||
Just 4 | ->
|
||
4
|
||
|
||
_ ->
|
||
2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PATTERN ────────────────────────── tmp/empty_or_pattern/Test.roc ─
|
||
|
||
I just started parsing a pattern, but I got stuck here:
|
||
|
||
5│ Just 4 | ->
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
// TODO check if "what_is_next" is a keyword
|
||
test_report!(
|
||
pattern_binds_keyword,
|
||
indoc!(
|
||
r#"
|
||
when Just 4 is
|
||
Just when ->
|
||
4
|
||
|
||
_ ->
|
||
2
|
||
"#
|
||
),
|
||
@r###"
|
||
── MISSING ARROW ────────────────────────── tmp/pattern_binds_keyword/Test.roc ─
|
||
|
||
I am partway through parsing a `when` expression, but got stuck here:
|
||
|
||
4│ when Just 4 is
|
||
5│ Just when ->
|
||
^
|
||
|
||
I was expecting to see an arrow next.
|
||
|
||
Note: Sometimes I get confused by indentation, so try to make your `when`
|
||
look something like this:
|
||
|
||
when List.first plants is
|
||
Ok n ->
|
||
n
|
||
|
||
Err _ ->
|
||
200
|
||
|
||
Notice the indentation. All patterns are aligned, and each branch is
|
||
indented a bit more than the corresponding pattern. That is important!
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
test_report!(
|
||
when_missing_arrow,
|
||
indoc!(
|
||
r#"
|
||
when 5 is
|
||
1 -> 2
|
||
_
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED WHEN ─────────────────────────── tmp/when_missing_arrow/Test.roc ─
|
||
|
||
I was partway through parsing a `when` expression, but I got stuck here:
|
||
|
||
4│ when 5 is
|
||
5│ 1 -> 2
|
||
6│ _
|
||
^
|
||
|
||
I was expecting to see a pattern next
|
||
|
||
Note: Here is an example of a valid `when` expression for reference.
|
||
|
||
when List.first plants is
|
||
Ok n ->
|
||
n
|
||
|
||
Err _ ->
|
||
200
|
||
|
||
Notice the indentation. All patterns are aligned, and each branch is
|
||
indented a bit more than the corresponding pattern. That is important!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
lambda_double_comma,
|
||
indoc!(
|
||
r#"
|
||
\a,,b -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ARGUMENT LIST ───────────────── tmp/lambda_double_comma/Test.roc ─
|
||
|
||
I am partway through parsing a function argument list, but I got stuck
|
||
at this comma:
|
||
|
||
4│ \a,,b -> 1
|
||
^
|
||
|
||
I was expecting an argument pattern before this, so try adding an
|
||
argument before the comma and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
lambda_leading_comma,
|
||
indoc!(
|
||
r#"
|
||
\,b -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ARGUMENT LIST ──────────────── tmp/lambda_leading_comma/Test.roc ─
|
||
|
||
I am partway through parsing a function argument list, but I got stuck
|
||
at this comma:
|
||
|
||
4│ \,b -> 1
|
||
^
|
||
|
||
I was expecting an argument pattern before this, so try adding an
|
||
argument before the comma and see if that helps?
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
// TODO this formerly gave
|
||
//
|
||
// ── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
||
//
|
||
// I was partway through parsing a `when` expression, but I got stuck here:
|
||
//
|
||
// 3│ _ -> 2
|
||
// ^
|
||
//
|
||
// I suspect this is a pattern that is not indented enough? (by 2 spaces)
|
||
//
|
||
// but that requires parsing the next pattern blindly, irrespective of indentation. Can
|
||
// we find an efficient solution that doesn't require parsing an extra pattern for
|
||
// every `when`, i.e. we want a good error message for the test case above, but for
|
||
// a valid `when`, we don't want to do extra work, e.g. here
|
||
//
|
||
// x
|
||
// when x is
|
||
// n -> n
|
||
//
|
||
// 4
|
||
//
|
||
// We don't want to parse the `4` and say it's an outdented pattern!
|
||
test_report!(
|
||
when_outdented_branch,
|
||
indoc!(
|
||
r#"
|
||
when 4 is
|
||
5 -> 2
|
||
2 -> 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT END OF FILE ──────────────────────── tmp/when_outdented_branch/Test.roc ─
|
||
|
||
I expected to reach the end of the file, but got stuck here:
|
||
|
||
6│ 2 -> 2
|
||
^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_over_indented_underscore,
|
||
indoc!(
|
||
r#"
|
||
when 4 is
|
||
5 -> 2
|
||
_ -> 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNEXPECTED ARROW ─────────────── tmp/when_over_indented_underscore/Test.roc ─
|
||
|
||
I am parsing a `when` expression right now, but this arrow is confusing
|
||
me:
|
||
|
||
5│ 5 -> 2
|
||
6│ _ -> 2
|
||
^^
|
||
|
||
It makes sense to see arrows around here, so I suspect it is something
|
||
earlier. Maybe this pattern is indented a bit farther from the
|
||
previous patterns?
|
||
|
||
Note: Here is an example of a valid `when` expression for reference.
|
||
|
||
when List.first plants is
|
||
Ok n ->
|
||
n
|
||
|
||
Err _ ->
|
||
200
|
||
|
||
Notice the indentation. All patterns are aligned, and each branch is
|
||
indented a bit more than the corresponding pattern. That is important!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_over_indented_int,
|
||
indoc!(
|
||
r#"
|
||
when 4 is
|
||
5 -> Num.neg
|
||
2 -> 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNEXPECTED ARROW ────────────────────── tmp/when_over_indented_int/Test.roc ─
|
||
|
||
I am parsing a `when` expression right now, but this arrow is confusing
|
||
me:
|
||
|
||
5│ 5 -> Num.neg
|
||
6│ 2 -> 2
|
||
^^
|
||
|
||
It makes sense to see arrows around here, so I suspect it is something
|
||
earlier. Maybe this pattern is indented a bit farther from the
|
||
previous patterns?
|
||
|
||
Note: Here is an example of a valid `when` expression for reference.
|
||
|
||
when List.first plants is
|
||
Ok n ->
|
||
n
|
||
|
||
Err _ ->
|
||
200
|
||
|
||
Notice the indentation. All patterns are aligned, and each branch is
|
||
indented a bit more than the corresponding pattern. That is important!
|
||
"###
|
||
);
|
||
|
||
// TODO I think we can do better here
|
||
test_report!(
|
||
if_outdented_then,
|
||
indoc!(
|
||
r#"
|
||
x =
|
||
if 5 == 5
|
||
then 2 else 3
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED IF ────────────────────────────── tmp/if_outdented_then/Test.roc ─
|
||
|
||
I was partway through parsing an `if` expression, but I got stuck here:
|
||
|
||
5│ if 5 == 5
|
||
^
|
||
|
||
I was expecting to see the `then` keyword next.
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
test_report!(
|
||
if_missing_else,
|
||
indoc!(
|
||
r#"
|
||
if 5 == 5 then 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED IF ──────────────────────────────── tmp/if_missing_else/Test.roc ─
|
||
|
||
I was partway through parsing an `if` expression, but I got stuck here:
|
||
|
||
4│ if 5 == 5 then 2
|
||
^
|
||
|
||
I was expecting to see the `else` keyword next.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_double_comma,
|
||
indoc!(
|
||
r#"
|
||
[1, 2, , 3]
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED LIST ──────────────────────────── tmp/list_double_comma/Test.roc ─
|
||
|
||
I am partway through started parsing a list, but I got stuck here:
|
||
|
||
4│ [1, 2, , 3]
|
||
^
|
||
|
||
I was expecting to see a list entry before this comma, so try adding a
|
||
list entry and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_without_end,
|
||
indoc!(
|
||
r#"
|
||
[1, 2,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED LIST ───────────────────────────── tmp/list_without_end/Test.roc ─
|
||
|
||
I am partway through started parsing a list, but I got stuck here:
|
||
|
||
4│ [1, 2,
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing square bracket before this, so try
|
||
adding a ] and see if that helps?
|
||
|
||
Note: When I get stuck like this, it usually means that there is a
|
||
missing parenthesis or bracket somewhere earlier. It could also be a
|
||
stray keyword or operator.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
number_double_dot,
|
||
indoc!(
|
||
r#"
|
||
1.1.1
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal contains an invalid digit:
|
||
|
||
4│ 1.1.1
|
||
^^^^^
|
||
|
||
Floating point literals can only contain the digits 0-9, or use
|
||
scientific notation 10e4, or have a float suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unicode_not_hex,
|
||
r#""abc\u(zzzz)def""#,
|
||
@r###"
|
||
── WEIRD CODE POINT ───────────────────────────── tmp/unicode_not_hex/Test.roc ─
|
||
|
||
I am partway through parsing a unicode code point, but I got stuck
|
||
here:
|
||
|
||
4│ "abc\u(zzzz)def"
|
||
^
|
||
|
||
I was expecting a hexadecimal number, like \u(1100) or \u(00FF).
|
||
|
||
Learn more about working with unicode in roc at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
interpolate_not_identifier,
|
||
r#""abc\(32)def""#,
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This string interpolation is invalid:
|
||
|
||
4│ "abc\(32)def"
|
||
^^
|
||
|
||
I was expecting an identifier, like \u(message) or
|
||
\u(LoremIpsum.text).
|
||
|
||
Learn more about string interpolation at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unicode_too_large,
|
||
r#""abc\u(110000)def""#,
|
||
@r###"
|
||
── INVALID UNICODE ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This unicode code point is invalid:
|
||
|
||
4│ "abc\u(110000)def"
|
||
^^^^^^
|
||
|
||
Learn more about working with unicode in roc at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
weird_escape,
|
||
r#""abc\qdef""#,
|
||
@r###"
|
||
── WEIRD ESCAPE ──────────────────────────────────── tmp/weird_escape/Test.roc ─
|
||
|
||
I was partway through parsing a string literal, but I got stuck here:
|
||
|
||
4│ "abc\qdef"
|
||
^^
|
||
|
||
This is not an escape sequence I recognize. After a backslash, I am
|
||
looking for one of these:
|
||
|
||
- A newline: \n
|
||
- A caret return: \r
|
||
- A tab: \t
|
||
- An escaped quote: \"
|
||
- An escaped backslash: \\
|
||
- A unicode code point: \u(00FF)
|
||
- An interpolated string: \(myVariable)
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
single_quote_too_long,
|
||
r#"'abcdef'"#,
|
||
@r###"
|
||
── INVALID SCALAR ───────────────────────── tmp/single_quote_too_long/Test.roc ─
|
||
|
||
I am part way through parsing this scalar literal (character literal),
|
||
but it's too long to fit in a U32 so it's not a valid scalar.
|
||
|
||
4│ 'abcdef'
|
||
^
|
||
|
||
You could change it to something like 'a' or '\n'. Note, roc strings
|
||
use double quotes, like "hello".
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
single_no_end,
|
||
r#""there is no end"#,
|
||
@r###"
|
||
── ENDLESS STRING ───────────────────────────────── tmp/single_no_end/Test.roc ─
|
||
|
||
I cannot find the end of this string:
|
||
|
||
4│ "there is no end
|
||
^
|
||
|
||
You could change it to something like "to be or not to be" or even
|
||
just "".
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multi_no_end,
|
||
r#""""there is no end"#,
|
||
@r###"
|
||
── ENDLESS STRING ────────────────────────────────── tmp/multi_no_end/Test.roc ─
|
||
|
||
I cannot find the end of this block string:
|
||
|
||
4│ """there is no end
|
||
^
|
||
|
||
You could change it to something like """to be or not to be""" or even
|
||
just """""".
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multi_insufficient_indent,
|
||
" \"\"\"\n testing\n \"\"\"", // 4 space indent on the start, 2 space on the `testing` line
|
||
@r###"
|
||
── INSUFFICIENT INDENT IN MULTI-LINE STRING ─ ..._insufficient_indent/Test.roc ─
|
||
|
||
This multiline string is not sufficiently indented:
|
||
|
||
4│ """
|
||
5│ testing
|
||
^
|
||
|
||
Lines in a multi-line string must be indented at least as much as the
|
||
beginning """. This extra indentation is automatically removed from
|
||
the string during compilation.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
dbg_without_final_expression,
|
||
indoc!(
|
||
r#"
|
||
dbg 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── INDENT ENDS AFTER EXPRESSION ──── tmp/dbg_without_final_expression/Test.roc ─
|
||
|
||
I am partway through parsing a dbg statement, but I got stuck here:
|
||
|
||
4│ dbg 42
|
||
^
|
||
|
||
I was expecting a final expression, like so
|
||
|
||
dbg 42
|
||
"done"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
expect_without_final_expression,
|
||
indoc!(
|
||
r#"
|
||
expect 1 + 1 == 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── INDENT ENDS AFTER EXPRESSION ─ tmp/expect_without_final_expression/Test.roc ─
|
||
|
||
I am partway through parsing an expect statement, but I got stuck
|
||
here:
|
||
|
||
4│ expect 1 + 1 == 2
|
||
^
|
||
|
||
I was expecting a final expression, like so
|
||
|
||
expect 1 + 1 == 2
|
||
"done"
|
||
"###
|
||
);
|
||
|
||
// https://github.com/roc-lang/roc/issues/1714
|
||
test_report!(
|
||
interpolate_concat_is_transparent_1714,
|
||
indoc!(
|
||
r#"
|
||
greeting = "Privet"
|
||
|
||
if Bool.true then 1 else "\(greeting), World!"
|
||
"#,
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` has an `else` branch with a different type from its `then` branch:
|
||
|
||
6│ if Bool.true then 1 else "\(greeting), World!"
|
||
^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The `else` branch is a string of type:
|
||
|
||
Str
|
||
|
||
but the `then` branch has the type:
|
||
|
||
Num *
|
||
|
||
All branches in an `if` must have the same type!
|
||
"###
|
||
);
|
||
|
||
macro_rules! comparison_binop_transparency_tests {
|
||
($($op:expr, $name:ident),* $(,)?) => {
|
||
$(
|
||
test_report!(
|
||
$name,
|
||
&format!(r#"if Bool.true then "abc" else 1 {} 2"#, $op),
|
||
|golden| assert_eq!(golden, format!(
|
||
r#"── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` has an `else` branch with a different type from its `then` branch:
|
||
|
||
4│ if Bool.true then "abc" else 1 {} 2
|
||
^^{}^^
|
||
|
||
This comparison produces:
|
||
|
||
Bool
|
||
|
||
but the `then` branch has the type:
|
||
|
||
Str
|
||
|
||
All branches in an `if` must have the same type!
|
||
"#,
|
||
$op, "^".repeat($op.len())
|
||
))
|
||
);
|
||
)*
|
||
}
|
||
}
|
||
|
||
comparison_binop_transparency_tests! {
|
||
"<", lt_binop_is_transparent,
|
||
">", gt_binop_is_transparent,
|
||
"==", eq_binop_is_transparent,
|
||
"!=", neq_binop_is_transparent,
|
||
"<=", leq_binop_is_transparent,
|
||
">=", geq_binop_is_transparent,
|
||
}
|
||
|
||
test_report!(
|
||
keyword_record_field_access,
|
||
indoc!(
|
||
r#"
|
||
foo = {}
|
||
|
||
foo.if
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `foo` record doesn’t have a `if` field:
|
||
|
||
6│ foo.if
|
||
^^^^^^
|
||
|
||
In fact, `foo` is a record with no fields at all!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
keyword_qualified_import,
|
||
indoc!(
|
||
r#"
|
||
Num.if
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The Num module does not expose `if`:
|
||
|
||
4│ Num.if
|
||
^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Num.sin
|
||
Num.div
|
||
Num.abs
|
||
Num.neg
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
stray_dot_expr,
|
||
indoc!(
|
||
r#"
|
||
Num.add . 23
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I trying to parse a record field access here:
|
||
|
||
4│ Num.add . 23
|
||
^
|
||
|
||
So I expect to see a lowercase letter next, like .name or .height.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_ref_field_access,
|
||
indoc!(
|
||
r#"
|
||
@UUID.bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am very confused by this field access:
|
||
|
||
4│ @UUID.bar
|
||
^^^^
|
||
|
||
It looks like a record field access on an opaque reference.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
weird_accessor,
|
||
indoc!(
|
||
r#"
|
||
.foo.bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am very confused by this field access
|
||
|
||
4│ .foo.bar
|
||
^^^^^^^^
|
||
|
||
It looks like a field access on an accessor. I parse.client.name as
|
||
(.client).name. Maybe use an anonymous function like
|
||
(\r -> r.client.name) instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
part_starts_with_number,
|
||
indoc!(
|
||
r#"
|
||
foo.100
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I trying to parse a record field access here:
|
||
|
||
4│ foo.100
|
||
^
|
||
|
||
So I expect to see a lowercase letter next, like .name or .height.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
closure_underscore_ident,
|
||
indoc!(
|
||
r#"
|
||
\the_answer -> 100
|
||
"#
|
||
),
|
||
@r###"
|
||
── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse an identifier here:
|
||
|
||
4│ \the_answer -> 100
|
||
^
|
||
|
||
Underscores are not allowed in identifiers. Use camelCase instead!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
double_binop,
|
||
indoc!(
|
||
r#"
|
||
key >= 97 && <= 122
|
||
"#
|
||
),
|
||
@r#"
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
case_of,
|
||
indoc!(
|
||
r#"
|
||
case 1 of
|
||
1 -> True
|
||
_ -> False
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ───────────────────────────────────── tmp/case_of/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ case 1 of
|
||
5│ 1 -> True
|
||
^^
|
||
|
||
The arrow -> is used to define cases in a `when` expression:
|
||
|
||
when color is
|
||
Red -> "stop!"
|
||
Green -> "go!"
|
||
|
||
And to define a function:
|
||
|
||
increment : I64 -> I64
|
||
increment = \n -> n + 1
|
||
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
argument_without_space,
|
||
indoc!(
|
||
r#"
|
||
["foo", bar("")]
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `bar` in this scope.
|
||
|
||
4│ ["foo", bar("")]
|
||
^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Nat
|
||
Str
|
||
Err
|
||
U8
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_operator,
|
||
indoc!(
|
||
r#"
|
||
main =
|
||
5 ** 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ──────────────────────────── tmp/invalid_operator/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main =
|
||
5│ 5 ** 3
|
||
^^
|
||
|
||
I have no specific suggestion for this operator, see TODO for the full
|
||
list of operators in Roc.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
double_plus,
|
||
indoc!(
|
||
r#"
|
||
main =
|
||
[] ++ []
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ───────────────────────────────── tmp/double_plus/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main =
|
||
5│ [] ++ []
|
||
^^
|
||
|
||
To concatenate two lists or strings, try using List.concat or
|
||
Str.concat instead.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inline_hastype,
|
||
indoc!(
|
||
r#"
|
||
main =
|
||
(\x -> x) : I64
|
||
|
||
3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ────────────────────────────── tmp/inline_hastype/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main =
|
||
5│ (\x -> x) : I64
|
||
^
|
||
|
||
The has-type operator : can only occur in a definition's type
|
||
signature, like
|
||
|
||
increment : I64 -> I64
|
||
increment = \x -> x + 1
|
||
"###
|
||
);
|
||
|
||
// this is still bad, but changing the order and progress of other parsers should improve it
|
||
// down the line
|
||
test_report!(
|
||
wild_case_arrow,
|
||
indoc!(
|
||
r#"
|
||
main = 5 -> 3
|
||
"#
|
||
),
|
||
|golden| pretty_assertions::assert_eq!(
|
||
golden,
|
||
&format!(
|
||
r###"── UNKNOWN OPERATOR ───────────────────────────── tmp/wild_case_arrow/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main = 5 -> 3
|
||
^^
|
||
|
||
Looks like you are trying to define a function.{}
|
||
|
||
In roc, functions are always written as a lambda, like{}
|
||
|
||
increment = \n -> n + 1"###,
|
||
' ', ' '
|
||
)
|
||
)
|
||
);
|
||
|
||
#[test]
|
||
fn provides_to_identifier() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
app "test-base64"
|
||
packages { pf: "platform/main.roc" }
|
||
imports [pf.Task, Base64]
|
||
provides [main, @Foo] to pf
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD PROVIDES ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a provides list, but I got stuck here:
|
||
|
||
3│ imports [pf.Task, Base64]
|
||
4│ provides [main, @Foo] to pf
|
||
^
|
||
|
||
I was expecting a type name, value name or function name next, like
|
||
|
||
provides [Animal, default, tame]
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn platform_requires_rigids() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
platform "folkertdev/foo"
|
||
requires { main : Effect {} }
|
||
exposes []
|
||
packages {}
|
||
imports [Task]
|
||
provides [mainForHost]
|
||
effects fx.Effect
|
||
{
|
||
putChar : I64 -> Effect {},
|
||
putLine : Str -> Effect {},
|
||
getLine : Effect Str
|
||
}
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── BAD REQUIRES ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but I got stuck here:
|
||
|
||
1│ platform "folkertdev/foo"
|
||
2│ requires { main : Effect {} }
|
||
^
|
||
|
||
I am expecting a list of type names like `{}` or `{ Model }` next. A full
|
||
`requires` definition looks like
|
||
|
||
requires { Model, Msg } {main : Effect {}}
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn missing_imports() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
interface Foobar
|
||
exposes [main, Foo]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD IMPORTS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but I got stuck here:
|
||
|
||
2│ exposes [main, Foo]
|
||
^
|
||
|
||
I am expecting the `imports` keyword next, like
|
||
|
||
imports [Animal, default, tame]
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn exposes_identifier() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
interface Foobar
|
||
exposes [main, @Foo]
|
||
imports [pf.Task, Base64]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD EXPOSES ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing an `exposes` list, but I got stuck here:
|
||
|
||
1│ interface Foobar
|
||
2│ exposes [main, @Foo]
|
||
^
|
||
|
||
I was expecting a type name, value name or function name next, like
|
||
|
||
exposes [Animal, default, tame]
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn invalid_module_name() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
interface foobar
|
||
exposes [main, @Foo]
|
||
imports [pf.Task, Base64]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD MODULE NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but got stuck here:
|
||
|
||
1│ interface foobar
|
||
^
|
||
|
||
I am expecting a module name next, like BigNum or Main. Module names
|
||
must start with an uppercase letter.
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn invalid_app_name() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
app foobar
|
||
exposes [main, @Foo]
|
||
imports [pf.Task, Base64]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD APP NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but got stuck here:
|
||
|
||
1│ app foobar
|
||
^
|
||
|
||
I am expecting an application name next, like app "main" or
|
||
app "editor". App names are surrounded by quotation marks.
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
test_report!(
|
||
apply_unary_negative,
|
||
indoc!(
|
||
r#"
|
||
foo = 3
|
||
|
||
-foo 1 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This value is not a function, but it was given 2 arguments:
|
||
|
||
6│ -foo 1 2
|
||
^^^^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
apply_unary_not,
|
||
indoc!(
|
||
r#"
|
||
foo = Bool.true
|
||
|
||
!foo 1 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This value is not a function, but it was given 2 arguments:
|
||
|
||
6│ !foo 1 2
|
||
^^^^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
applied_tag_function,
|
||
indoc!(
|
||
r#"
|
||
x : List [Foo Str]
|
||
x = List.map [1, 2] Foo
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : List [Foo Str]
|
||
5│ x = List.map [1, 2] Foo
|
||
^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `map` call produces:
|
||
|
||
List [Foo (Num *)]
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
List [Foo Str]
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_open,
|
||
indoc!(
|
||
r#"
|
||
\( a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ──────────────── tmp/pattern_in_parens_open/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ \( a
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_end_comma,
|
||
indoc!(
|
||
r#"
|
||
\( a,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ─────────── tmp/pattern_in_parens_end_comma/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ \( a,
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_end,
|
||
indoc!(
|
||
r#"
|
||
\( a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ───────────────── tmp/pattern_in_parens_end/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ \( a
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unfinished_closure_pattern_in_parens,
|
||
indoc!(
|
||
r#"
|
||
x = \( a
|
||
)
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED FUNCTION ───── tmp/unfinished_closure_pattern_in_parens/Test.roc ─
|
||
|
||
I was partway through parsing a function, but I got stuck here:
|
||
|
||
4│ x = \( a
|
||
5│ )
|
||
^
|
||
|
||
I just saw a pattern, so I was expecting to see a -> next.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_indent_open,
|
||
indoc!(
|
||
r#"
|
||
\(
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ───────── tmp/pattern_in_parens_indent_open/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ \(
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
backpassing_type_error,
|
||
indoc!(
|
||
r#"
|
||
x <- List.map ["a", "b"]
|
||
|
||
x + 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `map` has an unexpected type:
|
||
|
||
4│> x <- List.map ["a", "b"]
|
||
5│>
|
||
6│> x + 1
|
||
|
||
The argument is an anonymous function of type:
|
||
|
||
Num * -> Num *
|
||
|
||
But `map` needs its 2nd argument to be:
|
||
|
||
Str -> Num *
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
expect_expr_type_error,
|
||
indoc!(
|
||
r#"
|
||
expect "foobar"
|
||
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `expect` condition needs to be a Bool:
|
||
|
||
4│ expect "foobar"
|
||
^^^^^^^^
|
||
|
||
Right now it’s a string of type:
|
||
|
||
Str
|
||
|
||
But I need every `expect` condition to evaluate to a Bool—either
|
||
`Bool.true` or `Bool.false`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
num_too_general_wildcard,
|
||
indoc!(
|
||
r#"
|
||
mult : Num.Num *, Num.F64 -> Num.F64
|
||
mult = \a, b -> a * b
|
||
|
||
mult 0 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `mul` has an unexpected type:
|
||
|
||
5│ mult = \a, b -> a * b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64
|
||
|
||
But `mul` needs its 2nd argument to be:
|
||
|
||
Num *
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `mult` definition:
|
||
|
||
4│ mult : Num.Num *, Num.F64 -> Num.F64
|
||
5│ mult = \a, b -> a * b
|
||
^^^^^
|
||
|
||
This `mul` call produces:
|
||
|
||
Num *
|
||
|
||
But the type annotation on `mult` says it should be:
|
||
|
||
F64
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
num_too_general_named,
|
||
indoc!(
|
||
r#"
|
||
mult : Num.Num a, Num.F64 -> Num.F64
|
||
mult = \a, b -> a * b
|
||
|
||
mult 0 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `mul` has an unexpected type:
|
||
|
||
5│ mult = \a, b -> a * b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64
|
||
|
||
But `mul` needs its 2nd argument to be:
|
||
|
||
Num a
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `mult` definition:
|
||
|
||
4│ mult : Num.Num a, Num.F64 -> Num.F64
|
||
5│ mult = \a, b -> a * b
|
||
^^^^^
|
||
|
||
This `mul` call produces:
|
||
|
||
Num a
|
||
|
||
But the type annotation on `mult` says it should be:
|
||
|
||
F64
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_not_enough_in_alias,
|
||
indoc!(
|
||
r#"
|
||
Result a b : [Ok a, Err b]
|
||
|
||
canIGo : _ -> Result _
|
||
canIGo = \color ->
|
||
when color is
|
||
"green" -> Ok "go!"
|
||
"yellow" -> Err (SlowIt "whoa, let's slow down!")
|
||
"red" -> Err (StopIt "absolutely not")
|
||
_ -> Err (UnknownColor "this is a weird stoplight")
|
||
canIGo
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This alias has the same name as a builtin:
|
||
|
||
4│ Result a b : [Ok a, Err b]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
All builtin aliases are in scope by default, so I need this alias to
|
||
have a different name!
|
||
|
||
── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Result` alias expects 2 type arguments, but it got 1 instead:
|
||
|
||
6│ canIGo : _ -> Result _
|
||
^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_too_many_in_alias,
|
||
indoc!(
|
||
r#"
|
||
Result a b : [Ok a, Err b]
|
||
|
||
canIGo : _ -> Result _ _ _
|
||
canIGo = \color ->
|
||
when color is
|
||
"green" -> Ok "go!"
|
||
"yellow" -> Err (SlowIt "whoa, let's slow down!")
|
||
"red" -> Err (StopIt "absolutely not")
|
||
_ -> Err (UnknownColor "this is a weird stoplight")
|
||
canIGo
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This alias has the same name as a builtin:
|
||
|
||
4│ Result a b : [Ok a, Err b]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
All builtin aliases are in scope by default, so I need this alias to
|
||
have a different name!
|
||
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Result` alias expects 2 type arguments, but it got 3 instead:
|
||
|
||
6│ canIGo : _ -> Result _ _ _
|
||
^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_conflict_in_rigid_links,
|
||
indoc!(
|
||
r#"
|
||
f : a -> (_ -> b) | a has Eq
|
||
f = \x -> \y -> if x == y then x else y
|
||
f
|
||
"#
|
||
),
|
||
// TODO: We should tell the user that we inferred `_` as `a`
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : a -> (_ -> b) | a has Eq
|
||
5│ f = \x -> \y -> if x == y then x else y
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The body is an anonymous function of type:
|
||
|
||
a -> a | a has Eq, a has Eq
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
a -> b | a has Eq
|
||
|
||
Tip: Your type annotation uses `b` and `a` as separate type variables.
|
||
Your code seems to be saying they are the same though. Maybe they
|
||
should be the same in your type annotation? Maybe your code uses them
|
||
in a weird way?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_wildcards_are_related,
|
||
indoc!(
|
||
r#"
|
||
f : * -> *
|
||
f = \x -> x
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : * -> *
|
||
5│ f = \x -> x
|
||
^
|
||
|
||
The type annotation on `f` says this `x` value should have the type:
|
||
|
||
*
|
||
|
||
However, the type of this `x` value is connected to another type in a
|
||
way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `f` should have a named type variable in
|
||
place of the `*`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_nested_wildcards_are_related,
|
||
indoc!(
|
||
r#"
|
||
f : a, b, * -> {x: a, y: b, z: *}
|
||
f = \x, y, z -> {x, y, z}
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : a, b, * -> {x: a, y: b, z: *}
|
||
5│ f = \x, y, z -> {x, y, z}
|
||
^^^^^^^^^
|
||
|
||
The type annotation on `f` says the body is a record should have the
|
||
type:
|
||
|
||
{
|
||
x : a,
|
||
y : b,
|
||
z : *,
|
||
}
|
||
|
||
However, the type of the body is a record is connected to another type
|
||
in a way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `f` should have a named type variable in
|
||
place of the `*`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_wildcards_are_related_in_nested_defs,
|
||
indoc!(
|
||
r#"
|
||
f : a, b, * -> *
|
||
f = \_, _, x2 ->
|
||
inner : * -> *
|
||
inner = \y -> y
|
||
inner x2
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `inner` definition:
|
||
|
||
6│ inner : * -> *
|
||
7│ inner = \y -> y
|
||
^
|
||
|
||
The type annotation on `inner` says this `y` value should have the type:
|
||
|
||
*
|
||
|
||
However, the type of this `y` value is connected to another type in a
|
||
way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `inner` should have a named type variable
|
||
in place of the `*`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_inline_alias_not_an_alias,
|
||
indoc!(
|
||
r#"
|
||
f : List elem -> [Nil, Cons elem a] as a
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT AN INLINE ALIAS ────────── tmp/error_inline_alias_not_an_alias/Test.roc ─
|
||
|
||
The inline type after this `as` is not a type alias:
|
||
|
||
4│ f : List elem -> [Nil, Cons elem a] as a
|
||
^
|
||
|
||
Inline alias types must start with an uppercase identifier and be
|
||
followed by zero or more type arguments, like Point or List a.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_inline_alias_qualified,
|
||
indoc!(
|
||
r#"
|
||
f : List elem -> [Nil, Cons elem a] as Module.LinkedList a
|
||
"#
|
||
),
|
||
@r###"
|
||
── QUALIFIED ALIAS NAME ──────────── tmp/error_inline_alias_qualified/Test.roc ─
|
||
|
||
This type alias has a qualified name:
|
||
|
||
4│ f : List elem -> [Nil, Cons elem a] as Module.LinkedList a
|
||
^
|
||
|
||
An alias introduces a new name to the current scope, so it must be
|
||
unqualified.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_inline_alias_argument_uppercase,
|
||
indoc!(
|
||
r#"
|
||
f : List elem -> [Nil, Cons elem a] as LinkedList U
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE ARGUMENT NOT LOWERCASE ─ ...r_inline_alias_argument_uppercase/Test.roc ─
|
||
|
||
This alias type argument is not lowercase:
|
||
|
||
4│ f : List elem -> [Nil, Cons elem a] as LinkedList U
|
||
^
|
||
|
||
All type arguments must be lowercase.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mismatched_single_tag_arg,
|
||
indoc!(
|
||
r#"
|
||
isEmpty =
|
||
\email ->
|
||
Email str = email
|
||
Str.isEmpty str
|
||
|
||
isEmpty (Name "boo")
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `isEmpty` has an unexpected type:
|
||
|
||
9│ isEmpty (Name "boo")
|
||
^^^^^^^^^^
|
||
|
||
This `Name` tag application has the type:
|
||
|
||
[Name Str]
|
||
|
||
But `isEmpty` needs its 1st argument to be:
|
||
|
||
[Email Str]
|
||
|
||
Tip: Seems like a tag typo. Maybe `Name` should be `Email`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2326,
|
||
indoc!(
|
||
r#"
|
||
C a b : a -> D a b
|
||
D a b : { a, b }
|
||
|
||
f : C a Num.Nat -> D a Num.Nat
|
||
f = \c -> c 6
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `c` has an unexpected type:
|
||
|
||
8│ f = \c -> c 6
|
||
^
|
||
|
||
The argument is a number of type:
|
||
|
||
Num *
|
||
|
||
But `c` needs its 1st argument to be:
|
||
|
||
a
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `Num` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2380_annotations_only,
|
||
indoc!(
|
||
r#"
|
||
F : F
|
||
a : F
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `F` alias is self-recursive in an invalid way:
|
||
|
||
4│ F : F
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2380_typed_body,
|
||
indoc!(
|
||
r#"
|
||
F : F
|
||
a : F
|
||
a = 1
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `F` alias is self-recursive in an invalid way:
|
||
|
||
4│ F : F
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2380_alias_with_vars,
|
||
indoc!(
|
||
r#"
|
||
F a b : F a b
|
||
a : F Str Str
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `F` alias is self-recursive in an invalid way:
|
||
|
||
4│ F a b : F a b
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2167_record_field_optional_and_required_mismatch,
|
||
indoc!(
|
||
r#"
|
||
Job : [Job { inputs : List Str }]
|
||
job : { inputs ? List Str } -> Job
|
||
job = \{ inputs } ->
|
||
Job { inputs }
|
||
|
||
job { inputs: ["build", "test"] }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `job` is weird:
|
||
|
||
6│ job = \{ inputs } ->
|
||
^^^^^^^^^^
|
||
|
||
The argument is a pattern that matches record values of type:
|
||
|
||
{ inputs : List Str }
|
||
|
||
But the annotation on `job` says the 1st argument should be:
|
||
|
||
{ inputs ? List Str }
|
||
|
||
Tip: To extract the `.inputs` field it must be non-optional, but the
|
||
type says this field is optional. Learn more about optional fields at
|
||
TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unify_recursive_with_nonrecursive,
|
||
indoc!(
|
||
r#"
|
||
Job : [Job { inputs : List Job }]
|
||
|
||
job : { inputs : List Str } -> Job
|
||
job = \{ inputs } ->
|
||
Job { inputs }
|
||
|
||
job { inputs: ["build", "test"] }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `job` definition:
|
||
|
||
6│ job : { inputs : List Str } -> Job
|
||
7│ job = \{ inputs } ->
|
||
8│ Job { inputs }
|
||
^^^^^^^^^^^^^^
|
||
|
||
This `Job` tag application has the type:
|
||
|
||
[Job { inputs : List Str }]
|
||
|
||
But the type annotation on `job` says it should be:
|
||
|
||
[Job { inputs : List a }]a as a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
nested_datatype,
|
||
indoc!(
|
||
r#"
|
||
Nested a : [Chain a (Nested (List a)), Term]
|
||
|
||
s : Nested Str
|
||
|
||
s
|
||
"#
|
||
),
|
||
@r###"
|
||
── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Nested` is a nested datatype. Here is one recursive usage of it:
|
||
|
||
4│ Nested a : [Chain a (Nested (List a)), Term]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
But recursive usages of `Nested` must match its definition:
|
||
|
||
4│ Nested a : [Chain a (Nested (List a)), Term]
|
||
^^^^^^^^
|
||
|
||
Nested datatypes are not supported in Roc.
|
||
|
||
Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
nested_datatype_inline,
|
||
indoc!(
|
||
r#"
|
||
f : {} -> [Chain a (Nested (List a)), Term] as Nested a
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Nested` is a nested datatype. Here is one recursive usage of it:
|
||
|
||
4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a
|
||
^^^^^^^^^^^^^^^
|
||
|
||
But recursive usages of `Nested` must match its definition:
|
||
|
||
4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a
|
||
^^^^^^^^
|
||
|
||
Nested datatypes are not supported in Roc.
|
||
|
||
Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments.
|
||
"###
|
||
);
|
||
|
||
macro_rules! mismatched_suffix_tests {
|
||
($($number:expr, $suffix:expr, $name:ident)*) => {$(
|
||
test_report!(
|
||
$name,
|
||
&{
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_type = if $suffix == "u8" { "I8" } else { "U8" };
|
||
|
||
format!(indoc!(
|
||
r#"
|
||
use : Num.{} -> Num.U8
|
||
use {}{}
|
||
"#
|
||
), bad_type, number, $suffix)
|
||
},
|
||
|golden| {
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_type = if $suffix == "u8" { "I8" } else { "U8" };
|
||
let carets = "^".repeat(number.len() + $suffix.len());
|
||
let kind = match $suffix {
|
||
"dec"|"f32"|"f64" => "a fraction",
|
||
_ => "an integer",
|
||
};
|
||
|
||
let real = format!(indoc!(
|
||
r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `use` has an unexpected type:
|
||
|
||
5│ use {}{}
|
||
{}
|
||
|
||
The argument is {} of type:
|
||
|
||
{}
|
||
|
||
But `use` needs its 1st argument to be:
|
||
|
||
{}
|
||
"#
|
||
), number, $suffix, carets, kind, typ, bad_type);
|
||
|
||
assert_eq!(golden, real);
|
||
}
|
||
);
|
||
)*}
|
||
}
|
||
|
||
mismatched_suffix_tests! {
|
||
1, "u8", mismatched_suffix_u8
|
||
1, "u16", mismatched_suffix_u16
|
||
1, "u32", mismatched_suffix_u32
|
||
1, "u64", mismatched_suffix_u64
|
||
1, "u128", mismatched_suffix_u128
|
||
1, "i8", mismatched_suffix_i8
|
||
1, "i16", mismatched_suffix_i16
|
||
1, "i32", mismatched_suffix_i32
|
||
1, "i64", mismatched_suffix_i64
|
||
1, "i128", mismatched_suffix_i128
|
||
1, "nat", mismatched_suffix_nat
|
||
1, "dec", mismatched_suffix_dec
|
||
1, "f32", mismatched_suffix_f32
|
||
1, "f64", mismatched_suffix_f64
|
||
}
|
||
|
||
macro_rules! mismatched_suffix_tests_in_pattern {
|
||
($($number:expr, $suffix:expr, $name:ident)*) => {$(
|
||
test_report!(
|
||
$name,
|
||
&{
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" };
|
||
|
||
format!(indoc!(
|
||
r#"
|
||
when {}{} is
|
||
{}{} -> 1
|
||
_ -> 1
|
||
"#
|
||
), number, bad_suffix, number, $suffix)
|
||
},
|
||
|golden| {
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" };
|
||
let bad_type = if $suffix == "u8" { "I8" } else { "U8" };
|
||
|
||
let real = format!(indoc!(
|
||
r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when {}{} is
|
||
5│ {}{} -> 1
|
||
6│ _ -> 1
|
||
|
||
The `when` condition is an integer of type:
|
||
|
||
{}
|
||
|
||
But the branch patterns have type:
|
||
|
||
{}
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"#
|
||
), number, bad_suffix, number, $suffix, bad_type, typ);
|
||
|
||
assert_eq!(golden, real);
|
||
}
|
||
);
|
||
)*}
|
||
}
|
||
|
||
mismatched_suffix_tests_in_pattern! {
|
||
1, "u8", mismatched_suffix_u8_pattern
|
||
1, "u16", mismatched_suffix_u16_pattern
|
||
1, "u32", mismatched_suffix_u32_pattern
|
||
1, "u64", mismatched_suffix_u64_pattern
|
||
1, "u128", mismatched_suffix_u128_pattern
|
||
1, "i8", mismatched_suffix_i8_pattern
|
||
1, "i16", mismatched_suffix_i16_pattern
|
||
1, "i32", mismatched_suffix_i32_pattern
|
||
1, "i64", mismatched_suffix_i64_pattern
|
||
1, "i128", mismatched_suffix_i128_pattern
|
||
1, "nat", mismatched_suffix_nat_pattern
|
||
1, "dec", mismatched_suffix_dec_pattern
|
||
1, "f32", mismatched_suffix_f32_pattern
|
||
1, "f64", mismatched_suffix_f64_pattern
|
||
}
|
||
|
||
test_report!(
|
||
bad_numeric_literal_suffix,
|
||
indoc!(
|
||
r#"
|
||
1u256
|
||
"#
|
||
),
|
||
// TODO: link to number suffixes
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal contains an invalid digit:
|
||
|
||
4│ 1u256
|
||
^^^^^
|
||
|
||
Integer literals can only contain the digits
|
||
0-9, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
numer_literal_multi_suffix,
|
||
indoc!(
|
||
r#"
|
||
1u8u8
|
||
"#
|
||
),
|
||
// TODO: link to number suffixes
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal contains an invalid digit:
|
||
|
||
4│ 1u8u8
|
||
^^^^^
|
||
|
||
Integer literals can only contain the digits
|
||
0-9, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
int_literal_has_float_suffix,
|
||
indoc!(
|
||
r#"
|
||
0b1f32
|
||
"#
|
||
),
|
||
@r###"
|
||
── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This number literal is an integer, but it has a float suffix:
|
||
|
||
4│ 0b1f32
|
||
^^^^^^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
float_literal_has_int_suffix,
|
||
indoc!(
|
||
r#"
|
||
1.0u8
|
||
"#
|
||
),
|
||
@r###"
|
||
── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This number literal is a float, but it has an integer suffix:
|
||
|
||
4│ 1.0u8
|
||
^^^^^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u8_overflow,
|
||
"256u8",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 256u8
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U8, whose maximum value is
|
||
255.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u8,
|
||
"-1u8",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u8
|
||
^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U8, whose minimum value is
|
||
0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u16_overflow,
|
||
"65536u16",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 65536u16
|
||
^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U16, whose maximum value
|
||
is 65535.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u16,
|
||
"-1u16",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u16
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U16, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u32_overflow,
|
||
"4_294_967_296u32",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 4_294_967_296u32
|
||
^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U32, whose maximum value
|
||
is 4_294_967_295.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u32,
|
||
"-1u32",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u32
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U32, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u64_overflow,
|
||
"18_446_744_073_709_551_616u64",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 18_446_744_073_709_551_616u64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U64, whose maximum value
|
||
is 18_446_744_073_709_551_615.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u64,
|
||
"-1u64",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u64
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U64, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u128,
|
||
"-1u128",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u128
|
||
^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U128, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i8_overflow,
|
||
"128i8",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 128i8
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I8, whose maximum value is
|
||
127.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i8_underflow,
|
||
"-129i8",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -129i8
|
||
^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I8, whose minimum value is
|
||
-128.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i16_overflow,
|
||
"32768i16",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 32768i16
|
||
^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I16, whose maximum value
|
||
is 32767.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i16_underflow,
|
||
"-32769i16",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -32769i16
|
||
^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I16, whose minimum value
|
||
is -32768.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i32_overflow,
|
||
"2_147_483_648i32",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 2_147_483_648i32
|
||
^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I32, whose maximum value
|
||
is 2_147_483_647.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i32_underflow,
|
||
"-2_147_483_649i32",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -2_147_483_649i32
|
||
^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I32, whose minimum value
|
||
is -2_147_483_648.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i64_overflow,
|
||
"9_223_372_036_854_775_808i64",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 9_223_372_036_854_775_808i64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I64, whose maximum value
|
||
is 9_223_372_036_854_775_807.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i64_underflow,
|
||
"-9_223_372_036_854_775_809i64",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -9_223_372_036_854_775_809i64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I64, whose minimum value
|
||
is -9_223_372_036_854_775_808.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i128_overflow,
|
||
"170_141_183_460_469_231_731_687_303_715_884_105_728i128",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 170_141_183_460_469_231_731_687_303_715_884_105_728i128
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I128, whose maximum value
|
||
is 170_141_183_460_469_231_731_687_303_715_884_105_727.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_get_negative_number,
|
||
indoc!(
|
||
r#"
|
||
List.get [1,2,3] -1
|
||
"#
|
||
),
|
||
// TODO: this error message could be improved, e.g. something like "This argument can
|
||
// be used as ... because of its literal value"
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `get` has an unexpected type:
|
||
|
||
4│ List.get [1,2,3] -1
|
||
^^
|
||
|
||
The argument is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But `get` needs its 2nd argument to be:
|
||
|
||
Nat
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_get_negative_number_indirect,
|
||
indoc!(
|
||
r#"
|
||
a = -9_223_372_036_854
|
||
List.get [1,2,3] a
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `get` has an unexpected type:
|
||
|
||
5│ List.get [1,2,3] a
|
||
^
|
||
|
||
This `a` value is a:
|
||
|
||
F64, I64, I128, or Dec
|
||
|
||
But `get` needs its 2nd argument to be:
|
||
|
||
Nat
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_get_negative_number_double_indirect,
|
||
indoc!(
|
||
r#"
|
||
a = -9_223_372_036_854
|
||
b = a
|
||
List.get [1,2,3] b
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `get` has an unexpected type:
|
||
|
||
6│ List.get [1,2,3] b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64, I64, I128, or Dec
|
||
|
||
But `get` needs its 2nd argument to be:
|
||
|
||
Nat
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
compare_unsigned_to_signed,
|
||
indoc!(
|
||
r#"
|
||
when -1 is
|
||
1u8 -> 1
|
||
_ -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when -1 is
|
||
5│ 1u8 -> 1
|
||
6│ _ -> 1
|
||
|
||
The `when` condition is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But the branch patterns have type:
|
||
|
||
U8
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_type_alias_is_newtype,
|
||
indoc!(
|
||
r#"
|
||
R a : [Only (R a)]
|
||
|
||
v : R Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `R` alias is self-recursive in an invalid way:
|
||
|
||
4│ R a : [Only (R a)]
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_type_alias_is_newtype_deep,
|
||
indoc!(
|
||
r#"
|
||
R a : [Only { very: [Deep (R a)] }]
|
||
|
||
v : R Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `R` alias is self-recursive in an invalid way:
|
||
|
||
4│ R a : [Only { very: [Deep (R a)] }]
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_type_alias_is_newtype_mutual,
|
||
indoc!(
|
||
r#"
|
||
Foo a : [Thing (Bar a)]
|
||
Bar a : [Stuff (Foo a)]
|
||
|
||
v : Bar Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` alias is recursive in an invalid way:
|
||
|
||
4│ Foo a : [Thing (Bar a)]
|
||
^^^
|
||
|
||
The `Foo` alias depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ Foo
|
||
│ ↓
|
||
│ Bar
|
||
└─────┘
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2458,
|
||
indoc!(
|
||
r#"
|
||
Result a b : [Ok a, Err b]
|
||
|
||
Foo a : [Blah (Result (Bar a) [])]
|
||
Bar a : Foo a
|
||
|
||
v : Bar Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This alias has the same name as a builtin:
|
||
|
||
4│ Result a b : [Ok a, Err b]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
All builtin aliases are in scope by default, so I need this alias to
|
||
have a different name!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_type_not_in_scope,
|
||
indoc!(
|
||
r#"
|
||
@Age 21
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
4│ @Age 21
|
||
^^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_reference_not_opaque_type,
|
||
indoc!(
|
||
r#"
|
||
Age : Num.U8
|
||
|
||
@Age 21
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
6│ @Age 21
|
||
^^^^
|
||
|
||
Note: There is an alias of the same name:
|
||
|
||
4│ Age : Num.U8
|
||
^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Age` is not used anywhere in your code.
|
||
|
||
4│ Age : Num.U8
|
||
^^^^^^^^^^^^
|
||
|
||
If you didn't intend on using `Age` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
qualified_opaque_reference,
|
||
indoc!(
|
||
r#"
|
||
OtherModule.@Age 21
|
||
"#
|
||
),
|
||
// TODO: get rid of the first error. Consider parsing OtherModule.@Age to completion
|
||
// and checking it during can. The reason the error appears is because it is parsed as
|
||
// Apply(Error(OtherModule), [@Age, 21])
|
||
@r###"
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
4│ OtherModule.@Age 21
|
||
^^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse a qualified name here:
|
||
|
||
4│ OtherModule.@Age 21
|
||
^
|
||
|
||
I was expecting to see an identifier next, like height. A complete
|
||
qualified name looks something like Json.Decode.string.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_used_outside_declaration_scope,
|
||
indoc!(
|
||
r#"
|
||
age =
|
||
Age := Num.U8
|
||
21u8
|
||
|
||
@Age age
|
||
"#
|
||
),
|
||
// TODO(opaques): there is a potential for a better error message here, if the usage of
|
||
// `@Age` can be linked to the declaration of `Age` inside `age`, and a suggestion to
|
||
// raise that declaration to the outer scope.
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Age` is not used anywhere in your code.
|
||
|
||
5│ Age := Num.U8
|
||
^^^^^^^^^^^^^
|
||
|
||
If you didn't intend on using `Age` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
8│ @Age age
|
||
^^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unimported_modules_reported,
|
||
indoc!(
|
||
r#"
|
||
alt : Task.Task {} []
|
||
alt = "whatever man you don't even know my type"
|
||
alt
|
||
"#
|
||
),
|
||
@r###"
|
||
── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Task` module is not imported:
|
||
|
||
4│ alt : Task.Task {} []
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Is there an import missing? Perhaps there is a typo. Did you mean one
|
||
of these?
|
||
|
||
Hash
|
||
List
|
||
Num
|
||
Box
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_check,
|
||
indoc!(
|
||
r#"
|
||
Age := Num.U8
|
||
|
||
n : Age
|
||
n = @Age ""
|
||
|
||
n
|
||
"#
|
||
),
|
||
// TODO(opaques): error could be improved by saying that the opaque definition demands
|
||
// that the argument be a U8, and linking to the definitin!
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
7│ n = @Age ""
|
||
^^
|
||
|
||
This argument to an opaque type has type:
|
||
|
||
Str
|
||
|
||
But you are trying to use it as:
|
||
|
||
U8
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_infer,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
if Bool.true
|
||
then @F ""
|
||
else @F {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
8│ else @F {}
|
||
^^
|
||
|
||
This argument to an opaque type has type:
|
||
|
||
{}
|
||
|
||
But you are trying to use it as:
|
||
|
||
Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_creation_is_not_wrapped,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
v : F Str
|
||
v = ""
|
||
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `v` definition:
|
||
|
||
6│ v : F Str
|
||
7│ v = ""
|
||
^^
|
||
|
||
The body is a string of type:
|
||
|
||
Str
|
||
|
||
But the type annotation on `v` says it should be:
|
||
|
||
F Str
|
||
|
||
Tip: Type comparisons between an opaque type are only ever equal if
|
||
both types are the same opaque type. Did you mean to create an opaque
|
||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||
an instance of this opaque type by doing @Age 23.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_pattern_check,
|
||
indoc!(
|
||
r#"
|
||
Age := Num.U8
|
||
|
||
f : Age -> Num.U8
|
||
f = \Age n -> n
|
||
|
||
f
|
||
"#
|
||
),
|
||
// TODO(opaques): error could be improved by saying that the user-provided pattern
|
||
// probably wants to change "Age" to "@Age"!
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `f` is weird:
|
||
|
||
7│ f = \Age n -> n
|
||
^^^^^
|
||
|
||
The argument is a pattern that matches a `Age` tag of type:
|
||
|
||
[Age *]
|
||
|
||
But the annotation on `f` says the 1st argument should be:
|
||
|
||
Age
|
||
|
||
Tip: Type comparisons between an opaque type are only ever equal if
|
||
both types are the same opaque type. Did you mean to create an opaque
|
||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||
an instance of this opaque type by doing @Age 23.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_pattern_infer,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
\x ->
|
||
when x is
|
||
@F A -> ""
|
||
@F {} -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern in this `when` does not match the previous ones:
|
||
|
||
9│ @F {} -> ""
|
||
^^^^^
|
||
|
||
The 2nd pattern is trying to matchF unwrappings of type:
|
||
|
||
F {}a
|
||
|
||
But all the previous branches match:
|
||
|
||
F [A]
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_pattern_match_not_exhaustive_tag,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
v : F [A, B, C]
|
||
|
||
when v is
|
||
@F A -> ""
|
||
@F B -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
8│> when v is
|
||
9│ @F A -> ""
|
||
10│ @F B -> ""
|
||
|
||
This `v` value is a:
|
||
|
||
F [A, B, C]
|
||
|
||
But the branch patterns have type:
|
||
|
||
F [A, B]
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
|
||
Tip: Looks like the branches are missing coverage of the `C` tag.
|
||
|
||
Tip: Maybe you need to add a catch-all branch, like `_`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_pattern_match_not_exhaustive_int,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
v : F Num.U8
|
||
|
||
when v is
|
||
@F 1 -> ""
|
||
@F 2 -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
8│> when v is
|
||
9│> @F 1 -> ""
|
||
10│> @F 2 -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
@F _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
let_polymorphism_with_scoped_type_variables,
|
||
indoc!(
|
||
r#"
|
||
f : a -> a
|
||
f = \x ->
|
||
y : a -> a
|
||
y = \z -> z
|
||
|
||
n = y 1u8
|
||
x1 = y x
|
||
(\_ -> x1) n
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `y` has an unexpected type:
|
||
|
||
9│ n = y 1u8
|
||
^^^
|
||
|
||
The argument is an integer of type:
|
||
|
||
U8
|
||
|
||
But `y` needs its 1st argument to be:
|
||
|
||
a
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `U8` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
non_exhaustive_with_guard,
|
||
indoc!(
|
||
r#"
|
||
x : [A]
|
||
when x is
|
||
A if Bool.true -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
5│> when x is
|
||
6│> A if Bool.true -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
A (note the lack of an if clause)
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_record_extension_type,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.Nat }[]
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record extension type is invalid:
|
||
|
||
4│ f : { x : Num.Nat }[]
|
||
^^
|
||
|
||
Note: A record extension variable can only contain a type variable or
|
||
another record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_tag_extension_type,
|
||
indoc!(
|
||
r#"
|
||
f : [A]Str
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This tag union extension type is invalid:
|
||
|
||
4│ f : [A]Str
|
||
^^^
|
||
|
||
Note: A tag union extension variable can only contain a type variable
|
||
or another tag union.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unknown_type,
|
||
indoc!(
|
||
r#"
|
||
Type : [Constructor UnknownType]
|
||
|
||
insertHelper : UnknownType, Type -> Type
|
||
insertHelper = \h, m ->
|
||
when m is
|
||
Constructor _ -> Constructor h
|
||
|
||
insertHelper
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `UnknownType` in this scope.
|
||
|
||
4│ Type : [Constructor UnknownType]
|
||
^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Type
|
||
Unsigned8
|
||
Unsigned32
|
||
Unsigned16
|
||
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `UnknownType` in this scope.
|
||
|
||
6│ insertHelper : UnknownType, Type -> Type
|
||
^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Type
|
||
Unsigned8
|
||
Unsigned32
|
||
Unsigned16
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_first_demand_not_indented_enough,
|
||
indoc!(
|
||
r#"
|
||
MEq has
|
||
eq : a, a -> U64 | a has MEq
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ABILITY ── tmp/ability_first_demand_not_indented_enough/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
4│ MEq has
|
||
5│ eq : a, a -> U64 | a has MEq
|
||
^
|
||
|
||
I suspect this line is not indented enough (by 1 spaces)
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_demands_not_indented_with_first,
|
||
indoc!(
|
||
r#"
|
||
MEq has
|
||
eq : a, a -> U64 | a has MEq
|
||
neq : a, a -> U64 | a has MEq
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNFINISHED ABILITY ─── tmp/ability_demands_not_indented_with_first/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
5│ eq : a, a -> U64 | a has MEq
|
||
6│ neq : a, a -> U64 | a has MEq
|
||
^
|
||
|
||
I suspect this line is indented too much (by 4 spaces)"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_demand_value_has_args,
|
||
indoc!(
|
||
r#"
|
||
MEq has
|
||
eq b c : a, a -> U64 | a has MEq
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNFINISHED ABILITY ───────────── tmp/ability_demand_value_has_args/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
4│ MEq has
|
||
5│ eq b c : a, a -> U64 | a has MEq
|
||
^
|
||
|
||
I was expecting to see a : annotating the signature of this value
|
||
next."#
|
||
);
|
||
|
||
test_report!(
|
||
ability_non_signature_expression,
|
||
indoc!(
|
||
r#"
|
||
MEq has
|
||
123
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ABILITY ────────── tmp/ability_non_signature_expression/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
4│ MEq has
|
||
5│ 123
|
||
^
|
||
|
||
I was expecting to see a value signature next.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
wildcard_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : Num.Int *
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I : Num.Int *
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
wildcard_in_opaque,
|
||
indoc!(
|
||
r#"
|
||
I := Num.Int *
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I := Num.Int *
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:=`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multiple_wildcards_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : [A (Num.Int *), B (Num.Int *)]
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has 2 unbound type variables.
|
||
|
||
Here is one occurrence:
|
||
|
||
4│ I : [A (Num.Int *), B (Num.Int *)]
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : Num.Int _
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I : Num.Int _
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unbound_var_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : Num.Int a
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I : Num.Int a
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_bad_type_parameter,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [] to "./platform"
|
||
|
||
MHash a b c has
|
||
hash : a -> U64 | a has MHash
|
||
"#
|
||
),
|
||
@r###"
|
||
── ABILITY HAS TYPE VARIABLES ──────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the `MHash` ability includes type variables:
|
||
|
||
3│ MHash a b c has
|
||
^^^^^
|
||
|
||
Abilities cannot depend on type variables, but their member values
|
||
can!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`MHash` is not used anywhere in your code.
|
||
|
||
3│ MHash a b c has
|
||
^^^^^
|
||
|
||
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_has_clause,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash] to "./platform"
|
||
|
||
MHash has hash : a, b -> Num.U64 | a has MHash, b has Bool.Bool
|
||
"#
|
||
),
|
||
@r###"
|
||
── HAS CLAUSE IS NOT AN ABILITY ────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The type referenced in this "has" clause is not an ability:
|
||
|
||
3│ MHash has hash : a, b -> Num.U64 | a has MHash, b has Bool.Bool
|
||
^^^^^^^^^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
shadowed_type_variable_in_has_clause,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [ab1] to "./platform"
|
||
|
||
Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||
"#
|
||
),
|
||
@r#"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `a` name is first defined here:
|
||
|
||
3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||
^^^^^^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||
^^^^^^^^^
|
||
|
||
Since these variables have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_shadows_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [ab] to "./platform"
|
||
|
||
Ability has ab : a -> U64 | a has Ability
|
||
|
||
Ability has ab1 : a -> U64 | a has Ability
|
||
"#
|
||
),
|
||
@r#"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Ability` name is first defined here:
|
||
|
||
3│ Ability has ab : a -> U64 | a has Ability
|
||
^^^^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
5│ Ability has ab1 : a -> U64 | a has Ability
|
||
^^^^^^^
|
||
|
||
Since these abilities have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_member_does_not_bind_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [] to "./platform"
|
||
|
||
Ability has ab : {} -> {}
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the ability member `ab` does not include a `has` clause
|
||
binding a type variable to the ability `Ability`:
|
||
|
||
3│ Ability has ab : {} -> {}
|
||
^^
|
||
|
||
Ability members must include a `has` clause binding a type variable to
|
||
an ability, like
|
||
|
||
a has Ability
|
||
|
||
Otherwise, the function does not need to be part of the ability!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Ability` is not used anywhere in your code.
|
||
|
||
3│ Ability has 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 has eq : a, b -> Bool.Bool | a has MEq, b has MEq
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY MEMBER BINDS MULTIPLE VARIABLES ─────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the ability member `eq` includes multiple variables
|
||
bound to the `MEq`` ability:`
|
||
|
||
3│ MEq has eq : a, b -> Bool.Bool | a has MEq, b has 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 has hash : (a | a has MHash) -> Num.U64
|
||
|
||
f : a -> Num.U64 | a has MHash
|
||
"#
|
||
),
|
||
@r###"
|
||
── ILLEGAL HAS CLAUSE ──────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
A `has` clause is not allowed here:
|
||
|
||
3│ MHash has hash : (a | a has MHash) -> Num.U64
|
||
^^^^^^^^^^^
|
||
|
||
`has` clauses can only be specified on the top-level type annotations.
|
||
|
||
── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the ability member `hash` does not include a `has`
|
||
clause binding a type variable to the ability `MHash`:
|
||
|
||
3│ MHash has hash : (a | a has MHash) -> Num.U64
|
||
^^^^
|
||
|
||
Ability members must include a `has` clause binding a type variable to
|
||
an ability, like
|
||
|
||
a has 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 has hash : a -> U64 | a has MHash
|
||
|
||
Id := U32 has [MHash {hash}]
|
||
|
||
hash = \@Id n -> n
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with this specialization of `hash`:
|
||
|
||
7│ hash = \@Id n -> n
|
||
^^^^
|
||
|
||
This value is a declared specialization of type:
|
||
|
||
Id -> U32
|
||
|
||
But the type annotation on `hash` says it must match:
|
||
|
||
Id -> U64
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_is_incomplete,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [eq, le] to "./platform"
|
||
|
||
MEq has
|
||
eq : a, a -> Bool | a has MEq
|
||
le : a, a -> Bool | a has MEq
|
||
|
||
Id := U64 has [MEq {eq}]
|
||
|
||
eq = \@Id m, @Id n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `MEq` ability:
|
||
|
||
7│ Id := U64 has [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 has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
hash = \_ -> 0u64
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`hash` is not used anywhere in your code.
|
||
|
||
6│ hash = \_ -> 0u64
|
||
^^^^
|
||
|
||
If you didn't intend on using `hash` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_is_duplicated,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash, One, Two] to "./platform"
|
||
|
||
MHash has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
One := {} has [MHash {hash}]
|
||
Two := {} has [MHash {hash}]
|
||
|
||
hash = \_ -> 0u64
|
||
"#
|
||
),
|
||
// TODO: the error message here could be seriously improved!
|
||
@r###"
|
||
── OVERLOADED SPECIALIZATION ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability member specialization is already claimed to specialize
|
||
another opaque type:
|
||
|
||
7│ Two := {} has [MHash {hash}]
|
||
^^^^
|
||
|
||
Previously, we found it to specialize `hash` for `One`.
|
||
|
||
Ability specializations can only provide implementations for one
|
||
opaque type, since all opaque types are different!
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This specialization of `hash` is overly general:
|
||
|
||
9│ hash = \_ -> 0u64
|
||
^^^^
|
||
|
||
This value is a declared specialization of type:
|
||
|
||
* -> U64
|
||
|
||
But the type annotation on `hash` says it must match:
|
||
|
||
a -> U64 | a has 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 has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
One := {} has [MHash {hash}]
|
||
Two := {} has [MHash {hash}]
|
||
|
||
hash = \@One _ -> 0u64
|
||
"#
|
||
),
|
||
@r###"
|
||
── OVERLOADED SPECIALIZATION ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability member specialization is already claimed to specialize
|
||
another opaque type:
|
||
|
||
7│ Two := {} has [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 has
|
||
eq : a, a -> Bool | a has MEq
|
||
|
||
You := {} has [MEq {eq}]
|
||
AndI := {}
|
||
|
||
eq = \@You {}, @AndI {} -> False
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with this specialization of `eq`:
|
||
|
||
9│ eq = \@You {}, @AndI {} -> False
|
||
^^
|
||
|
||
This value is a declared specialization of type:
|
||
|
||
You, AndI -> [False]
|
||
|
||
But the type annotation on `eq` says it must match:
|
||
|
||
You, You -> Bool
|
||
|
||
Tip: Did you mean to use `Bool.false` rather than `False`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_checked_against_annotation,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash] to "./platform"
|
||
|
||
MHash has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
Id := U64 has [MHash {hash}]
|
||
|
||
hash : Id -> U32
|
||
hash = \@Id n -> n
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `hash` definition:
|
||
|
||
8│ hash : Id -> U32
|
||
9│ hash = \@Id n -> n
|
||
^
|
||
|
||
This `n` value is a:
|
||
|
||
U64
|
||
|
||
But the type annotation on `hash` says it should be:
|
||
|
||
U32
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_called_with_non_specializing,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [noGoodVeryBadTerrible] to "./platform"
|
||
|
||
MHash has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
Id := U64 has [MHash {hash}]
|
||
|
||
hash = \@Id n -> n
|
||
|
||
User := {}
|
||
|
||
noGoodVeryBadTerrible =
|
||
{
|
||
nope: hash (@User {}),
|
||
notYet: hash (A 1),
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
15│ notYet: hash (A 1),
|
||
^^^
|
||
|
||
I can't generate an implementation of the `MHash` ability for
|
||
|
||
[A (Num *)]
|
||
|
||
Only builtin abilities can have generated implementations!
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
14│ nope: hash (@User {}),
|
||
^^^^^^^^
|
||
|
||
The type `User` does not fully implement the ability `MHash`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_not_on_toplevel,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
main =
|
||
MHash has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
123
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY NOT ON TOP-LEVEL ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability definition is not on the top-level of a module:
|
||
|
||
4│> MHash has
|
||
5│> hash : a -> U64 | a has 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 has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
Id := U64 has [MHash {hash}]
|
||
hash = \@Id n -> n
|
||
|
||
hashable : a | a has MHash
|
||
hashable = @Id 15
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `hashable` definition:
|
||
|
||
9│ hashable : a | a has MHash
|
||
10│ hashable = @Id 15
|
||
^^^^^^
|
||
|
||
This Id opaque wrapping has the type:
|
||
|
||
Id
|
||
|
||
But the type annotation on `hashable` says it should be:
|
||
|
||
a | a has MHash
|
||
|
||
Note: The type variable `a` says it can take on any value that has 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 has
|
||
hash : a -> U64 | a has MHash
|
||
|
||
mulMHashes : MHash, MHash -> U64
|
||
mulMHashes = \x, y -> hash x * hash y
|
||
|
||
Id := U64 has [MHash {hash: hashId}]
|
||
hashId = \@Id n -> n
|
||
|
||
Three := {} has [MHash {hash: hashThree}]
|
||
hashThree = \@Three _ -> 3
|
||
|
||
result = mulMHashes (@Id 100) (@Three {})
|
||
"#
|
||
),
|
||
@r###"
|
||
── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
You are attempting to use the ability `MHash` as a type directly:
|
||
|
||
6│ mulMHashes : MHash, MHash -> U64
|
||
^^^^^
|
||
|
||
Abilities can only be used in type annotations to constrain type
|
||
variables.
|
||
|
||
Hint: Perhaps you meant to include a `has` annotation, like
|
||
|
||
a has MHash
|
||
|
||
── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
You are attempting to use the ability `MHash` as a type directly:
|
||
|
||
6│ mulMHashes : MHash, MHash -> U64
|
||
^^^^^
|
||
|
||
Abilities can only be used in type annotations to constrain type
|
||
variables.
|
||
|
||
Hint: Perhaps you meant to include a `has` annotation, like
|
||
|
||
b has MHash
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
branches_have_more_cases_than_condition,
|
||
indoc!(
|
||
r#"
|
||
foo : Bool -> Str
|
||
foo = \bool ->
|
||
when bool is
|
||
True -> "true"
|
||
False -> "false"
|
||
Wat -> "surprise!"
|
||
foo
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when bool is
|
||
7│ True -> "true"
|
||
8│ False -> "false"
|
||
9│ Wat -> "surprise!"
|
||
|
||
This `bool` value is a:
|
||
|
||
Bool
|
||
|
||
But the branch patterns have type:
|
||
|
||
[False, True, Wat]
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"#
|
||
);
|
||
|
||
// from https://github.com/roc-lang/roc/commit/1372737f5e53ee5bb96d7e1b9593985e5537023a
|
||
// There was a bug where this reported UnusedArgument("val")
|
||
// since it was used only in the returned function only.
|
||
//
|
||
// we want this to not give any warnings/errors!
|
||
test_report!(
|
||
always_function,
|
||
indoc!(
|
||
r#"
|
||
always = \val -> \_ -> val
|
||
|
||
always
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
imports_missing_comma,
|
||
indoc!(
|
||
r#"
|
||
app "test-missing-comma"
|
||
packages { pf: "platform/main.roc" }
|
||
imports [pf.Task Base64]
|
||
provides [main, @Foo] to pf
|
||
"#
|
||
),
|
||
@r#"
|
||
── WEIRD IMPORTS ────────────────────────── tmp/imports_missing_comma/Test.roc ─
|
||
|
||
I am partway through parsing a imports list, but I got stuck here:
|
||
|
||
2│ packages { pf: "platform/main.roc" }
|
||
3│ imports [pf.Task Base64]
|
||
^
|
||
|
||
I am expecting a comma or end of list, like
|
||
|
||
imports [Shape, Vector]"#
|
||
);
|
||
|
||
test_report!(
|
||
not_enough_cases_for_open_union,
|
||
indoc!(
|
||
r#"
|
||
foo : [A, B]a -> Str
|
||
foo = \it ->
|
||
when it is
|
||
A -> ""
|
||
foo
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when it is
|
||
7│> A -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
B
|
||
_
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
issue_2778_specialization_is_not_a_redundant_pattern,
|
||
indoc!(
|
||
r#"
|
||
formatColor = \color ->
|
||
when color is
|
||
Red -> "red"
|
||
Yellow -> "yellow"
|
||
_ -> "unknown"
|
||
|
||
Red |> formatColor |> Str.concat (formatColor Orange)
|
||
"#
|
||
),
|
||
@"" // no problem
|
||
);
|
||
|
||
test_report!(
|
||
nested_specialization,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
Default has default : {} -> a | a has Default
|
||
|
||
main =
|
||
A := {} has [Default {default}]
|
||
default = \{} -> @A {}
|
||
default {}
|
||
"#
|
||
),
|
||
@r#"
|
||
── SPECIALIZATION NOT ON TOP-LEVEL ─────────────────────── /code/proj/Main.roc ─
|
||
|
||
This specialization of the `default` ability member is in a nested
|
||
scope:
|
||
|
||
7│ default = \{} -> @A {}
|
||
^^^^^^^
|
||
|
||
Specializations can only be defined on the top-level of a module.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
recursion_var_specialization_error,
|
||
indoc!(
|
||
r#"
|
||
Job a : [Job (List (Job a))]
|
||
|
||
job : Job Str
|
||
|
||
when job is
|
||
Job lst -> lst == ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `isEq` has an unexpected type:
|
||
|
||
9│ Job lst -> lst == ""
|
||
^^
|
||
|
||
The argument is a string of type:
|
||
|
||
Str
|
||
|
||
But `isEq` needs its 2nd argument to be:
|
||
|
||
List [Job ∞] as ∞
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
type_error_in_apply_is_circular,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Set] provides [go] to "./platform"
|
||
|
||
S a : { set : Set.Set a }
|
||
|
||
go : a, S a -> Result (List a) *
|
||
go = \goal, model ->
|
||
if goal == goal
|
||
then Ok []
|
||
else
|
||
new = { model & set : Set.remove goal model.set }
|
||
go goal new
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `remove` has an unexpected type:
|
||
|
||
10│ new = { model & set : Set.remove goal model.set }
|
||
^^^^
|
||
|
||
This `goal` value is a:
|
||
|
||
a
|
||
|
||
But `remove` needs the 1st argument to be:
|
||
|
||
Set k
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `Set` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `new`:
|
||
|
||
10│ new = { model & set : Set.remove goal model.set }
|
||
^^^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
{ set : Set ∞ }
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `goal`:
|
||
|
||
6│ go = \goal, model ->
|
||
^^^^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
Set ∞
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cycle_through_non_function,
|
||
indoc!(
|
||
r#"
|
||
force : ({} -> I64) -> I64
|
||
force = \eval -> eval {}
|
||
|
||
t1 = \_ -> force (\_ -> t2)
|
||
|
||
t2 = t1 {}
|
||
|
||
t2
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `t1` definition is causing a very tricky infinite loop:
|
||
|
||
7│ t1 = \_ -> force (\_ -> t2)
|
||
^^
|
||
|
||
The `t1` value depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ t1
|
||
│ ↓
|
||
│ t2
|
||
└─────┘
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
function_does_not_implement_encoding,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [] provides [main] to "./platform"
|
||
|
||
main = Encode.toEncoder \x -> x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
3│ main = Encode.toEncoder \x -> x
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Encoding` ability for
|
||
|
||
a -> a
|
||
|
||
Note: `Encoding` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
nested_opaque_does_not_implement_encoding,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [] provides [main] to "./platform"
|
||
|
||
A := {}
|
||
main = Encode.toEncoder { x: @A {} }
|
||
"#
|
||
),
|
||
// TODO: this error message is quite unfortunate. We should remove the duplication, and
|
||
// also support regions that point to things in other modules. See also https://github.com/roc-lang/roc/issues/3056.
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
4│ main = Encode.toEncoder { x: @A {} }
|
||
^^^^^^^^^^^^
|
||
|
||
I can't generate an implementation of the `Encoding` ability for
|
||
|
||
{ x : A }
|
||
|
||
In particular, an implementation for
|
||
|
||
A
|
||
|
||
cannot be generated.
|
||
|
||
Tip: `A` does not implement `Encoding`. Consider adding a custom
|
||
implementation or `has Encode.Encoding` to the definition of `A`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cycle_through_non_function_top_level,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [t2] to "./platform"
|
||
|
||
force : ({} -> I64) -> I64
|
||
force = \eval -> eval {}
|
||
|
||
t1 = \_ -> force (\_ -> t2)
|
||
|
||
t2 = t1 {}
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `t1` definition is causing a very tricky infinite loop:
|
||
|
||
6│ t1 = \_ -> force (\_ -> t2)
|
||
^^
|
||
|
||
The `t1` value depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ t1
|
||
│ ↓
|
||
│ t2
|
||
└─────┘
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
opaque_ability_impl_not_found_shorthand_syntax,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
MEq has eq : a, a -> U64 | a has MEq
|
||
|
||
A := U8 has [MEq {eq}]
|
||
"#
|
||
),
|
||
@r###"
|
||
── IMPLEMENTATION NOT FOUND ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
An implementation of `eq` could not be found in this scope:
|
||
|
||
5│ A := U8 has [MEq {eq}]
|
||
^^
|
||
|
||
Tip: consider adding a value of name `eq` in this scope, or using
|
||
another variable that implements this ability member, like
|
||
{ eq: myeq }
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `MEq` ability:
|
||
|
||
5│ A := U8 has [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 has eq : a, a -> Bool | a has MEq
|
||
|
||
A := U8 has [ MEq {eq: aMEq} ]
|
||
|
||
myMEq = \m, n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `aMEq` in this scope.
|
||
|
||
5│ A := U8 has [ MEq {eq: aMEq} ]
|
||
^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
MEq
|
||
Eq
|
||
myMEq
|
||
eq
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `MEq` ability:
|
||
|
||
5│ A := U8 has [ 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 has eq : a, a -> Bool | a has MEq
|
||
|
||
A := U8 has [ MEq {eq ? aMEq} ]
|
||
|
||
myMEq = \m, n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPTIONAL ABILITY IMPLEMENTATION ─────────────────────── /code/proj/Main.roc ─
|
||
|
||
Ability implementations cannot be optional:
|
||
|
||
5│ A := U8 has [ MEq {eq ? aMEq} ]
|
||
^^^^^^^^^
|
||
|
||
Custom implementations must be supplied fully.
|
||
|
||
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `MEq` ability:
|
||
|
||
5│ A := U8 has [ 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 has [ Encoding {toEncoder ? myEncoder} ]
|
||
|
||
myEncoder = 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPTIONAL ABILITY IMPLEMENTATION ─────────────────────── /code/proj/Main.roc ─
|
||
|
||
Ability implementations cannot be optional:
|
||
|
||
5│ A := U8 has [ 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, has [Encoding] will attempt
|
||
to derive `Encoding`
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Encoding` ability:
|
||
|
||
5│ A := U8 has [ 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 has eq : a, a -> Bool | a has MEq
|
||
|
||
A := U8 has [ MEq {eq : Bool.eq} ]
|
||
"#
|
||
),
|
||
@r###"
|
||
── QUALIFIED ABILITY IMPLEMENTATION ────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability implementation is qualified:
|
||
|
||
5│ A := U8 has [ MEq {eq : Bool.eq} ]
|
||
^^^^^^^
|
||
|
||
Custom implementations must be defined in the local scope, and
|
||
unqualified.
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `MEq` ability:
|
||
|
||
5│ A := U8 has [ 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 has eq : a, a -> Bool | a has MEq
|
||
|
||
A := U8 has [ MEq {eq : \m, n -> m == n} ]
|
||
"#
|
||
),
|
||
@r###"
|
||
── ABILITY IMPLEMENTATION NOT IDENTIFIER ───────────────── /code/proj/Main.roc ─
|
||
|
||
This ability implementation is not an identifier:
|
||
|
||
5│ A := U8 has [ MEq {eq : \m, n -> m == n} ]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Custom ability implementations defined in this position can only be
|
||
unqualified identifiers, not arbitrary expressions.
|
||
|
||
Tip: consider defining this expression as a variable.
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `MEq` ability:
|
||
|
||
5│ A := U8 has [ 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 has eq : a, a -> Bool | a has MEq
|
||
|
||
A := U8 has [ MEq {eq: eqA, eq: eqA} ]
|
||
|
||
eqA = \@A m, @A n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE IMPLEMENTATION ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability member implementation is duplicate:
|
||
|
||
5│ A := U8 has [ MEq {eq: eqA, eq: eqA} ]
|
||
^^^^^^^
|
||
|
||
The first implementation was defined here:
|
||
|
||
5│ A := U8 has [ 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 has [ Foo {} ]
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT AN ABILITY ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This identifier is not an ability in scope:
|
||
|
||
5│ A := U8 has [ Foo {} ]
|
||
^^^
|
||
|
||
Only abilities can be implemented.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
derive_non_builtin_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
Ab has ab : a -> a | a has Ab
|
||
|
||
A := {} has [Ab]
|
||
"#
|
||
),
|
||
@r###"
|
||
── ILLEGAL DERIVE ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability cannot be derived:
|
||
|
||
5│ A := {} has [Ab]
|
||
^^
|
||
|
||
Only builtin abilities can be derived.
|
||
|
||
Note: The builtin abilities are `Encoding`, `Decoding`, `Hash`, `Eq`
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
has_encoding_for_function,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [] provides [A] to "./platform"
|
||
|
||
A a := a -> a has [Encode.Encoding]
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Encoding` ability for `A`:
|
||
|
||
3│ A a := a -> a has [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 has [Encode.Encoding]
|
||
|
||
B := {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Encoding` ability for `A`:
|
||
|
||
3│ A := B has [Encode.Encoding]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Tip: `B` does not implement `Encoding`. Consider adding a custom
|
||
implementation or `has 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 has [Encode.Encoding]
|
||
|
||
B := {} has [Encode.Encoding]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
has_encoding_for_recursive_deriving,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [] provides [MyNat] to "./platform"
|
||
|
||
MyNat := [S MyNat, Z] has [Encode.Encoding]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
shadowing_top_level_scope,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [ main ] to "./platform"
|
||
|
||
main = 1
|
||
|
||
main = \n -> n + 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `main` name is first defined here:
|
||
|
||
3│ main = 1
|
||
^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
5│ main = \n -> n + 2
|
||
^^^^
|
||
|
||
Since these variables have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
5│ main = \n -> n + 2
|
||
^^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_1755,
|
||
indoc!(
|
||
r#"
|
||
Handle := {}
|
||
|
||
await : Result a err, (a -> Result b err) -> Result b err
|
||
open : {} -> Result Handle *
|
||
close : Handle -> Result {} *
|
||
|
||
withOpen : (Handle -> Result {} *) -> Result {} *
|
||
withOpen = \callback ->
|
||
handle <- await (open {})
|
||
{} <- await (callback handle)
|
||
close handle
|
||
|
||
withOpen
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `withOpen` definition:
|
||
|
||
10│ withOpen : (Handle -> Result {} *) -> Result {} *
|
||
11│ withOpen = \callback ->
|
||
12│> handle <- await (open {})
|
||
13│> {} <- await (callback handle)
|
||
14│> close handle
|
||
|
||
The type annotation on `withOpen` says this `await` call should have the
|
||
type:
|
||
|
||
Result {} *
|
||
|
||
However, the type of this `await` call is connected to another type in a
|
||
way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `withOpen` should have a named type
|
||
variable in place of the `*`?
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
recursive_body_and_annotation_with_inference_disagree,
|
||
indoc!(
|
||
r#"
|
||
f : _ -> (_ -> Str)
|
||
f = \_ -> if Bool.true then {} else f {}
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
5│ f = \_ -> if Bool.true then {} else f {}
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
It is a value of type:
|
||
|
||
{}
|
||
|
||
But you are trying to use it as:
|
||
|
||
* -> Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
same_phantom_types_unify,
|
||
indoc!(
|
||
r#"
|
||
F a b := b
|
||
|
||
foo : F Str Str -> {}
|
||
|
||
x : F Str Str
|
||
|
||
foo x
|
||
"#
|
||
),
|
||
@r"" // okay
|
||
);
|
||
|
||
test_report!(
|
||
different_phantom_types,
|
||
indoc!(
|
||
r#"
|
||
F a b := b
|
||
|
||
foo : F Str Str -> {}
|
||
|
||
x : F U8 Str
|
||
|
||
foo x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `foo` has an unexpected type:
|
||
|
||
10│ foo x
|
||
^
|
||
|
||
This `x` value is a:
|
||
|
||
F U8 Str
|
||
|
||
But `foo` needs its 1st argument to be:
|
||
|
||
F Str Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore = "TODO This should be a type error"]
|
||
phantom_type_bound_to_ability_not_implementing,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [x] to "./platform"
|
||
|
||
Foo has foo : a -> a | a has Foo
|
||
|
||
F a b := b | a has Foo
|
||
|
||
MHash := {}
|
||
|
||
x : F MHash {}
|
||
"#
|
||
),
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
int_literals_cannot_fit_in_same_type,
|
||
indoc!(
|
||
r#"
|
||
0x80000000000000000000000000000000 == -0x80000000000000000000000000000000
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `isEq` has an unexpected type:
|
||
|
||
4│ 0x80000000000000000000000000000000 == -0x80000000000000000000000000000000
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The argument is an integer of type:
|
||
|
||
I128
|
||
|
||
But `isEq` needs its 2nd argument to be:
|
||
|
||
U128
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
num_literals_cannot_fit_in_same_type,
|
||
indoc!(
|
||
r#"
|
||
170141183460469231731687303715884105728 == -170141183460469231731687303715884105728
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `isEq` has an unexpected type:
|
||
|
||
4│ 170141183460469231731687303715884105728 == -170141183460469231731687303715884105728
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The argument is a number of type:
|
||
|
||
I128 or Dec
|
||
|
||
But `isEq` needs its 2nd argument to be:
|
||
|
||
U128
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_alias_cannot_leak_into_recursive_opaque,
|
||
indoc!(
|
||
r#"
|
||
OList := [Nil, Cons {} OList]
|
||
|
||
AList : [Nil, Cons {} AList]
|
||
|
||
alist : AList
|
||
|
||
olist : OList
|
||
olist =
|
||
when alist is
|
||
Nil -> @OList Nil
|
||
Cons _ lst -> lst
|
||
|
||
olist
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the 2nd branch of this `when` expression:
|
||
|
||
10│ olist : OList
|
||
11│ olist =
|
||
12│> when alist is
|
||
13│> Nil -> @OList Nil
|
||
14│> Cons _ lst -> lst
|
||
|
||
This `lst` value is a:
|
||
|
||
[Cons {} ∞, Nil] as ∞
|
||
|
||
But the type annotation on `olist` says it should be:
|
||
|
||
OList
|
||
|
||
Tip: Type comparisons between an opaque type are only ever equal if
|
||
both types are the same opaque type. Did you mean to create an opaque
|
||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||
an instance of this opaque type by doing @Age 23.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_wrap_function_mismatch,
|
||
indoc!(
|
||
r#"
|
||
A := U8
|
||
List.map [1u16, 2u16, 3u16] @A
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `map` has an unexpected type:
|
||
|
||
5│ List.map [1u16, 2u16, 3u16] @A
|
||
^^
|
||
|
||
This A opaque wrapping has the type:
|
||
|
||
U8 -> A
|
||
|
||
But `map` needs its 2nd argument to be:
|
||
|
||
U16 -> A
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
symbols_not_bound_in_all_patterns,
|
||
indoc!(
|
||
r#"
|
||
when A "" is
|
||
A x | B y -> x
|
||
"#
|
||
),
|
||
@r###"
|
||
── NAME NOT BOUND IN ALL PATTERNS ──────────────────────── /code/proj/Main.roc ─
|
||
|
||
`x` is not bound in all patterns of this `when` branch
|
||
|
||
5│ A x | B y -> x
|
||
^
|
||
|
||
Identifiers introduced in a `when` branch must be bound in all patterns
|
||
of the branch. Otherwise, the program would crash when it tries to use
|
||
an identifier that wasn't bound!
|
||
|
||
── NAME NOT BOUND IN ALL PATTERNS ──────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not bound in all patterns of this `when` branch
|
||
|
||
5│ A x | B y -> x
|
||
^
|
||
|
||
Identifiers introduced in a `when` branch must be bound in all patterns
|
||
of the branch. Otherwise, the program would crash when it tries to use
|
||
an identifier that wasn't bound!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not used in this `when` branch.
|
||
|
||
5│ A x | B y -> x
|
||
^
|
||
|
||
If you don't need to use `y`, prefix it with an underscore, like "_y",
|
||
or replace it with just an "_".
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
flip_flop_catch_all_branches_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
\x -> when x is
|
||
A B _ -> ""
|
||
A _ C -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> \x -> when x is
|
||
5│> A B _ -> ""
|
||
6│> A _ C -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
A _ _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
call_with_underscore_identifier,
|
||
indoc!(
|
||
r#"
|
||
f = \x, y, z -> x + y + z
|
||
|
||
f 1 _ 1
|
||
"#
|
||
),
|
||
|golden| pretty_assertions::assert_eq!(
|
||
golden,
|
||
&format!(
|
||
r###"── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Underscores are not allowed in identifier names:
|
||
|
||
6│ f 1 _ 1
|
||
{}
|
||
|
||
I recommend using camelCase. It's the standard style in Roc code!
|
||
"###,
|
||
" " // TODO make the reporter not insert extraneous spaces here in the first place!
|
||
),
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
destructure_assignment_introduces_no_variables_nested,
|
||
indoc!(
|
||
r#"
|
||
Pair _ _ = Pair 0 1
|
||
|
||
_ = Pair 0 1
|
||
|
||
{} = {}
|
||
|
||
Foo = Foo
|
||
|
||
0
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
4│ Pair _ _ = Pair 0 1
|
||
^^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
6│ _ = Pair 0 1
|
||
^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
8│ {} = {}
|
||
^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
10│ Foo = Foo
|
||
^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
destructure_assignment_introduces_no_variables_nested_toplevel,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [] to "./platform"
|
||
|
||
Pair _ _ = Pair 0 1
|
||
|
||
_ = Pair 0 1
|
||
|
||
{} = {}
|
||
|
||
Foo = Foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
3│ Pair _ _ = Pair 0 1
|
||
^^^^^^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
5│ _ = Pair 0 1
|
||
^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
7│ {} = {}
|
||
^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
9│ Foo = Foo
|
||
^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unused_shadow_specialization,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash, Id] to "./platform"
|
||
|
||
MHash has hash : a -> U64 | a has MHash
|
||
|
||
Id := {}
|
||
|
||
hash = \@Id _ -> 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`hash` is not used anywhere in your code.
|
||
|
||
7│ hash = \@Id _ -> 0
|
||
^^^^
|
||
|
||
If you didn't intend on using `hash` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
specialization_for_wrong_type,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash, Id, Id2] to "./platform"
|
||
|
||
MHash has hash : a -> U64 | a has MHash
|
||
|
||
Id := {} has [MHash {hash}]
|
||
Id2 := {}
|
||
|
||
hash = \@Id2 _ -> 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── WRONG SPECIALIZATION TYPE ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This specialization of `hash` is not for the expected type:
|
||
|
||
8│ hash = \@Id2 _ -> 0
|
||
^^^^
|
||
|
||
It was previously claimed to be a specialization for `Id`, but was
|
||
determined to actually specialize `Id2`!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mismatched_record_annotation,
|
||
indoc!(
|
||
r#"
|
||
x : { y : Str }
|
||
x = {}
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : { y : Str }
|
||
5│ x = {}
|
||
^^
|
||
|
||
The body is a record of type:
|
||
|
||
{}
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
{ y : Str }
|
||
|
||
Tip: Looks like the y field is missing.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cyclic_opaque,
|
||
indoc!(
|
||
r#"
|
||
Recursive := [Infinitely Recursive]
|
||
|
||
0
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Recursive` opaque is self-recursive in an invalid way:
|
||
|
||
4│ Recursive := [Infinitely Recursive]
|
||
^^^^^^^^^
|
||
|
||
Recursion in opaquees is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
derive_decoding_for_function,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [] provides [A] to "./platform"
|
||
|
||
A a := a -> a has [Decode.Decoding]
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Decoding` ability for `A`:
|
||
|
||
3│ A a := a -> a has [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 has [Decode.Decoding]
|
||
|
||
B := {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Decoding` ability for `A`:
|
||
|
||
3│ A := B has [Decode.Decoding]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Tip: `B` does not implement `Decoding`. Consider adding a custom
|
||
implementation or `has 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 has [Decode.Decoding]
|
||
|
||
B := {} has [Decode.Decoding]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
derive_decoding_for_recursive_deriving,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [] provides [MyNat] to "./platform"
|
||
|
||
MyNat := [S MyNat, Z] has [Decode.Decoding]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
function_cannot_derive_encoding,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||
|
||
main =
|
||
myDecoder : Decoder (a -> a) fmt | fmt has DecoderFormatting
|
||
myDecoder = decoder
|
||
|
||
myDecoder
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ myDecoder = decoder
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Decoding` ability for
|
||
|
||
a -> a
|
||
|
||
Note: `Decoding` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
nested_opaque_cannot_derive_encoding,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||
|
||
A := {}
|
||
|
||
main =
|
||
myDecoder : Decoder {x : A} fmt | fmt has DecoderFormatting
|
||
myDecoder = decoder
|
||
|
||
myDecoder
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
7│ myDecoder = decoder
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Decoding` ability for
|
||
|
||
{ x : A }
|
||
|
||
In particular, an implementation for
|
||
|
||
A
|
||
|
||
cannot be generated.
|
||
|
||
Tip: `A` does not implement `Decoding`. Consider adding a custom
|
||
implementation or `has Decode.Decoding` to the definition of `A`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
anonymous_function_does_not_use_param,
|
||
indoc!(
|
||
r#"
|
||
(\x -> 5) 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This function doesn't use `x`.
|
||
|
||
4│ (\x -> 5) 1
|
||
^
|
||
|
||
If you don't need `x`, then you can just remove it. However, if you
|
||
really do need `x` as an argument of this function, prefix it with an
|
||
underscore, like this: "_`x`". Adding an underscore at the start of a
|
||
variable name is a way of saying that the variable is not used.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
expected_tag_has_too_many_args,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [fromBytes] to "./platform"
|
||
|
||
u8 : [Good (List U8), Bad [DecodeProblem]]
|
||
|
||
fromBytes =
|
||
when u8 is
|
||
Good _ _ ->
|
||
Ok "foo"
|
||
|
||
Bad _ ->
|
||
Ok "foo"
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when u8 is
|
||
7│ Good _ _ ->
|
||
8│ Ok "foo"
|
||
9│
|
||
10│ Bad _ ->
|
||
11│ Ok "foo"
|
||
|
||
This `u8` value is a:
|
||
|
||
[Bad [DecodeProblem], Good (List U8)]
|
||
|
||
But the branch patterns have type:
|
||
|
||
[Bad [DecodeProblem], Good (List U8) *]
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
create_value_with_optional_record_field_type,
|
||
indoc!(
|
||
r#"
|
||
f : {a: Str, b ? Str}
|
||
f = {a: "b", b: ""}
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : {a: Str, b ? Str}
|
||
5│ f = {a: "b", b: ""}
|
||
^^^^^^^^^^^^^^^
|
||
|
||
The body is a record of type:
|
||
|
||
{
|
||
a : Str,
|
||
b : Str,
|
||
}
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
{
|
||
a : Str,
|
||
b ? Str,
|
||
}
|
||
|
||
Tip: To extract the `.b` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
create_value_with_conditionally_optional_record_field_type,
|
||
indoc!(
|
||
r#"
|
||
f : {a: Str, b ? Str}
|
||
f = if Bool.true then {a: ""} else {a: "b", b: ""}
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the `then` branch of this `if` expression:
|
||
|
||
4│ f : {a: Str, b ? Str}
|
||
5│ f = if Bool.true then {a: ""} else {a: "b", b: ""}
|
||
^^^^^^^
|
||
|
||
The 1st branch is a record of type:
|
||
|
||
{ a : Str }
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
{
|
||
a : Str,
|
||
b ? Str,
|
||
}
|
||
|
||
Tip: Looks like the b field is missing.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unused_def_in_branch_pattern,
|
||
indoc!(
|
||
r#"
|
||
when A "" is
|
||
A foo -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`foo` is not used in this `when` branch.
|
||
|
||
5│ A foo -> ""
|
||
^^^
|
||
|
||
If you don't need to use `foo`, prefix it with an underscore, like
|
||
"_foo", or replace it with just an "_".
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
infer_decoded_record_error_with_function_field,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Json] provides [main] to "./platform"
|
||
|
||
main =
|
||
decoded = Str.toUtf8 "{\"first\":\"ab\",\"second\":\"cd\"}" |> Decode.fromBytes Json.fromUtf8
|
||
when decoded is
|
||
Ok rcd -> rcd.first rcd.second
|
||
_ -> "something went wrong"
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
6│ Ok rcd -> rcd.first rcd.second
|
||
^^^^^^^^^
|
||
|
||
I can't generate an implementation of the `Decoding` ability for
|
||
|
||
* -> *
|
||
|
||
Note: `Decoding` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_with_optional_field_types_cannot_derive_decoding,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Decode.{decoder}] provides [main] to "./platform"
|
||
|
||
main =
|
||
myDecoder : Decoder {x : Str, y ? Str} fmt | fmt has DecoderFormatting
|
||
myDecoder = decoder
|
||
|
||
myDecoder
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ myDecoder = decoder
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Decoding` ability for
|
||
|
||
{
|
||
x : Str,
|
||
y ? Str,
|
||
}
|
||
|
||
Note: I can't derive decoding for a record with an optional field,
|
||
which in this case is `.y`. Optional record fields are polymorphic over
|
||
records that may or may not contain them at compile time, but are not
|
||
a concept that extends to runtime!
|
||
Maybe you wanted to use a `Result`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
uninhabited_type_is_trivially_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x : Result {} []
|
||
|
||
when x is
|
||
Ok {} -> ""
|
||
"#
|
||
),
|
||
// no problem!
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
uninhabited_type_is_trivially_exhaustive_nested,
|
||
indoc!(
|
||
r#"
|
||
x : Result (Result [A, B] []) []
|
||
|
||
when x is
|
||
Ok (Ok A) -> ""
|
||
Ok (Ok B) -> ""
|
||
"#
|
||
),
|
||
// no problem!
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
branch_patterns_missing_nested_case,
|
||
indoc!(
|
||
r#"
|
||
x : Result (Result [A, B] {}) {}
|
||
|
||
when x is
|
||
Ok (Ok A) -> ""
|
||
Ok (Err _) -> ""
|
||
Err _ -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when x is
|
||
7│> Ok (Ok A) -> ""
|
||
8│> Ok (Err _) -> ""
|
||
9│> Err _ -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
Ok (Ok B)
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
branch_patterns_missing_nested_case_with_trivially_exhausted_variant,
|
||
indoc!(
|
||
r#"
|
||
x : Result (Result [A, B] []) []
|
||
|
||
when x is
|
||
Ok (Ok A) -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when x is
|
||
7│> Ok (Ok A) -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
Ok (Ok B)
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
uninhabited_err_branch_is_redundant_when_err_is_matched,
|
||
indoc!(
|
||
r#"
|
||
x : Result {} []
|
||
|
||
when x is
|
||
Ok {} -> ""
|
||
Err _ -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNMATCHABLE PATTERN ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern will never be matched:
|
||
|
||
6│ when x is
|
||
7│ Ok {} -> ""
|
||
8│ Err _ -> ""
|
||
^^^^^
|
||
|
||
It's impossible to create a value of this shape, so this pattern can
|
||
be safely removed!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
uninhabited_err_branch_is_redundant_when_err_is_matched_nested,
|
||
indoc!(
|
||
r#"
|
||
x : Result (Result {} []) []
|
||
|
||
when x is
|
||
Ok (Ok {}) -> ""
|
||
Ok (Err _) -> ""
|
||
Err _ -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNMATCHABLE PATTERN ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern will never be matched:
|
||
|
||
6│ when x is
|
||
7│ Ok (Ok {}) -> ""
|
||
8│> Ok (Err _) -> ""
|
||
9│ Err _ -> ""
|
||
|
||
It's impossible to create a value of this shape, so this pattern can
|
||
be safely removed!
|
||
|
||
── UNMATCHABLE PATTERN ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd pattern will never be matched:
|
||
|
||
6│ when x is
|
||
7│ Ok (Ok {}) -> ""
|
||
8│ Ok (Err _) -> ""
|
||
9│ Err _ -> ""
|
||
^^^^^
|
||
|
||
It's impossible to create a value of this shape, so this pattern can
|
||
be safely removed!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
custom_type_conflicts_with_builtin,
|
||
indoc!(
|
||
r#"
|
||
Nat := [ S Nat, Z ]
|
||
|
||
""
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This opaque type has the same name as a builtin:
|
||
|
||
4│ Nat := [ S Nat, Z ]
|
||
^^^^^^^^^^^^^^^^^^^
|
||
|
||
All builtin opaque types are in scope by default, so I need this
|
||
opaque type to have a different name!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unused_value_import,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [List.{ concat }] provides [main] to "./platform"
|
||
|
||
main = ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED IMPORT ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`List.concat` is not used in this module.
|
||
|
||
1│ app "test" imports [List.{ concat }] provides [main] to "./platform"
|
||
^^^^^^
|
||
|
||
Since `List.concat` isn't used, you don't need to import it.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore = "https://github.com/roc-lang/roc/issues/4096"]
|
||
unnecessary_builtin_module_import,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Str] provides [main] to "./platform"
|
||
|
||
main = Str.concat "" ""
|
||
"#
|
||
),
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore = "https://github.com/roc-lang/roc/issues/4096"]
|
||
unnecessary_builtin_type_import,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Decode.{ DecodeError }] provides [main, E] to "./platform"
|
||
|
||
E : DecodeError
|
||
|
||
main = ""
|
||
"#
|
||
),
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_toplevel_cycle,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [] provides [main] to "./platform"
|
||
|
||
main =
|
||
if Bool.true then {} else main
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`main` is defined directly in terms of itself:
|
||
|
||
3│> main =
|
||
4│> if Bool.true then {} else main
|
||
|
||
Roc evaluates values strictly, so running this program would enter an
|
||
infinite loop!
|
||
|
||
Hint: Did you mean to define `main` as a function?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
bool_vs_true_tag,
|
||
indoc!(
|
||
r#"
|
||
if True then "" else ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` condition needs to be a Bool:
|
||
|
||
4│ if True then "" else ""
|
||
^^^^
|
||
|
||
This `True` tag has the type:
|
||
|
||
[True]
|
||
|
||
But I need every `if` condition to evaluate to a Bool—either `Bool.true`
|
||
or `Bool.false`.
|
||
|
||
Tip: Did you mean to use `Bool.true` rather than `True`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
bool_vs_false_tag,
|
||
indoc!(
|
||
r#"
|
||
if False then "" else ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` condition needs to be a Bool:
|
||
|
||
4│ if False then "" else ""
|
||
^^^^^
|
||
|
||
This `False` tag has the type:
|
||
|
||
[False]
|
||
|
||
But I need every `if` condition to evaluate to a Bool—either `Bool.true`
|
||
or `Bool.false`.
|
||
|
||
Tip: Did you mean to use `Bool.false` rather than `False`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
derive_hash_for_function,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
A a := a -> a has [Hash]
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Hash` ability for `A`:
|
||
|
||
3│ A a := a -> a has [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 has [Hash]
|
||
|
||
B := {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Hash` ability for `A`:
|
||
|
||
3│ A := B has [Hash]
|
||
^^^^
|
||
|
||
Tip: `B` does not implement `Hash`. Consider adding a custom
|
||
implementation or `has 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 has [Hash]
|
||
|
||
B := {} has [Hash]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
derive_hash_for_recursive_deriving,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [MyNat] to "./platform"
|
||
|
||
MyNat := [S MyNat, Z] has [Hash]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
derive_hash_for_record,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
foo : a -> {} | a has Hash
|
||
|
||
main = foo {a: "", b: 1}
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
derive_hash_for_tag,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
foo : a -> {} | a has 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 -> {} | a has Hash
|
||
|
||
main = foo (\x -> x)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ main = foo (\x -> x)
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Hash` ability for
|
||
|
||
a -> a
|
||
|
||
Note: `Hash` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cannot_derive_hash_for_structure_containing_function,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
foo : a -> {} | a has Hash
|
||
|
||
main = foo (A (\x -> x) B)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ main = foo (A (\x -> x) B)
|
||
^^^^^^^^^^^^^
|
||
|
||
I can't generate an implementation of the `Hash` ability for
|
||
|
||
[A (a -> a) [B]a]
|
||
|
||
In particular, an implementation for
|
||
|
||
a -> a
|
||
|
||
cannot be generated.
|
||
|
||
Note: `Hash` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
shift_by_negative,
|
||
indoc!(
|
||
r#"
|
||
{
|
||
a: Num.shiftLeftBy 1 -1,
|
||
b: Num.shiftRightBy 1 -1,
|
||
c: Num.shiftRightZfBy 1 -1,
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `shiftRightZfBy` has an unexpected type:
|
||
|
||
7│ c: Num.shiftRightZfBy 1 -1,
|
||
^^
|
||
|
||
The argument is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But `shiftRightZfBy` needs its 2nd argument to be:
|
||
|
||
U8
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `shiftRightBy` has an unexpected type:
|
||
|
||
6│ b: Num.shiftRightBy 1 -1,
|
||
^^
|
||
|
||
The argument is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But `shiftRightBy` needs its 2nd argument to be:
|
||
|
||
U8
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `shiftLeftBy` has an unexpected type:
|
||
|
||
5│ a: Num.shiftLeftBy 1 -1,
|
||
^^
|
||
|
||
The argument is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But `shiftLeftBy` needs its 2nd argument to be:
|
||
|
||
U8
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
big_char_does_not_fit_in_u8,
|
||
indoc!(
|
||
r#"
|
||
digits : List U8
|
||
digits = List.range { start: At '0', end: At '9' }
|
||
|
||
List.contains digits '☃'
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `contains` has an unexpected type:
|
||
|
||
7│ List.contains digits '☃'
|
||
^^^^^
|
||
|
||
The argument is a Unicode scalar value of type:
|
||
|
||
U16, I32, U32, I64, Nat, U64, I128, or U128
|
||
|
||
But `contains` needs its 2nd argument to be:
|
||
|
||
Int Unsigned8
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
derive_eq_for_function,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
A a := a -> a has [Eq]
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Eq` ability for `A`:
|
||
|
||
3│ A a := a -> a has [Eq]
|
||
^^
|
||
|
||
Note: `Eq` cannot be generated for functions.
|
||
|
||
Tip: You can define a custom implementation of `Eq` for `A`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
big_char_does_not_fit_in_u8_pattern,
|
||
indoc!(
|
||
r#"
|
||
x : U8
|
||
|
||
when x is
|
||
'☃' -> ""
|
||
_ -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when x is
|
||
7│ '☃' -> ""
|
||
8│ _ -> ""
|
||
|
||
This `x` value is a:
|
||
|
||
U8
|
||
|
||
But the branch patterns have type:
|
||
|
||
U16, I32, U32, I64, Nat, U64, I128, or U128
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
derive_eq_for_f32,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
A := F32 has [Eq]
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Eq` ability for `A`:
|
||
|
||
3│ A := F32 has [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 has [Eq]
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Eq` ability for `A`:
|
||
|
||
3│ A := F64 has [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 has [Eq]
|
||
|
||
B := {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Eq` ability for `A`:
|
||
|
||
3│ A := B has [Eq]
|
||
^^
|
||
|
||
Tip: `B` does not implement `Eq`. Consider adding a custom implementation
|
||
or `has 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 has [Eq]
|
||
|
||
B := {} has [Eq]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
derive_eq_for_recursive_deriving,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [MyNat] to "./platform"
|
||
|
||
MyNat := [S MyNat, Z] has [Eq]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
derive_eq_for_record,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
foo : a -> {} | a has Eq
|
||
|
||
main = foo {a: "", b: 1}
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
derive_eq_for_tag,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
foo : a -> {} | a has 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 -> {} | a has Eq
|
||
|
||
main = foo (\x -> x)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ main = foo (\x -> x)
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Eq` ability for
|
||
|
||
a -> a
|
||
|
||
Note: `Eq` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cannot_derive_eq_for_structure_containing_function,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
foo : a -> {} | a has Eq
|
||
|
||
main = foo (A (\x -> x) B)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ main = foo (A (\x -> x) B)
|
||
^^^^^^^^^^^^^
|
||
|
||
I can't generate an implementation of the `Eq` ability for
|
||
|
||
[A (a -> a) [B]a]
|
||
|
||
In particular, an implementation for
|
||
|
||
a -> a
|
||
|
||
cannot be generated.
|
||
|
||
Note: `Eq` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cannot_eq_functions,
|
||
indoc!(
|
||
r#"
|
||
(\x -> x) == (\x -> x)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
4│ (\x -> x) == (\x -> x)
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Eq` ability for
|
||
|
||
a -> a
|
||
|
||
Note: `Eq` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cannot_not_eq_functions,
|
||
indoc!(
|
||
r#"
|
||
(\x -> x) == (\x -> x)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
4│ (\x -> x) == (\x -> x)
|
||
^^^^^^^
|
||
|
||
I can't generate an implementation of the `Eq` ability for
|
||
|
||
a -> a
|
||
|
||
Note: `Eq` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cannot_import_structural_eq_not_eq,
|
||
indoc!(
|
||
r#"
|
||
{
|
||
a: Bool.structuralEq,
|
||
b: Bool.structuralNotEq,
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The Bool module does not expose `structuralEq`:
|
||
|
||
5│ a: Bool.structuralEq,
|
||
^^^^^^^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Bool.true
|
||
Bool.isNotEq
|
||
Bool.false
|
||
Bool.isEq
|
||
|
||
── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The Bool module does not expose `structuralNotEq`:
|
||
|
||
6│ b: Bool.structuralNotEq,
|
||
^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Bool.isNotEq
|
||
Bool.true
|
||
Bool.boolIsEq
|
||
Bool.false
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
expand_ability_from_type_alias_mismatch,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [f] to "./platform"
|
||
|
||
F a : a | a has Hash
|
||
|
||
f : F ({} -> {})
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ f : F ({} -> {})
|
||
^^^^^^^^
|
||
|
||
I can't generate an implementation of the `Hash` ability for
|
||
|
||
{} -> {}
|
||
|
||
Note: `Hash` cannot be generated for functions.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
demanded_vs_optional_record_field,
|
||
indoc!(
|
||
r#"
|
||
foo : { a : Str } -> Str
|
||
foo = \{ a ? "" } -> a
|
||
foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `foo` is weird:
|
||
|
||
5│ foo = \{ a ? "" } -> a
|
||
^^^^^^^^^^
|
||
|
||
The argument is a pattern that matches record values of type:
|
||
|
||
{ a ? Str }
|
||
|
||
But the annotation on `foo` says the 1st argument should be:
|
||
|
||
{ a : Str }
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
underivable_opaque_doesnt_error_for_derived_bodies,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
F := U8 -> U8 has [Hash, Eq, Encoding]
|
||
|
||
main = ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Hash` ability for `F`:
|
||
|
||
3│ F := U8 -> U8 has [Hash, Eq, Encoding]
|
||
^^^^
|
||
|
||
Note: `Hash` cannot be generated for functions.
|
||
|
||
Tip: You can define a custom implementation of `Hash` for `F`.
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Eq` ability for `F`:
|
||
|
||
3│ F := U8 -> U8 has [Hash, Eq, Encoding]
|
||
^^
|
||
|
||
Note: `Eq` cannot be generated for functions.
|
||
|
||
Tip: You can define a custom implementation of `Eq` for `F`.
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
I can't derive an implementation of the `Encoding` ability for `F`:
|
||
|
||
3│ F := U8 -> U8 has [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 -> {} | a has Hash & Hash
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE BOUND ABILITY ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I already saw that this type variable is bound to the `Hash` ability
|
||
once before:
|
||
|
||
4│ f : a -> {} | a has Hash & Hash
|
||
^^^^
|
||
|
||
Abilities only need to bound to a type variable once in a `has` clause!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
rigid_able_bounds_must_be_a_superset_of_flex_bounds,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
g : x -> x | x has Decoding & Encoding
|
||
|
||
main : x -> x | x has Encoding
|
||
main = \x -> g x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `g` has an unexpected type:
|
||
|
||
6│ main = \x -> g x
|
||
^
|
||
|
||
This `x` value is a:
|
||
|
||
x | x has Encoding
|
||
|
||
But `g` needs its 1st argument to be:
|
||
|
||
x | x has Encoding & Decoding
|
||
|
||
Note: The type variable `x` says it can take on any value that has only
|
||
the ability `Encoding`.
|
||
|
||
But, I see that it's also used as if it has the ability `Decoding`. Can
|
||
you use `x` without that ability? If not, consider adding it to the `has`
|
||
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 | x has Decoding & Encoding & Hash
|
||
|
||
main : x -> x | x has Encoding
|
||
main = \x -> g x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `g` has an unexpected type:
|
||
|
||
6│ main = \x -> g x
|
||
^
|
||
|
||
This `x` value is a:
|
||
|
||
x | x has Encoding
|
||
|
||
But `g` needs its 1st argument to be:
|
||
|
||
x | x has Hash & Encoding & Decoding
|
||
|
||
Note: The type variable `x` says it can take on any value that has only
|
||
the ability `Encoding`.
|
||
|
||
But, I see that it's also used as if it has the abilities `Hash` and
|
||
`Decoding`. Can you use `x` without those abilities? If not, consider
|
||
adding them to the `has` 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 | x has Hash
|
||
g : x -> x | x has Decoding & Encoding
|
||
|
||
main : x -> x | x has Hash & Encoding
|
||
main = \x -> g (f x)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `g` has an unexpected type:
|
||
|
||
7│ main = \x -> g (f x)
|
||
^^^
|
||
|
||
This `f` call produces:
|
||
|
||
x | x has Hash & Encoding
|
||
|
||
But `g` needs its 1st argument to be:
|
||
|
||
x | x has Encoding & Decoding
|
||
|
||
Note: The type variable `x` says it can take on any value that has only
|
||
the abilities `Hash` and `Encoding`.
|
||
|
||
But, I see that it's also used as if it has the ability `Decoding`. Can
|
||
you use `x` without that ability? If not, consider adding it to the `has`
|
||
clause of `x`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_pattern_not_terminated,
|
||
indoc!(
|
||
r#"
|
||
when [] is
|
||
[1, 2, -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED LIST PATTERN ────────── tmp/list_pattern_not_terminated/Test.roc ─
|
||
|
||
I am partway through parsing a list pattern, but I got stuck here:
|
||
|
||
5│ [1, 2, -> ""
|
||
^
|
||
|
||
I was expecting to see a closing square brace before this, so try
|
||
adding a ] and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_pattern_weird_rest_pattern,
|
||
indoc!(
|
||
r#"
|
||
when [] is
|
||
[...] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCORRECT REST PATTERN ─────── tmp/list_pattern_weird_rest_pattern/Test.roc ─
|
||
|
||
It looks like you may trying to write a list rest pattern, but it's
|
||
not the form I expect:
|
||
|
||
5│ [...] -> ""
|
||
^
|
||
|
||
List rest patterns, which match zero or more elements in a list, are
|
||
denoted with .. - is that what you meant?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unnecessary_extension_variable,
|
||
indoc!(
|
||
r#"
|
||
f : {} -> [A, B]*
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNNECESSARY WILDCARD ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This type annotation has a wildcard type variable (`*`) that isn't
|
||
needed.
|
||
|
||
4│ f : {} -> [A, B]*
|
||
^
|
||
|
||
Annotations for tag unions which are constants, or which are returned
|
||
from functions, work the same way with or without a `*` at the end. (The
|
||
`*` means something different when the tag union is an argument to a
|
||
function, though!)
|
||
|
||
You can safely remove this to make the code more concise without
|
||
changing what it means.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multiple_list_patterns_start_and_end,
|
||
indoc!(
|
||
r#"
|
||
when [] is
|
||
[.., A, ..] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── MULTIPLE LIST REST PATTERNS ─────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This list pattern match has multiple rest patterns:
|
||
|
||
5│ [.., A, ..] -> ""
|
||
^^
|
||
|
||
I only support compiling list patterns with one .. pattern! Can you
|
||
remove this additional one?
|
||
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> when [] is
|
||
5│> [.., A, ..] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
_
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multiple_list_patterns_in_a_row,
|
||
indoc!(
|
||
r#"
|
||
when [] is
|
||
[A, .., .., B] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── MULTIPLE LIST REST PATTERNS ─────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This list pattern match has multiple rest patterns:
|
||
|
||
5│ [A, .., .., B] -> ""
|
||
^^
|
||
|
||
I only support compiling list patterns with one .. pattern! Can you
|
||
remove this additional one?
|
||
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> when [] is
|
||
5│> [A, .., .., B] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
_
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mismatch_within_list_pattern,
|
||
indoc!(
|
||
r#"
|
||
when [] is
|
||
[A, 1u8] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This list element doesn't match the types of other elements in the
|
||
pattern:
|
||
|
||
5│ [A, 1u8] -> ""
|
||
^^^
|
||
|
||
It matches integers:
|
||
|
||
U8
|
||
|
||
But the other elements in this list pattern match
|
||
|
||
[A]
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mismatch_list_pattern_vs_condition,
|
||
indoc!(
|
||
r#"
|
||
when [A, B] is
|
||
["foo", "bar"] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when [A, B] is
|
||
5│ ["foo", "bar"] -> ""
|
||
|
||
The `when` condition is a list of type:
|
||
|
||
List [A, B]
|
||
|
||
But the branch patterns have type:
|
||
|
||
List Str
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_non_exhaustive_only_empty,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[_, ..]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
list_match_spread_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[..] -> ""
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
list_match_non_exhaustive_infinite,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[A] -> ""
|
||
[A, A] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [] -> ""
|
||
8│> [A] -> ""
|
||
9│> [A, A] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[_, _, _, ..]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
list_match_exhaustive_empty_and_rest_with_unary_head,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[_, ..] -> ""
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_no_problem!(
|
||
list_match_exhaustive_empty_and_rest_with_exhausted_head,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[A, ..] -> ""
|
||
[B, ..] -> ""
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
list_match_exhaustive_empty_and_rest_with_nonexhaustive_head,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[A, ..] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [] -> ""
|
||
8│> [A, ..] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[B, ..]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_no_small_sizes_and_non_exhaustive_head,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[A, B, ..] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [A, B, ..] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[]
|
||
[_]
|
||
[_, A, ..]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
list_match_exhaustive_empty_and_rest_with_exhausted_tail,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[.., A] -> ""
|
||
[.., B] -> ""
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
list_match_exhaustive_empty_and_rest_with_nonexhaustive_tail,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[.., A] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [] -> ""
|
||
8│> [.., A] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[.., B]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_no_small_sizes_and_non_exhaustive_tail,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[.., B, A] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [.., B, A] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[]
|
||
[_]
|
||
[.., _, B]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
list_match_exhaustive_empty_and_rest_with_exhausted_head_and_tail,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[A] -> ""
|
||
[B] -> ""
|
||
[A, .., A] -> ""
|
||
[A, .., B] -> ""
|
||
[B, .., A] -> ""
|
||
[B, .., B] -> ""
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
list_match_exhaustive_empty_and_rest_with_nonexhaustive_head_and_tail,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[_] -> ""
|
||
[A, .., B] -> ""
|
||
[B, .., A] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [] -> ""
|
||
8│> [_] -> ""
|
||
9│> [A, .., B] -> ""
|
||
10│> [B, .., A] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[_, .., _]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_no_small_sizes_and_non_exhaustive_head_and_tail,
|
||
indoc!(
|
||
r#"
|
||
l : List [A, B]
|
||
|
||
when l is
|
||
[A, .., B] -> ""
|
||
[B, .., A] -> ""
|
||
[B, .., B] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [A, .., B] -> ""
|
||
8│> [B, .., A] -> ""
|
||
9│> [B, .., B] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[]
|
||
[_]
|
||
[A, .., A]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_exhaustive_big_sizes_but_not_small_sizes,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[A, A, A, .., A, A, A] -> ""
|
||
[A, A, A, .., A, A] -> ""
|
||
[A, A, .., A, A] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [A, A, A, .., A, A, A] -> ""
|
||
8│> [A, A, A, .., A, A] -> ""
|
||
9│> [A, A, .., A, A] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[]
|
||
[_]
|
||
[_, _]
|
||
[_, _, _]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
list_match_nested_list_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
l : List (List [A])
|
||
|
||
when l is
|
||
[] -> ""
|
||
[[]] -> ""
|
||
[[A, ..]] -> ""
|
||
[[..], .., [..]] -> ""
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
list_match_nested_list_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
l : List (List [A, B])
|
||
|
||
when l is
|
||
[] -> ""
|
||
[[]] -> ""
|
||
[[A, ..]] -> ""
|
||
[[..], .., [.., B]] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when l is
|
||
7│> [] -> ""
|
||
8│> [[]] -> ""
|
||
9│> [[A, ..]] -> ""
|
||
10│> [[..], .., [.., B]] -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
[[B, ..]]
|
||
[_, .., []]
|
||
[_, .., [.., A]]
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_redundant_exact_size,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[_] -> ""
|
||
[_] -> ""
|
||
[..] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd pattern is redundant:
|
||
|
||
6│ when l is
|
||
7│ [] -> ""
|
||
8│ [_] -> ""
|
||
9│> [_] -> ""
|
||
10│ [..] -> ""
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_redundant_any_slice,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[_, ..] -> ""
|
||
[..] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd pattern is redundant:
|
||
|
||
6│ when l is
|
||
7│ [] -> ""
|
||
8│ [_, ..] -> ""
|
||
9│ [..] -> ""
|
||
^^^^
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_redundant_suffix_slice_with_sized_prefix,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[] -> ""
|
||
[_, ..] -> ""
|
||
[.., _] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd pattern is redundant:
|
||
|
||
6│ when l is
|
||
7│ [] -> ""
|
||
8│ [_, ..] -> ""
|
||
9│ [.., _] -> ""
|
||
^^^^^^^
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_match_redundant_based_on_ctors,
|
||
indoc!(
|
||
r#"
|
||
l : List {}
|
||
|
||
when l is
|
||
[{}, .., _] -> ""
|
||
[_, .., {}] -> ""
|
||
[..] -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern is redundant:
|
||
|
||
6│ when l is
|
||
7│ [{}, .., _] -> ""
|
||
8│> [_, .., {}] -> ""
|
||
9│ [..] -> ""
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
list_match_with_guard,
|
||
indoc!(
|
||
r#"
|
||
l : List [A]
|
||
|
||
when l is
|
||
[ A, .. ] if Bool.true -> ""
|
||
[ A, .. ] -> ""
|
||
_ -> ""
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_recursion_forces_ungeneralized_type,
|
||
indoc!(
|
||
r#"
|
||
foo : a, Bool -> Str
|
||
foo = \in, b -> if b then "done" else bar in
|
||
|
||
bar = \_ -> foo {} Bool.true
|
||
|
||
foo "" Bool.false
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `foo` has an unexpected type:
|
||
|
||
9│ foo "" Bool.false
|
||
^^
|
||
|
||
The argument is a string of type:
|
||
|
||
Str
|
||
|
||
But `foo` 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 `Str` 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!(
|
||
suggest_binding_rigid_var_to_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [f] to "./p"
|
||
|
||
f : List e -> List e
|
||
f = \l -> if l == l then l else l
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
4│ f = \l -> if l == l then l else l
|
||
^
|
||
|
||
I can't generate an implementation of the `Eq` ability for
|
||
|
||
List e
|
||
|
||
In particular, an implementation for
|
||
|
||
e
|
||
|
||
cannot be generated.
|
||
|
||
Tip: This type variable is not bound to `Eq`. Consider adding a `has`
|
||
clause to bind the type variable, like `| e has Bool.Eq`
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
crash_given_non_string,
|
||
indoc!(
|
||
r#"
|
||
crash {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This value passed to `crash` is not a string:
|
||
|
||
4│ crash {}
|
||
^^
|
||
|
||
The value is a record of type:
|
||
|
||
{}
|
||
|
||
But I can only `crash` with messages of type
|
||
|
||
Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
crash_unapplied,
|
||
indoc!(
|
||
r#"
|
||
crash
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNAPPLIED CRASH ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `crash` doesn't have a message given to it:
|
||
|
||
4│ crash
|
||
^^^^^
|
||
|
||
`crash` must be passed a message to crash with at the exact place it's
|
||
used. `crash` can't be used as a value that's passed around, like
|
||
functions can be - it must be applied immediately!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
crash_overapplied,
|
||
indoc!(
|
||
r#"
|
||
crash "" ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── OVERAPPLIED CRASH ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `crash` has too many values given to it:
|
||
|
||
4│ crash "" ""
|
||
^^^^^
|
||
|
||
`crash` must be given exacly one message to crash with.
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
resolve_eq_for_unbound_num,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
n : Num *
|
||
|
||
main = n == 1
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
resolve_eq_for_unbound_num_float,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
n : Num *
|
||
|
||
main = n == 1f64
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
5│ main = n == 1f64
|
||
^^^^
|
||
|
||
I can't generate an implementation of the `Eq` ability for
|
||
|
||
FloatingPoint ?
|
||
|
||
Note: I can't derive `Bool.isEq` for floating-point types. That's
|
||
because Roc's floating-point numbers cannot be compared for total
|
||
equality - in Roc, `NaN` is never comparable to `NaN`. If a type
|
||
doesn't support total equality, it cannot support the `Eq` ability!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
resolve_hash_for_unbound_num,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
n : Num *
|
||
|
||
main = \hasher -> Hash.hash hasher n
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
self_recursive_not_reached,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [f] to "./platform"
|
||
f = h {}
|
||
h = \{} -> 1
|
||
g = \{} -> if Bool.true then "" else g {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── DEFINITION ONLY USED IN RECURSION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This definition is only used in recursion with itself:
|
||
|
||
4│ g = \{} -> if Bool.true then "" else g {}
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
If you don't intend to use or export this definition, it should be
|
||
removed!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
self_recursive_not_reached_but_exposed,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [g] to "./platform"
|
||
g = \{} -> if Bool.true then "" else g {}
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
mutual_recursion_not_reached,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [h] to "./platform"
|
||
h = ""
|
||
f = \{} -> if Bool.true then "" else g {}
|
||
g = \{} -> if Bool.true then "" else f {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── DEFINITIONs ONLY USED IN RECURSION ──────────────────── /code/proj/Main.roc ─
|
||
|
||
These 2 definitions are only used in mutual recursion with themselves:
|
||
|
||
3│> f = \{} -> if Bool.true then "" else g {}
|
||
4│> g = \{} -> if Bool.true then "" else f {}
|
||
|
||
If you don't intend to use or export any of them, they should all be
|
||
removed!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mutual_recursion_not_reached_but_exposed,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [f] to "./platform"
|
||
f = \{} -> if Bool.true then "" else g {}
|
||
g = \{} -> if Bool.true then "" else f {}
|
||
"#
|
||
),
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
self_recursive_not_reached_nested,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
main =
|
||
g = \{} -> if Bool.true then "" else g {}
|
||
""
|
||
"#
|
||
),
|
||
@r###"
|
||
── DEFINITION ONLY USED IN RECURSION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This definition is only used in recursion with itself:
|
||
|
||
3│ g = \{} -> if Bool.true then "" else g {}
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
If you don't intend to use or export this definition, it should be
|
||
removed!
|
||
"###
|
||
);
|
||
|
||
test_no_problem!(
|
||
self_recursive_not_reached_but_exposed_nested,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
main =
|
||
g = \{} -> if Bool.true then "" else g {}
|
||
g
|
||
"#
|
||
)
|
||
);
|
||
|
||
test_report!(
|
||
mutual_recursion_not_reached_nested,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
main =
|
||
f = \{} -> if Bool.true then "" else g {}
|
||
g = \{} -> if Bool.true then "" else f {}
|
||
""
|
||
"#
|
||
),
|
||
@r###"
|
||
── DEFINITIONs ONLY USED IN RECURSION ──────────────────── /code/proj/Main.roc ─
|
||
|
||
These 2 definitions are only used in mutual recursion with themselves:
|
||
|
||
3│> f = \{} -> if Bool.true then "" else g {}
|
||
4│> g = \{} -> if Bool.true then "" else f {}
|
||
|
||
If you don't intend to use or export any of them, they should all be
|
||
removed!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mutual_recursion_not_reached_but_exposed_nested,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
main =
|
||
f = \{} -> if Bool.true then "" else g {}
|
||
g = \{} -> if Bool.true then "" else f {}
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
// TODO(weakening-reports)
|
||
test_report!(
|
||
concat_different_types,
|
||
indoc!(
|
||
r#"
|
||
empty = []
|
||
one = List.concat [1] empty
|
||
str = List.concat ["blah"] empty
|
||
|
||
{one, str}
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `concat` has an unexpected type:
|
||
|
||
6│ str = List.concat ["blah"] empty
|
||
^^^^^
|
||
|
||
This `empty` value is a:
|
||
|
||
List (Num *)
|
||
|
||
But `concat` needs its 2nd argument to be:
|
||
|
||
List Str
|
||
"###
|
||
);
|
||
}
|