diff --git a/compiler/can/src/def.rs b/compiler/can/src/def.rs index 2c263b5fe6..7f1302f384 100644 --- a/compiler/can/src/def.rs +++ b/compiler/can/src/def.rs @@ -1443,6 +1443,8 @@ fn to_pending_def<'a>( SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) | Nested(sub_def) => { to_pending_def(env, var_store, sub_def, scope, pattern_type) } + + NotYetImplemented(s) => todo!("{}", s), } } diff --git a/compiler/can/src/operator.rs b/compiler/can/src/operator.rs index edbf013919..dcc9e8559d 100644 --- a/compiler/can/src/operator.rs +++ b/compiler/can/src/operator.rs @@ -47,6 +47,8 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> { Nested(alias @ Alias { .. }) => Nested(alias), ann @ Annotation(_, _) => Nested(ann), Nested(ann @ Annotation(_, _)) => Nested(ann), + Nested(NotYetImplemented(s)) => todo!("{}", s), + NotYetImplemented(s) => todo!("{}", s), } } diff --git a/compiler/fmt/src/def.rs b/compiler/fmt/src/def.rs index 8cd03f076e..aae8679b35 100644 --- a/compiler/fmt/src/def.rs +++ b/compiler/fmt/src/def.rs @@ -20,6 +20,7 @@ impl<'a> Formattable<'a> for Def<'a> { spaces.iter().any(|s| is_comment(s)) || sub_def.is_multiline() } Nested(def) => def.is_multiline(), + NotYetImplemented(s) => todo!("{}", s), } } @@ -66,6 +67,7 @@ impl<'a> Formattable<'a> for Def<'a> { fmt_spaces(buf, spaces.iter(), indent); } Nested(def) => def.format(buf, indent), + NotYetImplemented(s) => todo!("{}", s), } } } diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index cb4e966b93..5b24e811ec 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -101,6 +101,8 @@ fn generate_module_doc<'a>( } => (acc, None), Body(_, _) | Nested(_) => (acc, None), + + NotYetImplemented(s) => todo!("{}", s), } } diff --git a/compiler/parse/fuzz/.gitignore b/compiler/parse/fuzz/.gitignore new file mode 100644 index 0000000000..572e03bdf3 --- /dev/null +++ b/compiler/parse/fuzz/.gitignore @@ -0,0 +1,4 @@ + +target +corpus +artifacts diff --git a/compiler/parse/fuzz/Cargo.lock b/compiler/parse/fuzz/Cargo.lock new file mode 100644 index 0000000000..78597e9fca --- /dev/null +++ b/compiler/parse/fuzz/Cargo.lock @@ -0,0 +1,182 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "arbitrary" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "bumpalo" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" + +[[package]] +name = "cc" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "im" +version = "14.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696059c87b83c5a258817ecd67c3af915e3ed141891fc35a1e79908801cf0ce7" +dependencies = [ + "bitmaps", + "rand_core 0.5.1", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "im-rc" +version = "14.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303f7e6256d546e01979071417432425f15c1891fb309a5f2d724ee908fabd6e" +dependencies = [ + "bitmaps", + "rand_core 0.5.1", + "rand_xoshiro", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "inlinable_string" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6ee2a7da03bfc3b66ca47c92c2e392fcc053ea040a85561749b026f7aad09a" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libfuzzer-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8c42ab62f43795ed77a965ed07994c5584cdc94fd0ebf14b22ac1524077acc" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_xoshiro" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "roc_collections" +version = "0.1.0" +dependencies = [ + "bumpalo", + "im", + "im-rc", + "wyhash", +] + +[[package]] +name = "roc_module" +version = "0.1.0" +dependencies = [ + "bumpalo", + "inlinable_string", + "lazy_static", + "roc_collections", + "roc_region", +] + +[[package]] +name = "roc_parse" +version = "0.1.0" +dependencies = [ + "bumpalo", + "encode_unicode", + "inlinable_string", + "roc_collections", + "roc_module", + "roc_region", +] + +[[package]] +name = "roc_parse-fuzz" +version = "0.0.0" +dependencies = [ + "bumpalo", + "libfuzzer-sys", + "roc_parse", +] + +[[package]] +name = "roc_region" +version = "0.1.0" + +[[package]] +name = "sized-chunks" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59044ea371ad781ff976f7b06480b9f0180e834eda94114f2afb4afc12b7718" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wyhash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782a50f48ac4336916227cd199c61c7b42f38d0ad705421b49eb12c74c53ae00" +dependencies = [ + "rand_core 0.4.2", +] diff --git a/compiler/parse/fuzz/Cargo.toml b/compiler/parse/fuzz/Cargo.toml new file mode 100644 index 0000000000..2b60a1683e --- /dev/null +++ b/compiler/parse/fuzz/Cargo.toml @@ -0,0 +1,39 @@ + +[package] +name = "roc_parse-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.3" +bumpalo = { version = "3.2", features = ["collections"] } + +[dependencies.roc_parse] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[[bin]] +name = "fuzz_expr" +path = "fuzz_targets/fuzz_expr.rs" +test = false +doc = false + +[[bin]] +name = "fuzz_defs" +path = "fuzz_targets/fuzz_defs.rs" +test = false +doc = false + +[[bin]] +name = "fuzz_header" +path = "fuzz_targets/fuzz_header.rs" +test = false +doc = false diff --git a/compiler/parse/fuzz/README.md b/compiler/parse/fuzz/README.md new file mode 100644 index 0000000000..c31c048a6d --- /dev/null +++ b/compiler/parse/fuzz/README.md @@ -0,0 +1,11 @@ +To setup fuzzing you will need to install cargo-fuzz and run with rust nightly: + +``` +$ cargo install cargo-fuzz +$ cargo +nightly fuzz run -j -- -dict=dict.txt +``` + +The different targets can be found by running `cargo fuzz list`. + +When a bug is found, it will be reported with commands to run it again and look for a minimized version. +If you are going to file a bug, please minimize the input before filing the bug. diff --git a/compiler/parse/fuzz/dict.txt b/compiler/parse/fuzz/dict.txt new file mode 100644 index 0000000000..c22976ccb2 --- /dev/null +++ b/compiler/parse/fuzz/dict.txt @@ -0,0 +1,36 @@ +"if" +"then" +"else" +"when" +"as" +"is" +"expect" + +"app" +"platform" +"provides" +"requires" +"exposes" +"imports" +"effects" +"interface" + +"|>" +"==" +"!=" +"&&" +"||" +"+" +"*" +"-" +"//" +"/" +"<=" +"<" +">=" +">" +"^" +"%%" +"%" + +"->" \ No newline at end of file diff --git a/compiler/parse/fuzz/fuzz_targets/fuzz_defs.rs b/compiler/parse/fuzz/fuzz_targets/fuzz_defs.rs new file mode 100644 index 0000000000..f02bfc587c --- /dev/null +++ b/compiler/parse/fuzz/fuzz_targets/fuzz_defs.rs @@ -0,0 +1,11 @@ +#![no_main] +use bumpalo::Bump; +use libfuzzer_sys::fuzz_target; +use roc_parse::test_helpers::parse_defs_with; + +fuzz_target!(|data: &[u8]| { + if let Ok(input) = std::str::from_utf8(data) { + let arena = Bump::new(); + let _actual = parse_defs_with(&arena, input.trim()); + } +}); diff --git a/compiler/parse/fuzz/fuzz_targets/fuzz_expr.rs b/compiler/parse/fuzz/fuzz_targets/fuzz_expr.rs new file mode 100644 index 0000000000..d130a0c621 --- /dev/null +++ b/compiler/parse/fuzz/fuzz_targets/fuzz_expr.rs @@ -0,0 +1,11 @@ +#![no_main] +use bumpalo::Bump; +use libfuzzer_sys::fuzz_target; +use roc_parse::test_helpers::parse_expr_with; + +fuzz_target!(|data: &[u8]| { + if let Ok(input) = std::str::from_utf8(data) { + let arena = Bump::new(); + let _actual = parse_expr_with(&arena, input.trim()); + } +}); diff --git a/compiler/parse/fuzz/fuzz_targets/fuzz_header.rs b/compiler/parse/fuzz/fuzz_targets/fuzz_header.rs new file mode 100644 index 0000000000..e04f338c87 --- /dev/null +++ b/compiler/parse/fuzz/fuzz_targets/fuzz_header.rs @@ -0,0 +1,11 @@ +#![no_main] +use bumpalo::Bump; +use libfuzzer_sys::fuzz_target; +use roc_parse::test_helpers::parse_header_with; + +fuzz_target!(|data: &[u8]| { + if let Ok(input) = std::str::from_utf8(data) { + let arena = Bump::new(); + let _actual = parse_header_with(&arena, input.trim()); + } +}); diff --git a/compiler/parse/src/ast.rs b/compiler/parse/src/ast.rs index 3339057ec9..a227e4a6d9 100644 --- a/compiler/parse/src/ast.rs +++ b/compiler/parse/src/ast.rs @@ -277,6 +277,8 @@ pub enum Def<'a> { /// This is used only to avoid cloning when reordering expressions (e.g. in desugar()). /// It lets us take a (&Def) and create a plain (Def) from it. Nested(&'a Def<'a>), + + NotYetImplemented(&'static str), } #[derive(Debug, Clone, PartialEq)] diff --git a/compiler/parse/src/expr.rs b/compiler/parse/src/expr.rs index 2d67ee3b33..54f40f6b02 100644 --- a/compiler/parse/src/expr.rs +++ b/compiler/parse/src/expr.rs @@ -561,7 +561,7 @@ fn annotation_or_alias<'a>( ann: loc_ann, }, Apply(_, _) => { - panic!("TODO gracefully handle invalid Apply in type annotation"); + Def::NotYetImplemented("TODO gracefully handle invalid Apply in type annotation") } SpaceAfter(value, spaces_before) => Def::SpaceAfter( arena.alloc(annotation_or_alias(arena, value, region, loc_ann)), @@ -574,19 +574,19 @@ fn annotation_or_alias<'a>( Nested(value) => annotation_or_alias(arena, value, region, loc_ann), PrivateTag(_) => { - panic!("TODO gracefully handle trying to use a private tag as an annotation."); + Def::NotYetImplemented("TODO gracefully handle trying to use a private tag as an annotation.") } QualifiedIdentifier { .. } => { - panic!("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`"); + Def::NotYetImplemented("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`") } NumLiteral(_) | NonBase10Literal { .. } | FloatLiteral(_) | StrLiteral(_) => { - panic!("TODO gracefully handle trying to annotate a litera"); + Def::NotYetImplemented("TODO gracefully handle trying to annotate a litera") } Underscore => { - panic!("TODO gracefully handle trying to give a type annotation to an undrscore"); + Def::NotYetImplemented("TODO gracefully handle trying to give a type annotation to an undrscore") } Malformed(_) => { - panic!("TODO translate a malformed pattern into a malformed annotation"); + Def::NotYetImplemented("TODO translate a malformed pattern into a malformed annotation") } Identifier(ident) => { // This is a regular Annotation @@ -633,7 +633,13 @@ fn parse_def_expr<'a>( )) // `<` because '=' should be same indent (or greater) as the entire def-expr } else if equals_sign_indent < def_start_col { - todo!("TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}", equals_sign_indent, def_start_col); + Err(( + Fail { + attempting: state.attempting, + reason: FailReason::NotYetImplemented(format!("TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}", equals_sign_indent, def_start_col)), + }, + state, + )) } else { // Indented more beyond the original indent of the entire def-expr. let indented_more = def_start_col + 1; @@ -720,7 +726,15 @@ fn parse_def_signature<'a>( )) // `<` because ':' should be same indent or greater } else if colon_indent < original_indent { - panic!("TODO the : in this declaration seems outdented"); + Err(( + Fail { + attempting: state.attempting, + reason: FailReason::NotYetImplemented( + "TODO the : in this declaration seems outdented".to_string(), + ), + }, + state, + )) } else { // Indented more beyond the original indent. let indented_more = original_indent + 1; @@ -1120,7 +1134,15 @@ mod when { ), move |arena, state, (case_indent, loc_condition)| { if case_indent < min_indent { - panic!("TODO case wasn't indented enough"); + return Err(( + Fail { + attempting: state.attempting, + reason: FailReason::NotYetImplemented( + "TODO case wasn't indented enough".to_string(), + ), + }, + state, + )); } // Everything in the branches must be indented at least as much as the case itself. @@ -1178,9 +1200,15 @@ mod when { if alternatives_indented_correctly(&loc_patterns, original_indent) { Ok(((loc_patterns, loc_guard), state)) } else { - panic!( - "TODO additional branch didn't have same indentation as first branch" - ); + Err(( + Fail { + attempting: state.attempting, + reason: FailReason::NotYetImplemented( + "TODO additional branch didn't have same indentation as first branch".to_string(), + ), + }, + state, + )) } }, ), @@ -1490,10 +1518,16 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { }); } Err(malformed) => { - panic!( - "TODO early return malformed pattern {:?}", - malformed - ); + return Err(( + Fail { + attempting: state.attempting, + reason: FailReason::NotYetImplemented(format!( + "TODO early return malformed pattern {:?}", + malformed + )), + }, + state, + )); } } } diff --git a/compiler/parse/src/lib.rs b/compiler/parse/src/lib.rs index af7f71fbcf..858070e101 100644 --- a/compiler/parse/src/lib.rs +++ b/compiler/parse/src/lib.rs @@ -24,4 +24,5 @@ pub mod number_literal; pub mod pattern; pub mod problems; pub mod string_literal; +pub mod test_helpers; pub mod type_annotation; diff --git a/compiler/parse/src/parser.rs b/compiler/parse/src/parser.rs index f83b9cbc46..4270c8d1ec 100644 --- a/compiler/parse/src/parser.rs +++ b/compiler/parse/src/parser.rs @@ -224,6 +224,7 @@ pub enum FailReason { BadUtf8, ReservedKeyword(Region), ArgumentsBeforeEquals(Region), + NotYetImplemented(String), } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/compiler/parse/src/string_literal.rs b/compiler/parse/src/string_literal.rs index 9c909bb645..85fcd772ff 100644 --- a/compiler/parse/src/string_literal.rs +++ b/compiler/parse/src/string_literal.rs @@ -1,8 +1,8 @@ use crate::ast::{Attempting, EscapedChar, StrLiteral, StrSegment}; use crate::expr; use crate::parser::{ - allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, - ParseResult, Parser, State, + allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, Fail, + FailReason, ParseResult, Parser, State, }; use bumpalo::collections::vec::Vec; use bumpalo::Bump; @@ -279,7 +279,16 @@ where // lines.push(line); // Ok((StrLiteral::Block(lines.into_bump_slice()), state)) - todo!("TODO parse this line in a block string: {:?}", line); + Err(( + Fail { + attempting: state.attempting, + reason: FailReason::NotYetImplemented(format!( + "TODO parse this line in a block string: {:?}", + line + )), + }, + state, + )) } Err(reason) => state.fail(reason), }; diff --git a/compiler/parse/src/test_helpers.rs b/compiler/parse/src/test_helpers.rs new file mode 100644 index 0000000000..b7a78e2354 --- /dev/null +++ b/compiler/parse/src/test_helpers.rs @@ -0,0 +1,45 @@ +use crate::ast::{self, Attempting}; +use crate::blankspace::space0_before; +use crate::expr::expr; +use crate::module::{header, module_defs}; +use crate::parser::{loc, Fail, Parser, State}; +use bumpalo::collections::Vec; +use bumpalo::Bump; +use roc_region::all::Located; + +#[allow(dead_code)] +pub fn parse_expr_with<'a>(arena: &'a Bump, input: &'a str) -> Result, Fail> { + parse_loc_with(arena, input).map(|loc_expr| loc_expr.value) +} + +#[allow(dead_code)] +pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result, Fail> { + let state = State::new(input.trim().as_bytes(), Attempting::Module); + let answer = header().parse(arena, state); + answer + .map(|(loc_expr, _)| loc_expr) + .map_err(|(fail, _)| fail) +} + +#[allow(dead_code)] +pub fn parse_defs_with<'a>( + arena: &'a Bump, + input: &'a str, +) -> Result>>, Fail> { + let state = State::new(input.trim().as_bytes(), Attempting::Module); + let answer = module_defs().parse(arena, state); + answer + .map(|(loc_expr, _)| loc_expr) + .map_err(|(fail, _)| fail) +} + +#[allow(dead_code)] +pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result>, Fail> { + let state = State::new(input.trim().as_bytes(), Attempting::Module); + let parser = space0_before(loc(expr(0)), 0); + let answer = parser.parse(&arena, state); + + answer + .map(|(loc_expr, _)| loc_expr) + .map_err(|(fail, _)| fail) +} diff --git a/compiler/parse/src/type_annotation.rs b/compiler/parse/src/type_annotation.rs index 03433541fc..d4d14c7bfe 100644 --- a/compiler/parse/src/type_annotation.rs +++ b/compiler/parse/src/type_annotation.rs @@ -4,8 +4,8 @@ use crate::expr::{global_tag, private_tag}; use crate::ident::join_module_parts; use crate::keyword; use crate::parser::{ - allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either, - ParseResult, Parser, State, + allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either, Fail, + FailReason, ParseResult, Parser, State, }; use bumpalo::collections::string::String; use bumpalo::collections::vec::Vec; @@ -239,7 +239,13 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located Ok((first, state)) } else { // e.g. `Int,Int` without an arrow and return type - panic!("Invalid function signature") + Err(( + Fail { + attempting: state.attempting, + reason: FailReason::NotYetImplemented("TODO: Decide the correct error to return for 'Invalid function signature'".to_string()), + }, + state, + )) } } } diff --git a/compiler/parse/tests/helpers/mod.rs b/compiler/parse/tests/helpers/mod.rs deleted file mode 100644 index c026a340f3..0000000000 --- a/compiler/parse/tests/helpers/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -extern crate bumpalo; - -use self::bumpalo::Bump; -use roc_parse::ast::{self, Attempting}; -use roc_parse::blankspace::space0_before; -use roc_parse::parser::{loc, Fail, Parser, State}; -use roc_region::all::Located; - -#[allow(dead_code)] -pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result, Fail> { - parse_loc_with(arena, input).map(|loc_expr| loc_expr.value) -} - -#[allow(dead_code)] -pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result>, Fail> { - let state = State::new(input.trim().as_bytes(), Attempting::Module); - let parser = space0_before(loc(roc_parse::expr::expr(0)), 0); - let answer = parser.parse(&arena, state); - - answer - .map(|(loc_expr, _)| loc_expr) - .map_err(|(fail, _)| fail) -} diff --git a/compiler/parse/tests/test_parse.rs b/compiler/parse/tests/test_parse.rs index b5e9917822..8c94db8d72 100644 --- a/compiler/parse/tests/test_parse.rs +++ b/compiler/parse/tests/test_parse.rs @@ -11,11 +11,8 @@ extern crate quickcheck_macros; extern crate roc_module; extern crate roc_parse; -mod helpers; - #[cfg(test)] mod test_parse { - use crate::helpers::parse_with; use bumpalo::collections::vec::Vec; use bumpalo::{self, Bump}; use roc_module::operator::BinOp::*; @@ -33,19 +30,20 @@ mod test_parse { use roc_parse::header::ModuleName; use roc_parse::module::{interface_header, module_defs}; use roc_parse::parser::{Fail, FailReason, Parser, State}; + use roc_parse::test_helpers::parse_expr_with; use roc_region::all::{Located, Region}; use std::{f64, i64}; fn assert_parses_to<'a>(input: &'a str, expected_expr: Expr<'a>) { let arena = Bump::new(); - let actual = parse_with(&arena, input.trim()); + let actual = parse_expr_with(&arena, input.trim()); assert_eq!(Ok(expected_expr), actual); } fn assert_parsing_fails<'a>(input: &'a str, reason: FailReason, attempting: Attempting) { let arena = Bump::new(); - let actual = parse_with(&arena, input); + let actual = parse_expr_with(&arena, input); let expected_fail = Fail { reason, attempting }; assert_eq!(Err(expected_fail), actual); @@ -53,7 +51,7 @@ mod test_parse { fn assert_segments Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) { let arena = Bump::new(); - let actual = parse_with(&arena, arena.alloc(input)); + let actual = parse_expr_with(&arena, arena.alloc(input)); let expected_slice = to_expected(&arena); let expected_expr = Expr::Str(Line(&expected_slice)); @@ -77,7 +75,7 @@ mod test_parse { ("\\t", EscapedChar::Tab), ("\\\"", EscapedChar::Quote), ] { - let actual = parse_with(&arena, arena.alloc(to_input(string))); + let actual = parse_expr_with(&arena, arena.alloc(to_input(string))); let expected_slice = to_expected(*escaped, &arena); let expected_expr = Expr::Str(Line(&expected_slice)); @@ -423,7 +421,7 @@ mod test_parse { fields: &[], update: None, }; - let actual = parse_with(&arena, "{}"); + let actual = parse_expr_with(&arena, "{}"); assert_eq!(Ok(expected), actual); } @@ -455,7 +453,7 @@ mod test_parse { fields, }; - let actual = parse_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }"); + let actual = parse_expr_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }"); assert_eq!(Ok(expected), actual); } @@ -470,7 +468,7 @@ mod test_parse { Located::new(0, 0, 2, 3, Num("2")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "1+2"); + let actual = parse_expr_with(&arena, "1+2"); assert_eq!(Ok(expected), actual); } @@ -484,7 +482,7 @@ mod test_parse { Located::new(0, 0, 2, 3, Num("2")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "1-2"); + let actual = parse_expr_with(&arena, "1-2"); assert_eq!(Ok(expected), actual); } @@ -498,7 +496,7 @@ mod test_parse { Located::new(0, 0, 7, 8, Num("2")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "1 + 2"); + let actual = parse_expr_with(&arena, "1 + 2"); assert_eq!(Ok(expected), actual); } @@ -512,7 +510,7 @@ mod test_parse { Located::new(0, 0, 7, 8, Num("2")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "1 - 2"); + let actual = parse_expr_with(&arena, "1 - 2"); assert_eq!(Ok(expected), actual); } @@ -535,7 +533,7 @@ mod test_parse { Located::new(0, 0, 4, 5, Num("2")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "x + 2"); + let actual = parse_expr_with(&arena, "x + 2"); assert_eq!(Ok(expected), actual); } @@ -557,7 +555,7 @@ mod test_parse { Located::new(0, 0, 4, 5, Num("2")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "x - 2"); + let actual = parse_expr_with(&arena, "x - 2"); assert_eq!(Ok(expected), actual); } @@ -572,7 +570,7 @@ mod test_parse { Located::new(1, 1, 2, 3, Num("4")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "3 \n+ 4"); + let actual = parse_expr_with(&arena, "3 \n+ 4"); assert_eq!(Ok(expected), actual); } @@ -587,7 +585,7 @@ mod test_parse { Located::new(1, 1, 2, 3, Num("4")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "3 \n- 4"); + let actual = parse_expr_with(&arena, "3 \n- 4"); assert_eq!(Ok(expected), actual); } @@ -602,7 +600,7 @@ mod test_parse { Located::new(1, 1, 2, 3, spaced_int), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "3 *\n 4"); + let actual = parse_expr_with(&arena, "3 *\n 4"); assert_eq!(Ok(expected), actual); } @@ -617,7 +615,7 @@ mod test_parse { Located::new(1, 1, 2, 3, spaced_int), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "3 -\n 4"); + let actual = parse_expr_with(&arena, "3 -\n 4"); assert_eq!(Ok(expected), actual); } @@ -632,7 +630,7 @@ mod test_parse { Located::new(1, 1, 2, 3, Num("4")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "3 # 2 × 2\n+ 4"); + let actual = parse_expr_with(&arena, "3 # 2 × 2\n+ 4"); assert_eq!(Ok(expected), actual); } @@ -647,7 +645,7 @@ mod test_parse { Located::new(1, 1, 2, 3, Num("4")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "3 # test!\n+ 4"); + let actual = parse_expr_with(&arena, "3 # test!\n+ 4"); assert_eq!(Ok(expected), actual); } @@ -662,7 +660,7 @@ mod test_parse { Located::new(1, 1, 1, 3, spaced_int), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "12 * # test!\n 92"); + let actual = parse_expr_with(&arena, "12 * # test!\n 92"); assert_eq!(Ok(expected), actual); } @@ -678,7 +676,7 @@ mod test_parse { Located::new(3, 3, 2, 3, spaced_int2), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "3 \n+ \n\n 4"); + let actual = parse_expr_with(&arena, "3 \n+ \n\n 4"); assert_eq!(Ok(expected), actual); } @@ -702,7 +700,7 @@ mod test_parse { Located::new(0, 0, 3, 4, var2), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "x- y"); + let actual = parse_expr_with(&arena, "x- y"); assert_eq!(Ok(expected), actual); } @@ -716,7 +714,7 @@ mod test_parse { Located::new(0, 0, 4, 5, Num("5")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "-12-5"); + let actual = parse_expr_with(&arena, "-12-5"); assert_eq!(Ok(expected), actual); } @@ -730,7 +728,7 @@ mod test_parse { Located::new(0, 0, 3, 5, Num("11")), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "10*11"); + let actual = parse_expr_with(&arena, "10*11"); assert_eq!(Ok(expected), actual); } @@ -749,7 +747,7 @@ mod test_parse { Located::new(0, 0, 3, 9, BinOp(inner)), )); let expected = BinOp(outer); - let actual = parse_with(&arena, "31*42+534"); + let actual = parse_expr_with(&arena, "31*42+534"); assert_eq!(Ok(expected), actual); } @@ -771,7 +769,7 @@ mod test_parse { Located::new(0, 0, 3, 4, var2), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "x==y"); + let actual = parse_expr_with(&arena, "x==y"); assert_eq!(Ok(expected), actual); } @@ -793,7 +791,7 @@ mod test_parse { Located::new(0, 0, 5, 6, var2), )); let expected = BinOp(tuple); - let actual = parse_with(&arena, "x == y"); + let actual = parse_expr_with(&arena, "x == y"); assert_eq!(Ok(expected), actual); } @@ -807,7 +805,7 @@ mod test_parse { module_name: "", ident: "whee", }; - let actual = parse_with(&arena, "whee"); + let actual = parse_expr_with(&arena, "whee"); assert_eq!(Ok(expected), actual); } @@ -819,7 +817,7 @@ mod test_parse { module_name: "", ident: "whee", })); - let actual = parse_with(&arena, "(whee)"); + let actual = parse_expr_with(&arena, "(whee)"); assert_eq!(Ok(expected), actual); } @@ -831,7 +829,7 @@ mod test_parse { module_name: "One.Two", ident: "whee", }; - let actual = parse_with(&arena, "One.Two.whee"); + let actual = parse_expr_with(&arena, "One.Two.whee"); assert_eq!(Ok(expected), actual); } @@ -842,7 +840,7 @@ mod test_parse { fn basic_global_tag() { let arena = Bump::new(); let expected = Expr::GlobalTag("Whee"); - let actual = parse_with(&arena, "Whee"); + let actual = parse_expr_with(&arena, "Whee"); assert_eq!(Ok(expected), actual); } @@ -851,7 +849,7 @@ mod test_parse { fn basic_private_tag() { let arena = Bump::new(); let expected = Expr::PrivateTag("@Whee"); - let actual = parse_with(&arena, "@Whee"); + let actual = parse_expr_with(&arena, "@Whee"); assert_eq!(Ok(expected), actual); } @@ -867,7 +865,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "@Whee 12 34"); + let actual = parse_expr_with(&arena, "@Whee 12 34"); assert_eq!(Ok(expected), actual); } @@ -883,7 +881,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "Whee 12 34"); + let actual = parse_expr_with(&arena, "Whee 12 34"); assert_eq!(Ok(expected), actual); } @@ -901,7 +899,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "Whee (12) (34)"); + let actual = parse_expr_with(&arena, "Whee (12) (34)"); assert_eq!(Ok(expected), actual); } @@ -910,7 +908,7 @@ mod test_parse { fn qualified_global_tag() { let arena = Bump::new(); let expected = Expr::MalformedIdent("One.Two.Whee"); - let actual = parse_with(&arena, "One.Two.Whee"); + let actual = parse_expr_with(&arena, "One.Two.Whee"); assert_eq!(Ok(expected), actual); } @@ -920,7 +918,7 @@ mod test_parse { // fn qualified_private_tag() { // let arena = Bump::new(); // let expected = Expr::MalformedIdent("One.Two.@Whee"); - // let actual = parse_with(&arena, "One.Two.@Whee"); + // let actual = parse_expr_with(&arena, "One.Two.@Whee"); // assert_eq!(Ok(expected), actual); // } @@ -931,7 +929,7 @@ mod test_parse { let pattern = Located::new(0, 0, 1, 6, Pattern::GlobalTag("Thing")); let patterns = &[pattern]; let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 10, 12, Num("42")))); - let actual = parse_with(&arena, "\\Thing -> 42"); + let actual = parse_expr_with(&arena, "\\Thing -> 42"); assert_eq!(Ok(expected), actual); } @@ -940,7 +938,7 @@ mod test_parse { fn private_qualified_tag() { let arena = Bump::new(); let expected = Expr::MalformedIdent("@One.Two.Whee"); - let actual = parse_with(&arena, "@One.Two.Whee"); + let actual = parse_expr_with(&arena, "@One.Two.Whee"); assert_eq!(Ok(expected), actual); } @@ -952,7 +950,7 @@ mod test_parse { let arena = Bump::new(); let elems = &[]; let expected = List(elems); - let actual = parse_with(&arena, "[]"); + let actual = parse_expr_with(&arena, "[]"); assert_eq!(Ok(expected), actual); } @@ -963,7 +961,7 @@ mod test_parse { let arena = Bump::new(); let elems = &[]; let expected = List(elems); - let actual = parse_with(&arena, "[ ]"); + let actual = parse_expr_with(&arena, "[ ]"); assert_eq!(Ok(expected), actual); } @@ -973,7 +971,7 @@ mod test_parse { let arena = Bump::new(); let elems = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))]; let expected = List(elems); - let actual = parse_with(&arena, "[1]"); + let actual = parse_expr_with(&arena, "[1]"); assert_eq!(Ok(expected), actual); } @@ -983,7 +981,7 @@ mod test_parse { let arena = Bump::new(); let elems = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))]; let expected = List(elems); - let actual = parse_with(&arena, "[ 1 ]"); + let actual = parse_expr_with(&arena, "[ 1 ]"); assert_eq!(Ok(expected), actual); } @@ -998,7 +996,7 @@ mod test_parse { ident: "rec", }; let expected = Access(arena.alloc(var), "field"); - let actual = parse_with(&arena, "rec.field"); + let actual = parse_expr_with(&arena, "rec.field"); assert_eq!(Ok(expected), actual); } @@ -1011,7 +1009,7 @@ mod test_parse { ident: "rec", })); let expected = Access(arena.alloc(paren_var), "field"); - let actual = parse_with(&arena, "(rec).field"); + let actual = parse_expr_with(&arena, "(rec).field"); assert_eq!(Ok(expected), actual); } @@ -1024,7 +1022,7 @@ mod test_parse { ident: "rec", })); let expected = Access(arena.alloc(paren_var), "field"); - let actual = parse_with(&arena, "(One.Two.rec).field"); + let actual = parse_expr_with(&arena, "(One.Two.rec).field"); assert_eq!(Ok(expected), actual); } @@ -1040,7 +1038,7 @@ mod test_parse { arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")), "ghi", ); - let actual = parse_with(&arena, "rec.abc.def.ghi"); + let actual = parse_expr_with(&arena, "rec.abc.def.ghi"); assert_eq!(Ok(expected), actual); } @@ -1056,7 +1054,7 @@ mod test_parse { arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")), "ghi", ); - let actual = parse_with(&arena, "One.Two.rec.abc.def.ghi"); + let actual = parse_expr_with(&arena, "One.Two.rec.abc.def.ghi"); assert_eq!(Ok(expected), actual); } @@ -1077,7 +1075,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "whee 1"); + let actual = parse_expr_with(&arena, "whee 1"); assert_eq!(Ok(expected), actual); } @@ -1102,7 +1100,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "whee 12 34"); + let actual = parse_expr_with(&arena, "whee 12 34"); assert_eq!(Ok(expected), actual); } @@ -1155,7 +1153,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "a b c d"); + let actual = parse_expr_with(&arena, "a b c d"); assert_eq!(Ok(expected), actual); } @@ -1174,7 +1172,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "(whee) 1"); + let actual = parse_expr_with(&arena, "(whee) 1"); assert_eq!(Ok(expected), actual); } @@ -1191,7 +1189,7 @@ mod test_parse { }; let loc_arg1_expr = Located::new(0, 0, 1, 4, arg1_expr); let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op); - let actual = parse_with(&arena, "-foo"); + let actual = parse_expr_with(&arena, "-foo"); assert_eq!(Ok(expected), actual); } @@ -1206,7 +1204,7 @@ mod test_parse { }; let loc_arg1_expr = Located::new(0, 0, 1, 5, arg1_expr); let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op); - let actual = parse_with(&arena, "!blah"); + let actual = parse_expr_with(&arena, "!blah"); assert_eq!(Ok(expected), actual); } @@ -1242,7 +1240,7 @@ mod test_parse { CalledVia::Space, ); let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op); - let actual = parse_with(&arena, "-whee 12 foo"); + let actual = parse_expr_with(&arena, "-whee 12 foo"); assert_eq!(Ok(expected), actual); } @@ -1278,7 +1276,7 @@ mod test_parse { CalledVia::Space, ); let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op); - let actual = parse_with(&arena, "!whee 12 foo"); + let actual = parse_expr_with(&arena, "!whee 12 foo"); assert_eq!(Ok(expected), actual); } @@ -1314,7 +1312,7 @@ mod test_parse { CalledVia::Space, ))); let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op); - let actual = parse_with(&arena, "-(whee 12 foo)"); + let actual = parse_expr_with(&arena, "-(whee 12 foo)"); assert_eq!(Ok(expected), actual); } @@ -1350,7 +1348,7 @@ mod test_parse { CalledVia::Space, ))); let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op); - let actual = parse_with(&arena, "!(whee 12 foo)"); + let actual = parse_expr_with(&arena, "!(whee 12 foo)"); assert_eq!(Ok(expected), actual); } @@ -1377,7 +1375,7 @@ mod test_parse { args, CalledVia::Space, ); - let actual = parse_with(&arena, "whee 12 -foo"); + let actual = parse_expr_with(&arena, "whee 12 -foo"); assert_eq!(Ok(expected), actual); } @@ -1394,7 +1392,7 @@ mod test_parse { let access = Access(arena.alloc(var), "field"); let loc_access = Located::new(0, 0, 1, 11, access); let expected = UnaryOp(arena.alloc(loc_access), loc_op); - let actual = parse_with(&arena, "-rec1.field"); + let actual = parse_expr_with(&arena, "-rec1.field"); assert_eq!(Ok(expected), actual); } @@ -1407,7 +1405,7 @@ mod test_parse { let pattern = Located::new(0, 0, 1, 2, Identifier("a")); let patterns = &[pattern]; let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42")))); - let actual = parse_with(&arena, "\\a -> 42"); + let actual = parse_expr_with(&arena, "\\a -> 42"); assert_eq!(Ok(expected), actual); } @@ -1418,7 +1416,7 @@ mod test_parse { let pattern = Located::new(0, 0, 1, 2, Underscore); let patterns = &[pattern]; let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42")))); - let actual = parse_with(&arena, "\\_ -> 42"); + let actual = parse_expr_with(&arena, "\\_ -> 42"); assert_eq!(Ok(expected), actual); } @@ -1429,7 +1427,7 @@ mod test_parse { // underscore in an argument name, it would parse as three arguments // (and would ignore the underscore as if it had been blank space). let arena = Bump::new(); - let actual = parse_with(&arena, "\\the_answer -> 42"); + let actual = parse_expr_with(&arena, "\\the_answer -> 42"); assert_eq!(Ok(MalformedClosure), actual); } @@ -1441,7 +1439,7 @@ mod test_parse { let arg2 = Located::new(0, 0, 4, 5, Identifier("b")); let patterns = &[arg1, arg2]; let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 9, 11, Num("42")))); - let actual = parse_with(&arena, "\\a, b -> 42"); + let actual = parse_expr_with(&arena, "\\a, b -> 42"); assert_eq!(Ok(expected), actual); } @@ -1457,7 +1455,7 @@ mod test_parse { arena.alloc(patterns), arena.alloc(Located::new(0, 0, 12, 14, Num("42"))), ); - let actual = parse_with(&arena, "\\a, b, c -> 42"); + let actual = parse_expr_with(&arena, "\\a, b, c -> 42"); assert_eq!(Ok(expected), actual); } @@ -1472,7 +1470,7 @@ mod test_parse { arena.alloc(patterns), arena.alloc(Located::new(0, 0, 9, 11, Num("42"))), ); - let actual = parse_with(&arena, "\\_, _ -> 42"); + let actual = parse_expr_with(&arena, "\\_, _ -> 42"); assert_eq!(Ok(expected), actual); } @@ -2040,7 +2038,7 @@ mod test_parse { }; let loc_cond = Located::new(0, 0, 5, 6, var); let expected = Expr::When(arena.alloc(loc_cond), branches); - let actual = parse_with( + let actual = parse_expr_with( &arena, indoc!( r#" @@ -2084,7 +2082,7 @@ mod test_parse { }; let loc_cond = Located::new(0, 0, 5, 6, var); let expected = Expr::When(arena.alloc(loc_cond), branches); - let actual = parse_with( + let actual = parse_expr_with( &arena, indoc!( r#" @@ -2133,7 +2131,7 @@ mod test_parse { }; let loc_cond = Located::new(0, 0, 5, 6, var); let expected = Expr::When(arena.alloc(loc_cond), branches); - let actual = parse_with( + let actual = parse_expr_with( &arena, indoc!( r#" @@ -2183,7 +2181,7 @@ mod test_parse { }; let loc_cond = Located::new(0, 0, 5, 6, var); let expected = Expr::When(arena.alloc(loc_cond), branches); - let actual = parse_with( + let actual = parse_expr_with( &arena, indoc!( r#" @@ -2490,7 +2488,7 @@ mod test_parse { }; let loc_cond = Located::new(0, 0, 5, 6, var); let expected = Expr::When(arena.alloc(loc_cond), branches); - let actual = parse_with( + let actual = parse_expr_with( &arena, indoc!( r#" @@ -2535,7 +2533,7 @@ mod test_parse { }; let loc_cond = Located::new(0, 0, 5, 6, var); let expected = Expr::When(arena.alloc(loc_cond), branches); - let actual = parse_with( + let actual = parse_expr_with( &arena, indoc!( r#"