mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
4657a957f7
When we unify two variables that end up merged, the rank of the resulting content is the lower of the two variables being merged. But during storage, we really do mean, take the target descriptor of the type we're merging against, and don't try to lower to a possibly-generalized rank! This fixes a couple bugs I didn't even realize were present!
10134 lines
275 KiB
Rust
10134 lines
275 KiB
Rust
#[macro_use]
|
||
extern crate pretty_assertions;
|
||
extern crate bumpalo;
|
||
extern crate indoc;
|
||
extern crate roc_reporting;
|
||
|
||
mod helpers;
|
||
|
||
#[cfg(test)]
|
||
mod test_reporting {
|
||
use crate::helpers::{can_expr, infer_expr, test_home, CanExprOut, ParseErrOut};
|
||
use bumpalo::Bump;
|
||
use indoc::indoc;
|
||
use roc_can::abilities::AbilitiesStore;
|
||
use roc_can::expr::PendingDerives;
|
||
use roc_load::{self, LoadedModule, LoadingProblem, Threading};
|
||
use roc_module::symbol::{Interns, ModuleId};
|
||
use roc_region::all::LineInfo;
|
||
use roc_reporting::report::{
|
||
can_problem, parse_problem, type_problem, RenderTarget, Report, Severity, ANSI_STYLE_CODES,
|
||
DEFAULT_PALETTE,
|
||
};
|
||
use roc_reporting::report::{RocDocAllocator, RocDocBuilder};
|
||
use roc_solve_problem::TypeError;
|
||
use roc_types::subs::Subs;
|
||
use std::path::PathBuf;
|
||
|
||
fn filename_from_string(str: &str) -> PathBuf {
|
||
let mut filename = PathBuf::new();
|
||
filename.push(str);
|
||
|
||
filename
|
||
}
|
||
|
||
fn to_simple_report(doc: RocDocBuilder) -> Report {
|
||
Report {
|
||
title: "".to_string(),
|
||
doc,
|
||
filename: filename_from_string(r"/code/proj/Main.roc"),
|
||
severity: Severity::RuntimeError,
|
||
}
|
||
}
|
||
|
||
fn promote_expr_to_module(src: &str) -> String {
|
||
let mut buffer = String::from("app \"test\" provides [main] to \"./platform\"\n\nmain =\n");
|
||
|
||
for line in src.lines() {
|
||
// indent the body!
|
||
buffer.push_str(" ");
|
||
buffer.push_str(line);
|
||
buffer.push('\n');
|
||
}
|
||
|
||
buffer
|
||
}
|
||
|
||
fn 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") {
|
||
// this is already a module
|
||
src.to_string()
|
||
} else {
|
||
// this is an expression, promote it to a module
|
||
promote_expr_to_module(src)
|
||
};
|
||
|
||
let exposed_types = Default::default();
|
||
let loaded = {
|
||
// Use a deterministic temporary directory.
|
||
// We can't have all tests use "tmp" because tests run in parallel,
|
||
// so append the test name to the tmp path.
|
||
let tmp = format!("tmp/{}", subdir);
|
||
let dir = roc_test_utils::TmpDir::new(&tmp);
|
||
|
||
let filename = PathBuf::from("Test.roc");
|
||
let file_path = dir.path().join(filename);
|
||
let full_file_path = file_path.clone();
|
||
let mut file = File::create(file_path).unwrap();
|
||
writeln!(file, "{}", module_src).unwrap();
|
||
let result = roc_load::load_and_typecheck(
|
||
arena,
|
||
full_file_path,
|
||
exposed_types,
|
||
roc_target::TargetInfo::default_x86_64(),
|
||
RenderTarget::Generic,
|
||
Threading::Single,
|
||
);
|
||
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,
|
||
..
|
||
} = can_expr(arena, expr_src)?;
|
||
let mut subs = Subs::new_from_varstore(var_store);
|
||
|
||
for named in output.introduced_variables.named {
|
||
subs.rigid_var(named.variable, named.name);
|
||
}
|
||
|
||
for var in output.introduced_variables.wildcards {
|
||
subs.rigid_var(var.value, "*".into());
|
||
}
|
||
|
||
let mut solve_aliases = roc_solve::solve::Aliases::default();
|
||
|
||
for (name, alias) in output.aliases {
|
||
solve_aliases.insert(name, alias);
|
||
}
|
||
|
||
let mut unify_problems = Vec::new();
|
||
let mut abilities_store = AbilitiesStore::default();
|
||
let (_content, _subs) = infer_expr(
|
||
subs,
|
||
&mut unify_problems,
|
||
&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;
|
||
|
||
use roc_parse::state::State;
|
||
|
||
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(subdir: &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(subdir, &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)
|
||
}
|
||
}
|
||
}
|
||
|
||
fn human_readable(str: &str) -> String {
|
||
str.replace(ANSI_STYLE_CODES.red, "<red>")
|
||
.replace(ANSI_STYLE_CODES.white, "<white>")
|
||
.replace(ANSI_STYLE_CODES.blue, "<blue>")
|
||
.replace(ANSI_STYLE_CODES.yellow, "<yellow>")
|
||
.replace(ANSI_STYLE_CODES.green, "<green>")
|
||
.replace(ANSI_STYLE_CODES.cyan, "<cyan>")
|
||
.replace(ANSI_STYLE_CODES.magenta, "<magenta>")
|
||
.replace(ANSI_STYLE_CODES.reset, "<reset>")
|
||
.replace(ANSI_STYLE_CODES.bold, "<bold>")
|
||
.replace(ANSI_STYLE_CODES.underline, "<underline>")
|
||
}
|
||
|
||
test_report!(
|
||
value_not_exposed,
|
||
indoc!(
|
||
r#"
|
||
List.isempty 1 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The List module does not expose `isempty`:
|
||
|
||
4│ List.isempty 1 2
|
||
^^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
List.isEmpty
|
||
List.set
|
||
List.get
|
||
List.keepIf
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_unused_def,
|
||
indoc!(
|
||
r#"
|
||
x = 1
|
||
y = 2
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not used anywhere in your code.
|
||
|
||
5│ y = 2
|
||
^
|
||
|
||
If you didn't intend on using `y` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_shadowing,
|
||
indoc!(
|
||
r#"
|
||
i = 1
|
||
|
||
s = \i ->
|
||
i + 1
|
||
|
||
s i
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `i` name is first defined here:
|
||
|
||
4│ i = 1
|
||
^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
6│ s = \i ->
|
||
^
|
||
|
||
Since these variables have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_shadowing_in_annotation,
|
||
indoc!(
|
||
r#"
|
||
Booly : [Yes, No]
|
||
|
||
Booly : [Yes, No, Maybe]
|
||
|
||
x : List Booly
|
||
x = []
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Booly` name is first defined here:
|
||
|
||
4│ Booly : [Yes, No]
|
||
^^^^^^^^^^^^^^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
6│ Booly : [Yes, No, Maybe]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Since these aliases have the same name, it's easy to use the wrong one
|
||
on accident. Give one of them a new name.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_precedence_problem_single_line,
|
||
indoc!(
|
||
r#"x = 1
|
||
y =
|
||
if selectedId != thisId == adminsId then
|
||
4
|
||
|
||
else
|
||
5
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Using != and == together requires parentheses, to clarify how they
|
||
should be grouped.
|
||
|
||
6│ if selectedId != thisId == adminsId then
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore = "Blocked on https://github.com/rtfeldman/roc/issues/3385"]
|
||
unrecognized_name,
|
||
indoc!(
|
||
r#"
|
||
foo = { x: 1 == 1, y: 0x4 }
|
||
|
||
baz = 3
|
||
|
||
main : Str
|
||
main =
|
||
when foo.y is
|
||
4 -> bar baz "yay"
|
||
_ -> "nay"
|
||
|
||
main
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `bar` in this scope.
|
||
|
||
8│ 4 -> bar baz "yay"
|
||
^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
baz
|
||
Str
|
||
Err
|
||
main
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
lowercase_primitive_tag_bool,
|
||
indoc!(
|
||
r#"
|
||
if true then 1 else 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `true` in this scope.
|
||
|
||
4│ if true then 1 else 2
|
||
^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
True
|
||
Str
|
||
Frac
|
||
Num
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
report_precedence_problem_multiline,
|
||
indoc!(
|
||
r#"
|
||
if
|
||
1
|
||
== 2
|
||
== 3
|
||
then
|
||
2
|
||
|
||
else
|
||
3
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Using more than one == like this requires parentheses, to clarify how
|
||
things should be grouped.
|
||
|
||
5│> 1
|
||
6│> == 2
|
||
7│> == 3
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unused_arg_and_unused_def,
|
||
indoc!(
|
||
r#"
|
||
y = 9
|
||
|
||
box = \class, htmlChildren ->
|
||
div [class] []
|
||
|
||
div = \_, _ -> 4
|
||
|
||
box "wizard" []
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`box` doesn't use `htmlChildren`.
|
||
|
||
6│ box = \class, htmlChildren ->
|
||
^^^^^^^^^^^^
|
||
|
||
If you don't need `htmlChildren`, then you can just remove it. However,
|
||
if you really do need `htmlChildren` as an argument of `box`, prefix it
|
||
with an underscore, like this: "_`htmlChildren`". Adding an underscore
|
||
at the start of a variable name is a way of saying that the variable
|
||
is not used.
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not used anywhere in your code.
|
||
|
||
4│ y = 9
|
||
^
|
||
|
||
If you didn't intend on using `y` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
#[test]
|
||
fn report_value_color() {
|
||
let src: &str = indoc!(
|
||
r#"
|
||
activityIndicatorLarge = div
|
||
|
||
view activityIndicatorLarge
|
||
"#
|
||
);
|
||
|
||
let arena = Bump::new();
|
||
let (_type_problems, _can_problems, home, interns) =
|
||
infer_expr_help(&arena, src).expect("parse error");
|
||
|
||
let mut buf = String::new();
|
||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||
|
||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||
|
||
let symbol = interns.symbol(test_home(), "activityIndicatorLarge".into());
|
||
|
||
to_simple_report(alloc.symbol_unqualified(symbol)).render_color_terminal(
|
||
&mut buf,
|
||
&alloc,
|
||
&DEFAULT_PALETTE,
|
||
);
|
||
|
||
assert_eq!(human_readable(&buf), "<blue>activityIndicatorLarge<reset>");
|
||
}
|
||
|
||
#[test]
|
||
fn report_module_color() {
|
||
let src: &str = indoc!(
|
||
r#"
|
||
x = 1
|
||
y = 2
|
||
|
||
x
|
||
"#
|
||
);
|
||
|
||
let arena = Bump::new();
|
||
let (_type_problems, _can_problems, home, mut interns) =
|
||
infer_expr_help(&arena, src).expect("parse error");
|
||
|
||
let mut buf = String::new();
|
||
let src_lines: Vec<&str> = src.split('\n').collect();
|
||
let module_id = interns.module_id(&"Util.Int".into());
|
||
|
||
let alloc = RocDocAllocator::new(&src_lines, home, &interns);
|
||
to_simple_report(alloc.module(module_id)).render_color_terminal(
|
||
&mut buf,
|
||
&alloc,
|
||
&DEFAULT_PALETTE,
|
||
);
|
||
|
||
assert_eq!(human_readable(&buf), "<green>Util.Int<reset>");
|
||
}
|
||
|
||
#[test]
|
||
fn report_region_in_color() {
|
||
color_report_problem_as(
|
||
indoc!(
|
||
r#"
|
||
isDisabled = \user -> user.isAdmin
|
||
|
||
theAdmin
|
||
|> isDisabled
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
<cyan>── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─<reset>
|
||
|
||
Nothing is named `theAdmin` in this scope.
|
||
|
||
<cyan>3<reset><cyan>│<reset> <white>theAdmin<reset>
|
||
<red>^^^^^^^^<reset>
|
||
|
||
Did you mean one of these?
|
||
|
||
List
|
||
True
|
||
Box
|
||
Str
|
||
"#
|
||
),
|
||
);
|
||
}
|
||
|
||
test_report!(
|
||
if_condition_not_bool,
|
||
indoc!(
|
||
r#"
|
||
if "foo" then 2 else 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` condition needs to be a Bool:
|
||
|
||
4│ if "foo" then 2 else 3
|
||
^^^^^
|
||
|
||
Right now it’s a string of type:
|
||
|
||
Str
|
||
|
||
But I need every `if` condition to evaluate to a Bool—either `True` or
|
||
`False`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_if_guard,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2 if 1 -> 0x0
|
||
_ -> 0x1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` guard condition needs to be a Bool:
|
||
|
||
4│ when 1 is
|
||
5│> 2 if 1 -> 0x0
|
||
6│ _ -> 0x1
|
||
|
||
Right now it’s a number of type:
|
||
|
||
Num a
|
||
|
||
But I need every `if` guard condition to evaluate to a Bool—either
|
||
`True` or `False`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
if_2_branch_mismatch,
|
||
indoc!(
|
||
r#"
|
||
if True then 2 else "foo"
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` has an `else` branch with a different type from its `then` branch:
|
||
|
||
4│ if True then 2 else "foo"
|
||
^^^^^
|
||
|
||
The `else` branch is a string of type:
|
||
|
||
Str
|
||
|
||
but the `then` branch has the type:
|
||
|
||
Num a
|
||
|
||
All branches in an `if` must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
if_3_branch_mismatch,
|
||
indoc!(
|
||
r#"
|
||
if True then 2 else if False then 2 else "foo"
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd branch of this `if` does not match all the previous branches:
|
||
|
||
4│ if True then 2 else if False then 2 else "foo"
|
||
^^^^^
|
||
|
||
The 3rd branch is a string of type:
|
||
|
||
Str
|
||
|
||
But all the previous branches have type:
|
||
|
||
Num a
|
||
|
||
All branches in an `if` must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_branch_mismatch,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2 -> "foo"
|
||
3 -> {}
|
||
_ -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd branch of this `when` does not match all the previous branches:
|
||
|
||
4│ when 1 is
|
||
5│ 2 -> "foo"
|
||
6│> 3 -> {}
|
||
7│ _ -> ""
|
||
|
||
The 2nd branch is a record of type:
|
||
|
||
{}
|
||
|
||
But all the previous branches have type:
|
||
|
||
Str
|
||
|
||
All branches of a `when` must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
elem_in_list,
|
||
indoc!(
|
||
r#"
|
||
[1, 3, "foo"]
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This list contains elements with different types:
|
||
|
||
4│ [1, 3, "foo"]
|
||
^^^^^
|
||
|
||
Its 3rd element is a string of type:
|
||
|
||
Str
|
||
|
||
However, the preceding elements in the list all have the type:
|
||
|
||
Num a
|
||
|
||
Every element in a list must have the same type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unwrap_num_elem_in_list,
|
||
indoc!(
|
||
r#"
|
||
[1, 2.2, 0x3]
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This list contains elements with different types:
|
||
|
||
4│ [1, 2.2, 0x3]
|
||
^^^
|
||
|
||
Its 3rd element is an integer of type:
|
||
|
||
Int a
|
||
|
||
However, the preceding elements in the list all have the type:
|
||
|
||
Frac a
|
||
|
||
Every element in a list must have the same type!
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_update_value,
|
||
indoc!(
|
||
r#"
|
||
x : { foo : {} }
|
||
x = { foo: {} }
|
||
|
||
{ x & foo: "bar" }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I cannot update the `.foo` field like this:
|
||
|
||
7│ { x & foo: "bar" }
|
||
^^^^^
|
||
|
||
You are trying to update `.foo` to be a string of type:
|
||
|
||
Str
|
||
|
||
But it should be:
|
||
|
||
{}
|
||
|
||
Record update syntax does not allow you to change the type of fields.
|
||
You can achieve that with record literal syntax.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
circular_type,
|
||
indoc!(
|
||
r#"
|
||
f = \g -> g g
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `g`:
|
||
|
||
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
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_recursion,
|
||
indoc!(
|
||
r#"
|
||
f = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
4│ f = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_mutual_recursion,
|
||
indoc!(
|
||
r#"
|
||
f = \x -> g x
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
4│ f = \x -> g x
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> a
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `g`:
|
||
|
||
5│ g = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_mutual_recursion_annotated,
|
||
indoc!(
|
||
r#"
|
||
f : a -> List a
|
||
f = \x -> g x
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
5│ f = \x -> g x
|
||
^^^
|
||
|
||
This `g` call produces:
|
||
|
||
List List a
|
||
|
||
But you are trying to use it as:
|
||
|
||
List a
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `List` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_mutual_recursion_dually_annotated_lie,
|
||
indoc!(
|
||
r#"
|
||
f : a -> List a
|
||
f = \x -> g x
|
||
g : b -> List b
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
7│ g = \x -> f [x]
|
||
^^^^^
|
||
|
||
This `f` call produces:
|
||
|
||
List List b
|
||
|
||
But you are trying to use it as:
|
||
|
||
List b
|
||
|
||
Tip: The type annotation uses the type variable `b` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `List` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_recursion_inference_var,
|
||
indoc!(
|
||
r#"
|
||
f : _
|
||
f = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
5│ f = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> a
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
polymorphic_recursion_with_deep_inference_var,
|
||
indoc!(
|
||
r#"
|
||
f : _ -> List _
|
||
f = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `x`:
|
||
|
||
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!(
|
||
mutual_polymorphic_recursion_with_inference_var,
|
||
indoc!(
|
||
r#"
|
||
f : _ -> List _
|
||
f = \x -> g x
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
// TODO: the second error is duplicated because when solving `f : _ -> List _`, we
|
||
// introduce the variable for `f` twice: once to solve `f` without generalization,
|
||
// and then a second time to properly generalize it. When a def is unannotated
|
||
// (like in `g`) the same variable gets used both times, because the type of `g` is
|
||
// only an unbound type variable. However, for `f`, we run `type_to_var` twice,
|
||
// receiving two separate variables, and the second variable doesn't have the cycle
|
||
// error already recorded for the first.
|
||
// The way to resolve this is to always give type annotation signatures an extra
|
||
// variables they can put themselves in, and to run the constraint algorithm
|
||
// against that extra variable, rather than possibly having to translate a `Type`
|
||
// again.
|
||
@r###"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
5│ f = \x -> g x
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List a
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `g`:
|
||
|
||
6│ g = \x -> f [x]
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List a
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `f`:
|
||
|
||
5│ f = \x -> g x
|
||
^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
List ∞ -> List a
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `main`:
|
||
|
||
3│ main =
|
||
^^^^
|
||
|
||
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 a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mutual_polymorphic_recursion_with_inference_var_second,
|
||
indoc!(
|
||
r#"
|
||
f = \x -> g x
|
||
g : _ -> List _
|
||
g = \x -> f [x]
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `x`:
|
||
|
||
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 ∞
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
record_field_mismatch,
|
||
indoc!(
|
||
r#"
|
||
bar = { bar : 0x3 }
|
||
|
||
f : { foo : Num.Int * } -> [Yes, No]
|
||
f = \_ -> Yes
|
||
|
||
f bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
9│ f bar
|
||
^^^
|
||
|
||
This `bar` value is a:
|
||
|
||
{ bar : Int a }
|
||
|
||
But `f` needs its 1st argument to be:
|
||
|
||
{ foo : Int * }
|
||
|
||
Tip: Seems like a record field typo. Maybe `bar` should be `foo`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_mismatch,
|
||
indoc!(
|
||
r#"
|
||
f : [Red, Green] -> [Yes, No]
|
||
f = \_ -> Yes
|
||
|
||
f Blue
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
7│ f Blue
|
||
^^^^
|
||
|
||
This `Blue` tag has the type:
|
||
|
||
[Blue]a
|
||
|
||
But `f` needs its 1st argument to be:
|
||
|
||
[Green, Red]
|
||
|
||
Tip: Seems like a tag typo. Maybe `Blue` should be `Red`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_with_arguments_mismatch,
|
||
indoc!(
|
||
r#"
|
||
f : [Red (Num.Int *), Green Str] -> Str
|
||
f = \_ -> "yes"
|
||
|
||
f (Blue 3.14)
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
7│ f (Blue 3.14)
|
||
^^^^^^^^^
|
||
|
||
This `Blue` tag application has the type:
|
||
|
||
[Blue (Frac a)]b
|
||
|
||
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 True then 3.14 else 4
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the `then` branch of this `if` expression:
|
||
|
||
4│ x : Num.Int *
|
||
5│ x = if True then 3.14 else 4
|
||
^^^^
|
||
|
||
The 1st branch is a fraction of type:
|
||
|
||
Frac a
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
Int *
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
from_annotation_when,
|
||
indoc!(
|
||
r#"
|
||
x : Num.Int *
|
||
x =
|
||
when True is
|
||
_ -> 3.14
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : Num.Int *
|
||
5│ x =
|
||
6│> when True is
|
||
7│> _ -> 3.14
|
||
|
||
This `when` expression produces:
|
||
|
||
Frac a
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
Int *
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
from_annotation_function,
|
||
indoc!(
|
||
r#"
|
||
x : Num.Int * -> Num.Int *
|
||
x = \_ -> 3.14
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : Num.Int * -> Num.Int *
|
||
5│ x = \_ -> 3.14
|
||
^^^^
|
||
|
||
The body is a fraction of type:
|
||
|
||
Frac a
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
Int *
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
fncall_value,
|
||
indoc!(
|
||
r#"
|
||
x : Num.I64
|
||
x = 42
|
||
|
||
x 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `x` value is not a function, but it was given 1 argument:
|
||
|
||
7│ x 3
|
||
^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
fncall_overapplied,
|
||
indoc!(
|
||
r#"
|
||
f : Num.I64 -> Num.I64
|
||
f = \_ -> 42
|
||
|
||
f 1 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `f` function expects 1 argument, but it got 2 instead:
|
||
|
||
7│ f 1 2
|
||
^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
fncall_underapplied,
|
||
indoc!(
|
||
r#"
|
||
f : Num.I64, Num.I64 -> Num.I64
|
||
f = \_, _ -> 42
|
||
|
||
f 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO FEW ARGS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `f` function expects 2 arguments, but it got only 1:
|
||
|
||
7│ f 1
|
||
^
|
||
|
||
Roc does not allow functions to be partially applied. Use a closure to
|
||
make partial application explicit.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_when_condition,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
{} -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when 1 is
|
||
5│ {} -> 42
|
||
|
||
The `when` condition is a number of type:
|
||
|
||
Num a
|
||
|
||
But the branch patterns have type:
|
||
|
||
{}a
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_when_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2 -> 3
|
||
{} -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern in this `when` does not match the previous ones:
|
||
|
||
6│ {} -> 42
|
||
^^
|
||
|
||
The 2nd pattern is trying to match record values of type:
|
||
|
||
{}a
|
||
|
||
But all the previous branches match:
|
||
|
||
Num a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_guard_mismatch_alias,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{ foo: True } -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when { foo: 1 } is
|
||
5│ { foo: True } -> 42
|
||
|
||
The `when` condition is a record of type:
|
||
|
||
{ foo : Num a }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ foo : [True] }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_guard_mismatch,
|
||
indoc!(
|
||
r#"
|
||
when { foo: "" } is
|
||
{ foo: True } -> 42
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when { foo: "" } is
|
||
5│ { foo: True } -> 42
|
||
|
||
The `when` condition is a record of type:
|
||
|
||
{ foo : Str }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ foo : [True] }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
// needs some improvement, but the principle works
|
||
test_report!(
|
||
pattern_guard_does_not_bind_label,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{ foo: _ } -> foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `foo` in this scope.
|
||
|
||
5│ { foo: _ } -> foo
|
||
^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Box
|
||
Bool
|
||
U8
|
||
F64
|
||
"###
|
||
);
|
||
|
||
test_report! {
|
||
pattern_guard_can_be_shadowed_above,
|
||
indoc!(
|
||
r#"
|
||
foo = 3
|
||
|
||
when { foo: 1 } is
|
||
{ foo: 2 } -> foo
|
||
_ -> foo
|
||
"#
|
||
),
|
||
@"" // should give no error
|
||
}
|
||
|
||
test_report! {
|
||
pattern_guard_can_be_shadowed_below,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{ foo: 2 } ->
|
||
foo = 3
|
||
|
||
foo
|
||
_ -> 3
|
||
"#
|
||
),
|
||
// should give no error
|
||
@""
|
||
}
|
||
|
||
test_report!(
|
||
pattern_or_pattern_mismatch,
|
||
indoc!(
|
||
r#"
|
||
when { foo: 1 } is
|
||
{} | 1 -> 3
|
||
"#
|
||
),
|
||
// Just putting this here. We should probably handle or-patterns better
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern in this branch does not match the previous ones:
|
||
|
||
5│ {} | 1 -> 3
|
||
^
|
||
|
||
The 2nd pattern is trying to match numbers:
|
||
|
||
Num a
|
||
|
||
But all the previous branches match:
|
||
|
||
{}a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_let_mismatch,
|
||
indoc!(
|
||
r#"
|
||
(Foo x) = 42
|
||
|
||
x
|
||
"#
|
||
),
|
||
// Maybe this should specifically say the pattern doesn't work?
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
4│ (Foo x) = 42
|
||
^^
|
||
|
||
It is a number of type:
|
||
|
||
Num a
|
||
|
||
But you are trying to use it as:
|
||
|
||
[Foo a]
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
from_annotation_complex_pattern,
|
||
indoc!(
|
||
r#"
|
||
{ x } : { x : Num.Int * }
|
||
{ x } = { x: 4.0 }
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of this definition:
|
||
|
||
4│ { x } : { x : Num.Int * }
|
||
5│ { x } = { x: 4.0 }
|
||
^^^^^^^^^^
|
||
|
||
The body is a record of type:
|
||
|
||
{ x : Frac a }
|
||
|
||
But the type annotation says it should be:
|
||
|
||
{ x : Int * }
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_int_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
100A -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer pattern is malformed:
|
||
|
||
5│ 100A -> 3
|
||
^^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_float_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
2.X -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float pattern is malformed:
|
||
|
||
5│ 2.X -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_hex_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
0xZ -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This hex integer pattern is malformed:
|
||
|
||
5│ 0xZ -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_oct_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
0o9 -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This octal integer pattern is malformed:
|
||
|
||
5│ 0o9 -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
malformed_bin_pattern,
|
||
indoc!(
|
||
r#"
|
||
when 1 is
|
||
0b4 -> 3
|
||
_ -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This binary integer pattern is malformed:
|
||
|
||
5│ 0b4 -> 3
|
||
^^^
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
missing_fields,
|
||
indoc!(
|
||
r#"
|
||
x : { a : Num.Int *, b : Num.Frac *, c : Str }
|
||
x = { b: 4.0 }
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : { a : Num.Int *, b : Num.Frac *, c : Str }
|
||
5│ x = { b: 4.0 }
|
||
^^^^^^^^^^
|
||
|
||
The body is a record of type:
|
||
|
||
{ b : Frac a }
|
||
|
||
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 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 True then x else y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the `else` branch of this `if` expression:
|
||
|
||
4│ f : a, b -> a
|
||
5│ f = \x, y -> if True then x else y
|
||
^
|
||
|
||
This `y` value is a:
|
||
|
||
b
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
a
|
||
|
||
Tip: Your type annotation uses `b` and `a` as separate type variables.
|
||
Your code seems to be saying they are the same though. Maybe they
|
||
should be the same in your type annotation? Maybe your code uses them
|
||
in a weird way?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
bad_rigid_function,
|
||
indoc!(
|
||
r#"
|
||
f : Str -> msg
|
||
f = \_ -> Foo
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : Str -> msg
|
||
5│ f = \_ -> Foo
|
||
^^^
|
||
|
||
This `Foo` tag has the type:
|
||
|
||
[Foo]a
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
msg
|
||
|
||
Tip: The type annotation uses the type variable `msg` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a tag value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
bad_rigid_value,
|
||
indoc!(
|
||
r#"
|
||
f : msg
|
||
f = 0x3
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : msg
|
||
5│ f = 0x3
|
||
^^^
|
||
|
||
The body is an integer of type:
|
||
|
||
Int a
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
msg
|
||
|
||
Tip: The type annotation uses the type variable `msg` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `Int` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
// TODO improve tag suggestions
|
||
test_report!(
|
||
typo_lowercase_ok,
|
||
indoc!(
|
||
r#"
|
||
f : Str -> [Ok Num.I64, InvalidFoo]
|
||
f = \_ -> ok 4
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `ok` in this scope.
|
||
|
||
5│ f = \_ -> ok 4
|
||
^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Ok
|
||
U8
|
||
Box
|
||
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 ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`ok` is not used anywhere in your code.
|
||
|
||
6│ ok = 3
|
||
^^
|
||
|
||
If you didn't intend on using `ok` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : Str -> Num.I64
|
||
5│ f = \_ ->
|
||
6│ ok = 3
|
||
7│
|
||
8│ Ok
|
||
^^
|
||
|
||
This `Ok` tag has the type:
|
||
|
||
[Ok]a
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
I64
|
||
"###
|
||
);
|
||
|
||
// invalid recursion
|
||
test_report!(
|
||
circular_definition_self,
|
||
indoc!(
|
||
r#"
|
||
f = f
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `f` value is defined directly in terms of itself, causing an
|
||
infinite loop.
|
||
"#
|
||
);
|
||
|
||
// invalid mutual recursion
|
||
test_report!(
|
||
circular_definition,
|
||
indoc!(
|
||
r#"
|
||
foo = bar
|
||
|
||
bar = foo
|
||
|
||
foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `foo` definition is causing a very tricky infinite loop:
|
||
|
||
4│ foo = bar
|
||
^^^
|
||
|
||
The `foo` value depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ foo
|
||
│ ↓
|
||
│ bar
|
||
└─────┘
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_empty_record,
|
||
indoc!(
|
||
r#"
|
||
x = {}
|
||
|
||
{ x & foo: 3 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `x` record doesn’t have a `foo` field:
|
||
|
||
6│ { x & foo: 3 }
|
||
^^^^^^
|
||
|
||
In fact, `x` is a record with no fields at all!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_record,
|
||
indoc!(
|
||
r#"
|
||
x = { fo: 3, bar: 4 }
|
||
|
||
{ x & foo: 3 }
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `x` record doesn’t have a `foo` field:
|
||
|
||
6│ { x & foo: 3 }
|
||
^^^^^^
|
||
|
||
There may be a typo. These `x` fields are the most similar:
|
||
|
||
{
|
||
fo : Num b,
|
||
bar : Num a,
|
||
}
|
||
|
||
Maybe `foo:` should be `fo:` instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_record_ext,
|
||
indoc!(
|
||
r#"
|
||
f : { fo: Num.I64 }ext -> Num.I64
|
||
f = \r ->
|
||
r2 = { r & foo: r.fo }
|
||
|
||
r2.fo
|
||
|
||
f
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `r` record doesn’t have a `foo` field:
|
||
|
||
6│ r2 = { r & foo: r.fo }
|
||
^^^^^^^^^
|
||
|
||
There may be a typo. These `r` fields are the most similar:
|
||
|
||
{
|
||
fo : I64,
|
||
}ext
|
||
|
||
Maybe `foo:` should be `fo:` instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
update_record_snippet,
|
||
indoc!(
|
||
r#"
|
||
x = { fo: 3, bar: 4, baz: 3, spam: 42, foobar: 3 }
|
||
|
||
{ x & foo: 3 }
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `x` record doesn’t have a `foo` field:
|
||
|
||
6│ { x & foo: 3 }
|
||
^^^^^^
|
||
|
||
There may be a typo. These `x` fields are the most similar:
|
||
|
||
{
|
||
fo : Num c,
|
||
foobar : Num d,
|
||
bar : Num a,
|
||
baz : Num b,
|
||
…
|
||
}
|
||
|
||
Maybe `foo:` should be `fo:` instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
plus_on_str,
|
||
indoc!(
|
||
r#"
|
||
0x4 + "foo"
|
||
"#
|
||
),
|
||
// TODO also suggest fields with the correct type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ 0x4 + "foo"
|
||
^^^^^
|
||
|
||
The argument is a string of type:
|
||
|
||
Str
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Int a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
int_frac,
|
||
indoc!(
|
||
r#"
|
||
0x4 + 3.14
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ 0x4 + 3.14
|
||
^^^^
|
||
|
||
The argument is a fraction of type:
|
||
|
||
Frac a
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Int a
|
||
|
||
Tip: You can convert between Int and Frac using functions like
|
||
`Num.toFrac` and `Num.round`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
boolean_tag,
|
||
indoc!(
|
||
r#"
|
||
42 + True
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ 42 + True
|
||
^^^^
|
||
|
||
This `True` boolean has the type:
|
||
|
||
[True]a
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Num a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_missing,
|
||
indoc!(
|
||
r#"
|
||
f : [A] -> [A, B]
|
||
f = \a -> a
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : [A] -> [A, B]
|
||
5│ f = \a -> a
|
||
^
|
||
|
||
This `a` value is a:
|
||
|
||
[A]
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
[A, B]
|
||
|
||
Tip: Looks like a closed tag union does not have the `B` tag.
|
||
|
||
Tip: Closed tag unions can't grow, because that might change the size
|
||
in memory. Can you use an open tag union?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tags_missing,
|
||
indoc!(
|
||
r#"
|
||
f : [A] -> [A, B, C]
|
||
f = \a -> a
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : [A] -> [A, B, C]
|
||
5│ f = \a -> a
|
||
^
|
||
|
||
This `a` value is a:
|
||
|
||
[A]
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
[A, B, C]
|
||
|
||
Tip: Looks like a closed tag union does not have the `B` and `C` tags.
|
||
|
||
Tip: Closed tag unions can't grow, because that might change the size
|
||
in memory. Can you use an open tag union?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_fn_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
Either : [Left {}, Right Str]
|
||
|
||
x : Either
|
||
x = Left {}
|
||
|
||
f : Either -> {}
|
||
f = \Left v -> v
|
||
|
||
f x
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This pattern does not cover all the possibilities:
|
||
|
||
10│ f = \Left v -> v
|
||
^^^^^^
|
||
|
||
Other possibilities include:
|
||
|
||
Right _
|
||
|
||
I would have to crash if I saw one of those! So rather than pattern
|
||
matching in function arguments, put a `when` in the function body to
|
||
account for all possibilities.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_let_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x : [Left {}, Right Str]
|
||
x = Left {}
|
||
|
||
|
||
(Left y) = x
|
||
|
||
y
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
8│ (Left y) = x
|
||
^
|
||
|
||
This `x` value is a:
|
||
|
||
[Left {}, Right Str]
|
||
|
||
But you are trying to use it as:
|
||
|
||
[Left a]
|
||
|
||
Tip: Looks like a closed tag union does not have the `Right` tag.
|
||
|
||
Tip: Closed tag unions can't grow, because that might change the size
|
||
in memory. Can you use an open tag union?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_when_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
when 0x1 is
|
||
2 -> 0x3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> when 0x1 is
|
||
5│> 2 -> 0x3
|
||
|
||
Other possibilities include:
|
||
|
||
_
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_bool_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x : [Red, Green]
|
||
x = Green
|
||
|
||
when x is
|
||
Red -> 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
7│> when x is
|
||
8│> Red -> 3
|
||
|
||
Other possibilities include:
|
||
|
||
Green
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_enum_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x : [Red, Green, Blue]
|
||
x = Red
|
||
|
||
when x is
|
||
Red -> 0
|
||
Green -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
7│> when x is
|
||
8│> Red -> 0
|
||
9│> Green -> 1
|
||
|
||
Other possibilities include:
|
||
|
||
Blue
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_remote_data_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
RemoteData e a : [NotAsked, Loading, Failure e, Success a]
|
||
|
||
x : RemoteData Num.I64 Str
|
||
|
||
when x is
|
||
NotAsked -> 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
8│> when x is
|
||
9│> NotAsked -> 3
|
||
|
||
Other possibilities include:
|
||
|
||
Failure _
|
||
Loading
|
||
Success _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_record_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
x = { a: 3 }
|
||
|
||
when x is
|
||
{ a: 4 } -> 4
|
||
"#
|
||
),
|
||
// Tip: Looks like a record field guard is not exhaustive. Learn more about record pattern matches at TODO.
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when x is
|
||
7│> { a: 4 } -> 4
|
||
|
||
Other possibilities include:
|
||
|
||
{ a }
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_record_guard_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
y : [Nothing, Just Num.I64]
|
||
y = Just 4
|
||
x = { a: y, b: 42}
|
||
|
||
when x is
|
||
{ a: Nothing } -> 4
|
||
{ a: Just 3 } -> 4
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
8│> when x is
|
||
9│> { a: Nothing } -> 4
|
||
10│> { a: Just 3 } -> 4
|
||
|
||
Other possibilities include:
|
||
|
||
{ a: Just _ }
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_nested_tag_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
when Record Nothing 1 is
|
||
Record (Nothing) b -> b
|
||
Record (Just 3) b -> b
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> when Record Nothing 1 is
|
||
5│> Record (Nothing) b -> b
|
||
6│> Record (Just 3) b -> b
|
||
|
||
Other possibilities include:
|
||
|
||
Record (Just _) _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
patterns_int_redundant,
|
||
indoc!(
|
||
r#"
|
||
when 0x1 is
|
||
2 -> 3
|
||
2 -> 4
|
||
_ -> 5
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern is redundant:
|
||
|
||
4│ when 0x1 is
|
||
5│ 2 -> 3
|
||
6│> 2 -> 4
|
||
7│ _ -> 5
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unify_alias_other,
|
||
indoc!(
|
||
r#"
|
||
Foo a : { x : Num.Int a }
|
||
|
||
f : Foo a -> Num.Int a
|
||
f = \r -> r.x
|
||
|
||
f { y: 3.14 }
|
||
"#
|
||
),
|
||
// de-aliases the alias to give a better error message
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `f` has an unexpected type:
|
||
|
||
9│ f { y: 3.14 }
|
||
^^^^^^^^^^^
|
||
|
||
The argument is a record of type:
|
||
|
||
{ y : Frac a }
|
||
|
||
But `f` needs its 1st argument to be:
|
||
|
||
{ x : Int a }
|
||
|
||
Tip: Seems like a record field typo. Maybe `y` should be `x`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
cyclic_alias,
|
||
indoc!(
|
||
r#"
|
||
Foo : { x : Bar }
|
||
Bar : { y : Foo }
|
||
|
||
f : Foo
|
||
|
||
f
|
||
"#
|
||
),
|
||
// should not report Bar as unused!
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` alias is self-recursive in an invalid way:
|
||
|
||
4│ Foo : { x : Bar }
|
||
^^^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
self_recursive_alias,
|
||
indoc!(
|
||
r#"
|
||
Foo : { x : Foo }
|
||
|
||
f : Foo
|
||
f = 3
|
||
|
||
f
|
||
"#
|
||
),
|
||
// should not report Bar as unused!
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` alias is self-recursive in an invalid way:
|
||
|
||
4│ Foo : { x : Foo }
|
||
^^^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_duplicate_field_same_type,
|
||
indoc!(
|
||
r#"
|
||
{ x: 4, y: 3, x: 4 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
4│ { x: 4, y: 3, x: 4 }
|
||
^^^^ ^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ { x: 4, y: 3, x: 4 }
|
||
^^^^
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_duplicate_field_different_types,
|
||
indoc!(
|
||
r#"
|
||
{ x: 4, y: 3, x: "foo" }
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
4│ { x: 4, y: 3, x: "foo" }
|
||
^^^^ ^^^^^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ { x: 4, y: 3, x: "foo" }
|
||
^^^^^^^^
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_duplicate_field_multiline,
|
||
indoc!(
|
||
r#"
|
||
{
|
||
x: 4,
|
||
y: 3,
|
||
x: "foo"
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
4│ {
|
||
5│> x: 4,
|
||
6│ y: 3,
|
||
7│> x: "foo"
|
||
8│ }
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ {
|
||
5│ x: 4,
|
||
6│ y: 3,
|
||
7│> x: "foo"
|
||
8│ }
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_update_duplicate_field_multiline,
|
||
indoc!(
|
||
r#"
|
||
\r ->
|
||
{ r &
|
||
x: 4,
|
||
y: 3,
|
||
x: "foo"
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record defines the `.x` field twice!
|
||
|
||
5│ { r &
|
||
6│> x: 4,
|
||
7│ y: 3,
|
||
8│> x: "foo"
|
||
9│ }
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
5│ { r &
|
||
6│ x: 4,
|
||
7│ y: 3,
|
||
8│> x: "foo"
|
||
9│ }
|
||
|
||
For clarity, remove the previous `.x` definitions from this record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_duplicate_field,
|
||
indoc!(
|
||
r#"
|
||
a : { foo : Num.I64, bar : {}, foo : Str }
|
||
a = { bar: {}, foo: "foo" }
|
||
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE FIELD NAME ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record type defines the `.foo` field twice!
|
||
|
||
4│ a : { foo : Num.I64, bar : {}, foo : Str }
|
||
^^^^^^^^^^^^^ ^^^^^^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ a : { foo : Num.I64, bar : {}, foo : Str }
|
||
^^^^^^^^^
|
||
|
||
For clarity, remove the previous `.foo` definitions from this record
|
||
type.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_duplicate_tag,
|
||
indoc!(
|
||
r#"
|
||
a : [Foo Num.I64, Bar {}, Foo Str]
|
||
a = Foo "foo"
|
||
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE TAG NAME ──────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This tag union type defines the `Foo` tag twice!
|
||
|
||
4│ a : [Foo Num.I64, Bar {}, Foo Str]
|
||
^^^^^^^^^^^ ^^^^^^^
|
||
|
||
In the rest of the program, I will only use the latter definition:
|
||
|
||
4│ a : [Foo Num.I64, Bar {}, Foo Str]
|
||
^^^^^^^
|
||
|
||
For clarity, remove the previous `Foo` definitions from this tag union
|
||
type.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
annotation_definition_mismatch,
|
||
indoc!(
|
||
r#"
|
||
bar : Num.I64
|
||
foo = \x -> x
|
||
|
||
# NOTE: neither bar or foo are defined at this point
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This annotation does not match the definition immediately following
|
||
it:
|
||
|
||
4│> bar : Num.I64
|
||
5│> foo = \x -> x
|
||
|
||
Is it a typo? If not, put either a newline or comment between them.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
annotation_newline_body_is_fine,
|
||
indoc!(
|
||
r#"
|
||
bar : Num.I64
|
||
|
||
foo = \x -> x
|
||
|
||
foo bar
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
invalid_alias_rigid_var_pattern,
|
||
indoc!(
|
||
r#"
|
||
MyAlias 1 : Num.I64
|
||
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This definition of `MyAlias` has an unexpected pattern:
|
||
|
||
4│ MyAlias 1 : Num.I64
|
||
^
|
||
|
||
Only type variables like `a` or `value` can occur in this position.
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`MyAlias` is not used anywhere in your code.
|
||
|
||
4│ MyAlias 1 : Num.I64
|
||
^^^^^^^^^^^^^^^^^^^
|
||
|
||
If you didn't intend on using `MyAlias` then remove it so future readers
|
||
of your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_opaque_rigid_var_pattern,
|
||
indoc!(
|
||
r#"
|
||
Age 1 := Num.I64
|
||
|
||
a : Age
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This definition of `Age` has an unexpected pattern:
|
||
|
||
4│ Age 1 := Num.I64
|
||
^
|
||
|
||
Only type variables like `a` or `value` can occur in this position.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_num,
|
||
indoc!(
|
||
r#"
|
||
a : Num.Num Num.I64 Num.F64
|
||
a = 3
|
||
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||
|
||
4│ a : Num.Num Num.I64 Num.F64
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_num_fn,
|
||
indoc!(
|
||
r#"
|
||
f : Str -> Num.Num Num.I64 Num.F64
|
||
f = \_ -> 3
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Num` alias expects 1 type argument, but it got 2 instead:
|
||
|
||
4│ f : Str -> Num.Num Num.I64 Num.F64
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
too_few_type_arguments,
|
||
indoc!(
|
||
r#"
|
||
Pair a b : [Pair a b]
|
||
|
||
x : Pair Num.I64
|
||
x = Pair 2 3
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Pair` alias expects 2 type arguments, but it got 1 instead:
|
||
|
||
6│ x : Pair Num.I64
|
||
^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
too_many_type_arguments,
|
||
indoc!(
|
||
r#"
|
||
Pair a b : [Pair a b]
|
||
|
||
x : Pair Num.I64 Num.I64 Num.I64
|
||
x = 3
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Pair` alias expects 2 type arguments, but it got 3 instead:
|
||
|
||
6│ x : Pair Num.I64 Num.I64 Num.I64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
phantom_type_variable,
|
||
indoc!(
|
||
r#"
|
||
Foo a : [Foo]
|
||
|
||
f : Foo Num.I64
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED TYPE ALIAS PARAMETER ─────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `a` type parameter is not used in the `Foo` alias definition:
|
||
|
||
4│ Foo a : [Foo]
|
||
^
|
||
|
||
Roc does not allow unused type alias parameters!
|
||
|
||
Tip: If you want an unused type parameter (a so-called "phantom
|
||
type"), read the guide section on phantom values.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
elm_function_syntax,
|
||
indoc!(
|
||
r#"
|
||
f x y = x
|
||
"#
|
||
),
|
||
@r###"
|
||
── ARGUMENTS BEFORE EQUALS ────────────────── tmp/elm_function_syntax/Test.roc ─
|
||
|
||
I am partway through parsing a definition, but I got stuck here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ f x y = x
|
||
^^^
|
||
|
||
Looks like you are trying to define a function. In roc, functions are
|
||
always written as a lambda, like increment = \n -> n + 1.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
two_different_cons,
|
||
indoc!(
|
||
r#"
|
||
ConsList a : [Cons a (ConsList a), Nil]
|
||
|
||
x : ConsList {}
|
||
x = Cons {} (Cons "foo" Nil)
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
6│ x : ConsList {}
|
||
7│ x = Cons {} (Cons "foo" Nil)
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `Cons` tag application has the type:
|
||
|
||
[Cons {} [Cons Str [Cons {} a, Nil] as a, Nil], Nil]
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
[Cons {} a, Nil] as a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mutually_recursive_types_with_type_error,
|
||
indoc!(
|
||
r#"
|
||
AList a b : [ACons a (BList a b), ANil]
|
||
BList a b : [BCons a (AList a b), BNil]
|
||
|
||
x : AList Num.I64 Num.I64
|
||
x = ACons 0 (BCons 1 (ACons "foo" BNil ))
|
||
|
||
y : BList a a
|
||
y = BNil
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
// TODO render tag unions across multiple lines
|
||
// TODO do not show recursion var if the recursion var does not render on the surface of a type
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
7│ x : AList Num.I64 Num.I64
|
||
8│ x = ACons 0 (BCons 1 (ACons "foo" BNil ))
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `ACons` tag application has the type:
|
||
|
||
[ACons (Int Signed64) [BCons (Int Signed64) [ACons Str [BCons I64 [ACons I64 (BList I64 I64),
|
||
ANil] as ∞, BNil], ANil], BNil], ANil]
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
[ACons I64 (BList I64 I64), ANil] as a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
integer_out_of_range,
|
||
indoc!(
|
||
r#"
|
||
x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
|
||
y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
|
||
h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
|
||
minlit = -170_141_183_460_469_231_731_687_303_715_884_105_728
|
||
maxlit = 340_282_366_920_938_463_463_374_607_431_768_211_455
|
||
|
||
x + y + h + l + minlit + maxlit
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too big:
|
||
|
||
4│ x = 170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The largest number representable in Roc is the maximum U128 value,
|
||
340_282_366_920_938_463_463_374_607_431_768_211_455.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too small:
|
||
|
||
6│ y = -170_141_183_460_469_231_731_687_303_715_884_105_728_000
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The smallest number representable in Roc is the minimum I128 value,
|
||
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too big:
|
||
|
||
8│ h = 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The largest number representable in Roc is the maximum U128 value,
|
||
340_282_366_920_938_463_463_374_607_431_768_211_455.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal is too small:
|
||
|
||
9│ l = -0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The smallest number representable in Roc is the minimum I128 value,
|
||
-170_141_183_460_469_231_731_687_303_715_884_105_728.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
14│ x + y + h + l + minlit + maxlit
|
||
^^^^^^
|
||
|
||
This `maxlit` value is a:
|
||
|
||
U128
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
I128 or Dec
|
||
"###
|
||
);
|
||
|
||
// have to deal with some whitespace issues because of the format! macro
|
||
test_report!(
|
||
float_out_of_range,
|
||
indoc!(
|
||
r#"
|
||
overflow = 11.7976931348623157e308
|
||
underflow = -11.7976931348623157e308
|
||
|
||
overflow + underflow
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal is too big:
|
||
|
||
4│ overflow = 11.7976931348623157e308
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Roc uses signed 64-bit floating points, allowing values between
|
||
-1.7976931348623157e308 and 1.7976931348623157e308
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal is too small:
|
||
|
||
5│ underflow = -11.7976931348623157e308
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Roc uses signed 64-bit floating points, allowing values between
|
||
-1.7976931348623157e308 and 1.7976931348623157e308
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
// the generated messages here are incorrect. Waiting for a rust nightly feature to land,
|
||
// see https://github.com/rust-lang/rust/issues/22639
|
||
// this test is here to spot regressions in error reporting
|
||
test_report!(
|
||
integer_malformed,
|
||
indoc!(
|
||
r#"
|
||
dec = 100A
|
||
|
||
hex = 0xZZZ
|
||
|
||
oct = 0o9
|
||
|
||
bin = 0b2
|
||
|
||
dec + hex + oct + bin
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal contains an invalid digit:
|
||
|
||
4│ dec = 100A
|
||
^^^^
|
||
|
||
Integer literals can only contain the digits
|
||
0-9, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This hex integer literal contains an invalid digit:
|
||
|
||
6│ hex = 0xZZZ
|
||
^^^^^
|
||
|
||
Hexadecimal (base-16) integer literals can only contain the digits
|
||
0-9, a-f and A-F, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This octal integer literal contains an invalid digit:
|
||
|
||
8│ oct = 0o9
|
||
^^^
|
||
|
||
Octal (base-8) integer literals can only contain the digits
|
||
0-7, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This binary integer literal contains an invalid digit:
|
||
|
||
10│ bin = 0b2
|
||
^^^
|
||
|
||
Binary (base-2) integer literals can only contain the digits
|
||
0 and 1, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
integer_empty,
|
||
indoc!(
|
||
r#"
|
||
dec = 20
|
||
|
||
hex = 0x
|
||
|
||
oct = 0o
|
||
|
||
bin = 0b
|
||
|
||
dec + hex + oct + bin
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This hex integer literal contains no digits:
|
||
|
||
6│ hex = 0x
|
||
^^
|
||
|
||
Hexadecimal (base-16) integer literals must contain at least one of
|
||
the digits 0-9, a-f and A-F, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This octal integer literal contains no digits:
|
||
|
||
8│ oct = 0o
|
||
^^
|
||
|
||
Octal (base-8) integer literals must contain at least one of the
|
||
digits 0-7, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This binary integer literal contains no digits:
|
||
|
||
10│ bin = 0b
|
||
^^
|
||
|
||
Binary (base-2) integer literals must contain at least one of the
|
||
digits 0 and 1, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
float_malformed,
|
||
indoc!(
|
||
r#"
|
||
x = 3.0A
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal contains an invalid digit:
|
||
|
||
4│ x = 3.0A
|
||
^^^^
|
||
|
||
Floating point literals can only contain the digits 0-9, or use
|
||
scientific notation 10e4, or have a float suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_record_update,
|
||
indoc!(
|
||
r#"
|
||
foo = { bar: 3 }
|
||
updateNestedRecord = { foo.bar & x: 4 }
|
||
|
||
example = { age: 42 }
|
||
|
||
# these should work
|
||
y = { Test.example & age: 3 }
|
||
x = { example & age: 4 }
|
||
|
||
{ updateNestedRecord, foo, x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression cannot be updated:
|
||
|
||
5│ updateNestedRecord = { foo.bar & x: 4 }
|
||
^^^^^^^
|
||
|
||
Only variables can be updated with record update syntax.
|
||
|
||
── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Test` module is not imported:
|
||
|
||
10│ y = { Test.example & age: 3 }
|
||
^^^^^^^^^^^^
|
||
|
||
Is there an import missing? Perhaps there is a typo. Did you mean one
|
||
of these?
|
||
|
||
List
|
||
Set
|
||
Dict
|
||
Result
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression cannot be updated:
|
||
|
||
10│ y = { Test.example & age: 3 }
|
||
^^^^^^^^^^^^
|
||
|
||
Only variables can be updated with record update syntax.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
module_not_imported,
|
||
indoc!(
|
||
r#"
|
||
Foo.test
|
||
"#
|
||
),
|
||
@r###"
|
||
── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` module is not imported:
|
||
|
||
4│ Foo.test
|
||
^^^^^^^^
|
||
|
||
Is there an import missing? Perhaps there is a typo. Did you mean one
|
||
of these?
|
||
|
||
Box
|
||
Bool
|
||
Num
|
||
Set
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_default_type_error,
|
||
indoc!(
|
||
r#"
|
||
\{ x, y ? True } -> x + y
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `add` has an unexpected type:
|
||
|
||
4│ \{ x, y ? True } -> x + y
|
||
^
|
||
|
||
This `y` value is a:
|
||
|
||
[True]a
|
||
|
||
But `add` needs its 2nd argument to be:
|
||
|
||
Num a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_default_with_signature,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `f` is weird:
|
||
|
||
5│ f = \{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||
^^^^^^^^^^^^^^^^
|
||
|
||
The argument is a pattern that matches record values of type:
|
||
|
||
{ x : I64, y ? Str }
|
||
|
||
But the annotation on `f` says the 1st argument should be:
|
||
|
||
{ x : I64, y ? I64 }
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_let_binding,
|
||
indoc!(
|
||
r#"
|
||
\rec ->
|
||
{ x, y } : { x : Num.I64, y ? Str }
|
||
{ x, y } = rec
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of this definition:
|
||
|
||
5│> { x, y } : { x : Num.I64, y ? Str }
|
||
6│> { x, y } = rec
|
||
|
||
The body is a value of type:
|
||
|
||
{ x : I64, y : Str }
|
||
|
||
But the type annotation says it should be:
|
||
|
||
{ x : I64, y ? Str }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_function,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \{ x, y } -> x + y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `f` is weird:
|
||
|
||
5│ f = \{ x, y } -> x + y
|
||
^^^^^^^^
|
||
|
||
The argument is a pattern that matches record values of type:
|
||
|
||
{ x : I64, y : I64 }
|
||
|
||
But the annotation on `f` says the 1st argument should be:
|
||
|
||
{ x : I64, y ? I64 }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_when,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r ->
|
||
when r is
|
||
{ x, y } -> x + y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when r is
|
||
7│ { x, y } -> x + y
|
||
|
||
This `r` value is a:
|
||
|
||
{ x : I64, y ? I64 }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ x : I64, y : I64 }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_access,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r -> r.y
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
5│ f = \r -> r.y
|
||
^^^
|
||
|
||
This `r` value is a:
|
||
|
||
{ x : I64, y ? I64 }
|
||
|
||
But you are trying to use it as:
|
||
|
||
{ x : I64, y : I64 }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_record_invalid_accessor,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r -> .y r
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to this function has an unexpected type:
|
||
|
||
5│ f = \r -> .y r
|
||
^
|
||
|
||
This `r` value is a:
|
||
|
||
{ x : I64, y ? I64 }
|
||
|
||
But this function needs its 1st argument to be:
|
||
|
||
{ x : I64, y : I64 }
|
||
|
||
Tip: To extract the `.y` field it must be non-optional, but the type
|
||
says this field is optional. Learn more about optional fields at TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
guard_mismatch_with_annotation,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y : Num.I64 } -> Num.I64
|
||
f = \r ->
|
||
when r is
|
||
{ x, y : "foo" } -> x + 0
|
||
_ -> 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when r is
|
||
7│ { x, y : "foo" } -> x + 0
|
||
8│ _ -> 0
|
||
|
||
This `r` value is a:
|
||
|
||
{ x : I64, y : I64 }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ x : I64, y : Str }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
optional_field_mismatch_with_annotation,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.I64, y ? Num.I64 } -> Num.I64
|
||
f = \r ->
|
||
when r is
|
||
{ x, y ? "foo" } -> (\g, _ -> g) x y
|
||
_ -> 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when r is
|
||
7│ { x, y ? "foo" } -> (\g, _ -> g) x y
|
||
8│ _ -> 0
|
||
|
||
This `r` value is a:
|
||
|
||
{ x : I64, y ? I64 }
|
||
|
||
But the branch patterns have type:
|
||
|
||
{ x : I64, y ? Str }
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
incorrect_optional_field,
|
||
indoc!(
|
||
r#"
|
||
{ x: 5, y ? 42 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── BAD OPTIONAL VALUE ──────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record uses an optional value for the `.y` field in an incorrect
|
||
context!
|
||
|
||
4│ { x: 5, y ? 42 }
|
||
^^^^^^
|
||
|
||
You can only use optional values in record destructuring, like:
|
||
|
||
{ answer ? 42, otherField } = myRecord
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
first_wildcard_is_required,
|
||
indoc!(
|
||
r#"
|
||
when Foo 1 2 3 is
|
||
Foo _ 1 _ -> 1
|
||
_ -> 2
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
second_wildcard_is_redundant,
|
||
indoc!(
|
||
r#"
|
||
when Foo 1 2 3 is
|
||
Foo _ 1 _ -> 1
|
||
_ -> 2
|
||
_ -> 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── REDUNDANT PATTERN ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 3rd pattern is redundant:
|
||
|
||
4│ when Foo 1 2 3 is
|
||
5│ Foo _ 1 _ -> 1
|
||
6│ _ -> 2
|
||
7│ _ -> 3
|
||
^
|
||
|
||
Any value of this shape will be handled by a previous pattern, so this
|
||
one should be removed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
alias_using_alias,
|
||
indoc!(
|
||
r#"
|
||
# The color of a node. Leaves are considered Black.
|
||
NodeColor : [Red, Black]
|
||
|
||
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty]
|
||
|
||
# Create an empty dictionary.
|
||
empty : RBTree k v
|
||
empty =
|
||
Empty
|
||
|
||
empty
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
unused_argument,
|
||
indoc!(
|
||
r#"
|
||
f = \foo -> 1
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED ARGUMENT ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`f` doesn't use `foo`.
|
||
|
||
4│ f = \foo -> 1
|
||
^^^
|
||
|
||
If you don't need `foo`, then you can just remove it. However, if you
|
||
really do need `foo` as an argument of `f`, prefix it with an underscore,
|
||
like this: "_`foo`". Adding an underscore at the start of a variable
|
||
name is a way of saying that the variable is not used.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
qualified_tag,
|
||
indoc!(
|
||
r#"
|
||
Foo.Bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse a qualified name here:
|
||
|
||
4│ Foo.Bar
|
||
^
|
||
|
||
This looks like a qualified tag name to me, but tags cannot be
|
||
qualified! Maybe you wanted a qualified name, something like
|
||
Json.Decode.string?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
module_ident_ends_with_dot,
|
||
indoc!(
|
||
r#"
|
||
Foo.Bar.
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse a qualified name here:
|
||
|
||
4│ Foo.Bar.
|
||
^
|
||
|
||
I was expecting to see an identifier next, like height. A complete
|
||
qualified name looks something like Json.Decode.string.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_access_ends_with_dot,
|
||
indoc!(
|
||
r#"
|
||
foo.bar.
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I trying to parse a record field access here:
|
||
|
||
4│ foo.bar.
|
||
^
|
||
|
||
So I expect to see a lowercase letter next, like .name or .height.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_annotation_double_colon,
|
||
indoc!(
|
||
r#"
|
||
f :: I64
|
||
f = 42
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ──────────────── tmp/type_annotation_double_colon/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ f :: I64
|
||
^^
|
||
|
||
I have no specific suggestion for this operator, see TODO for the full
|
||
list of operators in Roc.
|
||
"###
|
||
);
|
||
|
||
// NOTE: VERY BAD ERROR MESSAGE
|
||
//
|
||
// looks like `x y` are considered argument to the add, even though they are
|
||
// on a lower indentation level
|
||
test_report!(
|
||
double_equals_in_def,
|
||
indoc!(
|
||
r#"
|
||
x = 3
|
||
y =
|
||
x == 5
|
||
Num.add 1 2
|
||
|
||
{ x, y }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This value is not a function, but it was given 3 arguments:
|
||
|
||
6│ x == 5
|
||
^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_open,
|
||
indoc!(
|
||
r#"
|
||
f : [
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TAG UNION TYPE ───────────────────── tmp/tag_union_open/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [
|
||
^
|
||
|
||
I was expecting to see a closing square bracket before this, so try
|
||
adding a ] and see if that helps?
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_end,
|
||
indoc!(
|
||
r#"
|
||
f : [Yes,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TAG UNION TYPE ────────────────────── tmp/tag_union_end/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [Yes,
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing square bracket before this, so try
|
||
adding a ] and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_lowercase_tag_name,
|
||
indoc!(
|
||
r#"
|
||
f : [lowercase]
|
||
"#
|
||
),
|
||
@r###"
|
||
── WEIRD TAG NAME ────────────────── tmp/tag_union_lowercase_tag_name/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [lowercase]
|
||
^
|
||
|
||
I was expecting to see a tag name.
|
||
|
||
Hint: Tag names start with an uppercase letter, like Err or Green.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
tag_union_second_lowercase_tag_name,
|
||
indoc!(
|
||
r#"
|
||
f : [Good, bad]
|
||
"#
|
||
),
|
||
@r###"
|
||
── WEIRD TAG NAME ─────────── tmp/tag_union_second_lowercase_tag_name/Test.roc ─
|
||
|
||
I am partway through parsing a tag union type, but I got stuck here:
|
||
|
||
4│ f : [Good, bad]
|
||
^
|
||
|
||
I was expecting to see a tag name.
|
||
|
||
Hint: Tag names start with an uppercase letter, like Err or Green.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_open,
|
||
indoc!(
|
||
r#"
|
||
f : {
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ────────────────────── tmp/record_type_open/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : {
|
||
^
|
||
|
||
I was expecting to see a closing curly brace before this, so try
|
||
adding a } and see if that helps?
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_open_indent,
|
||
indoc!(
|
||
r#"
|
||
f : {
|
||
foo : I64,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ─────────────── tmp/record_type_open_indent/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : {
|
||
^
|
||
|
||
I was expecting to see a closing curly brace before this, so try
|
||
adding a } and see if that helps?
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_end,
|
||
indoc!(
|
||
r#"
|
||
f : { a: Int,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ─────────────────────── tmp/record_type_end/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : { a: Int,
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing curly brace before this, so try
|
||
adding a } and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
record_type_keyword_field_name,
|
||
indoc!(
|
||
r#"
|
||
f : { if : I64 }
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ──────── tmp/record_type_keyword_field_name/Test.roc ─
|
||
|
||
I just started parsing a record type, but I got stuck on this field
|
||
name:
|
||
|
||
4│ f : { if : I64 }
|
||
^^
|
||
|
||
Looks like you are trying to use `if` as a field name, but that is a
|
||
reserved word. Try using a different name!
|
||
"###
|
||
);
|
||
|
||
// a case where the message cannot be as good as elm's
|
||
test_report!(
|
||
record_type_missing_comma,
|
||
indoc!(
|
||
r#"
|
||
f : { foo bar }
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED RECORD TYPE ───────────── tmp/record_type_missing_comma/Test.roc ─
|
||
|
||
I am partway through parsing a record type, but I got stuck here:
|
||
|
||
4│ f : { foo bar }
|
||
^
|
||
|
||
I was expecting to see a colon, question mark, comma or closing curly
|
||
brace.
|
||
"###
|
||
);
|
||
|
||
// a case where the message cannot be as good as elm's
|
||
test_report!(
|
||
record_type_tab,
|
||
"f : { foo \t }",
|
||
@r###"
|
||
── TAB CHARACTER ──────────────────────────────── tmp/record_type_tab/Test.roc ─
|
||
|
||
I encountered a tab character
|
||
|
||
4│ f : { foo }
|
||
^
|
||
|
||
Tab characters are not allowed.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
comment_with_tab,
|
||
"# comment with a \t\n4",
|
||
@r###"
|
||
── TAB CHARACTER ─────────────────────────────── tmp/comment_with_tab/Test.roc ─
|
||
|
||
I encountered a tab character
|
||
|
||
4│ # comment with a
|
||
^
|
||
|
||
Tab characters are not allowed.
|
||
"###
|
||
);
|
||
|
||
// TODO bad error message
|
||
test_report!(
|
||
type_in_parens_start,
|
||
indoc!(
|
||
r#"
|
||
f : (
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ────────────────── tmp/type_in_parens_start/Test.roc ─
|
||
|
||
I just started parsing a type in parentheses, but I got stuck here:
|
||
|
||
4│ f : (
|
||
^
|
||
|
||
Tag unions look like [Many I64, None], so I was expecting to see a tag
|
||
name next.
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_in_parens_end,
|
||
indoc!(
|
||
r#"
|
||
f : ( I64
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ──────────────────── tmp/type_in_parens_end/Test.roc ─
|
||
|
||
I am partway through parsing a type in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ f : ( I64
|
||
^
|
||
|
||
I was expecting to see a parenthesis before this, so try adding a )
|
||
and see if that helps?
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_apply_double_dot,
|
||
indoc!(
|
||
r#"
|
||
f : Foo..Bar
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo..Bar
|
||
^^^^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
// ── DOUBLE DOT ──────────────────────────────────────────────────────────────────
|
||
//
|
||
// I encountered two dots in a row:
|
||
//
|
||
// 1│ f : Foo..Bar
|
||
// ^
|
||
//
|
||
// Try removing one of them.
|
||
|
||
test_report!(
|
||
type_apply_trailing_dot,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.Bar.
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo.Bar.
|
||
^^^^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
// ── TRAILING DOT ────────────────────────────────────────────────────────────────
|
||
//
|
||
// I encountered a dot with nothing after it:
|
||
//
|
||
// 1│ f : Foo.Bar.
|
||
// ^
|
||
//
|
||
// Dots are used to refer to a type in a qualified way, like
|
||
// Num.I64 or List.List a. Try adding a type name next.
|
||
|
||
test_report!(
|
||
type_apply_stray_dot,
|
||
indoc!(
|
||
r#"
|
||
f : .
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TYPE ───────────────────────── tmp/type_apply_stray_dot/Test.roc ─
|
||
|
||
I just started parsing a type, but I got stuck here:
|
||
|
||
4│ f : .
|
||
^
|
||
|
||
I am expecting a type next, like Bool or List a.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_apply_start_with_number,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.1
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo.1
|
||
^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
// ── WEIRD QUALIFIED NAME ────────────────────────────────────────────────────────
|
||
//
|
||
// I encountered a number at the start of a qualified name segment:
|
||
//
|
||
// 1│ f : Foo.1
|
||
// ^
|
||
//
|
||
// All parts of a qualified type name must start with an uppercase
|
||
// letter, like Num.I64 or List.List a.
|
||
|
||
test_report!(
|
||
type_apply_start_with_lowercase,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.foo
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am confused by this type name:
|
||
|
||
4│ f : Foo.foo
|
||
^^^^^^^
|
||
|
||
Type names start with an uppercase letter, and can optionally be
|
||
qualified by a module name, like Bool or Http.Request.Request.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
def_missing_final_expression,
|
||
indoc!(
|
||
r#"
|
||
f : Foo.foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── MISSING FINAL EXPRESSION ──────── tmp/def_missing_final_expression/Test.roc ─
|
||
|
||
I am partway through parsing a definition, but I got stuck here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ f : Foo.foo
|
||
^
|
||
|
||
This definition is missing a final expression. A nested definition
|
||
must be followed by either another definition, or an expression
|
||
|
||
x = 4
|
||
y = 2
|
||
|
||
x + y
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_inline_alias,
|
||
indoc!(
|
||
r#"
|
||
f : I64 as
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED INLINE ALIAS ──────────────────── tmp/type_inline_alias/Test.roc ─
|
||
|
||
I just started parsing an inline type alias, but I got stuck here:
|
||
|
||
4│ f : I64 as
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_double_comma,
|
||
indoc!(
|
||
r#"
|
||
f : I64,,I64 -> I64
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── DOUBLE COMMA ─────────────────────────────── tmp/type_double_comma/Test.roc ─
|
||
|
||
I just started parsing a function argument type, but I encountered two
|
||
commas in a row:
|
||
|
||
4│ f : I64,,I64 -> I64
|
||
^
|
||
|
||
Try removing one of them.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
type_argument_no_arrow,
|
||
indoc!(
|
||
r#"
|
||
f : I64, I64
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TYPE ─────────────────────── tmp/type_argument_no_arrow/Test.roc ─
|
||
|
||
I am partway through parsing a type, but I got stuck here:
|
||
|
||
4│ f : I64, I64
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
// TODO could do better by pointing out we're parsing a function type
|
||
test_report!(
|
||
type_argument_arrow_then_nothing,
|
||
indoc!(
|
||
r#"
|
||
f : I64, I64 ->
|
||
f = 0
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED TYPE ───────────── tmp/type_argument_arrow_then_nothing/Test.roc ─
|
||
|
||
I just started parsing a type, but I got stuck here:
|
||
|
||
4│ f : I64, I64 ->
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
// TODO could do better by pointing out we're parsing a function type
|
||
test_report!(
|
||
dict_type_formatting,
|
||
indoc!(
|
||
r#"
|
||
app "dict" imports [ Dict ] provides [main] to "./platform"
|
||
|
||
myDict : Dict.Dict Num.I64 Str
|
||
myDict = Dict.insert Dict.empty "foo" 42
|
||
|
||
main = myDict
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `myDict` definition:
|
||
|
||
3│ myDict : Dict.Dict Num.I64 Str
|
||
4│ myDict = Dict.insert Dict.empty "foo" 42
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `insert` call produces:
|
||
|
||
Dict Str (Num a)
|
||
|
||
But the type annotation on `myDict` says it should be:
|
||
|
||
Dict I64 Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
alias_type_diff,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Set.{ Set }] provides [main] to "./platform"
|
||
|
||
HSet a : Set a
|
||
|
||
foo : Str -> HSet {}
|
||
|
||
myDict : HSet Str
|
||
myDict = foo "bar"
|
||
|
||
main = myDict
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `myDict` definition:
|
||
|
||
7│ myDict : HSet Str
|
||
8│ myDict = foo "bar"
|
||
^^^^^^^^^
|
||
|
||
This `foo` call produces:
|
||
|
||
HSet {}
|
||
|
||
But the type annotation on `myDict` says it should be:
|
||
|
||
HSet Str
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
test_report!(
|
||
if_guard_without_condition,
|
||
indoc!(
|
||
r#"
|
||
when Just 4 is
|
||
Just if ->
|
||
4
|
||
|
||
_ ->
|
||
2
|
||
"#
|
||
),
|
||
@r###"
|
||
── IF GUARD NO CONDITION ───────────── tmp/if_guard_without_condition/Test.roc ─
|
||
|
||
I just started parsing an if guard, but there is no guard condition:
|
||
|
||
4│ when Just 4 is
|
||
5│ Just if ->
|
||
^
|
||
|
||
Try adding an expression before the arrow!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
empty_or_pattern,
|
||
indoc!(
|
||
r#"
|
||
when Just 4 is
|
||
Just 4 | ->
|
||
4
|
||
|
||
_ ->
|
||
2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PATTERN ────────────────────────── tmp/empty_or_pattern/Test.roc ─
|
||
|
||
I just started parsing a pattern, but I got stuck here:
|
||
|
||
5│ Just 4 | ->
|
||
^
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
// TODO check if "what_is_next" is a keyword
|
||
test_report!(
|
||
pattern_binds_keyword,
|
||
indoc!(
|
||
r#"
|
||
when Just 4 is
|
||
Just when ->
|
||
4
|
||
|
||
_ ->
|
||
2
|
||
"#
|
||
),
|
||
@r###"
|
||
── MISSING EXPRESSION ───────────────────── tmp/pattern_binds_keyword/Test.roc ─
|
||
|
||
I am partway through parsing a `when` expression, but I got stuck here:
|
||
|
||
5│ Just when ->
|
||
^
|
||
|
||
I was expecting to see an expression like 42 or "hello".
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
test_report!(
|
||
when_missing_arrow,
|
||
indoc!(
|
||
r#"
|
||
when 5 is
|
||
1 -> 2
|
||
_
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED WHEN ─────────────────────────── tmp/when_missing_arrow/Test.roc ─
|
||
|
||
I was partway through parsing a `when` expression, but I got stuck here:
|
||
|
||
6│ _
|
||
^
|
||
|
||
I was expecting to see a pattern next
|
||
|
||
Note: Here is an example of a valid `when` expression for reference.
|
||
|
||
when List.first plants is
|
||
Ok n ->
|
||
n
|
||
|
||
Err _ ->
|
||
200
|
||
|
||
Notice the indentation. All patterns are aligned, and each branch is
|
||
indented a bit more than the corresponding pattern. That is important!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
lambda_double_comma,
|
||
indoc!(
|
||
r#"
|
||
\a,,b -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ARGUMENT LIST ───────────────── tmp/lambda_double_comma/Test.roc ─
|
||
|
||
I am partway through parsing a function argument list, but I got stuck
|
||
at this comma:
|
||
|
||
4│ \a,,b -> 1
|
||
^
|
||
|
||
I was expecting an argument pattern before this, so try adding an
|
||
argument before the comma and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
lambda_leading_comma,
|
||
indoc!(
|
||
r#"
|
||
\,b -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ARGUMENT LIST ──────────────── tmp/lambda_leading_comma/Test.roc ─
|
||
|
||
I am partway through parsing a function argument list, but I got stuck
|
||
at this comma:
|
||
|
||
4│ \,b -> 1
|
||
^
|
||
|
||
I was expecting an argument pattern before this, so try adding an
|
||
argument before the comma and see if that helps?
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
// TODO this formerly gave
|
||
//
|
||
// ── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
||
//
|
||
// I was partway through parsing a `when` expression, but I got stuck here:
|
||
//
|
||
// 3│ _ -> 2
|
||
// ^
|
||
//
|
||
// I suspect this is a pattern that is not indented enough? (by 2 spaces)
|
||
//
|
||
// but that requires parsing the next pattern blindly, irrespective of indentation. Can
|
||
// we find an efficient solution that doesn't require parsing an extra pattern for
|
||
// every `when`, i.e. we want a good error message for the test case above, but for
|
||
// a valid `when`, we don't want to do extra work, e.g. here
|
||
//
|
||
// x
|
||
// when x is
|
||
// n -> n
|
||
//
|
||
// 4
|
||
//
|
||
// We don't want to parse the `4` and say it's an outdented pattern!
|
||
test_report!(
|
||
when_outdented_branch,
|
||
indoc!(
|
||
r#"
|
||
when 4 is
|
||
5 -> 2
|
||
2 -> 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT END OF FILE ──────────────────────── tmp/when_outdented_branch/Test.roc ─
|
||
|
||
I expected to reach the end of the file, but got stuck here:
|
||
|
||
6│ 2 -> 2
|
||
^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_over_indented_underscore,
|
||
indoc!(
|
||
r#"
|
||
when 4 is
|
||
5 -> 2
|
||
_ -> 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNEXPECTED ARROW ─────────────── tmp/when_over_indented_underscore/Test.roc ─
|
||
|
||
I am parsing a `when` expression right now, but this arrow is confusing
|
||
me:
|
||
|
||
6│ _ -> 2
|
||
^^
|
||
|
||
It makes sense to see arrows around here, so I suspect it is something
|
||
earlier.Maybe this pattern is indented a bit farther from the previous
|
||
patterns?
|
||
|
||
Note: Here is an example of a valid `when` expression for reference.
|
||
|
||
when List.first plants is
|
||
Ok n ->
|
||
n
|
||
|
||
Err _ ->
|
||
200
|
||
|
||
Notice the indentation. All patterns are aligned, and each branch is
|
||
indented a bit more than the corresponding pattern. That is important!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
when_over_indented_int,
|
||
indoc!(
|
||
r#"
|
||
when 4 is
|
||
5 -> Num.neg
|
||
2 -> 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNEXPECTED ARROW ────────────────────── tmp/when_over_indented_int/Test.roc ─
|
||
|
||
I am parsing a `when` expression right now, but this arrow is confusing
|
||
me:
|
||
|
||
6│ 2 -> 2
|
||
^^
|
||
|
||
It makes sense to see arrows around here, so I suspect it is something
|
||
earlier.Maybe this pattern is indented a bit farther from the previous
|
||
patterns?
|
||
|
||
Note: Here is an example of a valid `when` expression for reference.
|
||
|
||
when List.first plants is
|
||
Ok n ->
|
||
n
|
||
|
||
Err _ ->
|
||
200
|
||
|
||
Notice the indentation. All patterns are aligned, and each branch is
|
||
indented a bit more than the corresponding pattern. That is important!
|
||
"###
|
||
);
|
||
|
||
// TODO I think we can do better here
|
||
test_report!(
|
||
if_outdented_then,
|
||
indoc!(
|
||
r#"
|
||
x =
|
||
if 5 == 5
|
||
then 2 else 3
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED IF ────────────────────────────── tmp/if_outdented_then/Test.roc ─
|
||
|
||
I was partway through parsing an `if` expression, but I got stuck here:
|
||
|
||
5│ if 5 == 5
|
||
^
|
||
|
||
I was expecting to see the `then` keyword next.
|
||
"###
|
||
);
|
||
|
||
// this should get better with time
|
||
test_report!(
|
||
if_missing_else,
|
||
indoc!(
|
||
r#"
|
||
if 5 == 5 then 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED IF ──────────────────────────────── tmp/if_missing_else/Test.roc ─
|
||
|
||
I was partway through parsing an `if` expression, but I got stuck here:
|
||
|
||
4│ if 5 == 5 then 2
|
||
^
|
||
|
||
I was expecting to see the `else` keyword next.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_double_comma,
|
||
indoc!(
|
||
r#"
|
||
[1, 2, , 3]
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED LIST ──────────────────────────── tmp/list_double_comma/Test.roc ─
|
||
|
||
I am partway through started parsing a list, but I got stuck here:
|
||
|
||
4│ [1, 2, , 3]
|
||
^
|
||
|
||
I was expecting to see a list entry before this comma, so try adding a
|
||
list entry and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_without_end,
|
||
indoc!(
|
||
r#"
|
||
[1, 2,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED LIST ───────────────────────────── tmp/list_without_end/Test.roc ─
|
||
|
||
I am partway through started parsing a list, but I got stuck here:
|
||
|
||
4│ [1, 2,
|
||
5│
|
||
6│
|
||
^
|
||
|
||
I was expecting to see a closing square bracket before this, so try
|
||
adding a ] and see if that helps?
|
||
|
||
Note: When I get stuck like this, it usually means that there is a
|
||
missing parenthesis or bracket somewhere earlier. It could also be a
|
||
stray keyword or operator.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
number_double_dot,
|
||
indoc!(
|
||
r#"
|
||
1.1.1
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This float literal contains an invalid digit:
|
||
|
||
4│ 1.1.1
|
||
^^^^^
|
||
|
||
Floating point literals can only contain the digits 0-9, or use
|
||
scientific notation 10e4, or have a float suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unicode_not_hex,
|
||
r#""abc\u(zzzz)def""#,
|
||
@r###"
|
||
── WEIRD CODE POINT ───────────────────────────── tmp/unicode_not_hex/Test.roc ─
|
||
|
||
I am partway through parsing a unicode code point, but I got stuck
|
||
here:
|
||
|
||
4│ "abc\u(zzzz)def"
|
||
^
|
||
|
||
I was expecting a hexadecimal number, like \u(1100) or \u(00FF).
|
||
|
||
Learn more about working with unicode in roc at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
interpolate_not_identifier,
|
||
r#""abc\(32)def""#,
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This string interpolation is invalid:
|
||
|
||
4│ "abc\(32)def"
|
||
^^
|
||
|
||
I was expecting an identifier, like \u(message) or
|
||
\u(LoremIpsum.text).
|
||
|
||
Learn more about string interpolation at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unicode_too_large,
|
||
r#""abc\u(110000)def""#,
|
||
@r###"
|
||
── INVALID UNICODE ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This unicode code point is invalid:
|
||
|
||
4│ "abc\u(110000)def"
|
||
^^^^^^
|
||
|
||
Learn more about working with unicode in roc at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
weird_escape,
|
||
r#""abc\qdef""#,
|
||
@r###"
|
||
── WEIRD ESCAPE ──────────────────────────────────── tmp/weird_escape/Test.roc ─
|
||
|
||
I was partway through parsing a string literal, but I got stuck here:
|
||
|
||
4│ "abc\qdef"
|
||
^^
|
||
|
||
This is not an escape sequence I recognize. After a backslash, I am
|
||
looking for one of these:
|
||
|
||
- A newline: \n
|
||
- A caret return: \r
|
||
- A tab: \t
|
||
- An escaped quote: \"
|
||
- An escaped backslash: \\
|
||
- A unicode code point: \u(00FF)
|
||
- An interpolated string: \(myVariable)
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
single_no_end,
|
||
r#""there is no end"#,
|
||
@r###"
|
||
── ENDLESS STRING ───────────────────────────────── tmp/single_no_end/Test.roc ─
|
||
|
||
I cannot find the end of this string:
|
||
|
||
4│ "there is no end
|
||
^
|
||
|
||
You could change it to something like "to be or not to be" or even
|
||
just "".
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multi_no_end,
|
||
r#""""there is no end"#,
|
||
@r###"
|
||
── ENDLESS STRING ────────────────────────────────── tmp/multi_no_end/Test.roc ─
|
||
|
||
I cannot find the end of this block string:
|
||
|
||
4│ """there is no end
|
||
^
|
||
|
||
You could change it to something like """to be or not to be""" or even
|
||
just """""".
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multi_insufficient_indent,
|
||
" \"\"\"\n testing\n \"\"\"", // 4 space indent on the start, 2 space on the `testing` line
|
||
@r###"
|
||
── INSUFFICIENT INDENT IN MULTI-LINE STRING ─ ..._insufficient_indent/Test.roc ─
|
||
|
||
This multiline string is not sufficiently indented:
|
||
|
||
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.
|
||
"###
|
||
);
|
||
|
||
// https://github.com/rtfeldman/roc/issues/1714
|
||
test_report!(
|
||
interpolate_concat_is_transparent_1714,
|
||
indoc!(
|
||
r#"
|
||
greeting = "Privet"
|
||
|
||
if True then 1 else "\(greeting), World!"
|
||
"#,
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` has an `else` branch with a different type from its `then` branch:
|
||
|
||
6│ if True then 1 else "\(greeting), World!"
|
||
^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The `else` branch is a string of type:
|
||
|
||
Str
|
||
|
||
but the `then` branch has the type:
|
||
|
||
Num a
|
||
|
||
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 True then "abc" else 1 {} 2"#, $op),
|
||
|golden| assert_eq!(golden, format!(
|
||
r#"── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `if` has an `else` branch with a different type from its `then` branch:
|
||
|
||
4│ if True then "abc" else 1 {} 2
|
||
^^{}^^
|
||
|
||
This comparison produces:
|
||
|
||
Bool
|
||
|
||
but the `then` branch has the type:
|
||
|
||
Str
|
||
|
||
All branches in an `if` must have the same type!
|
||
"#,
|
||
$op, "^".repeat($op.len())
|
||
))
|
||
);
|
||
)*
|
||
}
|
||
}
|
||
|
||
comparison_binop_transparency_tests! {
|
||
"<", lt_binop_is_transparent,
|
||
">", gt_binop_is_transparent,
|
||
"==", eq_binop_is_transparent,
|
||
"!=", neq_binop_is_transparent,
|
||
"<=", leq_binop_is_transparent,
|
||
">=", geq_binop_is_transparent,
|
||
}
|
||
|
||
test_report!(
|
||
keyword_record_field_access,
|
||
indoc!(
|
||
r#"
|
||
foo = {}
|
||
|
||
foo.if
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `foo` record doesn’t have a `if` field:
|
||
|
||
6│ foo.if
|
||
^^^^^^
|
||
|
||
In fact, `foo` is a record with no fields at all!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
keyword_qualified_import,
|
||
indoc!(
|
||
r#"
|
||
Num.if
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT EXPOSED ─────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The Num module does not expose `if`:
|
||
|
||
4│ Num.if
|
||
^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Num.sin
|
||
Num.div
|
||
Num.abs
|
||
Num.neg
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
stray_dot_expr,
|
||
indoc!(
|
||
r#"
|
||
Num.add . 23
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I trying to parse a record field access here:
|
||
|
||
4│ Num.add . 23
|
||
^
|
||
|
||
So I expect to see a lowercase letter next, like .name or .height.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_ref_field_access,
|
||
indoc!(
|
||
r#"
|
||
@UUID.bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am very confused by this field access:
|
||
|
||
4│ @UUID.bar
|
||
^^^^
|
||
|
||
It looks like a record field access on an opaque reference.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
weird_accessor,
|
||
indoc!(
|
||
r#"
|
||
.foo.bar
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am very confused by this field access
|
||
|
||
4│ .foo.bar
|
||
^^^^^^^^
|
||
|
||
It looks like a field access on an accessor. I parse.client.name as
|
||
(.client).name. Maybe use an anonymous function like
|
||
(\r -> r.client.name) instead?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
part_starts_with_number,
|
||
indoc!(
|
||
r#"
|
||
foo.100
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I trying to parse a record field access here:
|
||
|
||
4│ foo.100
|
||
^
|
||
|
||
So I expect to see a lowercase letter next, like .name or .height.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
closure_underscore_ident,
|
||
indoc!(
|
||
r#"
|
||
\the_answer -> 100
|
||
"#
|
||
),
|
||
@r###"
|
||
── NAMING PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse an identifier here:
|
||
|
||
4│ \the_answer -> 100
|
||
^
|
||
|
||
Underscores are not allowed in identifiers. Use camelCase instead!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
double_binop,
|
||
indoc!(
|
||
r#"
|
||
key >= 97 && <= 122
|
||
"#
|
||
),
|
||
@r#"
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
case_of,
|
||
indoc!(
|
||
r#"
|
||
case 1 of
|
||
1 -> True
|
||
_ -> False
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ───────────────────────────────────── tmp/case_of/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ case 1 of
|
||
5│ 1 -> True
|
||
^^
|
||
|
||
The arrow -> is only used to define cases in a `when`.
|
||
|
||
when color is
|
||
Red -> "stop!"
|
||
Green -> "go!"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
argument_without_space,
|
||
indoc!(
|
||
r#"
|
||
["foo", bar("")]
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `bar` in this scope.
|
||
|
||
4│ ["foo", bar("")]
|
||
^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Nat
|
||
Str
|
||
Err
|
||
U8
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_operator,
|
||
indoc!(
|
||
r#"
|
||
main =
|
||
5 ** 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ──────────────────────────── tmp/invalid_operator/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main =
|
||
5│ 5 ** 3
|
||
^^
|
||
|
||
I have no specific suggestion for this operator, see TODO for the full
|
||
list of operators in Roc.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
double_plus,
|
||
indoc!(
|
||
r#"
|
||
main =
|
||
[] ++ []
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ───────────────────────────────── tmp/double_plus/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main =
|
||
5│ [] ++ []
|
||
^^
|
||
|
||
To concatenate two lists or strings, try using List.concat or
|
||
Str.concat instead.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inline_hastype,
|
||
indoc!(
|
||
r#"
|
||
main =
|
||
(\x -> x) : I64
|
||
|
||
3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ────────────────────────────── tmp/inline_hastype/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main =
|
||
5│ (\x -> x) : I64
|
||
^
|
||
|
||
The has-type operator : can only occur in a definition's type
|
||
signature, like
|
||
|
||
increment : I64 -> I64
|
||
increment = \x -> x + 1
|
||
"###
|
||
);
|
||
|
||
// this is still bad, but changing the order and progress of other parsers should improve it
|
||
// down the line
|
||
test_report!(
|
||
wild_case_arrow,
|
||
indoc!(
|
||
r#"
|
||
main = 5 -> 3
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNKNOWN OPERATOR ───────────────────────────── tmp/wild_case_arrow/Test.roc ─
|
||
|
||
This looks like an operator, but it's not one I recognize!
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ main = 5 -> 3
|
||
^^
|
||
|
||
The arrow -> is only used to define cases in a `when`.
|
||
|
||
when color is
|
||
Red -> "stop!"
|
||
Green -> "go!"
|
||
"###
|
||
);
|
||
|
||
#[test]
|
||
fn provides_to_identifier() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
app "test-base64"
|
||
packages { pf: "platform/main.roc" }
|
||
imports [pf.Task, Base64]
|
||
provides [main, @Foo] to pf
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD PROVIDES ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a provides list, but I got stuck here:
|
||
|
||
3│ imports [pf.Task, Base64]
|
||
4│ provides [main, @Foo] to pf
|
||
^
|
||
|
||
I was expecting a type name, value name or function name next, like
|
||
|
||
provides [Animal, default, tame]
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn platform_requires_rigids() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
platform "folkertdev/foo"
|
||
requires { main : Effect {} }
|
||
exposes []
|
||
packages {}
|
||
imports [Task]
|
||
provides [mainForHost]
|
||
effects fx.Effect
|
||
{
|
||
putChar : I64 -> Effect {},
|
||
putLine : Str -> Effect {},
|
||
getLine : Effect Str
|
||
}
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── BAD REQUIRES ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but I got stuck here:
|
||
|
||
1│ platform "folkertdev/foo"
|
||
2│ requires { main : Effect {} }
|
||
^
|
||
|
||
I am expecting a list of type names like `{}` or `{ Model }` next. A full
|
||
`requires` definition looks like
|
||
|
||
requires { Model, Msg } {main : Effect {}}
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn missing_imports() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
interface Foobar
|
||
exposes [main, Foo]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD IMPORTS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but I got stuck here:
|
||
|
||
2│ exposes [main, Foo]
|
||
^
|
||
|
||
I am expecting the `imports` keyword next, like
|
||
|
||
imports [Animal, default, tame]
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn exposes_identifier() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
interface Foobar
|
||
exposes [main, @Foo]
|
||
imports [pf.Task, Base64]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD EXPOSES ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing an `exposes` list, but I got stuck here:
|
||
|
||
1│ interface Foobar
|
||
2│ exposes [main, @Foo]
|
||
^
|
||
|
||
I was expecting a type name, value name or function name next, like
|
||
|
||
exposes [Animal, default, tame]
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn invalid_module_name() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
interface foobar
|
||
exposes [main, @Foo]
|
||
imports [pf.Task, Base64]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD MODULE NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but got stuck here:
|
||
|
||
1│ interface foobar
|
||
^
|
||
|
||
I am expecting a module name next, like BigNum or Main. Module names
|
||
must start with an uppercase letter.
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn invalid_app_name() {
|
||
report_header_problem_as(
|
||
indoc!(
|
||
r#"
|
||
app foobar
|
||
exposes [main, @Foo]
|
||
imports [pf.Task, Base64]
|
||
"#
|
||
),
|
||
indoc!(
|
||
r#"
|
||
── WEIRD APP NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am partway through parsing a header, but got stuck here:
|
||
|
||
1│ app foobar
|
||
^
|
||
|
||
I am expecting an application name next, like app "main" or
|
||
app "editor". App names are surrounded by quotation marks.
|
||
"#
|
||
),
|
||
)
|
||
}
|
||
|
||
test_report!(
|
||
apply_unary_negative,
|
||
indoc!(
|
||
r#"
|
||
foo = 3
|
||
|
||
-foo 1 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This value is not a function, but it was given 2 arguments:
|
||
|
||
6│ -foo 1 2
|
||
^^^^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
apply_unary_not,
|
||
indoc!(
|
||
r#"
|
||
foo = True
|
||
|
||
!foo 1 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── TOO MANY ARGS ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This value is not a function, but it was given 2 arguments:
|
||
|
||
6│ !foo 1 2
|
||
^^^^
|
||
|
||
Are there any missing commas? Or missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
applied_tag_function,
|
||
indoc!(
|
||
r#"
|
||
x : List [Foo Str]
|
||
x = List.map [1, 2] Foo
|
||
|
||
x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `x` definition:
|
||
|
||
4│ x : List [Foo Str]
|
||
5│ x = List.map [1, 2] Foo
|
||
^^^^^^^^^^^^^^^^^^^
|
||
|
||
This `map` call produces:
|
||
|
||
List [Foo Num a]
|
||
|
||
But the type annotation on `x` says it should be:
|
||
|
||
List [Foo Str]
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_open,
|
||
indoc!(
|
||
r#"
|
||
\( a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ──────────────── tmp/pattern_in_parens_open/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ \( a
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_end_comma,
|
||
indoc!(
|
||
r#"
|
||
\( a,
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ─────────── tmp/pattern_in_parens_end_comma/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ \( a,
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_end,
|
||
indoc!(
|
||
r#"
|
||
\( a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ───────────────── tmp/pattern_in_parens_end/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ \( a
|
||
^
|
||
|
||
I was expecting to see a closing parenthesis before this, so try
|
||
adding a ) and see if that helps?
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_indent_end,
|
||
indoc!(
|
||
r#"
|
||
x = \( a
|
||
)
|
||
"#
|
||
),
|
||
@r###"
|
||
── NEED MORE INDENTATION ─────────── tmp/pattern_in_parens_indent_end/Test.roc ─
|
||
|
||
I am partway through parsing a pattern in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ x = \( a
|
||
5│ )
|
||
^
|
||
|
||
I need this parenthesis to be indented more. Try adding more spaces
|
||
before it!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
pattern_in_parens_indent_open,
|
||
indoc!(
|
||
r#"
|
||
\(
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED PARENTHESES ───────── tmp/pattern_in_parens_indent_open/Test.roc ─
|
||
|
||
I just started parsing a pattern in parentheses, but I got stuck here:
|
||
|
||
4│ \(
|
||
^
|
||
|
||
Record pattern look like { name, age: currentAge }, so I was expecting
|
||
to see a field name next.
|
||
|
||
Note: I may be confused by indentation
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
outdented_in_parens,
|
||
indoc!(
|
||
r#"
|
||
Box : (
|
||
Str
|
||
)
|
||
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── NEED MORE INDENTATION ──────────────────── tmp/outdented_in_parens/Test.roc ─
|
||
|
||
I am partway through parsing a type in parentheses, but I got stuck
|
||
here:
|
||
|
||
4│ Box : (
|
||
5│ Str
|
||
6│ )
|
||
^
|
||
|
||
I need this parenthesis to be indented more. Try adding more spaces
|
||
before it!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
backpassing_type_error,
|
||
indoc!(
|
||
r#"
|
||
x <- List.map ["a", "b"]
|
||
|
||
x + 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `map` has an unexpected type:
|
||
|
||
4│> x <- List.map ["a", "b"]
|
||
5│>
|
||
6│> x + 1
|
||
|
||
The argument is an anonymous function of type:
|
||
|
||
Num a -> Num a
|
||
|
||
But `map` needs its 2nd argument to be:
|
||
|
||
Str -> Num a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
expect_expr_type_error,
|
||
indoc!(
|
||
r#"
|
||
expect "foobar"
|
||
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `expect` condition needs to be a Bool:
|
||
|
||
4│ expect "foobar"
|
||
^^^^^^^^
|
||
|
||
Right now it’s a string of type:
|
||
|
||
Str
|
||
|
||
But I need every `expect` condition to evaluate to a Bool—either `True`
|
||
or `False`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
num_too_general_wildcard,
|
||
indoc!(
|
||
r#"
|
||
mult : Num.Num *, Num.F64 -> Num.F64
|
||
mult = \a, b -> a * b
|
||
|
||
mult 0 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `mul` has an unexpected type:
|
||
|
||
5│ mult = \a, b -> a * b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64
|
||
|
||
But `mul` needs its 2nd argument to be:
|
||
|
||
Num *
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `mult` definition:
|
||
|
||
4│ mult : Num.Num *, Num.F64 -> Num.F64
|
||
5│ mult = \a, b -> a * b
|
||
^^^^^
|
||
|
||
This `mul` call produces:
|
||
|
||
Num *
|
||
|
||
But the type annotation on `mult` says it should be:
|
||
|
||
F64
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
num_too_general_named,
|
||
indoc!(
|
||
r#"
|
||
mult : Num.Num a, Num.F64 -> Num.F64
|
||
mult = \a, b -> a * b
|
||
|
||
mult 0 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `mul` has an unexpected type:
|
||
|
||
5│ mult = \a, b -> a * b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64
|
||
|
||
But `mul` needs its 2nd argument to be:
|
||
|
||
Num a
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `mult` definition:
|
||
|
||
4│ mult : Num.Num a, Num.F64 -> Num.F64
|
||
5│ mult = \a, b -> a * b
|
||
^^^^^
|
||
|
||
This `mul` call produces:
|
||
|
||
Num a
|
||
|
||
But the type annotation on `mult` says it should be:
|
||
|
||
F64
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_not_enough_in_alias,
|
||
indoc!(
|
||
r#"
|
||
Result a b : [Ok a, Err b]
|
||
|
||
canIGo : _ -> Result _
|
||
canIGo = \color ->
|
||
when color is
|
||
"green" -> Ok "go!"
|
||
"yellow" -> Err (SlowIt "whoa, let's slow down!")
|
||
"red" -> Err (StopIt "absolutely not")
|
||
_ -> Err (UnknownColor "this is a weird stoplight")
|
||
canIGo
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Result` name is first defined here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
|
||
|
||
But then it's defined a second time here:
|
||
|
||
4│ Result a b : [Ok a, Err b]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Since these aliases have the same name, it's easy to use the wrong one
|
||
on accident. Give one of them a new name.
|
||
|
||
── TOO FEW TYPE ARGUMENTS ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Result` alias expects 2 type arguments, but it got 1 instead:
|
||
|
||
6│ canIGo : _ -> Result _
|
||
^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_too_many_in_alias,
|
||
indoc!(
|
||
r#"
|
||
Result a b : [Ok a, Err b]
|
||
|
||
canIGo : _ -> Result _ _ _
|
||
canIGo = \color ->
|
||
when color is
|
||
"green" -> Ok "go!"
|
||
"yellow" -> Err (SlowIt "whoa, let's slow down!")
|
||
"red" -> Err (StopIt "absolutely not")
|
||
_ -> Err (UnknownColor "this is a weird stoplight")
|
||
canIGo
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Result` name is first defined here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
|
||
|
||
But then it's defined a second time here:
|
||
|
||
4│ Result a b : [Ok a, Err b]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Since these aliases have the same name, it's easy to use the wrong one
|
||
on accident. Give one of them a new name.
|
||
|
||
── TOO MANY TYPE ARGUMENTS ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Result` alias expects 2 type arguments, but it got 3 instead:
|
||
|
||
6│ canIGo : _ -> Result _ _ _
|
||
^^^^^^^^^^^^
|
||
|
||
Are there missing parentheses?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_conflict_in_rigid_links,
|
||
indoc!(
|
||
r#"
|
||
f : a -> (_ -> b)
|
||
f = \x -> \y -> if x == y then x else y
|
||
f
|
||
"#
|
||
),
|
||
// TODO: We should tell the user that we inferred `_` as `a`
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : a -> (_ -> b)
|
||
5│ f = \x -> \y -> if x == y then x else y
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The body is an anonymous function of type:
|
||
|
||
a -> a
|
||
|
||
But the type annotation on `f` says it should be:
|
||
|
||
a -> b
|
||
|
||
Tip: Your type annotation uses `a` and `b` as separate type variables.
|
||
Your code seems to be saying they are the same though. Maybe they
|
||
should be the same in your type annotation? Maybe your code uses them
|
||
in a weird way?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_wildcards_are_related,
|
||
indoc!(
|
||
r#"
|
||
f : * -> *
|
||
f = \x -> x
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : * -> *
|
||
5│ f = \x -> x
|
||
^
|
||
|
||
The type annotation on `f` says this `x` value should have the type:
|
||
|
||
*
|
||
|
||
However, the type of this `x` value is connected to another type in a
|
||
way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `f` should have a named type variable in
|
||
place of the `*`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_nested_wildcards_are_related,
|
||
indoc!(
|
||
r#"
|
||
f : a, b, * -> {x: a, y: b, z: *}
|
||
f = \x, y, z -> {x, y, z}
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `f` definition:
|
||
|
||
4│ f : a, b, * -> {x: a, y: b, z: *}
|
||
5│ f = \x, y, z -> {x, y, z}
|
||
^^^^^^^^^
|
||
|
||
The type annotation on `f` says the body is a record should have the
|
||
type:
|
||
|
||
{ x : a, y : b, z : * }
|
||
|
||
However, the type of the body is a record is connected to another type
|
||
in a way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `f` should have a named type variable in
|
||
place of the `*`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_wildcards_are_related_in_nested_defs,
|
||
indoc!(
|
||
r#"
|
||
f : a, b, * -> *
|
||
f = \_, _, x2 ->
|
||
inner : * -> *
|
||
inner = \y -> y
|
||
inner x2
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `inner` definition:
|
||
|
||
6│ inner : * -> *
|
||
7│ inner = \y -> y
|
||
^
|
||
|
||
The type annotation on `inner` says this `y` value should have the type:
|
||
|
||
*
|
||
|
||
However, the type of this `y` value is connected to another type in a
|
||
way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `inner` should have a named type variable
|
||
in place of the `*`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_inline_alias_not_an_alias,
|
||
indoc!(
|
||
r#"
|
||
f : List elem -> [Nil, Cons elem a] as a
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT AN INLINE ALIAS ────────── tmp/error_inline_alias_not_an_alias/Test.roc ─
|
||
|
||
The inline type after this `as` is not a type alias:
|
||
|
||
4│ f : List elem -> [Nil, Cons elem a] as a
|
||
^
|
||
|
||
Inline alias types must start with an uppercase identifier and be
|
||
followed by zero or more type arguments, like Point or List a.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_inline_alias_qualified,
|
||
indoc!(
|
||
r#"
|
||
f : List elem -> [Nil, Cons elem a] as Module.LinkedList a
|
||
"#
|
||
),
|
||
@r###"
|
||
── QUALIFIED ALIAS NAME ──────────── tmp/error_inline_alias_qualified/Test.roc ─
|
||
|
||
This type alias has a qualified name:
|
||
|
||
4│ f : List elem -> [Nil, Cons elem a] as Module.LinkedList a
|
||
^
|
||
|
||
An alias introduces a new name to the current scope, so it must be
|
||
unqualified.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
error_inline_alias_argument_uppercase,
|
||
indoc!(
|
||
r#"
|
||
f : List elem -> [Nil, Cons elem a] as LinkedList U
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE ARGUMENT NOT LOWERCASE ─ ...r_inline_alias_argument_uppercase/Test.roc ─
|
||
|
||
This alias type argument is not lowercase:
|
||
|
||
4│ f : List elem -> [Nil, Cons elem a] as LinkedList U
|
||
^
|
||
|
||
All type arguments must be lowercase.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
mismatched_single_tag_arg,
|
||
indoc!(
|
||
r#"
|
||
isEmpty =
|
||
\email ->
|
||
Email str = email
|
||
Str.isEmpty str
|
||
|
||
isEmpty (Name "boo")
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `isEmpty` has an unexpected type:
|
||
|
||
9│ isEmpty (Name "boo")
|
||
^^^^^^^^^^
|
||
|
||
This `Name` tag application has the type:
|
||
|
||
[Name Str]a
|
||
|
||
But `isEmpty` needs its 1st argument to be:
|
||
|
||
[Email Str]
|
||
|
||
Tip: Seems like a tag typo. Maybe `Name` should be `Email`?
|
||
|
||
Tip: Can more type annotations be added? Type annotations always help
|
||
me give more specific messages, and I think they could help a lot in
|
||
this case
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2326,
|
||
indoc!(
|
||
r#"
|
||
C a b : a -> D a b
|
||
D a b : { a, b }
|
||
|
||
f : C a Num.Nat -> D a Num.Nat
|
||
f = \c -> c 6
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `c` has an unexpected type:
|
||
|
||
8│ f = \c -> c 6
|
||
^
|
||
|
||
The argument is a number of type:
|
||
|
||
Num a
|
||
|
||
But `c` needs its 1st argument to be:
|
||
|
||
a
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `Num` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2380_annotations_only,
|
||
indoc!(
|
||
r#"
|
||
F : F
|
||
a : F
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `F` alias is self-recursive in an invalid way:
|
||
|
||
4│ F : F
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2380_typed_body,
|
||
indoc!(
|
||
r#"
|
||
F : F
|
||
a : F
|
||
a = 1
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `F` alias is self-recursive in an invalid way:
|
||
|
||
4│ F : F
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2380_alias_with_vars,
|
||
indoc!(
|
||
r#"
|
||
F a b : F a b
|
||
a : F Str Str
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `F` alias is self-recursive in an invalid way:
|
||
|
||
4│ F a b : F a b
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2167_record_field_optional_and_required_mismatch,
|
||
indoc!(
|
||
r#"
|
||
Job : [Job { inputs : List Str }]
|
||
job : { inputs ? List Str } -> Job
|
||
job = \{ inputs } ->
|
||
Job { inputs }
|
||
|
||
job { inputs: ["build", "test"] }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `job` is weird:
|
||
|
||
6│ job = \{ inputs } ->
|
||
^^^^^^^^^^
|
||
|
||
The argument is a pattern that matches record values of type:
|
||
|
||
{ inputs : List Str }
|
||
|
||
But the annotation on `job` says the 1st argument should be:
|
||
|
||
{ inputs ? List Str }
|
||
|
||
Tip: To extract the `.inputs` field it must be non-optional, but the
|
||
type says this field is optional. Learn more about optional fields at
|
||
TODO.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unify_recursive_with_nonrecursive,
|
||
indoc!(
|
||
r#"
|
||
Job : [Job { inputs : List Job }]
|
||
|
||
job : { inputs : List Str } -> Job
|
||
job = \{ inputs } ->
|
||
Job { inputs }
|
||
|
||
job { inputs: ["build", "test"] }
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `job` definition:
|
||
|
||
6│ job : { inputs : List Str } -> Job
|
||
7│ job = \{ inputs } ->
|
||
8│ Job { inputs }
|
||
^^^^^^^^^^^^^^
|
||
|
||
This `Job` tag application has the type:
|
||
|
||
[Job { inputs : List Str }]
|
||
|
||
But the type annotation on `job` says it should be:
|
||
|
||
[Job { inputs : List a }] as a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
nested_datatype,
|
||
indoc!(
|
||
r#"
|
||
Nested a : [Chain a (Nested (List a)), Term]
|
||
|
||
s : Nested Str
|
||
|
||
s
|
||
"#
|
||
),
|
||
@r###"
|
||
── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Nested` is a nested datatype. Here is one recursive usage of it:
|
||
|
||
4│ Nested a : [Chain a (Nested (List a)), Term]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
But recursive usages of `Nested` must match its definition:
|
||
|
||
4│ Nested a : [Chain a (Nested (List a)), Term]
|
||
^^^^^^^^
|
||
|
||
Nested datatypes are not supported in Roc.
|
||
|
||
Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
nested_datatype_inline,
|
||
indoc!(
|
||
r#"
|
||
f : {} -> [Chain a (Nested (List a)), Term] as Nested a
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── NESTED DATATYPE ─────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Nested` is a nested datatype. Here is one recursive usage of it:
|
||
|
||
4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a
|
||
^^^^^^^^^^^^^^^
|
||
|
||
But recursive usages of `Nested` must match its definition:
|
||
|
||
4│ f : {} -> [Chain a (Nested (List a)), Term] as Nested a
|
||
^^^^^^^^
|
||
|
||
Nested datatypes are not supported in Roc.
|
||
|
||
Hint: Consider rewriting the definition of `Nested` to use the recursive type with the same arguments.
|
||
"###
|
||
);
|
||
|
||
macro_rules! mismatched_suffix_tests {
|
||
($($number:expr, $suffix:expr, $name:ident)*) => {$(
|
||
test_report!(
|
||
$name,
|
||
&{
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_type = if $suffix == "u8" { "I8" } else { "U8" };
|
||
|
||
format!(indoc!(
|
||
r#"
|
||
use : Num.{} -> Num.U8
|
||
use {}{}
|
||
"#
|
||
), bad_type, number, $suffix)
|
||
},
|
||
|golden| {
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_type = if $suffix == "u8" { "I8" } else { "U8" };
|
||
let carets = "^".repeat(number.len() + $suffix.len());
|
||
let kind = match $suffix {
|
||
"dec"|"f32"|"f64" => "a fraction",
|
||
_ => "an integer",
|
||
};
|
||
|
||
let real = format!(indoc!(
|
||
r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `use` has an unexpected type:
|
||
|
||
5│ use {}{}
|
||
{}
|
||
|
||
The argument is {} of type:
|
||
|
||
{}
|
||
|
||
But `use` needs its 1st argument to be:
|
||
|
||
{}
|
||
"#
|
||
), number, $suffix, carets, kind, typ, bad_type);
|
||
|
||
assert_eq!(golden, real);
|
||
}
|
||
);
|
||
)*}
|
||
}
|
||
|
||
mismatched_suffix_tests! {
|
||
1, "u8", mismatched_suffix_u8
|
||
1, "u16", mismatched_suffix_u16
|
||
1, "u32", mismatched_suffix_u32
|
||
1, "u64", mismatched_suffix_u64
|
||
1, "u128", mismatched_suffix_u128
|
||
1, "i8", mismatched_suffix_i8
|
||
1, "i16", mismatched_suffix_i16
|
||
1, "i32", mismatched_suffix_i32
|
||
1, "i64", mismatched_suffix_i64
|
||
1, "i128", mismatched_suffix_i128
|
||
1, "nat", mismatched_suffix_nat
|
||
1, "dec", mismatched_suffix_dec
|
||
1, "f32", mismatched_suffix_f32
|
||
1, "f64", mismatched_suffix_f64
|
||
}
|
||
|
||
macro_rules! mismatched_suffix_tests_in_pattern {
|
||
($($number:expr, $suffix:expr, $name:ident)*) => {$(
|
||
test_report!(
|
||
$name,
|
||
&{
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" };
|
||
|
||
format!(indoc!(
|
||
r#"
|
||
when {}{} is
|
||
{}{} -> 1
|
||
_ -> 1
|
||
"#
|
||
), number, bad_suffix, number, $suffix)
|
||
},
|
||
|golden| {
|
||
let number = $number.to_string();
|
||
let mut typ = $suffix.to_string();
|
||
typ.get_mut(0..1).unwrap().make_ascii_uppercase();
|
||
let bad_suffix = if $suffix == "u8" { "i8" } else { "u8" };
|
||
let bad_type = if $suffix == "u8" { "I8" } else { "U8" };
|
||
|
||
let real = format!(indoc!(
|
||
r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when {}{} is
|
||
5│ {}{} -> 1
|
||
6│ _ -> 1
|
||
|
||
The `when` condition is an integer of type:
|
||
|
||
{}
|
||
|
||
But the branch patterns have type:
|
||
|
||
{}
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"#
|
||
), number, bad_suffix, number, $suffix, bad_type, typ);
|
||
|
||
assert_eq!(golden, real);
|
||
}
|
||
);
|
||
)*}
|
||
}
|
||
|
||
mismatched_suffix_tests_in_pattern! {
|
||
1, "u8", mismatched_suffix_u8_pattern
|
||
1, "u16", mismatched_suffix_u16_pattern
|
||
1, "u32", mismatched_suffix_u32_pattern
|
||
1, "u64", mismatched_suffix_u64_pattern
|
||
1, "u128", mismatched_suffix_u128_pattern
|
||
1, "i8", mismatched_suffix_i8_pattern
|
||
1, "i16", mismatched_suffix_i16_pattern
|
||
1, "i32", mismatched_suffix_i32_pattern
|
||
1, "i64", mismatched_suffix_i64_pattern
|
||
1, "i128", mismatched_suffix_i128_pattern
|
||
1, "nat", mismatched_suffix_nat_pattern
|
||
1, "dec", mismatched_suffix_dec_pattern
|
||
1, "f32", mismatched_suffix_f32_pattern
|
||
1, "f64", mismatched_suffix_f64_pattern
|
||
}
|
||
|
||
test_report!(
|
||
bad_numeric_literal_suffix,
|
||
indoc!(
|
||
r#"
|
||
1u256
|
||
"#
|
||
),
|
||
// TODO: link to number suffixes
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal contains an invalid digit:
|
||
|
||
4│ 1u256
|
||
^^^^^
|
||
|
||
Integer literals can only contain the digits
|
||
0-9, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
numer_literal_multi_suffix,
|
||
indoc!(
|
||
r#"
|
||
1u8u8
|
||
"#
|
||
),
|
||
// TODO: link to number suffixes
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal contains an invalid digit:
|
||
|
||
4│ 1u8u8
|
||
^^^^^
|
||
|
||
Integer literals can only contain the digits
|
||
0-9, or have an integer suffix.
|
||
|
||
Tip: Learn more about number literals at TODO
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
int_literal_has_float_suffix,
|
||
indoc!(
|
||
r#"
|
||
0b1f32
|
||
"#
|
||
),
|
||
@r###"
|
||
── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This number literal is an integer, but it has a float suffix:
|
||
|
||
4│ 0b1f32
|
||
^^^^^^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
float_literal_has_int_suffix,
|
||
indoc!(
|
||
r#"
|
||
1.0u8
|
||
"#
|
||
),
|
||
@r###"
|
||
── CONFLICTING NUMBER SUFFIX ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This number literal is a float, but it has an integer suffix:
|
||
|
||
4│ 1.0u8
|
||
^^^^^
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u8_overflow,
|
||
"256u8",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 256u8
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U8, whose maximum value is
|
||
255.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u8,
|
||
"-1u8",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u8
|
||
^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U8, whose minimum value is
|
||
0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u16_overflow,
|
||
"65536u16",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 65536u16
|
||
^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U16, whose maximum value
|
||
is 65535.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u16,
|
||
"-1u16",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u16
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U16, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u32_overflow,
|
||
"4_294_967_296u32",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 4_294_967_296u32
|
||
^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U32, whose maximum value
|
||
is 4_294_967_295.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u32,
|
||
"-1u32",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u32
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U32, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
u64_overflow,
|
||
"18_446_744_073_709_551_616u64",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 18_446_744_073_709_551_616u64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U64, whose maximum value
|
||
is 18_446_744_073_709_551_615.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u64,
|
||
"-1u64",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u64
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U64, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
negative_u128,
|
||
"-1u128",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -1u128
|
||
^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a U128, whose minimum value
|
||
is 0.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i8_overflow,
|
||
"128i8",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 128i8
|
||
^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I8, whose maximum value is
|
||
127.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i8_underflow,
|
||
"-129i8",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -129i8
|
||
^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I8, whose minimum value is
|
||
-128.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i16_overflow,
|
||
"32768i16",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 32768i16
|
||
^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I16, whose maximum value
|
||
is 32767.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i16_underflow,
|
||
"-32769i16",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -32769i16
|
||
^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I16, whose minimum value
|
||
is -32768.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i32_overflow,
|
||
"2_147_483_648i32",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 2_147_483_648i32
|
||
^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I32, whose maximum value
|
||
is 2_147_483_647.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i32_underflow,
|
||
"-2_147_483_649i32",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -2_147_483_649i32
|
||
^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I32, whose minimum value
|
||
is -2_147_483_648.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i64_overflow,
|
||
"9_223_372_036_854_775_808i64",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 9_223_372_036_854_775_808i64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I64, whose maximum value
|
||
is 9_223_372_036_854_775_807.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i64_underflow,
|
||
"-9_223_372_036_854_775_809i64",
|
||
@r###"
|
||
── NUMBER UNDERFLOWS SUFFIX ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal underflows the type indicated by its suffix:
|
||
|
||
4│ -9_223_372_036_854_775_809i64
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I64, whose minimum value
|
||
is -9_223_372_036_854_775_808.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
i128_overflow,
|
||
"170_141_183_460_469_231_731_687_303_715_884_105_728i128",
|
||
@r###"
|
||
── NUMBER OVERFLOWS SUFFIX ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This integer literal overflows the type indicated by its suffix:
|
||
|
||
4│ 170_141_183_460_469_231_731_687_303_715_884_105_728i128
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Tip: The suffix indicates this integer is a I128, whose maximum value
|
||
is 170_141_183_460_469_231_731_687_303_715_884_105_727.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_get_negative_number,
|
||
indoc!(
|
||
r#"
|
||
List.get [1,2,3] -1
|
||
"#
|
||
),
|
||
// TODO: this error message could be improved, e.g. something like "This argument can
|
||
// be used as ... because of its literal value"
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `get` has an unexpected type:
|
||
|
||
4│ List.get [1,2,3] -1
|
||
^^
|
||
|
||
The argument is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But `get` needs its 2nd argument to be:
|
||
|
||
Nat
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_get_negative_number_indirect,
|
||
indoc!(
|
||
r#"
|
||
a = -9_223_372_036_854
|
||
List.get [1,2,3] a
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `get` has an unexpected type:
|
||
|
||
5│ List.get [1,2,3] a
|
||
^
|
||
|
||
This `a` value is a:
|
||
|
||
F64, I64, I128, or Dec
|
||
|
||
But `get` needs its 2nd argument to be:
|
||
|
||
Nat
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
list_get_negative_number_double_indirect,
|
||
indoc!(
|
||
r#"
|
||
a = -9_223_372_036_854
|
||
b = a
|
||
List.get [1,2,3] b
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `get` has an unexpected type:
|
||
|
||
6│ List.get [1,2,3] b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64, I64, I128, or Dec
|
||
|
||
But `get` needs its 2nd argument to be:
|
||
|
||
Nat
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
compare_unsigned_to_signed,
|
||
indoc!(
|
||
r#"
|
||
when -1 is
|
||
1u8 -> 1
|
||
_ -> 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
4│> when -1 is
|
||
5│ 1u8 -> 1
|
||
6│ _ -> 1
|
||
|
||
The `when` condition is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But the branch patterns have type:
|
||
|
||
U8
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_type_alias_is_newtype,
|
||
indoc!(
|
||
r#"
|
||
R a : [Only (R a)]
|
||
|
||
v : R Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `R` alias is self-recursive in an invalid way:
|
||
|
||
4│ R a : [Only (R a)]
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_type_alias_is_newtype_deep,
|
||
indoc!(
|
||
r#"
|
||
R a : [Only { very: [Deep (R a)] }]
|
||
|
||
v : R Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `R` alias is self-recursive in an invalid way:
|
||
|
||
4│ R a : [Only { very: [Deep (R a)] }]
|
||
^
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_type_alias_is_newtype_mutual,
|
||
indoc!(
|
||
r#"
|
||
Foo a : [Thing (Bar a)]
|
||
Bar a : [Stuff (Foo a)]
|
||
|
||
v : Bar Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── CYCLIC ALIAS ────────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Foo` alias is recursive in an invalid way:
|
||
|
||
4│ Foo a : [Thing (Bar a)]
|
||
^^^
|
||
|
||
The `Foo` alias depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ Foo
|
||
│ ↓
|
||
│ Bar
|
||
└─────┘
|
||
|
||
Recursion in aliases is only allowed if recursion happens behind a
|
||
tagged union, at least one variant of which is not recursive.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_2458,
|
||
indoc!(
|
||
r#"
|
||
Result a b : [Ok a, Err b]
|
||
|
||
Foo a : [Blah (Result (Bar a) [])]
|
||
Bar a : Foo a
|
||
|
||
v : Bar Str
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Result` name is first defined here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
|
||
|
||
But then it's defined a second time here:
|
||
|
||
4│ Result a b : [Ok a, Err b]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Since these aliases have the same name, it's easy to use the wrong one
|
||
on accident. Give one of them a new name.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_type_not_in_scope,
|
||
indoc!(
|
||
r#"
|
||
@Age 21
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
4│ @Age 21
|
||
^^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_reference_not_opaque_type,
|
||
indoc!(
|
||
r#"
|
||
Age : Num.U8
|
||
|
||
@Age 21
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
6│ @Age 21
|
||
^^^^
|
||
|
||
Note: There is an alias of the same name:
|
||
|
||
4│ Age : Num.U8
|
||
^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Age` is not used anywhere in your code.
|
||
|
||
4│ Age : Num.U8
|
||
^^^^^^^^^^^^
|
||
|
||
If you didn't intend on using `Age` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
qualified_opaque_reference,
|
||
indoc!(
|
||
r#"
|
||
OtherModule.@Age 21
|
||
"#
|
||
),
|
||
// TODO: get rid of the first error. Consider parsing OtherModule.@Age to completion
|
||
// and checking it during can. The reason the error appears is because it is parsed as
|
||
// Apply(Error(OtherModule), [@Age, 21])
|
||
@r###"
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
4│ OtherModule.@Age 21
|
||
^^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I am trying to parse a qualified name here:
|
||
|
||
4│ OtherModule.@Age 21
|
||
^
|
||
|
||
I was expecting to see an identifier next, like height. A complete
|
||
qualified name looks something like Json.Decode.string.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_used_outside_declaration_scope,
|
||
indoc!(
|
||
r#"
|
||
age =
|
||
Age := Num.U8
|
||
21u8
|
||
|
||
@Age age
|
||
"#
|
||
),
|
||
// TODO(opaques): there is a potential for a better error message here, if the usage of
|
||
// `@Age` can be linked to the declaration of `Age` inside `age`, and a suggestion to
|
||
// raise that declaration to the outer scope.
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Age` is not used anywhere in your code.
|
||
|
||
5│ Age := Num.U8
|
||
^^^^^^^^^^^^^
|
||
|
||
If you didn't intend on using `Age` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
|
||
── OPAQUE TYPE NOT DEFINED ─────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The opaque type Age referenced here is not defined:
|
||
|
||
8│ @Age age
|
||
^^^^
|
||
|
||
Note: It looks like there are no opaque types declared in this scope yet!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore = "Blocked on https://github.com/rtfeldman/roc/issues/3385"]
|
||
unimported_modules_reported,
|
||
indoc!(
|
||
r#"
|
||
main : Task.Task {} []
|
||
main = "whatever man you don't even know my type"
|
||
main
|
||
"#
|
||
),
|
||
@r#"
|
||
── MODULE NOT IMPORTED ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Task` module is not imported:
|
||
|
||
1│ main : Task.Task {} []
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Is there an import missing? Perhaps there is a typo. Did you mean one
|
||
of these?
|
||
|
||
Test
|
||
List
|
||
Num
|
||
Box
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_check,
|
||
indoc!(
|
||
r#"
|
||
Age := Num.U8
|
||
|
||
n : Age
|
||
n = @Age ""
|
||
|
||
n
|
||
"#
|
||
),
|
||
// TODO(opaques): error could be improved by saying that the opaque definition demands
|
||
// that the argument be a U8, and linking to the definitin!
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
7│ n = @Age ""
|
||
^^
|
||
|
||
This argument to an opaque type has type:
|
||
|
||
Str
|
||
|
||
But you are trying to use it as:
|
||
|
||
U8
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_infer,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
if True
|
||
then @F ""
|
||
else @F {}
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
8│ else @F {}
|
||
^^
|
||
|
||
This argument to an opaque type has type:
|
||
|
||
{}
|
||
|
||
But you are trying to use it as:
|
||
|
||
Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_creation_is_not_wrapped,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
v : F Str
|
||
v = ""
|
||
|
||
v
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `v` definition:
|
||
|
||
6│ v : F Str
|
||
7│ v = ""
|
||
^^
|
||
|
||
The body is a string of type:
|
||
|
||
Str
|
||
|
||
But the type annotation on `v` says it should be:
|
||
|
||
F Str
|
||
|
||
Tip: Type comparisons between an opaque type are only ever equal if
|
||
both types are the same opaque type. Did you mean to create an opaque
|
||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||
an instance of this opaque type by doing @Age 23.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_pattern_check,
|
||
indoc!(
|
||
r#"
|
||
Age := Num.U8
|
||
|
||
f : Age -> Num.U8
|
||
f = \Age n -> n
|
||
|
||
f
|
||
"#
|
||
),
|
||
// TODO(opaques): error could be improved by saying that the user-provided pattern
|
||
// probably wants to change "Age" to "@Age"!
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `f` is weird:
|
||
|
||
7│ f = \Age n -> n
|
||
^^^^^
|
||
|
||
The argument is a pattern that matches a `Age` tag of type:
|
||
|
||
[Age a]
|
||
|
||
But the annotation on `f` says the 1st argument should be:
|
||
|
||
Age
|
||
|
||
Tip: Type comparisons between an opaque type are only ever equal if
|
||
both types are the same opaque type. Did you mean to create an opaque
|
||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||
an instance of this opaque type by doing @Age 23.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_mismatch_pattern_infer,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
\x ->
|
||
when x is
|
||
@F A -> ""
|
||
@F {} -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd pattern in this `when` does not match the previous ones:
|
||
|
||
9│ @F {} -> ""
|
||
^^^^^
|
||
|
||
The 2nd pattern is trying to matchF unwrappings of type:
|
||
|
||
F {}a
|
||
|
||
But all the previous branches match:
|
||
|
||
F [A]
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_pattern_match_not_exhaustive_tag,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
v : F [A, B, C]
|
||
|
||
when v is
|
||
@F A -> ""
|
||
@F B -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
8│> when v is
|
||
9│ @F A -> ""
|
||
10│ @F B -> ""
|
||
|
||
This `v` value is a:
|
||
|
||
F [A, B, C]
|
||
|
||
But the branch patterns have type:
|
||
|
||
F [A, B]
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
|
||
Tip: Looks like the branches are missing coverage of the `C` tag.
|
||
|
||
Tip: Maybe you need to add a catch-all branch, like `_`?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_pattern_match_not_exhaustive_int,
|
||
indoc!(
|
||
r#"
|
||
F n := n
|
||
|
||
v : F Num.U8
|
||
|
||
when v is
|
||
@F 1 -> ""
|
||
@F 2 -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
8│> when v is
|
||
9│> @F 1 -> ""
|
||
10│> @F 2 -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
@F _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
let_polymorphism_with_scoped_type_variables,
|
||
indoc!(
|
||
r#"
|
||
f : a -> a
|
||
f = \x ->
|
||
y : a -> a
|
||
y = \z -> z
|
||
|
||
n = y 1u8
|
||
x1 = y x
|
||
(\_ -> x1) n
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `y` has an unexpected type:
|
||
|
||
9│ n = y 1u8
|
||
^^^
|
||
|
||
The argument is an integer of type:
|
||
|
||
U8
|
||
|
||
But `y` needs its 1st argument to be:
|
||
|
||
a
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `U8` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
non_exhaustive_with_guard,
|
||
indoc!(
|
||
r#"
|
||
x : [A]
|
||
when x is
|
||
A if True -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
5│> when x is
|
||
6│> A if True -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
A (note the lack of an if clause)
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_record_extension_type,
|
||
indoc!(
|
||
r#"
|
||
f : { x : Num.Nat }[]
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This record extension type is invalid:
|
||
|
||
4│ f : { x : Num.Nat }[]
|
||
^^
|
||
|
||
Note: A record extension variable can only contain a type variable or
|
||
another record.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
invalid_tag_extension_type,
|
||
indoc!(
|
||
r#"
|
||
f : [A]Str
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── INVALID_EXTENSION_TYPE ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This tag union extension type is invalid:
|
||
|
||
4│ f : [A]Str
|
||
^^^
|
||
|
||
Note: A tag union extension variable can only contain a type variable
|
||
or another tag union.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unknown_type,
|
||
indoc!(
|
||
r#"
|
||
Type : [Constructor UnknownType]
|
||
|
||
insertHelper : UnknownType, Type -> Type
|
||
insertHelper = \h, m ->
|
||
when m is
|
||
Constructor _ -> Constructor h
|
||
|
||
insertHelper
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `UnknownType` in this scope.
|
||
|
||
4│ Type : [Constructor UnknownType]
|
||
^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Type
|
||
Unsigned8
|
||
Unsigned32
|
||
Unsigned16
|
||
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `UnknownType` in this scope.
|
||
|
||
6│ insertHelper : UnknownType, Type -> Type
|
||
^^^^^^^^^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Type
|
||
Unsigned8
|
||
Unsigned32
|
||
Unsigned16
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_first_demand_not_indented_enough,
|
||
indoc!(
|
||
r#"
|
||
Eq has
|
||
eq : a, a -> U64 | a has Eq
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ABILITY ── tmp/ability_first_demand_not_indented_enough/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
4│ Eq has
|
||
5│ eq : a, a -> U64 | a has Eq
|
||
^
|
||
|
||
I suspect this line is not indented enough (by 1 spaces)
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_demands_not_indented_with_first,
|
||
indoc!(
|
||
r#"
|
||
Eq has
|
||
eq : a, a -> U64 | a has Eq
|
||
neq : a, a -> U64 | a has Eq
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNFINISHED ABILITY ─── tmp/ability_demands_not_indented_with_first/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
5│ eq : a, a -> U64 | a has Eq
|
||
6│ neq : a, a -> U64 | a has Eq
|
||
^
|
||
|
||
I suspect this line is indented too much (by 4 spaces)"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_demand_value_has_args,
|
||
indoc!(
|
||
r#"
|
||
Eq has
|
||
eq b c : a, a -> U64 | a has Eq
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNFINISHED ABILITY ───────────── tmp/ability_demand_value_has_args/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
5│ eq b c : a, a -> U64 | a has Eq
|
||
^
|
||
|
||
I was expecting to see a : annotating the signature of this value
|
||
next."#
|
||
);
|
||
|
||
test_report!(
|
||
ability_non_signature_expression,
|
||
indoc!(
|
||
r#"
|
||
Eq has
|
||
123
|
||
|
||
1
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNFINISHED ABILITY ────────── tmp/ability_non_signature_expression/Test.roc ─
|
||
|
||
I was partway through parsing an ability definition, but I got stuck
|
||
here:
|
||
|
||
4│ Eq has
|
||
5│ 123
|
||
^
|
||
|
||
I was expecting to see a value signature next.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
wildcard_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : Num.Int *
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I : Num.Int *
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
wildcard_in_opaque,
|
||
indoc!(
|
||
r#"
|
||
I := Num.Int *
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I := Num.Int *
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:=`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
multiple_wildcards_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : [A (Num.Int *), B (Num.Int *)]
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has 2 unbound type variables.
|
||
|
||
Here is one occurrence:
|
||
|
||
4│ I : [A (Num.Int *), B (Num.Int *)]
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
inference_var_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : Num.Int _
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I : Num.Int _
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unbound_var_in_alias,
|
||
indoc!(
|
||
r#"
|
||
I : Num.Int a
|
||
a : I
|
||
a
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNBOUND TYPE VARIABLE ───────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of `I` has an unbound type variable:
|
||
|
||
4│ I : Num.Int a
|
||
^
|
||
|
||
Tip: Type variables must be bound before the `:`. Perhaps you intended
|
||
to add a type parameter to this type?
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_bad_type_parameter,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [] to "./platform"
|
||
|
||
Hash a b c has
|
||
hash : a -> U64 | a has Hash
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY HAS TYPE VARIABLES ──────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the `Hash` ability includes type variables:
|
||
|
||
3│ Hash a b c has
|
||
^^^^^
|
||
|
||
Abilities cannot depend on type variables, but their member values
|
||
can!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Hash` is not used anywhere in your code.
|
||
|
||
3│ Hash a b c has
|
||
^^^^
|
||
|
||
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!(
|
||
alias_in_has_clause,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash] to "./platform"
|
||
|
||
Hash has hash : a, b -> Num.U64 | a has Hash, b has Bool.Bool
|
||
"#
|
||
),
|
||
@r#"
|
||
── HAS CLAUSE IS NOT AN ABILITY ────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The type referenced in this "has" clause is not an ability:
|
||
|
||
3│ Hash has hash : a, b -> Num.U64 | a has Hash, b has Bool.Bool
|
||
^^^^^^^^^
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
shadowed_type_variable_in_has_clause,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [ab1] to "./platform"
|
||
|
||
Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||
"#
|
||
),
|
||
@r#"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `a` name is first defined here:
|
||
|
||
3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||
^^^^^^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
3│ Ab1 has ab1 : a -> {} | a has Ab1, a has Ab1
|
||
^^^^^^^^^
|
||
|
||
Since these variables have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_shadows_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [ab] to "./platform"
|
||
|
||
Ability has ab : a -> U64 | a has Ability
|
||
|
||
Ability has ab1 : a -> U64 | a has Ability
|
||
"#
|
||
),
|
||
@r#"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `Ability` name is first defined here:
|
||
|
||
3│ Ability has ab : a -> U64 | a has Ability
|
||
^^^^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
5│ Ability has ab1 : a -> U64 | a has Ability
|
||
^^^^^^^
|
||
|
||
Since these abilities have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_member_does_not_bind_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [] to "./platform"
|
||
|
||
Ability has ab : {} -> {}
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the ability member `ab` does not include a `has` clause
|
||
binding a type variable to the ability `Ability`:
|
||
|
||
3│ Ability has ab : {} -> {}
|
||
^^
|
||
|
||
Ability members must include a `has` clause binding a type variable to
|
||
an ability, like
|
||
|
||
a has Ability
|
||
|
||
Otherwise, the function does not need to be part of the ability!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`Ability` is not used anywhere in your code.
|
||
|
||
3│ Ability has ab : {} -> {}
|
||
^^^^^^^
|
||
|
||
If you didn't intend on using `Ability` then remove it so future readers
|
||
of your code don't wonder why it is there.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_member_binds_parent_twice,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [] to "./platform"
|
||
|
||
Eq has eq : a, b -> Bool.Bool | a has Eq, b has Eq
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY MEMBER BINDS MULTIPLE VARIABLES ─────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the ability member `eq` includes multiple variables
|
||
bound to the `Eq`` ability:`
|
||
|
||
3│ Eq has eq : a, b -> Bool.Bool | a has Eq, b has Eq
|
||
^^^^^^^^^^^^^^^^^^
|
||
|
||
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 `Eq`?
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
has_clause_not_on_toplevel,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [f] to "./platform"
|
||
|
||
Hash has hash : (a | a has Hash) -> Num.U64
|
||
|
||
f : a -> Num.U64 | a has Hash
|
||
"#
|
||
),
|
||
@r#"
|
||
── ILLEGAL HAS CLAUSE ──────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
A `has` clause is not allowed here:
|
||
|
||
3│ Hash has hash : (a | a has Hash) -> Num.U64
|
||
^^^^^^^^^^
|
||
|
||
`has` clauses can only be specified on the top-level type annotations.
|
||
|
||
── ABILITY MEMBER MISSING HAS CLAUSE ───────────────────── /code/proj/Main.roc ─
|
||
|
||
The definition of the ability member `hash` does not include a `has`
|
||
clause binding a type variable to the ability `Hash`:
|
||
|
||
3│ Hash has hash : (a | a has Hash) -> Num.U64
|
||
^^^^
|
||
|
||
Ability members must include a `has` clause binding a type variable to
|
||
an ability, like
|
||
|
||
a has Hash
|
||
|
||
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"
|
||
|
||
Hash has hash : a -> U64 | a has Hash
|
||
|
||
Id := U32 has [Hash {hash}]
|
||
|
||
hash = \@Id n -> n
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with this specialization of `hash`:
|
||
|
||
7│ hash = \@Id n -> n
|
||
^^^^
|
||
|
||
This value is a declared specialization of type:
|
||
|
||
Id -> U32
|
||
|
||
But the type annotation on `hash` says it must match:
|
||
|
||
Id -> U64
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_is_incomplete,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [eq, le] to "./platform"
|
||
|
||
Eq has
|
||
eq : a, a -> Bool | a has Eq
|
||
le : a, a -> Bool | a has Eq
|
||
|
||
Id := U64 has [Eq {eq}]
|
||
|
||
eq = \@Id m, @Id n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Eq` ability:
|
||
|
||
7│ Id := U64 has [Eq {eq}]
|
||
^^^^^^^
|
||
|
||
The following necessary members are missing implementations:
|
||
|
||
le
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_is_unused,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash] to "./platform"
|
||
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
hash = \_ -> 0u64
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`hash` is not used anywhere in your code.
|
||
|
||
6│ hash = \_ -> 0u64
|
||
^^^^
|
||
|
||
If you didn't intend on using `hash` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_is_duplicated,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash, One, Two] to "./platform"
|
||
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
One := {} has [Hash {hash}]
|
||
Two := {} has [Hash {hash}]
|
||
|
||
hash = \_ -> 0u64
|
||
"#
|
||
),
|
||
// TODO: the error message here could be seriously improved!
|
||
@r###"
|
||
── OVERLOADED SPECIALIZATION ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability member specialization is already claimed to specialize
|
||
another opaque type:
|
||
|
||
7│ Two := {} has [Hash {hash}]
|
||
^^^^
|
||
|
||
Previously, we found it to specialize `hash` for `One`.
|
||
|
||
Ability specializations can only provide implementations for one
|
||
opauqe type, since all opaque types are different!
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This specialization of `hash` is overly general:
|
||
|
||
9│ hash = \_ -> 0u64
|
||
^^^^
|
||
|
||
This value is a declared specialization of type:
|
||
|
||
a -> U64
|
||
|
||
But the type annotation on `hash` says it must match:
|
||
|
||
a -> U64 | a has Hash
|
||
|
||
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!(
|
||
#[ignore = "TODO does not error yet"]
|
||
ability_specialization_is_duplicated_with_type_mismatch,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash, One, Two] to "./platform"
|
||
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
One := {} has [Hash {hash}]
|
||
Two := {} has [Hash {hash}]
|
||
|
||
hash = \@One _ -> 0u64
|
||
"#
|
||
),
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_specialization_conflicting_specialization_types,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [eq] to "./platform"
|
||
|
||
Eq has
|
||
eq : a, a -> Bool | a has Eq
|
||
|
||
You := {} has [Eq {eq}]
|
||
AndI := {}
|
||
|
||
eq = \@You {}, @AndI {} -> False
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with this specialization of `eq`:
|
||
|
||
9│ eq = \@You {}, @AndI {} -> False
|
||
^^
|
||
|
||
This value is a declared specialization of type:
|
||
|
||
You, AndI -> [False, True]
|
||
|
||
But the type annotation on `eq` says it must match:
|
||
|
||
You, You -> Bool
|
||
|
||
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!(
|
||
ability_specialization_checked_against_annotation,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash] to "./platform"
|
||
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
Id := U64 has [Hash {hash}]
|
||
|
||
hash : Id -> U32
|
||
hash = \@Id n -> n
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `hash` definition:
|
||
|
||
8│ hash : Id -> U32
|
||
9│ hash = \@Id n -> n
|
||
^
|
||
|
||
This `n` value is a:
|
||
|
||
U64
|
||
|
||
But the type annotation on `hash` says it should be:
|
||
|
||
U32
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with this specialization of `hash`:
|
||
|
||
9│ 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_called_with_non_specializing,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [noGoodVeryBadTerrible] to "./platform"
|
||
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
Id := U64 has [Hash {hash}]
|
||
|
||
hash = \@Id n -> n
|
||
|
||
User := {}
|
||
|
||
noGoodVeryBadTerrible =
|
||
{
|
||
nope: hash (@User {}),
|
||
notYet: hash (A 1),
|
||
}
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
15│ notYet: hash (A 1),
|
||
^^^
|
||
|
||
Roc can't generate an implementation of the `#UserApp.Hash` ability for
|
||
|
||
[A (Num a)]b
|
||
|
||
Only builtin abilities can have generated implementations!
|
||
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
14│ nope: hash (@User {}),
|
||
^^^^^^^^
|
||
|
||
The type `User` does not fully implement the ability `Hash`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
ability_not_on_toplevel,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
main =
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
123
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY NOT ON TOP-LEVEL ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability definition is not on the top-level of a module:
|
||
|
||
4│> Hash has
|
||
5│> hash : a -> U64 | a has Hash
|
||
|
||
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"
|
||
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
Id := U64 has [Hash {hash}]
|
||
hash = \@Id n -> n
|
||
|
||
hashable : a | a has Hash
|
||
hashable = @Id 15
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `hashable` definition:
|
||
|
||
9│ hashable : a | a has Hash
|
||
10│ hashable = @Id 15
|
||
^^^^^^
|
||
|
||
This Id opaque wrapping has the type:
|
||
|
||
Id
|
||
|
||
But the type annotation on `hashable` says it should be:
|
||
|
||
a | a has Hash
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any value implementing the `Hash` ability. But in
|
||
the body I see that it will only produce a `Id` 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!(
|
||
ability_value_annotations_are_an_error,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [result] to "./platform"
|
||
|
||
Hash has
|
||
hash : a -> U64 | a has Hash
|
||
|
||
mulHashes : Hash, Hash -> U64
|
||
mulHashes = \x, y -> hash x * hash y
|
||
|
||
Id := U64 has [Hash {hash: hashId}]
|
||
hashId = \@Id n -> n
|
||
|
||
Three := {} has [Hash {hash: hashThree}]
|
||
hashThree = \@Three _ -> 3
|
||
|
||
result = mulHashes (@Id 100) (@Three {})
|
||
"#
|
||
),
|
||
@r#"
|
||
── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
You are attempting to use the ability `Hash` as a type directly:
|
||
|
||
6│ mulHashes : Hash, Hash -> U64
|
||
^^^^
|
||
|
||
Abilities can only be used in type annotations to constrain type
|
||
variables.
|
||
|
||
Hint: Perhaps you meant to include a `has` annotation, like
|
||
|
||
a has Hash
|
||
|
||
── ABILITY USED AS TYPE ────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
You are attempting to use the ability `Hash` as a type directly:
|
||
|
||
6│ mulHashes : Hash, Hash -> U64
|
||
^^^^
|
||
|
||
Abilities can only be used in type annotations to constrain type
|
||
variables.
|
||
|
||
Hint: Perhaps you meant to include a `has` annotation, like
|
||
|
||
b has Hash
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
branches_have_more_cases_than_condition,
|
||
indoc!(
|
||
r#"
|
||
foo : Bool -> Str
|
||
foo = \bool ->
|
||
when bool is
|
||
True -> "true"
|
||
False -> "false"
|
||
Wat -> "surprise!"
|
||
foo
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The branches of this `when` expression don't match the condition:
|
||
|
||
6│> when bool is
|
||
7│ True -> "true"
|
||
8│ False -> "false"
|
||
9│ Wat -> "surprise!"
|
||
|
||
This `bool` value is a:
|
||
|
||
Bool
|
||
|
||
But the branch patterns have type:
|
||
|
||
[False, True, Wat]
|
||
|
||
The branches must be cases of the `when` condition's type!
|
||
"#
|
||
);
|
||
|
||
// from https://github.com/rtfeldman/roc/commit/1372737f5e53ee5bb96d7e1b9593985e5537023a
|
||
// There was a bug where this reported UnusedArgument("val")
|
||
// since it was used only in the returned function only.
|
||
//
|
||
// we want this to not give any warnings/errors!
|
||
test_report!(
|
||
always_function,
|
||
indoc!(
|
||
r#"
|
||
always = \val -> \_ -> val
|
||
|
||
always
|
||
"#
|
||
),
|
||
@""
|
||
);
|
||
|
||
test_report!(
|
||
imports_missing_comma,
|
||
indoc!(
|
||
r#"
|
||
app "test-missing-comma"
|
||
packages { pf: "platform/main.roc" }
|
||
imports [pf.Task Base64]
|
||
provides [main, @Foo] to pf
|
||
"#
|
||
),
|
||
@r#"
|
||
── WEIRD IMPORTS ────────────────────────── tmp/imports_missing_comma/Test.roc ─
|
||
|
||
I am partway through parsing a imports list, but I got stuck here:
|
||
|
||
2│ packages { pf: "platform/main.roc" }
|
||
3│ imports [pf.Task Base64]
|
||
^
|
||
|
||
I am expecting a comma or end of list, like
|
||
|
||
imports [Shape, Vector]"#
|
||
);
|
||
|
||
test_report!(
|
||
not_enough_cases_for_open_union,
|
||
indoc!(
|
||
r#"
|
||
foo : [A, B]a -> Str
|
||
foo = \it ->
|
||
when it is
|
||
A -> ""
|
||
foo
|
||
"#
|
||
),
|
||
@r#"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
6│> when it is
|
||
7│> A -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
B
|
||
_
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
issue_2778_specialization_is_not_a_redundant_pattern,
|
||
indoc!(
|
||
r#"
|
||
formatColor = \color ->
|
||
when color is
|
||
Red -> "red"
|
||
Yellow -> "yellow"
|
||
_ -> "unknown"
|
||
|
||
Red |> formatColor |> Str.concat (formatColor Orange)
|
||
"#
|
||
),
|
||
@"" // no problem
|
||
);
|
||
|
||
test_report!(
|
||
nested_specialization,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [main] to "./platform"
|
||
|
||
Default has default : {} -> a | a has Default
|
||
|
||
main =
|
||
A := {} has [Default {default}]
|
||
default = \{} -> @A {}
|
||
default {}
|
||
"#
|
||
),
|
||
@r#"
|
||
── SPECIALIZATION NOT ON TOP-LEVEL ─────────────────────── /code/proj/Main.roc ─
|
||
|
||
This specialization of the `default` ability member is in a nested
|
||
scope:
|
||
|
||
7│ default = \{} -> @A {}
|
||
^^^^^^^
|
||
|
||
Specializations can only be defined on the top-level of a module.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
recursion_var_specialization_error,
|
||
indoc!(
|
||
r#"
|
||
Job a : [Job (List (Job a))]
|
||
|
||
job : Job Str
|
||
|
||
when job is
|
||
Job lst -> lst == ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `isEq` has an unexpected type:
|
||
|
||
9│ Job lst -> lst == ""
|
||
^^
|
||
|
||
The argument is a string of type:
|
||
|
||
Str
|
||
|
||
But `isEq` needs its 2nd argument to be:
|
||
|
||
List [Job ∞] as ∞
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore]
|
||
type_error_in_apply_is_circular,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Set] provides [go] to "./platform"
|
||
|
||
S a : { set : Set.Set a }
|
||
|
||
go : a, S a -> Result (List a) *
|
||
go = \goal, model ->
|
||
if goal == goal
|
||
then Ok []
|
||
else
|
||
new = { model & set : Set.remove goal model.set }
|
||
go goal new
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `remove` has an unexpected type:
|
||
|
||
10│ new = { model & set : Set.remove goal model.set }
|
||
^^^^
|
||
|
||
This `goal` value is a:
|
||
|
||
a
|
||
|
||
But `remove` needs the 1st argument to be:
|
||
|
||
Set k
|
||
|
||
Tip: The type annotation uses the type variable `a` to say that this
|
||
definition can produce any type of value. But in the body I see that
|
||
it will only produce a `Set` value of a single specific type. Maybe
|
||
change the type annotation to be more specific? Maybe change the code
|
||
to be more general?
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `new`:
|
||
|
||
10│ new = { model & set : Set.remove goal model.set }
|
||
^^^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
{ set : Set ∞ }
|
||
|
||
── CIRCULAR TYPE ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
I'm inferring a weird self-referential type for `goal`:
|
||
|
||
6│ go = \goal, model ->
|
||
^^^^
|
||
|
||
Here is my best effort at writing down the type. You will see ∞ for
|
||
parts of the type that repeat something already printed out
|
||
infinitely.
|
||
|
||
Set ∞
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cycle_through_non_function,
|
||
indoc!(
|
||
r#"
|
||
force : ({} -> I64) -> I64
|
||
force = \eval -> eval {}
|
||
|
||
t1 = \_ -> force (\_ -> t2)
|
||
|
||
t2 = t1 {}
|
||
|
||
t2
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `t1` definition is causing a very tricky infinite loop:
|
||
|
||
7│ t1 = \_ -> force (\_ -> t2)
|
||
^^
|
||
|
||
The `t1` value depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ t1
|
||
│ ↓
|
||
│ t2
|
||
└─────┘
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
function_does_not_implement_encoding,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Encode] provides [main] to "./platform"
|
||
|
||
main = Encode.toEncoder \x -> x
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
3│ main = Encode.toEncoder \x -> x
|
||
^^^^^^^
|
||
|
||
Roc can't generate an implementation of the `Encode.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 [Encode] 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/rtfeldman/roc/issues/3056.
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression has a type that does not implement the abilities it's expected to:
|
||
|
||
4│ main = Encode.toEncoder { x: @A {} }
|
||
^^^^^^^^^^^^
|
||
|
||
Roc can't generate an implementation of the `Encode.Encoding` ability
|
||
for
|
||
|
||
{ x : A }
|
||
|
||
In particular, an implementation for
|
||
|
||
A
|
||
|
||
cannot be generated.
|
||
|
||
Tip: `A` does not implement `Encoding`. Consider adding a custom
|
||
implementation or `has Encode.Encoding` to the definition of `A`.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
cycle_through_non_function_top_level,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [t2] to "./platform"
|
||
|
||
force : ({} -> I64) -> I64
|
||
force = \eval -> eval {}
|
||
|
||
t1 = \_ -> force (\_ -> t2)
|
||
|
||
t2 = t1 {}
|
||
"#
|
||
),
|
||
@r#"
|
||
── CIRCULAR DEFINITION ─────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `t1` definition is causing a very tricky infinite loop:
|
||
|
||
6│ t1 = \_ -> force (\_ -> t2)
|
||
^^
|
||
|
||
The `t1` value depends on itself through the following chain of
|
||
definitions:
|
||
|
||
┌─────┐
|
||
│ t1
|
||
│ ↓
|
||
│ t2
|
||
└─────┘
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
opaque_ability_impl_not_found_shorthand_syntax,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
Eq has eq : a, a -> U64 | a has Eq
|
||
|
||
A := U8 has [Eq {eq}]
|
||
"#
|
||
),
|
||
@r###"
|
||
── IMPLEMENTATION NOT FOUND ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
An implementation of `eq` could not be found in this scope:
|
||
|
||
5│ A := U8 has [Eq {eq}]
|
||
^^
|
||
|
||
Tip: consider adding a value of name `eq` in this scope, or using
|
||
another variable that implements this ability member, like
|
||
{ eq: myeq }
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Eq` ability:
|
||
|
||
5│ A := U8 has [Eq {eq}]
|
||
^^^^^^^
|
||
|
||
The following necessary members are missing implementations:
|
||
|
||
eq
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_ability_impl_not_found,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A, myEq] to "./platform"
|
||
|
||
Eq has eq : a, a -> Bool | a has Eq
|
||
|
||
A := U8 has [ Eq {eq: aEq} ]
|
||
|
||
myEq = \m, n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNRECOGNIZED NAME ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Nothing is named `aEq` in this scope.
|
||
|
||
5│ A := U8 has [ Eq {eq: aEq} ]
|
||
^^^
|
||
|
||
Did you mean one of these?
|
||
|
||
Eq
|
||
myEq
|
||
eq
|
||
U8
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Eq` ability:
|
||
|
||
5│ A := U8 has [ Eq {eq: aEq} ]
|
||
^^^^^^^^^^^^
|
||
|
||
The following necessary members are missing implementations:
|
||
|
||
eq
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_ability_impl_optional,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A, myEq] to "./platform"
|
||
|
||
Eq has eq : a, a -> Bool | a has Eq
|
||
|
||
A := U8 has [ Eq {eq ? aEq} ]
|
||
|
||
myEq = \m, n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPTIONAL ABILITY IMPLEMENTATION ─────────────────────── /code/proj/Main.roc ─
|
||
|
||
Ability implementations cannot be optional:
|
||
|
||
5│ A := U8 has [ Eq {eq ? aEq} ]
|
||
^^^^^^^^
|
||
|
||
Custom implementations must be supplied fully.
|
||
|
||
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Eq` ability:
|
||
|
||
5│ A := U8 has [ Eq {eq ? aEq} ]
|
||
^^^^^^^^^^^^^
|
||
|
||
The following necessary members are missing implementations:
|
||
|
||
eq
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_builtin_ability_impl_optional,
|
||
indoc!(
|
||
r#"
|
||
app "test"
|
||
imports [Encode.{ Encoding }]
|
||
provides [A, myEncoder] to "./platform"
|
||
|
||
A := U8 has [ Encoding {toEncoder ? myEncoder} ]
|
||
|
||
myEncoder = 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── OPTIONAL ABILITY IMPLEMENTATION ─────────────────────── /code/proj/Main.roc ─
|
||
|
||
Ability implementations cannot be optional:
|
||
|
||
5│ A := U8 has [ Encoding {toEncoder ? myEncoder} ]
|
||
^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
Custom implementations must be supplied fully.
|
||
|
||
Hint: if you want this implementation to be derived, don't include a
|
||
record of implementations. For example, has [Encoding] will attempt
|
||
to derive `Encoding`
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Encoding` ability:
|
||
|
||
5│ A := U8 has [ Encoding {toEncoder ? myEncoder} ]
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The following necessary members are missing implementations:
|
||
|
||
toEncoder
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_ability_impl_qualified,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
Eq has eq : a, a -> Bool | a has Eq
|
||
|
||
A := U8 has [ Eq {eq : Bool.eq} ]
|
||
"#
|
||
),
|
||
@r###"
|
||
── QUALIFIED ABILITY IMPLEMENTATION ────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability implementation is qualified:
|
||
|
||
5│ A := U8 has [ Eq {eq : Bool.eq} ]
|
||
^^^^^^^
|
||
|
||
Custom implementations must be defined in the local scope, and
|
||
unqualified.
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Eq` ability:
|
||
|
||
5│ A := U8 has [ Eq {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"
|
||
|
||
Eq has eq : a, a -> Bool | a has Eq
|
||
|
||
A := U8 has [ Eq {eq : \m, n -> m == n} ]
|
||
"#
|
||
),
|
||
@r###"
|
||
── ABILITY IMPLEMENTATION NOT IDENTIFIER ───────────────── /code/proj/Main.roc ─
|
||
|
||
This ability implementation is not an identifier:
|
||
|
||
5│ A := U8 has [ Eq {eq : \m, n -> m == n} ]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Custom ability implementations defined in this position can only be
|
||
unqualified identifiers, not arbitrary expressions.
|
||
|
||
Tip: consider defining this expression as a variable.
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
This type does not fully implement the `Eq` ability:
|
||
|
||
5│ A := U8 has [ Eq {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"
|
||
|
||
Eq has eq : a, a -> Bool | a has Eq
|
||
|
||
A := U8 has [ Eq {eq: eqA, eq: eqA} ]
|
||
|
||
eqA = \@A m, @A n -> m == n
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE IMPLEMENTATION ────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability member implementation is duplicate:
|
||
|
||
5│ A := U8 has [ Eq {eq: eqA, eq: eqA} ]
|
||
^^^^^^^
|
||
|
||
The first implementation was defined here:
|
||
|
||
5│ A := U8 has [ Eq {eq: eqA, eq: eqA} ]
|
||
^^^^^^^
|
||
|
||
Only one custom implementation can be defined for an ability member.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
implements_type_not_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A, Foo] to "./platform"
|
||
|
||
Foo := {}
|
||
|
||
A := U8 has [ Foo {} ]
|
||
"#
|
||
),
|
||
@r###"
|
||
── NOT AN ABILITY ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This identifier is not an ability in scope:
|
||
|
||
5│ A := U8 has [ Foo {} ]
|
||
^^^
|
||
|
||
Only abilities can be implemented.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
derive_non_builtin_ability,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [A] to "./platform"
|
||
|
||
Ab has ab : a -> a | a has Ab
|
||
|
||
A := {} has [Ab]
|
||
"#
|
||
),
|
||
@r#"
|
||
── ILLEGAL DERIVE ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This ability cannot be derived:
|
||
|
||
5│ A := {} has [Ab]
|
||
^^
|
||
|
||
Only builtin abilities can be derived.
|
||
|
||
Note: The builtin abilities are `Encode.Encoding`
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
has_encoding_for_function,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Encode] provides [A] to "./platform"
|
||
|
||
A a := a -> a has [Encode.Encoding]
|
||
"#
|
||
),
|
||
@r#"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
Roc can't derive an implementation of the `Encode.Encoding` for `A`:
|
||
|
||
3│ A a := a -> a has [Encode.Encoding]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Note: `Encoding` cannot be generated for functions.
|
||
|
||
Tip: You can define a custom implementation of `Encode.Encoding` for `A`.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
has_encoding_for_non_encoding_alias,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Encode] provides [A] to "./platform"
|
||
|
||
A := B has [Encode.Encoding]
|
||
|
||
B := {}
|
||
"#
|
||
),
|
||
@r#"
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
Roc can't derive an implementation of the `Encode.Encoding` for `A`:
|
||
|
||
3│ A := B has [Encode.Encoding]
|
||
^^^^^^^^^^^^^^^
|
||
|
||
Tip: `B` does not implement `Encoding`. Consider adding a custom
|
||
implementation or `has Encode.Encoding` to the definition of `B`.
|
||
|
||
Tip: You can define a custom implementation of `Encode.Encoding` for `A`.
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
has_encoding_for_other_has_encoding,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Encode] provides [A] to "./platform"
|
||
|
||
A := B has [Encode.Encoding]
|
||
|
||
B := {} has [Encode.Encoding]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
has_encoding_for_recursive_deriving,
|
||
indoc!(
|
||
r#"
|
||
app "test" imports [Encode] provides [MyNat] to "./platform"
|
||
|
||
MyNat := [S MyNat, Z] has [Encode.Encoding]
|
||
"#
|
||
),
|
||
@"" // no error
|
||
);
|
||
|
||
test_report!(
|
||
shadowing_top_level_scope,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [ main ] to "./platform"
|
||
|
||
main = 1
|
||
|
||
main = \n -> n + 2
|
||
"#
|
||
),
|
||
@r###"
|
||
── DUPLICATE NAME ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The `main` name is first defined here:
|
||
|
||
3│ main = 1
|
||
^^^^
|
||
|
||
But then it's defined a second time here:
|
||
|
||
5│ main = \n -> n + 2
|
||
^^^^
|
||
|
||
Since these variables have the same name, it's easy to use the wrong
|
||
one on accident. Give one of them a new name.
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
5│ main = \n -> n + 2
|
||
^^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
issue_1755,
|
||
indoc!(
|
||
r#"
|
||
Handle := {}
|
||
|
||
await : Result a err, (a -> Result b err) -> Result b err
|
||
open : {} -> Result Handle *
|
||
close : Handle -> Result {} *
|
||
|
||
withOpen : (Handle -> Result {} *) -> Result {} *
|
||
withOpen = \callback ->
|
||
handle <- await (open {})
|
||
{} <- await (callback handle)
|
||
close handle
|
||
|
||
withOpen
|
||
"#
|
||
),
|
||
@r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the body of the `withOpen` definition:
|
||
|
||
10│ withOpen : (Handle -> Result {} *) -> Result {} *
|
||
11│ withOpen = \callback ->
|
||
12│> handle <- await (open {})
|
||
13│> {} <- await (callback handle)
|
||
14│> close handle
|
||
|
||
The type annotation on `withOpen` says this `await` call should have the
|
||
type:
|
||
|
||
Result {} *
|
||
|
||
However, the type of this `await` call is connected to another type in a
|
||
way that isn't reflected in this annotation.
|
||
|
||
Tip: Any connection between types must use a named type variable, not
|
||
a `*`! Maybe the annotation on `withOpen` should have a named type
|
||
variable in place of the `*`?
|
||
"#
|
||
);
|
||
|
||
test_report!(
|
||
recursive_body_and_annotation_with_inference_disagree,
|
||
indoc!(
|
||
r#"
|
||
f : _ -> (_ -> Str)
|
||
f = \_ -> if True then {} else f {}
|
||
|
||
f
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This expression is used in an unexpected way:
|
||
|
||
5│ f = \_ -> if True then {} else f {}
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
It is a value of type:
|
||
|
||
{}
|
||
|
||
But you are trying to use it as:
|
||
|
||
a -> Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
same_phantom_types_unify,
|
||
indoc!(
|
||
r#"
|
||
F a b := b
|
||
|
||
foo : F Str Str -> {}
|
||
|
||
x : F Str Str
|
||
|
||
foo x
|
||
"#
|
||
),
|
||
@r"" // okay
|
||
);
|
||
|
||
test_report!(
|
||
different_phantom_types,
|
||
indoc!(
|
||
r#"
|
||
F a b := b
|
||
|
||
foo : F Str Str -> {}
|
||
|
||
x : F U8 Str
|
||
|
||
foo x
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 1st argument to `foo` has an unexpected type:
|
||
|
||
10│ foo x
|
||
^
|
||
|
||
This `x` value is a:
|
||
|
||
F U8 Str
|
||
|
||
But `foo` needs its 1st argument to be:
|
||
|
||
F Str Str
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
#[ignore = "TODO This should be a type error"]
|
||
phantom_type_bound_to_ability_not_implementing,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [x] to "./platform"
|
||
|
||
Foo has foo : a -> a | a has Foo
|
||
|
||
F a b := b | a has Foo
|
||
|
||
Hash := {}
|
||
|
||
x : F Hash {}
|
||
"#
|
||
),
|
||
@r###"
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
int_literals_cannot_fit_in_same_type,
|
||
indoc!(
|
||
r#"
|
||
0x80000000000000000000000000000000 == -0x80000000000000000000000000000000
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `isEq` has an unexpected type:
|
||
|
||
4│ 0x80000000000000000000000000000000 == -0x80000000000000000000000000000000
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The argument is an integer of type:
|
||
|
||
I128
|
||
|
||
But `isEq` needs its 2nd argument to be:
|
||
|
||
U128
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
num_literals_cannot_fit_in_same_type,
|
||
indoc!(
|
||
r#"
|
||
170141183460469231731687303715884105728 == -170141183460469231731687303715884105728
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `isEq` has an unexpected type:
|
||
|
||
4│ 170141183460469231731687303715884105728 == -170141183460469231731687303715884105728
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
The argument is a number of type:
|
||
|
||
I128 or Dec
|
||
|
||
But `isEq` needs its 2nd argument to be:
|
||
|
||
U128
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
recursive_alias_cannot_leak_into_recursive_opaque,
|
||
indoc!(
|
||
r#"
|
||
OList := [Nil, Cons {} OList]
|
||
|
||
AList : [Nil, Cons {} AList]
|
||
|
||
alist : AList
|
||
|
||
olist : OList
|
||
olist =
|
||
when alist is
|
||
Nil -> @OList Nil
|
||
Cons _ lst -> lst
|
||
|
||
olist
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Something is off with the 2nd branch of this `when` expression:
|
||
|
||
10│ olist : OList
|
||
11│ olist =
|
||
12│> when alist is
|
||
13│> Nil -> @OList Nil
|
||
14│> Cons _ lst -> lst
|
||
|
||
This `lst` value is a:
|
||
|
||
[Cons {} ∞, Nil] as ∞
|
||
|
||
But the type annotation on `olist` says it should be:
|
||
|
||
OList
|
||
|
||
Tip: Type comparisons between an opaque type are only ever equal if
|
||
both types are the same opaque type. Did you mean to create an opaque
|
||
type by wrapping it? If I have an opaque type Age := U32 I can create
|
||
an instance of this opaque type by doing @Age 23.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
opaque_wrap_function_mismatch,
|
||
indoc!(
|
||
r#"
|
||
A := U8
|
||
List.map [1u16, 2u16, 3u16] @A
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This 2nd argument to `map` has an unexpected type:
|
||
|
||
5│ List.map [1u16, 2u16, 3u16] @A
|
||
^^
|
||
|
||
This A opaque wrapping has the type:
|
||
|
||
U8 -> A
|
||
|
||
But `map` needs its 2nd argument to be:
|
||
|
||
U16 -> A
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
symbols_not_bound_in_all_patterns,
|
||
indoc!(
|
||
r#"
|
||
when A "" is
|
||
A x | B y -> x
|
||
"#
|
||
),
|
||
@r###"
|
||
── NAME NOT BOUND IN ALL PATTERNS ──────────────────────── /code/proj/Main.roc ─
|
||
|
||
`x` is not bound in all patterns of this `when` branch
|
||
|
||
5│ A x | B y -> x
|
||
^
|
||
|
||
Identifiers introduced in a `when` branch must be bound in all patterns
|
||
of the branch. Otherwise, the program would crash when it tries to use
|
||
an identifier that wasn't bound!
|
||
|
||
── NAME NOT BOUND IN ALL PATTERNS ──────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not bound in all patterns of this `when` branch
|
||
|
||
5│ A x | B y -> x
|
||
^
|
||
|
||
Identifiers introduced in a `when` branch must be bound in all patterns
|
||
of the branch. Otherwise, the program would crash when it tries to use
|
||
an identifier that wasn't bound!
|
||
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`y` is not used anywhere in your code.
|
||
|
||
5│ A x | B y -> x
|
||
^
|
||
|
||
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!(
|
||
flip_flop_catch_all_branches_not_exhaustive,
|
||
indoc!(
|
||
r#"
|
||
\x -> when x is
|
||
A B _ -> ""
|
||
A _ C -> ""
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNSAFE PATTERN ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This `when` does not cover all the possibilities:
|
||
|
||
4│> \x -> when x is
|
||
5│> A B _ -> ""
|
||
6│> A _ C -> ""
|
||
|
||
Other possibilities include:
|
||
|
||
A _ _
|
||
|
||
I would have to crash if I saw one of those! Add branches for them!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
call_with_underscore_identifier,
|
||
indoc!(
|
||
r#"
|
||
f = \x, y, z -> x + y + z
|
||
|
||
f 1 _ 1
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Underscores are not allowed in identifier names:
|
||
|
||
6│ f 1 _ 1
|
||
|
||
|
||
I recommend using camelCase, it is the standard in the Roc ecosystem.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
destructure_assignment_introduces_no_variables_nested,
|
||
indoc!(
|
||
r#"
|
||
Pair _ _ = Pair 0 1
|
||
|
||
_ = Pair 0 1
|
||
|
||
{} = {}
|
||
|
||
Foo = Foo
|
||
|
||
0
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
4│ Pair _ _ = Pair 0 1
|
||
^^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
6│ _ = Pair 0 1
|
||
^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
8│ {} = {}
|
||
^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
10│ Foo = Foo
|
||
^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
destructure_assignment_introduces_no_variables_nested_toplevel,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [] to "./platform"
|
||
|
||
Pair _ _ = Pair 0 1
|
||
|
||
_ = Pair 0 1
|
||
|
||
{} = {}
|
||
|
||
Foo = Foo
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
3│ Pair _ _ = Pair 0 1
|
||
^^^^^^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
5│ _ = Pair 0 1
|
||
^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
7│ {} = {}
|
||
^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
|
||
── UNNECESSARY DEFINITION ──────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This destructure assignment doesn't introduce any new variables:
|
||
|
||
9│ Foo = Foo
|
||
^^^
|
||
|
||
If you don't need to use the value on the right-hand-side of this
|
||
assignment, consider removing the assignment. Since Roc is purely
|
||
functional, assignments that don't introduce variables cannot affect a
|
||
program's behavior!
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
unused_shadow_specialization,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash, Id] to "./platform"
|
||
|
||
Hash has hash : a -> U64 | a has Hash
|
||
|
||
Id := {}
|
||
|
||
hash = \@Id _ -> 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── UNUSED DEFINITION ───────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
`hash` is not used anywhere in your code.
|
||
|
||
7│ hash = \@Id _ -> 0
|
||
^^^^
|
||
|
||
If you didn't intend on using `hash` then remove it so future readers of
|
||
your code don't wonder why it is there.
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
specialization_for_wrong_type,
|
||
indoc!(
|
||
r#"
|
||
app "test" provides [hash, Id, Id2] to "./platform"
|
||
|
||
Hash has hash : a -> U64 | a has Hash
|
||
|
||
Id := {} has [Hash {hash}]
|
||
Id2 := {}
|
||
|
||
hash = \@Id2 _ -> 0
|
||
"#
|
||
),
|
||
@r###"
|
||
── WRONG SPECIALIZATION TYPE ───────────────────────────── /code/proj/Main.roc ─
|
||
|
||
This specialization of `hash` is not for the expected type:
|
||
|
||
8│ hash = \@Id2 _ -> 0
|
||
^^^^
|
||
|
||
It was previously claimed to be a specialization for `Id`, but was
|
||
determined to actually specialize `Id2`!
|
||
"###
|
||
);
|
||
}
|