diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index bcb43bc65d..f343091efd 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -240,8 +240,8 @@ pub fn canonicalize_module_defs<'a>( // exposed_symbols and added to exposed_vars_by_symbol. If any were // not, that means they were declared as exposed but there was // no actual declaration with that name! - if !exposed_symbols.is_empty() { - panic!("TODO gracefully handle invalid `exposes` entry (or entries) which had no corresponding definition: {:?}", exposed_symbols); + for symbol in exposed_symbols { + env.problem(Problem::ExposedButNotDefined(symbol)); } // Incorporate any remaining output.lookups entries into references. diff --git a/compiler/problem/src/can.rs b/compiler/problem/src/can.rs index 0a9ec9a263..592a941446 100644 --- a/compiler/problem/src/can.rs +++ b/compiler/problem/src/can.rs @@ -12,6 +12,7 @@ use roc_region::all::{Located, Region}; pub enum Problem { UnusedDef(Symbol, Region), UnusedImport(ModuleId, Region), + ExposedButNotDefined(Symbol), /// First symbol is the name of the closure with that argument /// Second symbol is the name of the argument that is unused UnusedArgument(Symbol, Symbol, Region), diff --git a/compiler/reporting/src/error/canonicalize.rs b/compiler/reporting/src/error/canonicalize.rs index a5b2925421..a119604f5f 100644 --- a/compiler/reporting/src/error/canonicalize.rs +++ b/compiler/reporting/src/error/canonicalize.rs @@ -37,6 +37,19 @@ pub fn can_problem<'b>( alloc.module(module_id), alloc.reflow(" isn't used, you don't need to import it."), ]), + Problem::ExposedButNotDefined(symbol) => { + alloc.stack(vec![ + alloc + .symbol_unqualified(symbol) + .append(alloc.reflow(" is listed as exposed, but it isn't defined in this module.")), + alloc + .reflow("You can fix this by adding a definition for ") + .append(alloc.symbol_unqualified(symbol)) + .append(alloc.reflow(", or by removing it from ")) + .append(alloc.keyword("exposes")) + .append(alloc.reflow(".")) + ]) + } Problem::UnusedArgument(closure_symbol, argument_symbol, region) => { let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";