diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 978f42d4602..10eb20efb01 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -72,6 +72,7 @@ pub const FLAG_STDOUT: &str = "stdout"; pub const FLAG_WASM_STACK_SIZE_KB: &str = "wasm-stack-size-kb"; pub const FLAG_OUTPUT: &str = "output"; pub const FLAG_FUZZ: &str = "fuzz"; +pub const FLAG_IGNORE_BUILD_ERRORS: &str = "ignore-build-errors"; pub const ROC_FILE: &str = "ROC_FILE"; pub const ROC_DIR: &str = "ROC_DIR"; pub const GLUE_DIR: &str = "GLUE_DIR"; @@ -149,6 +150,12 @@ pub fn build_app() -> Command { .action(ArgAction::SetTrue) .required(false); + let flag_ignore_build_errors = Arg::new(FLAG_IGNORE_BUILD_ERRORS) + .long(FLAG_IGNORE_BUILD_ERRORS) + .help("Run tests even if there were build errors") + .action(ArgAction::SetTrue) + .required(false); + let roc_file_to_run = Arg::new(ROC_FILE) .help("The .roc file of an app to run") .value_parser(value_parser!(PathBuf)) @@ -237,6 +244,7 @@ pub fn build_app() -> Command { .arg(flag_linker.clone()) .arg(flag_prebuilt.clone()) .arg(flag_fuzz.clone()) + .arg(flag_ignore_build_errors.clone()) .arg( Arg::new(FLAG_VERBOSE) .long(FLAG_VERBOSE) @@ -496,6 +504,12 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result { // TODO may need to determine this dynamically based on dev builds. let function_kind = FunctionKind::LambdaSet; + let exec_mode = if matches.get_flag(FLAG_IGNORE_BUILD_ERRORS) { + ExecutionMode::TestIgnoreErrors + } else { + ExecutionMode::Test + }; + // Step 1: compile the app and generate the .o file let load_config = LoadConfig { target, @@ -504,7 +518,7 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result { render: roc_reporting::report::RenderTarget::ColorTerminal, palette: roc_reporting::report::DEFAULT_PALETTE, threading, - exec_mode: ExecutionMode::Test, + exec_mode, }; let load_result = roc_load::load_and_monomorphize( arena, @@ -541,10 +555,12 @@ pub fn test(matches: &ArgMatches, target: Target) -> io::Result { // Print warnings before running tests. { - debug_assert_eq!( - problems.errors, 0, - "if there were errors, we would have already exited." - ); + if matches!(exec_mode, ExecutionMode::Test) { + debug_assert_eq!( + problems.errors, 0, + "if there were errors, we would have already exited." + ); + } if problems.warnings > 0 { problems.print_error_warning_count(start_time.elapsed()); println!(".\n\nRunning tests…\n\n\x1B[36m{}\x1B[39m", "─".repeat(80)); diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index e2faf3c25d0..5439039d69e 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -647,6 +647,20 @@ mod cli_run { ); } + #[test] + fn expect_ignore_build_errors() { + test_roc_expect( + "crates/cli/tests/expects", + "IgnoreBuildErrors.roc", + &["--ignore-build-errors"], + indoc!( + r#" + 1 failed and 1 passed in ms. + "# + ), + ) + } + #[test] #[cfg_attr(windows, ignore)] fn transitive_expects() { diff --git a/crates/cli/tests/expects/IgnoreBuildErrors.roc b/crates/cli/tests/expects/IgnoreBuildErrors.roc new file mode 100644 index 00000000000..8f0136d7a82 --- /dev/null +++ b/crates/cli/tests/expects/IgnoreBuildErrors.roc @@ -0,0 +1,12 @@ +interface IgnoreBuildErrors + exposes [] + imports [] + +x = 1 + +expect x == 1 + +foo = \x -> x + 1 + +expect foo 2 == 3 + diff --git a/crates/compiler/build/src/program.rs b/crates/compiler/build/src/program.rs index 767ac6c4fc4..79b5bd02211 100644 --- a/crates/compiler/build/src/program.rs +++ b/crates/compiler/build/src/program.rs @@ -692,8 +692,8 @@ pub fn standard_load_config( threading: Threading, ) -> LoadConfig { let exec_mode = match order { - BuildOrdering::BuildIfChecks => ExecutionMode::ExecutableIfCheck, - BuildOrdering::AlwaysBuild => ExecutionMode::Executable, + BuildOrdering::BuildIfChecks => ExecutionMode::Executable, + BuildOrdering::AlwaysBuild => ExecutionMode::ExecutableIgnoreErrors, }; // UNSTABLE(lambda-erasure) diff --git a/crates/compiler/load_internal/src/file.rs b/crates/compiler/load_internal/src/file.rs index d11a0031387..f18b34f210e 100644 --- a/crates/compiler/load_internal/src/file.rs +++ b/crates/compiler/load_internal/src/file.rs @@ -115,11 +115,11 @@ pub struct LoadConfig { #[derive(Debug, Clone, Copy)] pub enum ExecutionMode { Check, + ExecutableIgnoreErrors, + /// Like [`ExecutionMode::ExecutableIgnoreErrors`], but stops in the presence of type errors. Executable, - /// Like [`ExecutionMode::Executable`], but stops in the presence of type errors. - ExecutableIfCheck, - /// Test is like [`ExecutionMode::ExecutableIfCheck`], but rather than producing a proper - /// executable, run tests. + TestIgnoreErrors, + /// Like [`ExecutionMode::TestIgnoreErrors`], but stops in the presence of type errors. Test, } @@ -128,13 +128,17 @@ impl ExecutionMode { use ExecutionMode::*; match self { - Executable => Phase::MakeSpecializations, - Check | ExecutableIfCheck | Test => Phase::SolveTypes, + ExecutableIgnoreErrors | TestIgnoreErrors => Phase::MakeSpecializations, + Check | Executable | Test => Phase::SolveTypes, } } fn build_if_checks(&self) -> bool { - matches!(self, Self::ExecutableIfCheck | Self::Test) + matches!(self, Self::Executable | Self::Test) + } + + fn build_tests(&self) -> bool { + matches!(self, Self::Test | Self::TestIgnoreErrors) } } @@ -397,8 +401,7 @@ fn start_phase<'a>( let derived_module = SharedDerivedModule::clone(&state.derived_module); - let build_expects = - matches!(state.exec_mode, ExecutionMode::Test) && expectations.is_some(); + let build_expects = state.exec_mode.build_tests() && expectations.is_some(); BuildTask::BuildPendingSpecializations { layout_cache, @@ -2638,7 +2641,7 @@ fn update<'a>( let add_to_host_exposed = is_host_exposed && // During testing, we don't need to expose anything to the host. - !matches!(state.exec_mode, ExecutionMode::Test); + !state.exec_mode.build_tests(); if add_to_host_exposed { state.exposed_to_host.top_level_values.extend( @@ -3215,8 +3218,8 @@ fn finish_specialization<'a>( let entry_point = { let interns: &mut Interns = &mut interns; match state.exec_mode { - ExecutionMode::Test => Ok(EntryPoint::Test), - ExecutionMode::Executable | ExecutionMode::ExecutableIfCheck => { + ExecutionMode::Test | ExecutionMode::TestIgnoreErrors => Ok(EntryPoint::Test), + ExecutionMode::ExecutableIgnoreErrors | ExecutionMode::Executable => { use PlatformPath::*; let platform_path = match &state.platform_path { diff --git a/crates/compiler/test_gen/src/helpers/llvm.rs b/crates/compiler/test_gen/src/helpers/llvm.rs index feb543f6ec4..002b5bae848 100644 --- a/crates/compiler/test_gen/src/helpers/llvm.rs +++ b/crates/compiler/test_gen/src/helpers/llvm.rs @@ -75,7 +75,7 @@ fn create_llvm_module<'a>( render: RenderTarget::ColorTerminal, palette: DEFAULT_PALETTE, threading: Threading::Single, - exec_mode: ExecutionMode::Executable, + exec_mode: ExecutionMode::ExecutableIgnoreErrors, }; let loaded = roc_load::load_and_monomorphize_from_str( arena, diff --git a/crates/compiler/test_mono/src/tests.rs b/crates/compiler/test_mono/src/tests.rs index 736a2710ebc..be9b77135a0 100644 --- a/crates/compiler/test_mono/src/tests.rs +++ b/crates/compiler/test_mono/src/tests.rs @@ -82,7 +82,7 @@ fn compiles_to_ir(test_name: &str, src: &str, mode: &str, allow_type_errors: boo use std::path::PathBuf; let exec_mode = match mode { - "exec" => ExecutionMode::Executable, + "exec" => ExecutionMode::ExecutableIgnoreErrors, "test" => ExecutionMode::Test, _ => panic!("Invalid test_mono exec mode {mode}"), }; diff --git a/crates/compiler/uitest/src/mono.rs b/crates/compiler/uitest/src/mono.rs index 9920448d28f..94d9aeb6d51 100644 --- a/crates/compiler/uitest/src/mono.rs +++ b/crates/compiler/uitest/src/mono.rs @@ -29,7 +29,7 @@ pub(crate) fn write_compiled_ir<'a>( use roc_packaging::cache::RocCacheDir; use std::path::PathBuf; - let exec_mode = ExecutionMode::Executable; + let exec_mode = ExecutionMode::ExecutableIgnoreErrors; let arena = &Bump::new(); diff --git a/crates/linker/src/lib.rs b/crates/linker/src/lib.rs index 7378c8662fe..6224eafe263 100644 --- a/crates/linker/src/lib.rs +++ b/crates/linker/src/lib.rs @@ -86,7 +86,7 @@ pub fn generate_stub_lib( render: RenderTarget::Generic, palette: DEFAULT_PALETTE, threading: Threading::AllAvailable, - exec_mode: ExecutionMode::Executable, + exec_mode: ExecutionMode::ExecutableIgnoreErrors, }, ) .unwrap_or_else(|problem| todo!("{:?}", problem)); diff --git a/crates/repl_eval/src/gen.rs b/crates/repl_eval/src/gen.rs index 8fbf00fe920..b1e2e56c474 100644 --- a/crates/repl_eval/src/gen.rs +++ b/crates/repl_eval/src/gen.rs @@ -67,7 +67,7 @@ pub fn compile_to_mono<'a, 'i, I: Iterator>( render: roc_reporting::report::RenderTarget::ColorTerminal, palette, threading: Threading::Single, - exec_mode: ExecutionMode::Executable, + exec_mode: ExecutionMode::ExecutableIgnoreErrors, }, );