mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-02 19:32:17 +00:00
9705 lines
262 KiB
Rust
9705 lines
262 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::solve;
|
||
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<solve::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<solve::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.iterate
|
||
List.get
|
||
"###
|
||
);
|
||
|
||
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
|
||
"#
|
||
);
|
||
|
||
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 ─
|
||
|
||
The 1st argument to `f` is not what I expect:
|
||
|
||
9│ f bar
|
||
^^^
|
||
|
||
This `bar` value is a:
|
||
|
||
{ bar : Int a }
|
||
|
||
But `f` needs the 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 ─
|
||
|
||
The 1st argument to `f` is not what I expect:
|
||
|
||
7│ f Blue
|
||
^^^^
|
||
|
||
This `Blue` tag has the type:
|
||
|
||
[Blue]a
|
||
|
||
But `f` needs the 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 ─
|
||
|
||
The 1st argument to `f` is not what I expect:
|
||
|
||
7│ f (Blue 3.14)
|
||
^^^^^^^^^
|
||
|
||
This `Blue` tag application has the type:
|
||
|
||
[Blue (Frac a)]b
|
||
|
||
But `f` needs the 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 frac 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 frac 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 ─
|
||
|
||
The 2nd argument to `add` is not what I expect:
|
||
|
||
4│ 0x4 + "foo"
|
||
^^^^^
|
||
|
||
This argument is a string of type:
|
||
|
||
Str
|
||
|
||
But `add` needs the 2nd argument to be:
|
||
|
||
Int a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
int_frac,
|
||
indoc!(
|
||
r#"
|
||
0x4 + 3.14
|
||
"#
|
||
),
|
||
@r###"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 2nd argument to `add` is not what I expect:
|
||
|
||
4│ 0x4 + 3.14
|
||
^^^^
|
||
|
||
This argument is a frac of type:
|
||
|
||
Frac a
|
||
|
||
But `add` needs the 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 ─
|
||
|
||
The 2nd argument to `add` is not what I expect:
|
||
|
||
4│ 42 + True
|
||
^^^^
|
||
|
||
This `True` boolean has the type:
|
||
|
||
[True]a
|
||
|
||
But `add` needs the 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 ─
|
||
|
||
The 1st argument to `f` is not what I expect:
|
||
|
||
9│ f { y: 3.14 }
|
||
^^^^^^^^^^^
|
||
|
||
This argument is a record of type:
|
||
|
||
{ y : Frac a }
|
||
|
||
But `f` needs the 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 pattern in the definition of `MyAlias` is not what I expect:
|
||
|
||
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 pattern in the definition of `Age` is not what I expect:
|
||
|
||
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 ─
|
||
|
||
The 2nd argument to `add` is not what I expect:
|
||
|
||
14│ x + y + h + l + minlit + maxlit
|
||
^^^^^^
|
||
|
||
This `maxlit` value is a:
|
||
|
||
U128
|
||
|
||
But `add` needs the 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 ─
|
||
|
||
The 2nd argument to `add` is not what I expect:
|
||
|
||
4│ \{ x, y ? True } -> x + y
|
||
^
|
||
|
||
This `y` value is a:
|
||
|
||
[True]a
|
||
|
||
But `add` needs the 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 ─
|
||
|
||
The 1st argument to this function is not what I expect:
|
||
|
||
5│ f = \r -> .y r
|
||
^
|
||
|
||
This `r` value is a:
|
||
|
||
{ x : I64, y ? I64 }
|
||
|
||
But this function needs the 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 definition, but I got stuck here:
|
||
|
||
1│ app "test" provides [main] to "./platform"
|
||
2│
|
||
3│ main =
|
||
4│ when Just 4 is
|
||
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###"
|
||
── NOT END OF FILE ──────────────── tmp/when_over_indented_underscore/Test.roc ─
|
||
|
||
I expected to reach the end of the file, but got stuck here:
|
||
|
||
6│ _ -> 2
|
||
^
|
||
"###
|
||
);
|
||
|
||
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 """""".
|
||
"###
|
||
);
|
||
|
||
// 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 ─
|
||
|
||
The 2nd argument to `map` is not what I expect:
|
||
|
||
4│> x <- List.map ["a", "b"]
|
||
5│>
|
||
6│> x + 1
|
||
|
||
This argument is an anonymous function of type:
|
||
|
||
Num a -> Num a
|
||
|
||
But `map` needs the 2nd argument to be:
|
||
|
||
Str -> Num a
|
||
"###
|
||
);
|
||
|
||
test_report!(
|
||
underscore_let,
|
||
indoc!(
|
||
r#"
|
||
_ = 3
|
||
|
||
4
|
||
"#
|
||
),
|
||
@r###"
|
||
── SYNTAX PROBLEM ──────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
Underscore patterns are not allowed in definitions
|
||
|
||
4│ _ = 3
|
||
^
|
||
"###
|
||
);
|
||
|
||
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 ─
|
||
|
||
The 2nd argument to `mul` is not what I expect:
|
||
|
||
5│ mult = \a, b -> a * b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64
|
||
|
||
But `mul` needs the 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 ─
|
||
|
||
The 2nd argument to `mul` is not what I expect:
|
||
|
||
5│ mult = \a, b -> a * b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64
|
||
|
||
But `mul` needs the 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 ─
|
||
|
||
The 1st argument to `isEmpty` is not what I expect:
|
||
|
||
9│ isEmpty (Name "boo")
|
||
^^^^^^^^^^
|
||
|
||
This `Name` tag application has the type:
|
||
|
||
[Name Str]a
|
||
|
||
But `isEmpty` needs the 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 ─
|
||
|
||
The 1st argument to `c` is not what I expect:
|
||
|
||
8│ f = \c -> c 6
|
||
^
|
||
|
||
This argument is a number of type:
|
||
|
||
Num a
|
||
|
||
But `c` needs the 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 frac",
|
||
_ => "an integer",
|
||
};
|
||
|
||
let real = format!(indoc!(
|
||
r#"
|
||
── TYPE MISMATCH ───────────────────────────────────────── /code/proj/Main.roc ─
|
||
|
||
The 1st argument to `use` is not what I expect:
|
||
|
||
5│ use {}{}
|
||
{}
|
||
|
||
This argument is {} of type:
|
||
|
||
{}
|
||
|
||
But `use` needs the 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 ─
|
||
|
||
The 2nd argument to `get` is not what I expect:
|
||
|
||
4│ List.get [1,2,3] -1
|
||
^^
|
||
|
||
This argument is a number of type:
|
||
|
||
I8, I16, F32, I32, F64, I64, I128, or Dec
|
||
|
||
But `get` needs the 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 ─
|
||
|
||
The 2nd argument to `get` is not what I expect:
|
||
|
||
5│ List.get [1,2,3] a
|
||
^
|
||
|
||
This `a` value is a:
|
||
|
||
F64, I64, I128, or Dec
|
||
|
||
But `get` needs the 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 ─
|
||
|
||
The 2nd argument to `get` is not what I expect:
|
||
|
||
6│ List.get [1,2,3] b
|
||
^
|
||
|
||
This `b` value is a:
|
||
|
||
F64, I64, I128, or Dec
|
||
|
||
But `get` needs the 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]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 ─
|
||
|
||
The 1st argument to `y` is not what I expect:
|
||
|
||
9│ n = y 1u8
|
||
^^^
|
||
|
||
This argument is an integer of type:
|
||
|
||
U8
|
||
|
||
But `y` needs the 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 ─
|
||
|
||
The type `Id` does not fully implement the ability `Eq`. The following
|
||
specializations are missing:
|
||
|
||
A specialization for `le`, which is defined here:
|
||
|
||
5│ le : a, a -> Bool | a has Eq
|
||
^^
|
||
"#
|
||
);
|
||
|
||
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###"
|
||
── 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`. The following
|
||
specializations are missing:
|
||
|
||
A specialization for `hash`, which is defined here:
|
||
|
||
4│ hash : a -> U64 | a has 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 ─
|
||
|
||
The 2nd argument to `isEq` is not what I expect:
|
||
|
||
9│ Job lst -> lst == ""
|
||
^^
|
||
|
||
This argument is a string of type:
|
||
|
||
Str
|
||
|
||
But `isEq` needs the 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 ─
|
||
|
||
The 1st argument to `remove` is not what I expect:
|
||
|
||
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`.
|
||
|
||
── INCOMPLETE ABILITY IMPLEMENTATION ───────────────────── /code/proj/Main.roc ─
|
||
|
||
The type `A` does not fully implement the ability `Encoding`. The
|
||
following specializations are missing:
|
||
|
||
A specialization for `toEncoder`, which is defined here:
|
||
|
||
5│
|
||
^^^^^^^^^
|
||
"###
|
||
);
|
||
|
||
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 }
|
||
"###
|
||
);
|
||
|
||
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
|
||
"###
|
||
);
|
||
|
||
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.
|
||
|
||
|
||
"###
|
||
);
|
||
|
||
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`
|
||
"###
|
||
);
|
||
|
||
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.
|
||
"###
|
||
);
|
||
|
||
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.
|
||
"###
|
||
);
|
||
|
||
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.
|
||
"#
|
||
);
|
||
|
||
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 ─
|
||
|
||
The 1st argument to `foo` is not what I expect:
|
||
|
||
10│ foo x
|
||
^
|
||
|
||
This `x` value is a:
|
||
|
||
F U8 Str
|
||
|
||
But `foo` needs the 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 ─
|
||
|
||
The 2nd argument to `isEq` is not what I expect:
|
||
|
||
4│ 0x80000000000000000000000000000000 == -0x80000000000000000000000000000000
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This argument is an integer of type:
|
||
|
||
I128
|
||
|
||
But `isEq` needs the 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 ─
|
||
|
||
The 2nd argument to `isEq` is not what I expect:
|
||
|
||
4│ 170141183460469231731687303715884105728 == -170141183460469231731687303715884105728
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
This argument is a number of type:
|
||
|
||
I128 or Dec
|
||
|
||
But `isEq` needs the 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 ─
|
||
|
||
The 2nd argument to `map` is not what I expect:
|
||
|
||
5│ List.map [1u16, 2u16, 3u16] @A
|
||
^^
|
||
|
||
This A opaque wrapping has the type:
|
||
|
||
U8 -> A
|
||
|
||
But `map` needs the 2nd argument to be:
|
||
|
||
U16 -> A
|
||
"###
|
||
);
|
||
}
|