roc/crates/compiler/load/tests/test_reporting.rs
2024-07-03 12:40:53 -03:00

14406 lines
384 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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