mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 20:28:02 +00:00
Report errors without mono when roc foo.roc
has errors
This commit is contained in:
parent
b809d6d452
commit
7c6d811769
4 changed files with 146 additions and 74 deletions
|
@ -5,7 +5,10 @@ use roc_build::{
|
|||
};
|
||||
use roc_builtins::bitcode;
|
||||
use roc_collections::VecMap;
|
||||
use roc_load::{EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadingProblem, Threading};
|
||||
use roc_load::{
|
||||
EntryPoint, ExecutionMode, Expectations, LoadConfig, LoadMonomorphizedError, LoadedModule,
|
||||
LoadingProblem, Threading,
|
||||
};
|
||||
use roc_module::symbol::{Interns, ModuleId};
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_reporting::report::RenderTarget;
|
||||
|
@ -42,6 +45,15 @@ pub enum BuildOrdering {
|
|||
AlwaysBuild,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BuildFileError<'a> {
|
||||
LoadingProblem(LoadingProblem<'a>),
|
||||
ErrorModule {
|
||||
module: LoadedModule,
|
||||
total_time: Duration,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_file<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -56,7 +68,7 @@ pub fn build_file<'a>(
|
|||
threading: Threading,
|
||||
wasm_dev_stack_bytes: Option<u32>,
|
||||
order: BuildOrdering,
|
||||
) -> Result<BuiltFile, LoadingProblem<'a>> {
|
||||
) -> Result<BuiltFile, BuildFileError<'a>> {
|
||||
let compilation_start = Instant::now();
|
||||
let target_info = TargetInfo::from(target);
|
||||
|
||||
|
@ -75,12 +87,24 @@ pub fn build_file<'a>(
|
|||
threading,
|
||||
exec_mode,
|
||||
};
|
||||
let loaded = roc_load::load_and_monomorphize(
|
||||
let load_result = roc_load::load_and_monomorphize(
|
||||
arena,
|
||||
app_module_path.clone(),
|
||||
subs_by_module,
|
||||
load_config,
|
||||
)?;
|
||||
);
|
||||
let loaded = match load_result {
|
||||
Ok(loaded) => loaded,
|
||||
Err(LoadMonomorphizedError::LoadingProblem(problem)) => {
|
||||
return Err(BuildFileError::LoadingProblem(problem))
|
||||
}
|
||||
Err(LoadMonomorphizedError::ErrorModule(module)) => {
|
||||
return Err(BuildFileError::ErrorModule {
|
||||
module,
|
||||
total_time: compilation_start.elapsed(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
use target_lexicon::Architecture;
|
||||
let emit_wasm = matches!(target.architecture, Architecture::Wasm32);
|
||||
|
@ -172,9 +196,7 @@ pub fn build_file<'a>(
|
|||
.prefix("roc_app")
|
||||
.suffix(&format!(".{}", app_extension))
|
||||
.tempfile()
|
||||
.map_err(|err| {
|
||||
todo!("TODO Gracefully handle tempfile creation error {:?}", err);
|
||||
})?;
|
||||
.map_err(|err| todo!("TODO Gracefully handle tempfile creation error {:?}", err))?;
|
||||
let app_o_file = app_o_file.path();
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
|
@ -340,12 +362,12 @@ pub fn build_file<'a>(
|
|||
link_type
|
||||
)
|
||||
.map_err(|_| {
|
||||
todo!("gracefully handle `ld` failing to spawn.");
|
||||
todo!("gracefully handle `ld` failing to spawn.")
|
||||
})?;
|
||||
|
||||
let exit_status = child.wait().map_err(|_| {
|
||||
todo!("gracefully handle error after `ld` spawned");
|
||||
})?;
|
||||
let exit_status = child
|
||||
.wait()
|
||||
.map_err(|_| todo!("gracefully handle error after `ld` spawned"))?;
|
||||
|
||||
if exit_status.success() {
|
||||
problems
|
||||
|
|
|
@ -33,7 +33,7 @@ pub mod build;
|
|||
mod format;
|
||||
pub use format::format;
|
||||
|
||||
use crate::build::BuildOrdering;
|
||||
use crate::build::{BuildFileError, BuildOrdering};
|
||||
|
||||
const DEFAULT_ROC_FILENAME: &str = "main.roc";
|
||||
|
||||
|
@ -640,55 +640,13 @@ pub fn build(
|
|||
x
|
||||
}
|
||||
BuildAndRunIfNoErrors => {
|
||||
if problems.errors == 0 {
|
||||
if problems.warnings > 0 {
|
||||
println!(
|
||||
"\x1B[32m0\x1B[39m errors and \x1B[33m{}\x1B[39m {} found in {} ms.\n\nRunning program…\n\n\x1B[36m{}\x1B[39m",
|
||||
problems.warnings,
|
||||
if problems.warnings == 1 {
|
||||
"warning"
|
||||
} else {
|
||||
"warnings"
|
||||
},
|
||||
total_time.as_millis(),
|
||||
"─".repeat(80)
|
||||
);
|
||||
}
|
||||
|
||||
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||
|
||||
let mut bytes = std::fs::read(&binary_path).unwrap();
|
||||
|
||||
let x = roc_run(
|
||||
arena,
|
||||
opt_level,
|
||||
triple,
|
||||
args,
|
||||
&mut bytes,
|
||||
expectations,
|
||||
interns,
|
||||
);
|
||||
std::mem::forget(bytes);
|
||||
x
|
||||
} else {
|
||||
let mut output = format!(
|
||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with \x1B[32mroc run",
|
||||
if problems.errors == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
problems.errors,
|
||||
if problems.errors == 1 {
|
||||
"error"
|
||||
} else {
|
||||
"errors"
|
||||
},
|
||||
if problems.warnings == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
debug_assert!(
|
||||
problems.errors == 0,
|
||||
"if there are errors, they should have been returned as an error variant"
|
||||
);
|
||||
if problems.warnings > 0 {
|
||||
println!(
|
||||
"\x1B[32m0\x1B[39m errors and \x1B[33m{}\x1B[39m {} found in {} ms.\n\nRunning program…\n\n\x1B[36m{}\x1B[39m",
|
||||
problems.warnings,
|
||||
if problems.warnings == 1 {
|
||||
"warning"
|
||||
|
@ -696,22 +654,74 @@ pub fn build(
|
|||
"warnings"
|
||||
},
|
||||
total_time.as_millis(),
|
||||
"─".repeat(80)
|
||||
);
|
||||
// If you're running "main.roc" then you can just do `roc run`
|
||||
// to re-run the program.
|
||||
if filename != DEFAULT_ROC_FILENAME {
|
||||
output.push(' ');
|
||||
output.push_str(&filename.to_string_lossy());
|
||||
}
|
||||
|
||||
println!("{}\x1B[39m", output);
|
||||
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
|
||||
let args = matches.values_of_os(ARGS_FOR_APP).unwrap_or_default();
|
||||
|
||||
let mut bytes = std::fs::read(&binary_path).unwrap();
|
||||
|
||||
let x = roc_run(
|
||||
arena,
|
||||
opt_level,
|
||||
triple,
|
||||
args,
|
||||
&mut bytes,
|
||||
expectations,
|
||||
interns,
|
||||
);
|
||||
std::mem::forget(bytes);
|
||||
x
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(LoadingProblem::FormattedReport(report)) => {
|
||||
Err(BuildFileError::ErrorModule {
|
||||
mut module,
|
||||
total_time,
|
||||
}) => {
|
||||
debug_assert!(module.total_problems() > 0);
|
||||
|
||||
let problems = roc_build::program::report_problems_typechecked(&mut module);
|
||||
|
||||
let mut output = format!(
|
||||
"\x1B[{}m{}\x1B[39m {} and \x1B[{}m{}\x1B[39m {} found in {} ms.\n\nYou can run the program anyway with \x1B[32mroc run",
|
||||
if problems.errors == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
problems.errors,
|
||||
if problems.errors == 1 {
|
||||
"error"
|
||||
} else {
|
||||
"errors"
|
||||
},
|
||||
if problems.warnings == 0 {
|
||||
32 // green
|
||||
} else {
|
||||
33 // yellow
|
||||
},
|
||||
problems.warnings,
|
||||
if problems.warnings == 1 {
|
||||
"warning"
|
||||
} else {
|
||||
"warnings"
|
||||
},
|
||||
total_time.as_millis(),
|
||||
);
|
||||
// If you're running "main.roc" then you can just do `roc run`
|
||||
// to re-run the program.
|
||||
if filename != DEFAULT_ROC_FILENAME {
|
||||
output.push(' ');
|
||||
output.push_str(&filename.to_string_lossy());
|
||||
}
|
||||
|
||||
println!("{}\x1B[39m", output);
|
||||
|
||||
Ok(problems.exit_code())
|
||||
}
|
||||
Err(BuildFileError::LoadingProblem(LoadingProblem::FormattedReport(report))) => {
|
||||
print!("{}", report);
|
||||
|
||||
Ok(1)
|
||||
|
|
|
@ -54,6 +54,28 @@ pub fn load_single_threaded<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoadMonomorphizedError<'a> {
|
||||
LoadingProblem(LoadingProblem<'a>),
|
||||
/// Errors in the module that should be reported, without compiling the executable.
|
||||
/// Relevant in check-and-then-build mode.
|
||||
ErrorModule(LoadedModule),
|
||||
}
|
||||
|
||||
impl<'a> From<LoadingProblem<'a>> for LoadMonomorphizedError<'a> {
|
||||
fn from(problem: LoadingProblem<'a>) -> Self {
|
||||
Self::LoadingProblem(problem)
|
||||
}
|
||||
}
|
||||
|
||||
// HACK only relevant because of some uses of `map_err` that decay into this error, but call `todo` -
|
||||
// rustc seems to be unhappy with that.
|
||||
impl<'a> From<()> for LoadMonomorphizedError<'a> {
|
||||
fn from(_: ()) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn load_and_monomorphize_from_str<'a>(
|
||||
arena: &'a Bump,
|
||||
|
@ -78,14 +100,14 @@ pub fn load_and_monomorphize(
|
|||
filename: PathBuf,
|
||||
exposed_types: ExposedByModule,
|
||||
load_config: LoadConfig,
|
||||
) -> Result<MonomorphizedModule<'_>, LoadingProblem<'_>> {
|
||||
) -> Result<MonomorphizedModule<'_>, LoadMonomorphizedError<'_>> {
|
||||
use LoadResult::*;
|
||||
|
||||
let load_start = LoadStart::from_path(arena, filename, load_config.render)?;
|
||||
|
||||
match load(arena, load_start, exposed_types, load_config)? {
|
||||
Monomorphized(module) => Ok(module),
|
||||
TypeChecked(_) => unreachable!(""),
|
||||
TypeChecked(module) => Err(LoadMonomorphizedError::ErrorModule(module)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2408,6 +2408,24 @@ fn update<'a>(
|
|||
|
||||
state.timings.insert(module_id, module_timing);
|
||||
|
||||
if matches!(state.exec_mode, ExecutionMode::ExecutableIfCheck) {
|
||||
// We there may outstanding modules in the typecheked cache whose ident IDs
|
||||
// aren't registered; transfer all of their idents over to the state, since
|
||||
// we're now done and ready to report errors.
|
||||
for (
|
||||
module_id,
|
||||
TypeCheckedModule {
|
||||
ident_ids,
|
||||
module_timing,
|
||||
..
|
||||
},
|
||||
) in state.module_cache.typechecked.drain()
|
||||
{
|
||||
state.constrained_ident_ids.insert(module_id, ident_ids);
|
||||
state.timings.insert(module_id, module_timing);
|
||||
}
|
||||
}
|
||||
|
||||
let documentation = {
|
||||
let mut empty = MutMap::default();
|
||||
std::mem::swap(&mut empty, &mut state.module_cache.documentation);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue